1.3.10. Peer Streaming

1.3.10.1. Overview

Peer streaming is feature of the SDK that allows two SDK applications to directly share media streams without using Proximie’s infrastructure, e.g. over a LAN without needing internet access.

Without the support of Proximie services, there are a number of limitations to using peer streaming:

  1. The session itself is implied by the peers connecting, rather than being arranged using Proximie services.

  2. Only the two local peers are participants; peer streaming does not use any remote services.

  3. Traffic is encrypted between peers, but there is no authentication/authorization of the peers themselves, other than both peers needing to agree on an encryption key/passphrase.

1.3.10.2. Concepts

Peer session

The peer session is the vehicle that connects two peers together. Each peer is defined by: the details of where it can be found (namely, its host name or IP address), and a set of named “channels” that they may transmit media with. In addition to the peers themselves, the session has an encryption key/passphrase that is used to encode outgoing and decode incoming peer feeds.

The application instantiates a peer session using the configuration and declaring which of the two peer roles it is assuming.

Peer feeds

Feeds are created by the application in association with the peer session. An outgoing peer feed must use a channel from the local peer’s details, and an incoming feed must use a channel from the remote peer’s details.

When a peer shares a feed, it does so by listening on a port defined for the channel it is sharing on. The other peer receives that feed by contacting the host of the peer sharing the feed, on the port defined for the channel. The receiving peer local port is dynamically assigned.

1.3.10.3. Creating a peer session

The PeerSession class defines a peer session. It is created by calling its factory function create(). To create the peer session, the application needs to provide three key pieces of information:

  1. The encryption key to use for the session.

  2. The local (i.e. outgoing) peer details.

  3. The remote (i.e. incoming) peer details.

Both local and remote peer details use the same data structure: PeerSession::PeerDetails.

// Details for the peer application with the patient
PeerSession::PeerDetails patientPeer;
patientPeer.label                = "Patient";
patientPeer.host.hostname        = "patient-peer";
patientPeer.channels["room-cam"] = {"Patient: Room", VIDEO_PORT_ROOM_CAM};
patientPeer.channels["room-mic"] = {"Patient: Mic", AUDIO_PORT_ROOM_MIC};
// Details for the peer application with operating console
PeerSession::PeerDetails consolePeer;
consolePeer.label                 = "Console";
consolePeer.host.hostname         = "console-peer";
consolePeer.channels["web-cam"]   = {"Console: Webcam", VIDEO_PORT_CONSOLE_CAM};
consolePeer.channels["local-mic"] = {"Console: Mic", AUDIO_PORT_CONSOLE_MIC};

Note that this is merely illustrative of the possible data values; in a real application, the configuration of the peers would likely be read from a configuration file or other source, rather than being hard-coded as in this example.

In the above example, the two peers are called “Patient” and “Console”. The patient peer can be found on the host resolved from patient-peer, which declares two channels (room-cam and room-mic) and the ports assigned to each.

Once the peers are obtained, the application can create the peer session using the usual ProximieContext (see ProximieContext) and a PeerSession::PeerSessionSettings structure that defines the peers and the encryption key to use.

PeerSession::PeerSessionSettings peerSettings(patientPeer,      // Local peer
                                              consolePeer,      // Remote peer
                                              ENCRYPTION_KEY);  // Encryption key
auto peerSessionCreated = PeerSession::create(pxcontext, peerSettings);
if (!peerSessionCreated) {
    // Handle error...
}
auto peerSession = peerSessionCreated.value();

Note that the first peer is the local (outgoing) peer, and the second is the remote (incoming) peer.

Important

The encryption key/passphrase needs to be a specific format, this being:

  1. Between 10 and 79 characters in length, inclusive

If the key is not valid, the peer session creation will fail with an error.

Keen observers may have noticed that we do not need to specify the host for the local peer, as this is always outgoing from the host of the application. However, by making the peer details data structures identical, applications can share the configuration data (e.g. configuration data file) and decide which peer role to use at runtime with the same data.

1.3.10.4. Creating an outgoing peer feed

Outgoing feeds must only use channels that are defined for the peer that was chosen as the local role in the peer session. Starting a local peer feed is similar to the process when creating feeds for Proximie remote sessions (see the section: Media Server Session).

using namespace Proximie::PxMedia::Literals;

