WebRTC API

Peer-to-peer communication, video streaming, and real-time data channels for interactive gaming experiences

Real-time Streaming

High-quality video and audio streaming with adaptive bitrate control

Quality Management

Dynamic quality adjustment based on network conditions and preferences

Data Channels

Low-latency data transmission for game inputs and control messages

Quick Start

Get started with Playcast WebRTC peer connections:

Create a Client Peer

import {
  PlaycastPeerClient,
  MakeClientPeerConfig
} from '@playcastdotio/Peer';

const config: MakeClientPeerConfig = {
  realtimeApiManager: {
    send: (message) => websocket.send(JSON.stringify(message))
  },
  iceServers: {
    urls: ['stun:stun.l.google.com:19302'],
    username: 'your_username',
    credential: 'your_credential'
  },
  forceTurn: false,
  additionalMetadata: {
    isAppleDevice: false,
    browserType: 'chrome',
    version: '1.0.0'
  }
};

const clientPeer = createClientPeer(config);

// Connect to host
clientPeer.connect('host-peer-id-123');

Create a Host Peer

import {
  PlaycastPeerHost,
  MakeHostPeerConfig
} from '@playcastdotio/Peer';

const config: MakeHostPeerConfig = {
  realtimeApiManager: {
    send: (message) => websocket.send(JSON.stringify(message))
  },
  iceServers: {
    urls: ['stun:stun.l.google.com:19302'],
    username: 'your_username',
    credential: 'your_credential'
  },
  forceTurn: false,
  validatorFunction: async (localPeer, remotePeer) => {
    // Validate connection request
    return true;
  }
};

const hostPeer = createHostPeer(config);

Peer Types

Playcast supports different types of peer connections for various use cases.

PlaycastPeerClient Client

Client peer that connects to a host for receiving video streams and sending input events.

Key Methods

  • connect(targetHostId: string) - Connect to a host peer
  • sendMessage(message) - Send message to host
  • close(kickReason?) - Close connection
  • changeQualityProfile(profile) - Change stream quality
PlaycastPeerHost Host

Host peer that accepts client connections and streams video while receiving input events.

Key Methods

  • broadcastMessage(message) - Send message to all clients
  • addTracksForAll(tracks) - Add media tracks for all clients
  • setDefaultTracks(tracks) - Set default media tracks
  • updateConfig(newConfig) - Update peer configuration
PlaycastPeerRemoteControlClient Remote

Specialized client for remote control scenarios with enhanced input handling.

PlaycastPeerRemoteControlHost Remote

Host peer optimized for remote control applications with low-latency input processing.

Connection Management

Managing peer connections, signaling, and connection states.

Connection States

State Description Available Actions
signalling Establishing WebRTC connection Wait for connection or handle signaling messages
validating Validating connection request Accept or reject connection
connected Active peer connection Send messages, manage tracks, adjust quality
closed Connection closed Reconnect or clean up resources
refused Connection rejected Handle rejection, possibly retry

Handling Connection Events

// Monitor connection status
clientPeer.status.subscribe((status) => {
  switch (status.connectionState) {
    case 'signalling':
      console.log('Establishing connection...');
      break;
    case 'connected':
      console.log('Connected successfully');
      // Start sending input events
      break;
    case 'closed':
      console.log('Connection closed:', status.kickReason);
      // Handle reconnection logic
      break;
    case 'refused':
      console.log('Connection refused');
      break;
  }
});

// Handle incoming messages
clientPeer.onMessage((message) => {
  console.log('Received message:', message);
  // Process game state updates, etc.
});

Signaling with PeerMessages

// Handle peer messages for signaling
clientPeer.handlePeerMessage({
  type: 'offer',
  peerId: 'remote-peer-id',
  sdp: {
    type: 'offer',
    sdp: 'v=0\r\no=- ...'
  }
});

// Send ICE candidates
clientPeer.handlePeerMessage({
  type: 'ice-candidate',
  peerId: 'remote-peer-id',
  candidate: {
    candidate: 'candidate:...',
    sdpMLineIndex: 0,
    sdpMid: 'video'
  }
});

Quality Management

Dynamic quality adjustment based on network conditions, device capabilities, and user preferences.

Quality Profiles

type PlaycastDefaultProfiles = 'ultra' | 'high' | 'medium' | 'low' | 'potato'
Profile Resolution Bitrate FPS Use Case
ultra 1920x1080 8-12 Mbps 60 High-end gaming, minimal compression
high 1920x1080 4-6 Mbps 60 Good quality gaming
medium 1280x720 2-3 Mbps 30 Balanced quality and bandwidth
low 854x480 1-1.5 Mbps 30 Limited bandwidth scenarios
potato 640x360 0.5-1 Mbps 15 Very poor network conditions

Changing Quality Profiles

// Change quality profile for client
clientPeer.changeQualityProfile('high');

// Set custom quality preset for specific channel
clientPeer.setQualityPreset('video', {
  maxBitrate: 3000000,      // 3 Mbps
  maxFramerate: 30,
  scaleResolutionDownBy: 1.5
});

// Apply preset group for multiple channels
const presetGroup = {
  video: {
    maxBitrate: 4000000,
    maxFramerate: 60,
    scaleResolutionDownBy: 1.0
  },
  audio: {
    maxBitrate: 128000
  }
};

clientPeer.setQualityPresetGroup(presetGroup);

Quality Monitoring

// Monitor quality preset changes
clientPeer.qualityPresetStore.subscribe((presetGroup) => {
  console.log('Quality presets updated:', presetGroup);

  // Update UI indicators
  updateQualityIndicator(presetGroup.video.maxBitrate);
});

