Multiple Streams

<< Click to Display Table of Contents >>

Navigation:  Migration Guide for the Acquisition Stacks > CVB++ >

Multiple Streams

This example expands the use case of the single data stream example to multi stream devices, for example devices that potentially deliver more than just one data stream with potentially different data types per stream and potentially asynchronously delivered data. An example for such a device is a multispectral camera providing multiple streams transferring different spectral information.

 

Code Examples

3rd generation stack

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

auto devices = DeviceFactory::Discover(DiscoverFlags::IgnoreVins);

auto device = DeviceFactory::Open<GenICamDevice>(devices[0].AccessToken(),

 AcquisitionStack::GenTL);

std::vector<ImageStreamPtr> streams;

std::generate_n(std::back_inserter(streams), device->StreamCount(),

 [&device = *device, i = 0]() mutable

 {

  return device.Stream<ImageStream>(i++);

 });

for (auto& stream : streams)

stream->Start();

for (int i = 0; i < 10; ++i)

{

std::vector<Driver::WaitResultTuple<MultiPartImage>> images;

for (auto& stream : streams)

  images.push_back(stream->Wait());

}

for (auto& stream : streams)

stream->Abort();

(5) Where previously only one data stream was accessed, now all available streams on the device are accessed. Therefore, the index based stream fetching is used. The number of available streams gets queried with the stream count function on the device - the queried streams are collected as image stream type. This enables parallel streaming over all streams.

(10) The approach to starting the acquisition and acquiring the data basically remains the same in the multi stream case – the only difference being that it is now necessary to start the acquisition in a loop for all streams.

(16) The multi stream wait also requires a separate processing of the streams. For simplicity, in this code snippet the Wait() function is called sequentially for all streams - a more reasonable real-life implementation would of course be to work with multiple threads to facilitate true asynchronous processing of the Wait() calls.

(18) Again, the only difference to the single stream example is that the stop/abort function needs to be called on each of the streams. Following the reverse order compared to starting the stream, now the stream control is stopped before the acquisition engines for each stream.

 

When working in a multi stream scenario, it might make sense to split the stream start into its two separate steps (engine start and device start). This is because the sum of both steps is significantly longer than the DeviceStart step alone - which can lead to notable latencies between the streams: When working with Stream::Start() the first stream might already have acquired some three to five images before the next one is up and running. Coding it as follows will drastically reduce this effect:

3rd generation stack

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

auto devices = DeviceFactory::Discover(DiscoverFlags::IgnoreVins);

auto device = DeviceFactory::Open<GenICamDevice>(devices[0].AccessToken(),

 AcquisitionStack::GenTL);;

auto stream = device->Stream<ImageStream>();

std::vector<ImageStreamPtr> streams;

std::generate_n(std::back_inserter(streams), device->StreamCount(),

 [&device = *device, i = 0]() mutable

 {

  return device.Stream<ImageStream>(i++);

 });

for (auto& stream : streams)

stream->EngineStart();

for (auto& stream : streams)

stream->DeviceStart();

// your acquisition here  

for (auto& stream : streams)

stream->DeviceAbort();

for (auto& stream : streams)

stream->EngineAbort();

(12) The acquisition engine on all streams is started.

(14) The device acquisition is started in all streams.

(17) The device acquisition has to be either stopped or aborted. Following the reverse order compared to starting the stream: now the stream control needs to be stopped before the acquisition engine for each stream.

(19) Finally the acquisition engine is stopped / aborted on all streams.

 

Summary

When using multi stream devices, the sequence of actions necessary on a single stream device simply needs to be extended from 1 stream to N streams. This means that start, stop and wait for on the stream need to be called in a loop over all the required streams. When working with asynchronous streams it should be considered to put the Wait() calls into dedicated threads to make sure that the streams don't stall each other.