# rs-multicam sample
## Overview
The multicam sample demonstrates the ability to use the SDK for streaming and rendering multiple devices simultaneously.
## Expected Output
The application opens and renders a mosaic view of all automatically-selected streams provided by the connected devices. (The selection is device-tailored and takes into account USB-type constrains).
Each tile displays an unique stream produced by a specific camera. The stream name appear at the top left.
In the following snapshot we use five Intel® RealSense™ devices to produce the mosaic: D435i, D415, SR300 and two T265 tracking cameras. Those are responsible to generate:
- Three Depth streams (D435i, D415, SR300)
- Three Color streams (D435i, D415, SR300)
- Two Pose streams (T265x2)
- Four Fisheye streams (T265x2)
- Six IMU streams (D435i, T265x2)
Alltogether the mosaic comprise of 18 simultaneous live feeds:
## Code Overview
As with any SDK application we include the Intel RealSense Cross Platform API:
```cpp
#include // Include RealSense Cross Platform API
```
In this example we will also use the auxiliary library of `example.hpp`:
```cpp
#include "example.hpp" // Include short list of convenience functions for rendering
```
`examples.hpp` lets us easily open a new window and prepare textures for rendering.
The first object we use is a `window`, that will be used to display the images from all the cameras.
```cpp
// Create a simple OpenGL window for rendering:
window app(1280, 960, "CPP Multi-Camera Example");
```
The `window` class resides in `example.hpp` and lets us easily open a new window and prepare textures for rendering.
Next, we define the objects to be used in the example.
```cpp
rs2::context ctx; // Create librealsense context for managing devices
std::map colorizers; // Declare map from device serial number to colorizer (utility class to convert depth data RGB colorspace)
std::vector pipelines;
```
The `rs2::context` encapsulates all of the devices and sensors, and provides some additional functionalities. We employ the `rs2::colorizer ` to convert depth data to RGB format.
In the example we use multiple `rs2::pipeline` objects, each controlling a lifetime of a single HW device. Similarly, we initialize a separate `rs2::colorizer` object for each device. We keep a mapping from the device's serial number to it's `rs2::colorizer` object, this way we'll be able to apply the correct `rs2::colorizer` to each frame.
The example's flow starts with listing and activating all the connected Intel® RealSense™ devices:
```cpp
// Start a streaming pipe per each connected device
for (auto&& dev : ctx.query_devices())
{
rs2::pipeline pipe(ctx);
rs2::config cfg;
cfg.enable_device(dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER));
pipe.start(cfg);
pipelines.emplace_back(pipe);
}
```
First, we allocate `rs2::pipeline` object per recognized device. Note that we share the `rs2::context` object between all `rs2::pipeline` instances.
```cpp
rs2::pipeline pipe(ctx);
```
To map the specific device to the newly-allocated pipeline we define `rs2::config` object, and assign it with the device's serial number.
Then we request `rs::pipeline` to start streaming and produce frames.
```cpp
pipe.start(cfg);
```
Since we do not specify explicit stream requests, each device is configured internally to run a set of predefined stream profiles recommended for that specific device.
After adding the device, we begin our main loop of the application:
```cpp
while (app)
```
Every application cycle we traverse the registered devices and retrieve all the available frames:
```cpp
// Collect the new frames from all the connected devices
std::vector new_frames;
for (auto &&pipe : pipelines)
{
rs2::frameset fs;
if (pipe.poll_for_frames(&fs))
{
for (rs2::frame& f : fs)
new_frames.emplace_back(f);
}
}
```
Each `rs::pipeline` produces a synchronized collection of frames for all streams configured for its allocated device. These are contained in `rs2::frameset` object.
The `rs2::frameset` itself is an wrapper for a `composite_frame`, which can holds more than a single type of frame.
To minimize UI impact we're using non-blocking frames polling method:
```cpp
if (pipe.poll_for_frames(&fs))
```
In order to simplify the presentation, we split those `rs2::frameset` containers into separate frames and store them with a standard C++ container for later use:
```cpp
for (rs2::frame& f : fs)
new_frames.emplace_back(f);
```
The Depth data is delivered as `uint16_t` type which cannot be rendered directly, therefore we use `rs2::colorizer` to convert the depth representation into human-readable RGB map. We use `rs2::sensor_from_frame` function to retrieve the serial number of the frame's device. Then we can use that device's `rs2::colorizer` to process the frame:
```cpp
// Convert the newly-arrived frames to render-friendly format
for (const auto& frame : new_frames)
{
// Get the serial number of the current frame's device
auto serial = rs2::sensor_from_frame(frame)->get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
// Apply the colorizer of the matching device and store the colorized frame
render_frames[frame.get_profile().unique_id()] = colorizers[serial].process(frame);
}
```
And finally send the collected frames to update the openGl mosaic:
```cpp
app.show(render_frames);
```