Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CeeblueTV/wrts-client/llms.txt

Use this file to discover all available pages before exploring further.

The Player class is the main entry point for WebRTS streaming. It manages the full playback lifecycle — buffering, track selection, adaptive bitrate, and live-edge synchronization — while exposing a set of events and properties that let you observe and control every aspect of playback. This guide walks through each area in detail.

Lifecycle Events

The Player exposes event callbacks that fire at each key moment of the playback lifecycle. Override any of them before calling player.start() to respond to state changes.
Fires once streaming is fully initialized and the first frame is ready to play.
player.onStart = () => {
  console.log('Playback started');
};
Fires when streaming stops, either cleanly or due to an error. When error is defined, the stop was abnormal.
player.onStop = (error) => {
  if (error) {
    console.error('Playback stopped with error:', error);
  } else {
    console.log('Playback stopped cleanly');
  }
};
Fires when stream metadata is first received. The optional return value allows you to specify the initial track selection. By default the player picks the middle-quality rendition.
player.onMetadata = (metadata) => {
  console.log('Tracks available:', metadata.videoTracks);
  // Return a track selection to override the default
  return { video: metadata.videoTracks[0].id };
};
Fires whenever the player switches to a different audio, video, or data track — either by ABR or by manual assignment.
player.onTrackChange = (audioTrack, videoTrack, dataTrack) => {
  console.log('Now playing audio:', audioTrack, 'video:', videoTrack);
};
Fires when the buffer crosses a threshold and transitions between BufferState.LOW, BufferState.OK, and BufferState.HIGH. The previous state is passed as oldState.
import { BufferState } from '@ceeblue/wrts-client';

player.onBufferState = (oldState) => {
  console.log(`Buffer changed from ${oldState} to ${player.bufferState}`);
};
Fires when the video element pauses waiting for data (a buffer underrun). The player automatically recovers when enough data arrives.
player.onStall = () => {
  console.warn('Playback stalled — waiting for data');
};
Fires when a gap in audio or video is detected and skipped. holeMs is the size of the skipped hole in milliseconds.
player.onAudioSkipping = (holeMs) => {
  console.warn(`Skipped ${holeMs} ms of audio`);
};

player.onVideoSkipping = (holeMs) => {
  console.warn(`Skipped ${holeMs} ms of video`);
};

Starting and Stopping

1

Start playback

Call player.start() with a Connect.Params object. The endPoint is the WRTS index URL obtained from the Ceeblue API.
import { Player } from '@ceeblue/wrts-client';

const player = new Player(videoElement);

player.start({
  endPoint: 'https://<hostname>/wrts/out+<stream-id>/index.json'
});
An optional second argument idleTimeout controls how long the player waits for a connection or data before stopping with a 'Start timeout' or 'Data timeout' error. The default is approximately 14 seconds — slightly above the maximum GOP duration.
// Custom 20 second timeout
player.start({ endPoint: '...' }, 20000);
2

Stop playback

Call player.stop() at any time to end the session and release all resources.
player.stop();
Pass an error object to stop() only when stopping due to an application-level error — the error is forwarded to onStop.

Track Selection

The player exposes audioTrack, videoTrack, and dataTrack for reading and writing the current track selection.
You must not assign tracks before player.onStart fires. The source is not initialized until that point, and an attempt to set a track on a stopped player throws an error: Cannot assign video track on stopped player.

Reading the current tracks

player.onStart = () => {
  console.log('Audio track:', player.audioTrack);
  console.log('Video track:', player.videoTrack);
  console.log('Data tracks:', player.dataTrack); // Set<number>
};

Setting the initial track via onMetadata

