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 Source abstract class is the extension point for custom media transports. You might build a custom source to integrate a proprietary ingest protocol, drive a WebSocket-based push feed, perform specialized pre-processing, or mock a stream for testing — all while keeping the full Player API (track selection, ABR, statistics, DRM) working unchanged above it.

The Source Abstract Class

A concrete Source subclass must implement exactly three abstract methods:

play(url, tracks, playing)

Called once when the source should begin fetching and delivering media. Use the protected readAudio(), readVideo(), readData(), and readMetadata() helpers to push samples and metadata into the player. May return immediately (non-blocking) or remain running for the lifetime of the stream (blocking).

setReliability(reliable)

Called when player.reliable is changed at runtime. Toggle frame skipping behaviour in your transport accordingly.

setTracks(tracks)

Called when the player requests a track change — either by the user or by the ABR algorithm. Update your transport to fetch the new track IDs.

Registering a Custom Source

Use the @Source.registerClass decorator to map one or more protocol names to your class. When player.start() is called with an endpoint whose protocol matches, the player instantiates your source automatically.
Source.registerClass is a TypeScript decorator. In TypeScript 5.0 and later (which this library requires), class decorators are supported natively — no experimentalDecorators flag is needed. If you are using an older TypeScript version you would need to enable "experimentalDecorators": true in tsconfig.json, but upgrading to TypeScript 5.x is strongly recommended. If you prefer to avoid decorators entirely, pass the class directly to the Player constructor instead (see Option A below).
import { Source, Player } from '@ceeblue/wrts-client';
import type { IPlaying } from '@ceeblue/wrts-client';
import type { Connect } from '@ceeblue/web-utils';
import * as Media from '@ceeblue/wrts-client';

@Source.registerClass('myproto')
class MySource extends Source {
  constructor(playing: IPlaying, params: Connect.Params) {
    super(playing, 'myproto', params);
  }

  protected play(url: URL, tracks: Media.Tracks, playing: IPlaying): void {
    // 1. Announce stream metadata
    // this.readMetadata(metadata);

    // 2. Announce the tracks to receive
    // this.initTracks({ audio: audioTrackId, video: videoTrackId });

    // 3. Push media samples as they arrive
    // this.readAudio(trackId, sample);
    // this.readVideo(trackId, sample);
    // this.readData(trackId, sample);

    // 4. Close on error
    // this.close({ type: 'SourceError', name: 'Unexpected source issue', detail: err.message });
  }

  protected setReliability(reliable: boolean): void {
    // Toggle frame skipping in your transport
  }

  protected setTracks(tracks: Media.Tracks): void {
    // Request the new track IDs from your transport layer
  }
}

Using the Custom Source with Player

1

Option A — pass the class to the constructor

The safest option: pass your Source subclass directly to the Player constructor. This does not require the decorator or a matching protocol prefix in the endpoint URL.
import { Player } from '@ceeblue/wrts-client';

const player = new Player(videoElement, MySource);

player.start({
  endPoint: 'https://<hostname>/wrts/out+<stream-id>/index.json'
});
2

Option B — auto-select via protocol prefix

If the source is registered with @Source.registerClass, the player picks it up automatically when the endpoint URL starts with the registered protocol.
// 'myproto' was registered in @Source.registerClass('myproto')
const player = new Player(videoElement);

player.start({
  endPoint: 'myproto://example.com/stream'
});
You can also look up any registered class by protocol name:
import { Source } from '@ceeblue/wrts-client';

const Cls = Source.getClass('https'); // returns HTTPAdaptiveSource
const WsCls = Source.getClass('wss'); // returns WSSource

Protected Helper Methods

The Source base class provides a rich set of protected helpers that your implementation can call:
MethodDescription
readAudio(trackId, sample)Push an audio sample into the player pipeline
readVideo(trackId, sample)Push a video sample into the player pipeline
readData(trackId, sample)Push a data or metadata sample into the player pipeline
readSample(type, trackId, sample)Convenience dispatcher — routes to the correct read* method by Media.Type
MethodDescription
readMetadata(metadata)Announce stream metadata; triggers onMetadata on the player
initTracks(tracks)Declare which tracks will be received; must be called after readMetadata
MethodDescription
fetch(url, init?)Fetch a URL with request finalization via onFinalizeRequest
fetchMedia(url, trackIds, options?)Fetch with optional CMCD instrumentation attached
fetchWithRTT(url, init?)Fetch and measure round-trip time
finalizeRequest(url, headers)Invoke onFinalizeRequest and return the finalized URL
MethodDescription
skipAudio(duration)Signal that duration ms of audio was skipped; fires onAudioSkipping
skipVideo(duration)Signal that duration ms of video was skipped; fires onVideoSkipping
fixTimestamp(type, trackId, currentTime, sample)Correct timestamp discontinuities to maintain monotonic time
MethodDescription
newReader(params?)Create a Reader (RTS or CMAF) matching the current mediaExt ('rts' or 'mp4'); wires onSample, onMetadata, and onError automatically

Source Properties Available to Subclasses

PropertyTypeDescription
this.urlstringThe resolved playback URL
this.streamNamestringStream name extracted from the endpoint
this.metadataMetadata | undefinedCurrent stream metadata (available after readMetadata is called)
this.audioTimenumberLatest ingested audio timestamp (ms)
this.videoTimenumberLatest ingested video timestamp (ms)
this.currentTimenumberMax of audio, video, and data times (ms)
this.mediaExtstringMedia container extension, e.g. 'rts' or 'mp4'
this.reliablebooleanWhether reliable mode is active
this.closedbooleantrue after this.close() has been called

WSSource as a Reference Implementation

WSSource in src/sources/WSSource.ts is a good real-world reference for a push-based transport. It demonstrates:
  • Registering for multiple protocols: @Source.registerClass('wss', 'ws')
  • Opening a WebSocketReliable connection in play()
  • Parsing JSON metadata and binary frames on the same WebSocket
  • Delegating binary frames to newReader() for demuxing
  • Sending a tracks message in setTracks() to request a server-side switch
Always call this.close() when your transport encounters an unrecoverable error. Failing to close properly leaves the player in a running state with no data arriving, which eventually triggers a 'Data timeout' error. Pass a SourceError object to close() so the player can surface a meaningful error through onStop.
// Correct error handling pattern
protected play(url: URL, tracks: Media.Tracks, playing: IPlaying): void {
  myTransport.onError = (err) => {
    this.close({
      type: 'SourceError',
      name: 'Unexpected source issue',
      detail: err.message
    });
  };
}

Build docs developers (and LLMs) love