ImFusion C++ SDK 4.4.0
Stream Groups

Bundling Multiple Streams Together. More...

Collaboration diagram for Stream Groups:

Detailed Description

Bundling Multiple Streams Together.

Stream groups provide a mechanism to bundle multiple related streams into a single logical unit, enabling synchronized data handling and coordinated stream control across multiple data sources.

Concept

A StreamGroup consists of a proxy stream and an arbitrary number of substreams. The proxy stream acts as the representative and controller for all substreams, managing their lifecycle and coordinating their operation. This architecture is particularly useful when dealing with multi-sensor devices or systems that need to present multiple synchronized data streams.

Key characteristics:

  • The proxy stream owns and controls all substreams
  • Substreams synchronize their state with the proxy stream
  • The stream group appears as a CompoundData structure in the data model, with the proxy stream as the representative and substreams as children
  • Stream data from all streams can be accessed both individually and as a compound unit

Architecture

The stream group architecture consists of several key components:

Proxy Stream

The proxy stream can be any class deriving from Stream (e.g., ImageStream, TrackingStream) or Stream itself. It serves as the main entry point for controlling the entire stream group. The proxy stream may represent the primary data source or serve as a purely organizational container. When implemented as a concrete stream type, the proxy stream can emit its own StreamData alongside the substream data.

Substreams

Substreams are owned by the stream group and implement the SubstreamBase interface. They appear as child nodes in the data model, bundled under the proxy stream. While grouped together, they remain accessible individually or as a subset for processing. Substreams are tightly bound to the proxy stream lifecycle. There are two main types of substreams:

  • Substream template class: For dummy substreams that are fully controlled by the proxy stream. These substreams do not run their own doWork() job and rely entirely on the proxy stream for data emission. Available specializations include ImageSubstream and TrackingSubstream.
  • SubstreamWrapper class: For wrapping existing Stream objects into substreams. This allows bundling of independent streams that may have their own implementation logic.

Compound Stream Data

The CompoundStreamData class bundles StreamData from all streams in the group. It contains:

  • Optional StreamData from the proxy stream (nullptr for abstract proxy streams)
  • A vector of StreamData from any subset of the substreams

The stream group emits CompoundStreamData via signalNewStreamGroupData for processing all streams together, while individual stream data is also emitted through each stream's signalStreamData for individual access. This dual emission strategy allows receivers to work with the bundled stream group as a whole or access individual substreams as needed.

Implementation

Basic Usage

To create a custom stream group, inherit from both a Stream class and the StreamGroup template using CRTP: /

// Stream group example using dummy image substreams
class MyStreamGroup : public TrackingStream, public StreamGroup<MyStreamGroup>
{
public:
MyStreamGroup()
: TrackingStream("My Tracking Stream")
{
// Add image substreams for camera data
addSubstream(std::make_unique<ImageSubstream>(this, "Left Camera"));
addSubstream(std::make_unique<ImageSubstream>(this, "Right Camera"));
}
protected:
std::optional<WorkContinuation> doWork() override
{
// Compute next interation time based on desired framerate
double fps = 30.0;
auto nextIterationTime = std::chrono::system_clock::now() + std::chrono::microseconds(static_cast<int>(1000000.0 / fps));
// Acquire data from all sources
std::vector<TrackingInstrument> trackingData = acquireTrackingData();
SharedImageSet leftImageData = acquireLeftImage();
auto rightImageData = acquireRightImage();
// Create stream data for all streams
auto proxyData = std::make_shared<TrackingStreamData>(this, trackingData);
auto leftData = std::make_shared<ImageStreamData>(substreams()[0], std::move(leftImageData));
auto rightData = std::make_shared<ImageStreamData>(substreams()[1], std::move(rightImageData));
// Emit all data together
auto substreamStreamData = std::vector<std::shared_ptr<const StreamData>>{leftData, rightData};
auto compound = std::make_shared<CompoundStreamData>(proxyData, std::move(substreamStreamData));
emitMainAndSubSignals(compound);
return WorkContinuation{nextIterationTime};
}
};

Wrapping Existing Streams

To bundle multiple independent streams, use SubstreamWrapper:

class BundledStreamGroup : public Stream, public StreamGroup<BundledStreamGroup>
{
public:
BundledStreamGroup(std::unique_ptr<Stream> stream1, std::unique_ptr<Stream> stream2)
: Stream("Bundled Streams")
{
addSubstream(std::make_unique<SubstreamWrapper>(std::move(stream1), this));
addSubstream(std::make_unique<SubstreamWrapper>(std::move(stream2), this));
}
// Implement required Stream methods...
};
T make_unique(T... args)

Dynamic Management

Substreams can be added or removed at any time after construction:

// Adding a substream
addSubstream(std::make_unique<ImageSubstream>(this, "New Camera"));
// Removing a substream
removeSubstream(substreams()[0]); // Thread-safe, removal happens on main thread

Thread Safety

Stream groups are designed with thread safety in mind:

  • The substreams() method is thread-safe and returns a snapshot of current substreams
  • Substreams are guaranteed not to be deleted during a doWork() execution until emitMainAndSubSignals() completes
  • Removal of substreams is automatically marshaled to the main thread to ensure data model consistency

Example of safe usage in doWork():

{
auto streams = substreams(); // Get snapshot
// Safe to iterate even if another thread removes a substream
for (auto& s : streams)
{
// Process stream data...
}
CompoundStreamData compound(proxyData, substreamData);
emitMainAndSubSignals(compound); // Actual deletion of removed substreams happens here
return WorkContinuation{nextIterationTime};
}

Use Cases

Stream groups are particularly useful for:

  • ** Multi-camera systems : Bundle multiple camera streams with optional tracking data
  • ** Stereo imaging **: Combine left and right camera streams (see StereoImageStream)
  • ** Optical tracking devices: Pair tracking data with camera streams for visualization
  • ** Multi-modal acquisition **: Synchronize different sensor types in a single device
  • ** Data synchronization **: Ensure temporally aligned data emission across multiple sources

Examples

The SDK provides several example implementations:

See also
StreamGroup, StreamGroupBase, Substream, SubstreamWrapper, CompoundStreamData

Classes

class  ImFusion::CompoundStreamData
 Container for StreamData from the individual Streams of a StreamGroup. More...
class  ImFusion::SubstreamBase
 Non-templated base class for substreams in a StreamGroup. More...
class  ImFusion::StreamGroupBase
 Non-templated base class for stream groups. More...
class  ImFusion::StreamGroup< Proxy >
 Templated stream group class for bundling multiple streams using the CRTP pattern. More...
class  ImFusion::Substream< BaseStream >
 Template class for creating dummy substreams fully controlled by the proxy stream. More...
class  ImFusion::TrackingSubstream
 Dummy tracking substream for use in StreamGroups. More...
class  ImFusion::SubstreamWrapper
 Generic wrapper to integrate an existing Stream into a StreamGroup as a substream. More...

Typedefs

using ImFusion::ImageSubstream = Substream<ImageStream>
 Dummy image substream for use in StreamGroups.

Typedef Documentation

◆ ImageSubstream

#include <ImFusion/Stream/StreamGroup.h>

Dummy image substream for use in StreamGroups.

ImageSubstream is a specialization of Substream for ImageStream, providing a lightweight image stream that is fully controlled by the proxy stream. Use this when the proxy stream handles image acquisition and you need to expose the image data as a separate stream.

See also
Substream, TrackingSubstream
Search Tab / S to search, Esc to close