Use onMetadata to influence the very first track before playback starts. Returning a Media.Tracks object sets the starting track while keeping MBR active — the adaptive algorithm can still switch away from it.
// Start on the highest-quality track; MBR adjusts it as bandwidth allows
player.onMetadata = (metadata) => ({ video: metadata.videoTracks[0].id });
To disable MBR and lock to a specific track, assign it directly instead of returning:
// Lock video to the highest quality and disable MBR
player.onMetadata = (metadata) => {
  player.videoTrack = metadata.videoTracks[0].id;
};

Switching tracks during playback

Assign player.videoTrack or player.audioTrack at any time after onStart. Setting a track disables MBR for that track type. Set the value back to undefined to re-enable adaptive selection.
// Lock to a specific track index
player.videoTrack = 3;

// Re-enable ABR
player.videoTrack = undefined;

Data track selection

The dataTrack setter accepts a single index, an array of indices, a Set<number>, or undefined (which selects all available data tracks).
// Select a single data track
player.dataTrack = 5;

// Select multiple data tracks
player.dataTrack = [5, 6];

// Re-enable all data tracks
player.dataTrack = undefined;

Checking track selectability

if (player.trackSelectable) {
  // The active source supports manual track switching
}

Pause and Resume

// Pause playback
player.paused = true;

// Resume playback
player.paused = false;
The player must already be running (i.e., player.start() must have been called) before setting paused. Setting it on a stopped player throws: Start the player before to pause playback.

Seeking to the Live Edge

player.goLive() moves the playback head as close as possible to the live point by seeking to endTime − bufferLimitMiddle. This keeps the player within safe buffer bounds and avoids triggering an immediate stall.
// Jump back to live after the viewer has paused or fallen behind
player.goLive();
You can pass an optional reason string that appears in the log output for debugging:
player.goLive('user-requested');

Player State Properties

PropertyTypeDescription
player.runningbooleantrue between start() and stop()
player.startedbooleantrue after onStart fires (source is live)
player.bufferingbooleantrue while filling the initial buffer or recovering from a stall
player.bufferStateBufferStateCurrent buffer state: NONE, LOW, OK, or HIGH
player.bufferAmountnumberCurrent buffer depth in milliseconds
player.currentTimenumberCurrent playback position in seconds
player.startTimenumberStart of the available buffer window in seconds
player.endTimenumberEnd of the available buffer window (live point) in seconds
player.latencynumber | undefinedEstimated live latency in milliseconds
player.playbackRatenumberCurrent <video>.playbackRate (1.0 = real-time)
player.playbackSpeednumberEffective measured playback speed
player.passthroughCMAFboolean | undefinedRead-only; true when the source is streaming CMAF in passthrough mode

Statistics

player.computeStats() returns a PlayerStats snapshot you can use for monitoring dashboards or logging.
const stats = player.computeStats();

console.log({
  bufferAmount:   stats.bufferAmount,   // ms
  latency:        stats.latency,        // ms
  audioByteRate:  stats.audioByteRate,  // bytes/s
  videoByteRate:  stats.videoByteRate,  // bytes/s
  audioPerSecond: stats.audioPerSecond, // audio frames/s
  videoPerSecond: stats.videoPerSecond, // video samples/s
  stallCount:     stats.stallCount,     // total stalls since start
  skippedAudio:   stats.skippedAudio,   // total ms of audio skipped
  skippedVideo:   stats.skippedVideo,   // total ms of video skipped
});

Byte Rates

The player exposes live byte-rate measurements for each media channel:
player.recvByteRate;   // total receive rate (audio + video + data overhead)
player.audioByteRate;  // audio payload bytes per second
player.videoByteRate;  // video payload bytes per second
player.dataByteRate;   // data / metadata bytes per second
These are averaged over the most recent GOP duration for stability.
Use player.onFinalizeRequest(url, headers) to inject custom authorization headers on every media request — for example to add a bearer token or a signed cookie. The callback fires just before each HTTP request is dispatched.
player.onFinalizeRequest = (url, headers) => {
  headers.set('Authorization', 'Bearer ' + myToken);
};

Build docs developers (and LLMs) love