// Get current statistics
clientPeer.statistics.subscribe((stats) => {
  if (stats) {
    console.log('Current bitrate:', stats.bitrate);
    console.log('Packet loss:', stats.packetLoss);
    console.log('RTT:', stats.roundTripTime);
  }
});

Media Tracks

Managing video and audio tracks for streaming and communication.

Track Management for Hosts

// Set default tracks for new connections
const defaultTracks = {
  video: videoTrack,
  audio: audioTrack,
  screen: screenShareTrack
};

hostPeer.setDefaultTracks(defaultTracks);

// Add tracks for all connected clients
hostPeer.addTracksForAll({
  webcam: webcamTrack
});

// Remove specific tracks from all clients
hostPeer.removeTracksForAll({
  screen: null  // Remove screen share
});

// Set new track set (replaces existing)
hostPeer.setTracksForAll({
  video: newVideoTrack,
  audio: newAudioTrack
});

Track Management for Remote Clients

// Add tracks to specific remote client
const remotePeer = hostPeer.remotePeers.get('client-id');
if (remotePeer) {
  remotePeer.addTracks({
    personalVideo: personalVideoTrack
  });

  // Set tracks (replaces existing)
  remotePeer.setTracks({
    video: videoTrack,
    audio: audioTrack
  });

  // Remove specific tracks
  remotePeer.removeTracks({
    personalVideo: null
  });
}

Track Events and Monitoring

// Monitor outgoing tracks (client)
clientPeer.outgoingTracks.subscribe((tracks) => {
  console.log('Outgoing tracks:', Object.keys(tracks));

  // Handle track changes
  Object.entries(tracks).forEach(([name, track]) => {
    if (track) {
      console.log(`Track ${name} is active`);
    }
  });
});

// Monitor incoming tracks (client)
clientPeer.incomingTracks.subscribe((tracks) => {
  console.log('Incoming tracks:', Object.keys(tracks));

  // Attach tracks to video elements
  Object.entries(tracks).forEach(([name, track]) => {
    if (track && name === 'video') {
      const videoElement = document.getElementById('game-video');
      if (videoElement) {
        videoElement.srcObject = new MediaStream([track]);
      }
    }
  });
});

WebRTC Statistics

Monitor connection quality, bandwidth usage, and performance metrics.

Available Statistics

type PlaycastWebrtcStats = { bitrate: number; packetLoss: number; roundTripTime: number; jitter: number; frameRate: number; resolution: { width: number; height: number }; codecName: string; bandwidth: number; }

Monitoring Statistics

// Subscribe to statistics updates
clientPeer.statistics.subscribe((stats) => {
  if (stats) {
    // Display connection quality
    updateConnectionQuality({
      bitrate: `${(stats.bitrate / 1000000).toFixed(1)} Mbps`,
      packetLoss: `${(stats.packetLoss * 100).toFixed(2)}%`,
      latency: `${stats.roundTripTime}ms`,
      fps: stats.frameRate,
      resolution: `${stats.resolution.width}x${stats.resolution.height}`,
      codec: stats.codecName
    });

    // Auto-adjust quality based on performance
    if (stats.packetLoss > 0.05) {
      // High packet loss, reduce quality
      clientPeer.changeQualityProfile('low');
    } else if (stats.packetLoss < 0.01 && stats.bitrate < 2000000) {
      // Good conditions, can increase quality
      clientPeer.changeQualityProfile('medium');
    }
  }
});

// Disable statistics collection to save resources
clientPeer.disableStats();

Statistics for Remote Peers

// Monitor statistics for all remote peers (host)
hostPeer.remotePeers.subscribe((remotePeers) => {
  Object.entries(remotePeers).forEach(([peerId, peer]) => {
    peer.statistics.subscribe((stats) => {
      if (stats) {
        console.log(`Client ${peerId} stats:`, {
          bitrate: stats.bitrate,
          latency: stats.roundTripTime,
          quality: stats.resolution
        });

        // Handle poor performance
        if (stats.roundTripTime > 200) {
          console.warn(`High latency for client ${peerId}`);
          // Maybe reduce quality or show warning
        }
      }
    });
  });
});
Performance Note: Statistics collection can impact performance. Use disableStats() when detailed metrics aren't needed, or adjust collection intervals for better performance.

Error Handling

Handling connection failures, network issues, and recovery strategies.

Common Error Scenarios

// Handle connection failures
clientPeer.status.subscribe((status) => {
  if (status.connectionState === 'closed' && status.kickReason) {
    switch (status.kickReason) {
      case 'network-error':
        // Network connectivity issues
        showNetworkErrorMessage();
        attemptReconnect();
        break;

      case 'host-disconnected':
        // Host ended the session
        showHostDisconnectedMessage();
        redirectToLobby();
        break;

      case 'validation-failed':
        // Connection was rejected
        showValidationFailedMessage();
        break;

      case 'peer-timeout':
        // Connection timed out
        showTimeoutMessage();
        attemptReconnect();
        break;

      default:
        showGenericErrorMessage(status.kickReason);
    }
  }
});

// Implement reconnection logic
async function attemptReconnect() {
  const maxRetries = 3;
  let retryCount = 0;

  while (retryCount < maxRetries) {
    try {
      await clientPeer.connect(hostId);
      console.log('Reconnection successful');
      break;
    } catch (error) {
      retryCount++;
      console.log(`Reconnection attempt ${retryCount} failed:`, error);

      if (retryCount < maxRetries) {
        // Wait before retrying
        await new Promise(resolve => setTimeout(resolve, 2000 * retryCount));
      }
    }
  }

  if (retryCount === maxRetries) {
    showReconnectionFailedMessage();
  }
}