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:
- Connects to the server's WebRTC signaling endpoint via WebSocket
- Receives an SDP offer from the server and creates a WebRtcClientSource
- Exchanges ICE candidates to establish the peer connection
- 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
- Sample Applications — overview of all demo projects
- MAUI App Demo — parent page with app initialization and demo page overview