Table of Contents

One-Way WebRTC

The WebRtcPlaybackPage demonstrates one-way WebRTC playback from a streaming server. It connects to the server's WebRTC signaling endpoint over WebSocket, negotiates a peer connection, and plays the received media stream using MediaPlayerControl.

Overview

The WebRtcPlaybackPage performs the following:

  1. Connects to the server's WebRTC signaling endpoint via WebSocket
  2. Receives an SDP offer from the server and creates a WebRtcClientSource
  3. Exchanges ICE candidates to establish the peer connection
  4. Plays the received video and audio stream in the media player

Signaling

WebRTC requires a signaling mechanism to exchange session descriptions (SDP) and ICE candidates between peers. The demo uses a WebSocket-based signaling client (VastSignalingPlaybackClient) that implements IWebRtcSignalling. VastSignalingPlaybackClient is designed to work with VASTreaming StreamingServer and implements its signaling protocol. Other signaling protocols can be implemented by the user based on this sample.

Connection Flow

Client                          Server
  |                               |
  |--- WebSocket connect -------->|  ws://server:8888/rtc/sign-in?channel=builtin
  |<-- peer-list -----------------|  Assigns peer IDs
  |<-- SDP offer -----------------|  Server sends offer
  |--- SDP answer --------------->|  Client responds
  |<-- ICE candidates ------------|  Exchange ICE candidates
  |--- ICE candidates ----------->|
  |                               |
  |<== Media stream ==============|  WebRTC media flows

Starting the Signaling Client

this.signalingClient = new VastSignalingPlaybackClient(
    this.player, this.textUri.Text, this.textPublishingPath.Text);
this.signalingClient.Error += SignalingClient_Error;
this.signalingClient.Start();

The signaling client takes the player, the signaling server URI (e.g., ws://192.168.0.102:8888/rtc), and the publishing path (e.g., builtin). The client connects to {uri}/sign-in?channel={publishingPath}.

Signaling Protocol

The signaling client communicates with the server using JSON messages over WebSocket:

Message Type Direction Description
peer-list Server to client Assigns peer IDs to the client and server
message (SDP offer) Server to client Contains the SDP offer with media capabilities
message (SDP answer) Client to server Contains the SDP answer
message (ICE candidate) Both directions ICE connectivity candidates
ping Client to server Keep-alive sent every 10 seconds
action (disconnect) Server to client Server-initiated disconnection

Messages are sent using the IWebRtcSignalling interface:

public void SendToPeer(object caller, long peerId, string data)
{
    // sends: {"data-type":"message","from":<ourPeerId>,"to":<peerId>,"data":<data>}
}

Creating the WebRTC Source

When the server sends an SDP offer, the signaling client creates a WebRtcClientSource:

this.webRtcSource = new WebRtcClientSource();
this.webRtcSource.Signalling = this;
this.webRtcSource.OurPeerId = this.ourPeerId;
this.webRtcSource.RemotePeerId = this.serverPeerId;
this.webRtcSource.IceServers = this.IceServers;

The SDP offer is inspected to determine which media types are expected:

if (peerMessage.Sdp.Contains("m=audio"))
    this.webRtcSource.IsAudioExpected = true;

if (peerMessage.Sdp.Contains("m=video"))
    this.webRtcSource.IsVideoExpected = true;

If the SDP contains H.264 in the codec list, VP8 and VP9 codecs are removed from the offer to prefer H.264.

After the source reaches the Opened state, the SDP offer is processed and playback begins:

this.webRtcSource.Open();

// wait for source to reach Opened state...

this.webRtcSource.ProcessPeerMessage(this.serverPeerId, unparsedMessage);
this.player.SourceMedia = this.webRtcSource;
this.player.Play();

ICE Configuration

The default ICE server is Google's public STUN server:

public string IceServers { get; set; } = "stun:stun.l.google.com:19302";

For production use behind NAT, a TURN server should be configured.

Error Handling and Reconnection

The signaling client handles critical errors and fires the Error event:

this.signalingClient.Error += (object sender, VAST.Media.ErrorEventArgs e) =>
{
    if (e.IsCritical)
    {
        this.stop();
        // display e.ErrorDescription
    }
};

If the WebSocket connection drops unexpectedly during playback, the signaling client automatically stops, resets its state, and reconnects from scratch.

Stopping

this.signalingClient.Stop();
this.signalingClient.Dispose();

Stopping the signaling client stops the player, disposes the WebRTC source, cancels background tasks, and closes the WebSocket connection. The page also stops automatically when the user navigates away.

Send Log

The page includes a Send Log button that uploads the application log file to VASTreaming support for diagnostics:

await VAST.Common.License.SendLog("MAUI WebRTC playback issue");

SendLog sends the current log file to the support server. A valid license key must be configured for this feature to work.

See Also