RemoteScreenStream interface

extends ScreenStream  REMOTE

A remote participant's screen-share stream. Adds server-driven state, simulcast quality control, and pause/resume on top of ScreenStream. Access via p.screen after subscribe().

Blueprint

Members added on top of ScreenStream. Bundled audio is exposed via the nested .audio property when the publisher enabled audio.

interface RemoteScreenStream extends ScreenStream { // Bundled screen audio โ€” null unless publisher enabled audio in their share readonly audio: RemoteScreenAudioStream | null; // Combined state โ€” routing (server) + decoder (WebRTC stats) merged readonly streamState: "active" | "paused" | "frozen" | "stuck" | "ended"; readonly currentQuality: "high" | "medium" | "low"; // Simulcast layer pick setPreferredQuality(q: MediaQuality): Promise<void>; // BUNDLE-LEVEL byte flow control โ€” pauses BOTH screen video AND screen audio pause(): Promise<void>; resume(): Promise<void>; // === Events fired === // 'state-changed' ({ state }) โ€” every streamState transition (incl. frozen / stuck / ended) // 'quality-changed' ({ prev, current }) โ€” simulcast layer switch }

Properties (added)

Plus inherited from VideoStream via ScreenStream.

audio

The bundled screen-audio stream from this participant. Non-null only when the publisher started screen sharing with audio enabled. SDK auto-plays remote screen audio โ€” no attach needed for the common case.

readonly audio: RemoteScreenAudioStream | null
Example โ€” duck volume / read level
p.screen?.audio?.setVolume(0.5);
const level = p.screen?.audio?.audioLevel;

streamState

Combined state โ€” routing transitions plus decoder observations from WebRTC stats (the SDK monitors freeze and stuck on screen video the same way it does on camera video). See StreamState.

readonly streamState: "active" | "paused" | "frozen" | "stuck" | "ended"

currentQuality

The simulcast layer currently being received. Distinct from streamState โ€” the stream is still active when the layer drops. Driven by both your setPreferredQuality() calls and server-side bandwidth adaptation.

readonly currentQuality: MediaQuality // "high" | "medium" | "low"

Methods (added)

Plus inherited from VideoStream: attach, createElement, getStats, getMediaStreamTrack.

setPreferredQuality async

Pick which simulcast layer of the screen share to receive. Useful for sidebar previews vs main pane rendering.

setPreferredQuality(q: MediaQuality): Promise<void>

pause / resume async

Stop / restart byte flow without dropping the subscription. Useful when the screen-share tile scrolls offscreen.

pause(): Promise<void> resume(): Promise<void>

Events

Two stream-level events. state-changed covers every transition of streamState โ€” routing (paused / ended) and decoder observations (frozen / stuck). quality-changed covers simulcast layer switches. Subscribe-completion events (screen-subscribed, screen-unsubscribed) fire on RemoteParticipant.

EventPayload
state-changed{ state: "active" | "paused" | "frozen" | "stuck" | "ended" }
quality-changed{ prev: MediaQuality, current: MediaQuality }

state-changed fires when streamState changes. Transitions to "ended" when the publisher stops sharing.

quality-changed fires on simulcast layer switches โ€” driven by your setPreferredQuality() call OR server-side bandwidth adaptation.

Example
p.screen.on('state-changed', ({ state }) => {
  if (state === 'ended') {
    document.querySelector('#shared-screen').srcObject = null;
    showMessage(`${p.displayName} stopped sharing`);
  }
});

p.screen.on('quality-changed', ({ current }) => {
  console.log('screen-share now at', current);
});
Example โ€” render remote screen with auto-quality
// React to publishes via the consolidated stream-published event
p.on('stream-published', ({ kind }) => {
  if (kind === MediaKind.Screen) p.subscribe(kind);
});

p.on('stream-subscribed', async (sub) => {
  if (sub.screen) {
    sub.screen.attach(document.querySelector('#shared-screen'));
    await sub.screen.setPreferredQuality('high');
  }
});

// Publisher stopped sharing โ†’ stream-unpublished with kind === Screen
p.on('stream-unpublished', ({ kind }) => {
  if (kind !== MediaKind.Screen) return;
  document.querySelector('#shared-screen').srcObject = null;
  showMessage(`${p.displayName} stopped sharing`);
});

See also: ScreenStream RemoteVideoStream LocalScreenStream RemoteParticipant