PrivMX DOCS
C++

Working with Streams

Streams allow users to communicate in real time using audio, video, and desktop sharing inside Stream Rooms. Each Context can contain any number of Stream Rooms identified by streamRoomId.

Before working with Streams, follow our Getting Started Guide. It will show you how to set up your project to work with PrivMX Bridge. Sample code on this page is based on the initial assumptions.

Working with Streams

When working with Streams, you will use StreamApi, which provides methods used to manage Stream Rooms and real-time media streams

Let's modify the program from the First App chapter to connect to PrivMX Bridge server, create a Stream Room, and publish a Stream in it.

CMakeLists.txt

...
target_link_libraries(test_program PUBLIC 
		privmxendpoint::privmxendpointcore
        privmxendpoint::privmxendpointstream
		privmxendpoint::crypto
)

main.cpp

C++
// setup some defaults
core::PagingQuery defaultListQuery = {.skip = 0, .limit = 100, .sortOrder = "desc"};

// initialize Endpoint connection and APIs required by Stream API
auto connection {core::Connection::connect(USER1_PRIVATE_KEY, SOLUTION_ID, BRIDGE_URL)};
auto eventApi {event::EventApi::create(connection)};
auto streamApi {stream::StreamApi::create(connection, eventApi)};

// ...

Creating Stream Rooms

Use createStreamRoom(...) to create a room with selected users, managers, and metadata.

C++
// ...

std::vector<core::UserWithPubKey> managers {
    {.userId = USER1_ID, .pubKey = USER1_PUBLIC_KEY}
};

std::vector<core::UserWithPubKey> users {
    {.userId = USER1_ID, .pubKey = USER1_PUBLIC_KEY},
    {.userId = USER2_ID, .pubKey = USER2_PUBLIC_KEY}
};

// create a new Stream Room with access for USER_1 as manager and USER_2 as regular user
auto streamRoomId {streamApi.createStreamRoom(
    CONTEXT_ID,
    users, managers,
    core::Buffer::from("some Stream Room's public meta-data"),
    core::Buffer::from("some Stream Room's private meta-data"),
    std::nullopt
)};

// ...

Hint: You can assign any data to private and public meta fields (e.g. the Stream Room's name), as long as it is serialized and can be given as the core::Buffer.

Listing and Reading Stream Rooms

Use listStreamRooms(...) for paged room listing and getStreamRoom(...) to fetch a single room.

C++
// ...

auto streamRoomsList = streamApi.listStreamRooms(CONTEXT_ID, defaultListQuery);

// ...

As a result you will receive an object:

C++
// streamRoomsList:
{
    readItems: [<streamRoomObject1>, <streamRoomObject2>,..., <streamRoomObjectN>],
    totalAvailable: <number_of_all_stream_rooms>
}

Getting a single Stream Room:

C++
// ...

auto streamRoom = streamApi.getStreamRoom(streamRoomId);

// ...

Updating and Deleting Stream Rooms

To update a Stream Room you must always provide a full list of parameters.

The updateStreamRoom(...) method needs all the parameters as in the createStreamRoom(...) method and a few more. If you want to update one of the parameters – provide it in a new modified form. If, on the other hand, you want to leave the parameter unchanged – provide it as it was before.

C++
// ...
auto currentStreamRoom {streamApi.getStreamRoom(streamRoomId)};

std::vector<core::UserWithPubKey> managers {
    {.userId = USER1_ID, .pubKey = USER1_PUBLIC_KEY}
};

std::vector<core::UserWithPubKey> users {
    {.userId = USER1_ID, .pubKey = USER1_PUBLIC_KEY}
};

// update the Stream Room with access for USER_1 as the only user
streamApi.updateStreamRoom(
    streamRoomId,
    users, managers,
    currentStreamRoom.publicMeta,
    core::Buffer::from("new-private-meta"),
    currentStreamRoom.version, // <- pass the version of the Stream Room you will perform the update on
    false, // <- force update (without checking version)
    false, // <- force to regenerate a key for the Stream Room
    currentStreamRoom.policy
);

// ...

To delete a Stream Room, use the deleteStreamRoom(...) method.

C++
// ...
streamApi.deleteStreamRoom(streamRoomId);
// ...

Devices and Track Sources

The API lets you inspect local capture sources using:

  • getAudioDevices()
  • getVideoDevices()
  • getDesktopDevices(...)
C++
// ...

auto audioDevices = streamApi.getAudioDevices();
auto videoDevices = streamApi.getVideoDevices();
auto desktopDevices = streamApi.getDesktopDevices(stream::DesktopType::Screen);

// ...

Joining a Room and Publishing a Stream

Join the room before creating local streams or subscribing to remote ones. Then create a stream handle, add tracks, and publish it.

C++
// ...

// joining the Stream Room
streamApi.joinStreamRoom(streamRoomId);

// creating a local Stream handle
auto streamHandle {streamApi.createStream(streamRoomId)};

// adding a local audio track
std::optional<stream::MediaDevice> selectedAudioDevice;
if (!audioDevices.empty()) {
    selectedAudioDevice = audioDevices.front();
    stream::MediaTrackConstrains constrains;
    auto localTrack = streamApi.addTrack(streamHandle, *selectedAudioDevice, constrains);
    localTrack.setEnabled(true);
}

// publishing the Stream
auto publishResult {streamApi.publishStream(streamHandle)};

// ...

If you add or remove tracks after publishing, call updateStream(...). To stop publishing entirely, call removeStream(...).

C++
// ...

// updating the published Stream after removing a track
if (selectedAudioDevice.has_value()) {
    streamApi.removeTrack(streamHandle, *selectedAudioDevice);
    auto updateResult = streamApi.updateStream(streamHandle);
    std::cout << "UPDATE_RESULT: " << updateResult.published << std::endl;
}

// stopping the Stream
streamApi.removeStream(streamHandle);

// ...

Receiving Remote Streams

Use listStreams(...) to inspect currently published Streams and createSubscriberStream(...) to subscribe to selected ones. Incoming media reaches your application through an OnTrackInterface implementation registered with addRemoteStreamListener(...).

C++
// a minimal listener which logs the remote tracks and the data they deliver
class SampleStreamListener : public stream::OnTrackInterface {
public:
    void OnRemoteTrack(stream::Track track, stream::TrackAction action) override {
        std::cout << "TRACK: " << track.trackId << " action: " << action << std::endl;
    }

    void OnData(std::shared_ptr<stream::Data> data) override {
        std::cout << "DATA on track: " << data->track << std::endl;
    }
};
C++
// ...

auto publishedStreams = streamApi.listStreams(streamRoomId);

if (!publishedStreams.empty()) {
    stream::StreamSubscription subscription;
    subscription.streamId = publishedStreams.front().id;

    // registering a listener for the incoming media
    auto listener {std::make_shared<SampleStreamListener>()};
    streamApi.addRemoteStreamListener(streamRoomId, std::nullopt, listener);

    // subscribing to the selected Stream
    auto subscriberHandle = streamApi.createSubscriberStream(streamRoomId, {subscription});

    // modifying the Streams this Subscriber Stream receives
    streamApi.updateSubscriberStream(subscriberHandle, {}, {subscription});

    // unsubscribing from everything and closing the Subscriber Stream
    streamApi.removeSubscriberStream(subscriberHandle);
}

// ...

To react to room and stream changes, continue with Stream Events.

We use cookies on our website. We use them to ensure proper functioning of the site and, if you agree, for purposes such as analytics, marketing, and targeting ads.

On this page

Working with Streams | PrivMX Docs