// Properties for the video feed
PxMedia::PeerLocalVideoFeed::FeedProperties localVideoProps;
localVideoProps.label   = "local-webcam";
localVideoProps.channel = "room-cam";  // Needs to match a local channel key
// Video feed input source
PxMedia::VideoInputFeedV4Linux2 localVideo;
localVideo.deviceProperties().device("/dev/video0");
localVideo.videoCapabilities().frameRate(30);
// Encoder configuration
PxMedia::X264EncoderFeedComponent encoderProperties;
encoderProperties.bitrate(2000_kbps);
// Create the feed
auto localVideoCreated = PxMedia::PeerLocalVideoFeed::create(
    peerSession, localVideoProps, localVideo, &encoderProperties);
if (!localVideoCreated) {
    // Handle error...
}
auto localVideoFeed    = localVideoCreated.value();
auto localVideoStarted = localVideoFeed->startFeed();
if (!localVideoStarted) {
    // Handle error...
}

// Properties for the audio feed
PxMedia::PeerLocalAudioFeed::FeedProperties localAudioProps;
localAudioProps.label   = "local-mic";
localAudioProps.channel = "room-mic";  // Needs to match a local channel key
// Audio feed input source
PxMedia::AudioInputFeedAuto localAudio;
// Create the audio feed
auto localAudioCreated =
    PxMedia::PeerLocalAudioFeed::create(peerSession, localAudioProps, localAudio);
if (!localAudioCreated) {
    // Handle error...
}
auto localAudioFeed    = localAudioCreated.value();
auto localAudioStarted = localAudioFeed->startFeed();
if (!localAudioStarted) {
    // Handle error...
}

1.3.10.5. Creating an incoming peer feed

Incoming feeds must only use channels that are defined for the peer that was chosen as the remote role in the peer session. Again, the process should be quite familiar.

// Properties for the video feed
PxMedia::PeerRemoteVideoFeed::FeedProperties remoteVideoProps;
remoteVideoProps.label   = "remote-cam";
remoteVideoProps.channel = "web-cam";  // Needs to match a remote channel key
// Output locally
PxMedia::VideoOutputFeedAuto videoOut;
// Create the feed
auto remoteVideoCreated =
    PxMedia::PeerRemoteVideoFeed::create(peerSession, remoteVideoProps, videoOut);
if (!remoteVideoCreated) {
    // Handle error...
}
auto remoteVideoFeed    = remoteVideoCreated.value();
auto remoteVideoStarted = remoteVideoFeed->startFeed();
if (!remoteVideoStarted) {
    // Handle error...
}

// Properties for the audio feed
PxMedia::PeerRemoteAudioFeed::FeedProperties remoteAudioProps;
remoteAudioProps.label   = "remote-audio";
remoteAudioProps.channel = "room-mic";  // Needs to match a remote channel key
// Output locally
PxMedia::AudioOutputFeedAuto audioOut;
// Create the feed
auto remoteAudioCreated =
    PxMedia::PeerRemoteAudioFeed::create(peerSession, remoteAudioProps, audioOut);
if (!remoteAudioCreated) {
    // Handle error...
}
auto remoteAudioFeed    = remoteAudioCreated.value();
auto remoteAudioStarted = remoteAudioFeed->startFeed();
if (!remoteAudioStarted) {
    // Handle error...
}

1.3.10.6. Stopping Feeds

Stopping a peer feed is a simple matter of calling the stopFeed() method.

auto stopped = peerFeed->stopFeed();
if (!stopped) {
    // Handle error...
}

1.3.10.7. Peer feed statistics

You can request statistics for peer feed using a SrtFeedStatsRequest (for more details on feed requests, see Feed Requests).

auto requested = peerFeed->feedRequest(PxMedia::SrtFeedStatsRequest(
    [](const auto& stats)
    {
        // The stats parameter is an outcome containing the collected stats or an error
        if (stats) {
            const auto& srtStats = stats.value();
            // Use stats...
        } else {
            // Handle error...
        }
    }));
if (!requested) {
    // The feed could not handle the request, e.g. it does not support stats
    // ...
}

Peer feeds use SRT as the transport protocol, and so the stats returned in the callback are of type SrtFeedStats.

Note that a stats request could fail in two ways:

  1. The feed is not able to accept the request - e.g. the feed does not support SRT statistics, or is not playing. This failure is indicated directly when making the request.

  2. The request itself fails - e.g. due to a network problem, or unexpected parsing of stats. This failure is indicated in the callback function.