@ceeblue/webrtc-client
Version:
Ceeblue WebRTC Client
1,409 lines (1,383 loc) • 61.4 kB
TypeScript
import * as utils from '@ceeblue/web-utils';
import { WebSocketReliableError, EventEmitter, Connect, Loggable } from '@ceeblue/web-utils';
export { utils };
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* List the media type in their string version as received from server
*/
declare enum MType {
AUDIO = "audio",
VIDEO = "video",
DATA = "data"
}
/**
* Media Source representation
*/
type MSource = {
hrn: string;
type: string;
priority: number;
relurl: string;
url: string;
simul_tracks: number;
total_matches: number;
};
/**
* Media Track representation
*/
type MTrack = {
idx: number;
trackid: number;
name: string;
type: MType;
codec: string;
firstms: number;
lastms: number;
bps: number;
ebps: number;
maxbps: number;
init: string;
channels: number;
rate: number;
size: number;
fpks: number;
width: number;
height: number;
up?: MTrack;
down?: MTrack;
};
/**
* Metadata representation
*/
declare class Metadata {
type: string;
/**
* Width resolution size
*/
width: number;
/**
* Height resolution size
*/
height: number;
/**
* Sources available to play the stream
*/
sources: Map<string, MSource>;
/**
* Tracks sorted by descending BPS
*/
tracks: Map<number, MTrack>;
/**
* Audio tracks sorted by descending BPS
*/
audios: Array<MTrack>;
/**
* Video tracks sorted by descending BPS
*/
videos: Array<MTrack>;
/**
* Data track
*/
datas: Array<MTrack>;
/**
* Return a new metadata subset with only track with a codec supported
* @param codecs codecs supported
* @returns the subset of metadata
*/
subset(codecs?: Set<string>): Metadata;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
declare enum StreamState {
UNKNOWN = "",
ONLINE = "Stream is online",
OFFLINE = "Stream is offline",
INITIALIZING = "Stream is initializing",
BOOTING = "Stream is booting",
WAITING = "Stream is waiting for data"
}
type StreamMetadataError =
/**
* Represents a Stream metadata error.
*/
{
type: 'StreamMetadataError';
name: string;
stream: string;
}
/**
* Represents a {@link WebSocketReliableError} error
*/
| WebSocketReliableError;
/**
* Use StreamMetadata to get real-time information on a server stream, including:
* - the list of tracks and their properties,
* - the list of availables sources and their properties,
* @example
* const streamMetadata = new StreamMetadata(Connect.buildURL(endPoint, streamName));
* streamMetadata.onMetadata = metadata => {
* console.log(metadata);
* }
*
*/
declare class StreamMetadata extends EventEmitter {
/**
* Event fired when stream state is changing
* @param state
*/
onState(state: StreamState): void;
/**
* Event fired when the stream is closed
* @param error error description on an improper closure
* @event
*/
onClose(error?: StreamMetadataError): void;
/**
* Event fired when metadata is present in the stream
* @param metadata
* @event
*/
onMetadata(metadata: Metadata): void;
/**
* URL of the connection
*/
get url(): string;
/**
* State of the stream as indicated by the server
*/
get streamState(): StreamState;
/**
* Returns the {@link Connect.Params} object containing the connection parameters
*/
get connectParams(): Connect.Params;
/**
* Returns the {@link Metadata} object description
*/
get metadata(): Metadata | undefined;
/**
* Returns true if the connection is closed
*/
get closed(): boolean;
private _ws;
private _metadata?;
private _connectParams;
private _streamState;
/**
* Create a new StreamMetadata instance, connects to the server using WebSocket and
* listen to metadata events.
*/
constructor(connectParams: Connect.Params);
/**
* Close the stream metadata channel
* @param error error description on an improper closure
*/
close(error?: StreamMetadataError): void;
private _addSortedTrack;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* IStats is the interface used to implement statistics seralization
* The serialized object can then be sent to a server for analysis with {@link Telemetry}.
* @example
* // MyStats implementation
* class MyStats extends IStats {
* constructor(obj) {
* this._obj = obj;
* // Subscribe to onClose event to signal object release (will stop this stats report)
* obj.onClose = () => this.onRelease();
* }
* async serialize() {
* // serialize stats to a JSON object {recvBytes, sendBytes}
* return {
* recvBytes: this._obj.recvBytes,
* sendBytes: this._obj.sendBytes
* }
* }
* }
*/
interface IStats extends Loggable {
/**
* Must be called when the resource is closed
* @event
*/
onRelease(): void;
/**
* Implements this function to define how to serialize data.
* The function must return the metrics object or reject the Promise if the serialization is not possible.
* @returns an async string for a text/plain response, an object for a JSON response, or ArrayBuffer for a binary response
*/
serialize(): Promise<string | object | ArrayBuffer>;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* This is the structure returned by the connectionInfos() method
* to get statistics about current connection
*/
type ConnectionInfos = {
/**
* inputs channel
*/
inputs: {
audio?: RTCInboundRtpStreamStats;
video?: RTCInboundRtpStreamStats;
};
/**
* outputs channel
*/
outputs: {
audio?: RTCOutboundRtpStreamStats;
video?: RTCOutboundRtpStreamStats;
};
/**
* Selected candidate pair
*/
candidate?: RTCIceCandidatePairStats;
};
type ConnectorError =
/**
* Represents a Connection error.
*/
{
type: 'ConnectorError';
name: 'Connection failed';
detail: string;
}
/**
* Represents a Connection idle error.
*/
| {
type: 'ConnectorError';
name: 'Connection idle';
}
/**
* Represents a RTCPeerConnection creation error.
*/
| {
type: 'ConnectorError';
name: 'RTCPeerConnection failed';
detail: string;
}
/**
* Represents a SIP handshake error.
*/
| {
type: 'ConnectorError';
name: 'SIP failed';
detail: string;
}
/**
* Represents access denied error.
*/
| {
type: 'ConnectorError';
name: 'Access denied';
}
/**
* Represents an RTCRtpSender::replaceTrack error.
*/
| {
type: 'ConnectorError';
name: 'Replace track failed';
detail: string;
}
/**
* Represents a {@link WebSocketReliableError} error
*/
| WebSocketReliableError;
/**
* IConnector is a common interface for representing a stream connection with the server.
*
* This interface can serve the both roles: player or streamer.
*/
interface IConnector extends EventEmitter {
/**
* Call when connector is open
* @param stream MediaStream description provided from the server if we are the player,
* or build from the local camera if we are the streamer.
* @event
*/
onOpen(stream: MediaStream): void;
/**
* Call when connector is closed
* @param error error description on an improper closure
* @event
*/
onClose(error?: ConnectorError): void;
/**
* True when connector is opened, in other words when {@link onOpen} event is fired
*/
readonly opened: boolean;
/**
* True when connector is closed, in other words when {@link onClose} event is fired
*/
readonly closed: boolean;
/**
* Media Stream description delivred from the server if we are player,
* or build from the local camera if we are the streamer.
*/
readonly stream?: MediaStream;
/**
* Stream name, for example `as+bc3f535f-37f3-458b-8171-b4c5e77a6137`
*/
readonly streamName: string;
/**
* Indicate codecs supported, should be set before than {@link onOpen} happen
*/
readonly codecs: Set<string>;
/**
* Request connections infos with caching option to save loading cost
* @param cacheDuration indicate how many time we can cache the last connection informations
* @returns Promise with a ConnectionInfos on success
*/
connectionInfos(cacheDuration?: number): Promise<ConnectionInfos>;
/**
* Close the connector
* @param error the error reason if is not a proper close
*/
close(error?: ConnectorError): void;
/**
*
* @param kind 'video' or 'audio' to replace the track of the given kind
* @returns Promise that resolves when the track is replaced
* @throws Error if the kind is not supported or if the operation fails
*
* This method allows to replace the current track of the given kind with a new one.
* If the track is null, it will remove the current track of that kind.
*/
replaceTrack(kind: 'audio' | 'video', track: MediaStreamTrack | null): Promise<void>;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
declare class PlayerStats extends utils.PlayerStats implements IStats {
audioByteRate?: number;
videoByteRate?: number;
private _prevTime;
private _prevAudioBytes;
private _prevVideoBytes;
private _prevVideoEmittedCount;
private _prevAudioEmittedCount;
private _prevVideoJitterDelay;
private _prevAudioJitterDelay;
private _prevSkippedAudio;
private _prevAudioConcealedSamples;
private _prevSkippedVideo;
private _prevVideoDroppedFrames;
private _prevVideoTime;
private _prevRealTime;
constructor();
/**
* @override{@inheritDoc IStats.onRelease}
* @event
*/
onRelease(): void;
/**
* @returns a JSON representation of the player stats, which is the object itself in this case
*/
serialize(): Promise<object>;
/**
* Computes and updates all player statistics based on the current connection infos, metadata, and playback state.
* Updates the internal properties of this class including those inherited from {@link utils.PlayerStats}.
* @param infos ConnectionInfos: WebRTC connection and input stats.
* @param metadata Metadata: Stream metadata and track info.
* @param currentTime number: Current playback time (media time) in seconds.
* @param audioTrackId number (optional): Selected audio track ID.
* @param videoTrackId number (optional): Selected video track ID.
*/
compute(infos: ConnectionInfos, metadata: Metadata, currentTime: number, audioTrackId?: number, videoTrackId?: number): void;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* MediaReport is a structure returned by {@link IController.onMediaReport} event.
* It contains the media controller statistics.
*/
type MediaReport = {
type: string;
millis: number;
tracks: string[];
stats: {
jitter_ms: number;
loss_num: number;
loss_perc: number;
nack_num: number;
};
};
/**
* RTPProps is a structure returned by {@link IController.onRTPProps} event.
* It contains the RTP controller statistics.
*/
type RTPProps = {
type: string;
result: boolean;
drop: number;
nack: number;
};
/**
* PlayingInfos is a structure returned by {@link IController.onPlaying} event.
* It contains the current playing information, updated every second.
*/
type PlayingInfos = {
type: string;
begin: number;
current: number;
end: number;
tracks: number[];
};
/**
* IController is a controller interface extending a stream connector
* with the capability of sending and receiving commands with the server.
*
* This interface can serve the both roles: player or streamer.
*/
interface IController extends IConnector {
/**
* Call to distribute {@link RTPProps}
* @param rtpProps RTP properties
* @event
*/
onRTPProps(rtpProps: RTPProps): void;
/**
* Call to distribute {@link MediaReport}
* @param mediaReport Media report informations
* @event
*/
onMediaReport(mediaReport: MediaReport): void;
/**
* Call to distribute video bitrate informations
* @param videoBitrate Current video bitrate
* @param videoBitrateConstraint Video bitrate constraint computed by the server
* @event
*/
onVideoBitrate(videoBitrate: number, videoBitrateConstraint: number): void;
/**
* Call to distribute {@link PlayingInfos}
* @param playingInfos Current playing informations
* @event
*/
onPlaying(playingInfos: PlayingInfos): void;
/**
* Sets server properties for packet error (nack) and delayed packet loss (drop)
* for a streamer controller and fires an onRTPProps event if changed successfully.
* NOTE: Method can also retrieve current server values if called without arguments.
* @param nack Waiting period before declaring a packet error
* @param drop Waiting period before considering delayed packets as lost
*/
setRTPProps(nack?: number, drop?: number): void;
/**
* Configure the video bitrate on the server side for a streamer controller
* @param value video bitrate in bps
*/
setVideoBitrate(value: number): void;
/**
* Configure the audio and video track to play for a player controller
* @param tracks.audio Audio track
* @param tracks.video Video track
*/
setTracks(tracks: {
audio?: number;
video?: number;
}): void;
/**
* Send a generic command to control the streaming session
* @param type type of command
* @param params parameters of the command
* @example
* controller.send('video_bitrate', {video_bitrate: 0});
*/
send(type: string, params: object): void;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* MBRParams is the structure used to initialize an {@link MBRAbstract} instance.
*/
type MBRParams = {
/**
* Number of milliseconds to increase the delay of the next Up try when we are congested
* @defaultValue 1400
*/
learningUpStep?: number;
/**
* Maximum delay in milliseconds to wait before trying to Up again
* @defaultValue 28000
*/
maximumUpDelay?: number;
};
/**
* MBRAbstract is the base class for multi-bitrate algorithm used by {@link Player}
* to switch between tracks quality depending on the network's congestion.
*/
declare abstract class MBRAbstract extends Loggable implements MBRParams {
/**
* delay before to increase bitrate when network quality is good
*/
get upDelay(): number;
/**
* delay added on every congestion to report next bitrate increasing
*/
get learningUpStep(): number;
/**
* maximum delay before to increase bitrate when network quality is good
*/
get maximumUpDelay(): number;
private _mTrack?;
private _learningUpStep;
private _upDelay;
private _maximumUpDelay;
private _testTime;
private _appreciationTime?;
/**
* Build the MBR implementation, call {@link compute} to use it
* @param params MBR parameters
*/
constructor(params: MBRParams);
/**
* Reset the MBR algorithm to its initial state
*/
reset(): void;
/**
* Call this method regularly to control if we have to change track, it will update
* the tracks if needed and return true if a track has changed.
* @param metadata Metadata of the stream
* @param tracks the audio and video track number, this object can be updated with the new track numbers
* @param stats Statistics to use to determine if we have to decrease bitrate now
* @returns true if a track has changed, false otherwise
*/
compute(metadata: Metadata, tracks: {
audio?: number;
video?: number;
}, stats: {
audio?: RTCInboundRtpStreamStats;
video?: RTCInboundRtpStreamStats;
}): boolean;
/**
* Try to select the next track to use if available
* @param track the track number to update
* @param metadata the metadata of the stream
* @param down True if it is a down change, false if it is an up change
* @returns the new track to use or undefined if no change is possible
*/
private updateTrack;
/**
* Check if we are congested and need to reduce the bitrate now
* Implement this method to define your own congestion algorithm
*
* @param elapsed Time since beginning of congestion, on a new state 'elapsed' is equals to 0 on first call
* @param trackRef Track used as reference for statistics, is always the video track playing excepting if the stream is a pure audio stream
* @param stats Statistics to use to determine if we have to decrease bitrate now
* @returns true if we can decrease bitrate quality now
*/
protected abstract _downBitrate(elapsed: number, trackRef: MTrack, stats: RTCInboundRtpStreamStats): boolean;
/**
* Called when {@link _downBitrate} returns false to check if we can increase the bitrate now
* Implement this method to define your own congestion algorithm
*
* @param elapsed Time since beginning of good network state, on the first call 'elapsed' is equals to 0
* @param trackRef Track used as reference for statistics, is always the video track playing excepting if the stream is a pure audio stream
* @param stats Statistics to use to determine if we have to increase bitrate now
* @returns true if we can increase bitrate quality now
*/
protected abstract _upBitrate(elapsed: number, trackRef: MTrack, stats: RTCInboundRtpStreamStats): boolean;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
type PlayerError =
/**
* Represents a {@link ConnectorError} error
*/
ConnectorError
/**
* Represents a {@link WebSocketReliableError} error
*/
| WebSocketReliableError
/**
* Represents a {@link StreamMetadataError} error
*/
| StreamMetadataError;
/**
* Use Player to start playing a WebRTC stream.
* You can use a controllable version using a `WSController` as connector, or change it to use a `HTTPConnector` (HTTP WHEP).
* By default it uses a `WSController` excepting if on {@link Player.start} you use a {@link Connect.Params.endPoint} prefixed with a `http://` protocol.
* With a controllable connector you can change track during the playback, what is not possible with a HTTP(WHEP) connector.
*
* In addition then a player with a controllable connector uses a MultiBitrate algorithm to switch track automatically
* regarding network congestion. You can disable it and fix manually a track by selecting it after a start call.
* At last it's possible to indicate which are tracks to play in first by setting audio and video tracks before
* start call. In this case it can be usefull to get metadata information about the stream before to play it,
* see {@link Player.start} to achieve it.
*
* @example
* const player = new Player();
* // const player = new Player(isWHEP ? HTTPConnector : WSController);
* player.onStart = stream => {
* videoElement.srcObject = stream;
* console.log('start playing');
* }
* player.onStop = _ => {
* console.log('stop playing');
* }
* // optionnaly set initial video track to play before start() call
* player.videoTrack = 1;
* // start playback
* player.start({
* endPoint: address, // if address is prefixed by `http://` it uses a HTTPConnector (HTTP-WHEP) if Player is build without contructor argument
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* });
* // Tracks are in a MBR mode: video track 1 can change regarding network congestion
* ...
* // Fix video track to 2, disable MBR mode
* player.videoTrack = 2;
* ...
* player.stop();
*
*/
declare class Player extends EventEmitter {
private Connector?;
/**
* Event fired when streaming starts
* @param stream
* @event
*/
onStart(stream: MediaStream): void;
/**
* Event fired when streaming stops
* @param error error description on an improper stop
* @event
*/
onStop(error?: PlayerError): void;
/**
* Event fired when stream state is changing
* @param state
* @event
*/
onState(state: StreamState): void;
/**
* Event fired every second to report information while content plays
* @param playing
* @event
*/
onPlaying(playing: PlayingInfos): void;
/**
* Event fired when metadata is present in the stream
* @param metadata
* @event
*/
onMetadata(metadata: Metadata): void;
/**
* Event fired when data is received in the stream
* @param time
* @param track
* @param data
* @event
*/
onData(time: number, track: number, data: any): void;
/**
* Stream name, for example `as+bc3f535f-37f3-458b-8171-b4c5e77a6137`
*/
get streamName(): string;
/**
* Playable media stream as specified by [MediaStream](https://developer.mozilla.org/docs/Web/API/MediaStream)
*/
get stream(): MediaStream | undefined;
/**
* Returns true when player is running (between a {@link Player.start} and a {@link Player.stop})
*/
get running(): boolean;
/**
* Returns the {@link IController} instance when starting with a connector with controllable ability,
* or undefined if stream is not starting or stream is not controllable.
*/
get controller(): IController | undefined;
/**
* Returns the {@link IConnector} instance, or undefined if stream is not starting.
*/
get connector(): IConnector | undefined;
/**
* State of the stream as indicated by the server
*/
get streamState(): StreamState;
/**
* Returns the {@link Metadata} object description
*/
get metadata(): Metadata | undefined;
/**
* Last playing information, such as current position and playback buffer bounds
* See {@link PlayingInfos}
*/
get playingInfos(): PlayingInfos | undefined;
/**
* Index of the audio track, can be undefined if player is not playing
*/
get audioTrack(): number | undefined;
/**
* Sets the current audio track to the index provided, it can be set before calling {@link Player.start}
* to select the initial audio track, or set after {@link Player.start} to fix track and disable MBR
* While playing you can set it to `undefined` to activate MBR when session is controllable
*/
set audioTrack(idx: number | undefined);
/**
* Index of the video track, can be undefined if player is not playing
*/
get videoTrack(): number | undefined;
/**
* Sets the current video track to the index provided, it can be set before calling {@link Player.start}
* to select the initial video track, or set after {@link Player.start} to fix track and disable MBR
* While playing you can set it to `undefined` to activate MBR when session is controllable
*/
set videoTrack(idx: number | undefined);
/**
* The list of tracks currently receiving
*/
get dataTracks(): Array<number>;
/**
* Set the data track ids to receive
*/
set dataTracks(tracks: Array<number>);
private _connector?;
private _controller?;
private _streamMetadata?;
private _streamMetadataReconnectTimeout?;
private _audioTrack?;
private _videoTrack?;
private _audioTrackFixed?;
private _videoTrackFixed?;
private _dataTracks;
private _playingInfos?;
private _streamData?;
private _streamDataReconnectTimeout?;
private _statsPollingTimeout?;
private _metadata?;
private _videoElement;
private _playerStats;
/**
* Constructs a new Player instance, optionally with a custom connector
* This doesn't start the playback, you must call {@link Player.start} method
* By default if no connector is indicated it uses a {@link WSController} (WebSocket {@link IController})
* excepting if {@link Connect.Params.endPoint} is prefixed with a `http://` protocol in such case it uses
* instead a {@link HTTPConnector} (HTTP {@link IConnector}). To force a HTTPConnector build explicitly
* with {@link HTTPConnector} as Connector argument.
* @param Connector Connector class to use for signaling, or nothing to let's {@link Player.start} method determines it automatically.
* @example
* // Default build
* const player = new Player();
* player.start({
* endPoint: address, // if address is prefixed by `http://` it uses a HTTPConnector (HTTP-WHIP), otherwise it uses a WSController (WebSocket)
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* });
* // Force connector selection whatever the address used in endPoint
* const player = new Player(isWHIP ? HTTPConnector : WSController);
* player.start({
* endPoint: address, // optional protocol prefix has no incidence on connector selection
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* })
*/
constructor(videoElement: HTMLVideoElement, Connector?: {
new (connectParams: Connect.Params): IConnector;
} | undefined);
/**
* Returns connection info, such as round trip time, requests sent and received,
* bytes sent and received, and bitrates
* NOTE: This call is resource-intensive for the CPU.
* @returns {Promise<ConnectionInfos>} A promise for a ConnectionInfos
*/
connectionInfos(): Promise<ConnectionInfos>;
/**
* Starts playing the stream
* The connector is determined automatically from {@link Connect.Params.endPoint} if not forced in the constructor.
*
* Instead to use a {@link Connect.Params} you can prefer use a already built {@link StreamMetadata} instance to
* the same end-point, it allows to discover tracks in amount and initialize track selection to start playback.
*
* The `multiBitrate` option can take three different types of value:
* - A {@link MBRParams} object to configured the default MBRLinear implementation, default value `{}`
* - `undefined` to disable MBR management
* - Use a custom {@link MBRAbstract} implementation instance
*
* In addition you can disable MBR while playing by set audio or video track to an explicit value ({@link Player.videoTrack} and {@link Player.audioTrack}).
*
* @param params Connection parameters or a StreamMetadata object already connected to the same end-point
* Note that if you pass a StreamMetadata object its life-time management is fully delegated to the Player
* @param multiBitrate Multi-bitrate implementation or MBRParams to configure the default implementation
* @example
* // Default start with MBR activated
* player.start({
* endPoint: address,
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* });
* // Start with selected initial track, in using a StreamMetadata object preinitialized
* const streamMetadata = new StreamMetadata({
* endPoint: address,
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* });
* streamMetadata.onMetadata = metadata => {
* // start to play with higher bitrate selection (audios and videos are sorted by descending BPS)
* player.audioTrack = metadata.audios[0].idx;
* player.videoTrack = metadata.videos[0].idx;
* // give the streamMetadata channel to reuse instead of opening a new again
* player.start(streamMetadata);
* // Now MBR select automatically the best tracks regarding network congestion
* ...
* // Fix audioTrack to the high rendition (disable MBR for audio track)
* player.audioTrack = 1;
* ...
* // Reactivate MBR for audio
* player.audioTrack = undefined;
* }
*/
start(params: Connect.Params | StreamMetadata, multiBitrate?: MBRAbstract | MBRParams | undefined): void;
/**
* Stops playing the stream
* @param error error description on an improper stop
*/
stop(error?: PlayerError): void;
/**
* Compute the current player statistics as a {@link PlayerStats} object
* @returns {PlayerStats} the current player statistics
*/
computeStats(): PlayerStats;
private _updateTracks;
private _initStreamMetadata;
private _newStreamData;
private _pollStats;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* ABRParams is the structure used to initialize an {@link ABRAbstract} instance.
*/
type ABRParams = {
/**
* Startup bitrate in bps
* @defaultValue 2000000
*/
startup?: number;
/**
* Minimum bitrate in bps
* @defaultValue 200000
*/
minimum?: number;
/**
* Maximum bitrate in bps
* @defaultValue 3000000
*/
maximum?: number;
/**
* The `recoverySteps` parameter defines the step size used to gradually restore the bitrate
* towards the ideal bandwidth, aiming to approach the {@link ABRParams.maximum} limit.
*
* Initially set to the configured value (default: 2), this factor determines the number of steps
* taken to adapt the bitrate based on network conditions :
* - If network congestion is detected, the step count increases to avoid overshooting.
* - Once the network stabilizes, the step count decreases, returning gradually to its initial value.
*
* In essence, `recoverySteps` controls the initial speed at which the system recovers a high transfer rate.
*
* @warning Only used by ABRLinear
* @defaultValue 2
*/
recoverySteps?: number;
/**
* The `appreciationDuration` parameter defines the duration (in milliseconds) for recognizing a stable network
* condition. By default it is set to 4 seconds, which is longer than the typical GOP unit, usually set to 2 seconds.
*
* @warning Only used by ABRLinear.
* @defaultValue 4000
*/
appreciationDuration?: number;
};
/**
* ABRAbstract is the base class for adaptive bitrate algorithm used by {@link Streamer}
* when it has a controller connector.
*/
declare abstract class ABRAbstract extends EventEmitter implements ABRParams {
/**
* Get the configured initial bitrate
*/
get startup(): number;
/**
* Update the initial bitrate
*/
set startup(value: number);
/**
* Get the minimum bitrate
*/
get minimum(): number;
/**
* Update the minimum bitrate
*/
set minimum(value: number);
/**
* Get the maximum bitrate
*/
get maximum(): number;
/**
* Update the maximum bitrate
*/
set maximum(value: number);
/**
* Get the current bitrate constraint
*/
get constraint(): number | undefined;
/**
* Get {@link ABRParams.recoverySteps}
*/
get recoverySteps(): number;
/**
* Set {@link ABRParams.recoverySteps}
*/
set recoverySteps(value: number);
/**
* Get {@link ABRParams.appreciationDuration}
*/
get appreciationDuration(): number;
/**
* Set {@link ABRParams.appreciationDuration}
*/
set appreciationDuration(value: number);
/**
* Get the current bitrate
*/
get value(): number | undefined;
/**
* @returns the current bitrate
* @override
*/
valueOf(): number | undefined;
/**
* Get the {@link https://developer.mozilla.org/docs/Web/API/MediaStream MediaStream} if set
*/
get stream(): MediaStream | undefined;
private _bitrateConstraint?;
private _bitrate?;
private _startup;
private _maximum;
private _minimum;
private _recoverySteps;
private _appreciationDuration;
private _stream?;
/**
* Build the ABR implementation, call {@link compute} to use it
* @param params ABR parameters
* @param stream If set it can change dynamically the source resolution regarding the network quality
*/
constructor(params: ABRParams, stream?: MediaStream);
/**
* Call this method regularly to control if we have to increase or decrease the stream bitrate
* depending on the network conditions.
* @param bitrate the current bitrate
* @param bitrateConstraint the current bitrate constraint
* @param mediaReport the media report structure received from the server
* @returns the wanted bitrate
*/
compute(bitrate: number | undefined, bitrateConstraint?: number, mediaReport?: MediaReport): number;
/**
* Reset the ABR algorithm to its initial state
*/
reset(): void;
/**
* Implement this method to define your own congestion algorithm, the method must
* return the wanted bitrate or undefined to not change the current bitrate.
* @param bitrate the current bitrate
* @param bitrateConstraint the current bitrate constraint
* @param mediaReport the media report structure received from the server
* @returns the wanted bitrate or undefined to not change the current bitrate
*/
protected abstract _computeBitrate(bitrate: number, bitrateConstraint?: number, mediaReport?: MediaReport): number;
protected _updateVideoConstraints(videoBitrate: number): void;
private _upgradeVideoConstraint;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
type StreamerError =
/**
* Represents a {@link ConnectorError} error
*/
ConnectorError
/**
* Represents a {@link WebSocketReliableError} error
*/
| WebSocketReliableError;
/**
* Use Streamer to broadcast to a WebRTC server.
*
* You can use a controllable version using a `WSController` as connector, or change it to use a `HTTPConnector` (HTTP WHIP).
* By default it uses a `WSController` excepting if on {@link Streamer.start} you use a {@link Connect.Params.endPoint} prefixed with a `http://` protocol.
* With a controllable connector you can change video bitrate during the streaming, what is not possible with a HTTP(WHIP) connector.
*
* @example
* const streamer = new Streamer();
* // const streamer = new Streamer(isWHEP ? HTTPConnector : WSController);
* streamer.onStart = stream => {
* console.log('start streaming');
* }
* streamer.onStop = _ => {
* console.log('stop streaming');
* }
* navigator.mediaDevices
* .getUserMedia({ audio: true, video: true })
* .then(stream => {
* streamer.start(stream, {
* endPoint: address, // if address is prefixed by `http://` it uses a HTTPConnector (HTTP-WHIP) if Streamer is build without contructor argument
* streamName: 'as+bc3f535f-37f3-458b-8171-b4c5e77a6137'
* });
* ...
* streamer.stop();
* });
*/
declare class Streamer extends EventEmitter {
private Connector?;
/**
* Event fired when the stream has started
* @param stream
* @event
*/
onStart(stream: MediaStream): void;
/**
* Event fired when the stream has stopped
* @param error error description on an improper stop
* @event
*/
onStop(error?: StreamerError): void;
/**
* Event fired when an RTP setting change occurs
* @param props
*/
onRTPProps(props: RTPProps): void;
/**
* Event fired to report media statistics
* @param mediaReport
*/
onMediaReport(mediaReport: MediaReport): void;
/**
* Event fired when a video bitrate change occurs
* @param videoBitrate
* @param videoBitrateConstraint
*/
onVideoBitrate(videoBitrate: number, videoBitrateConstraint: number): void;
/**
* Stream name, for example `as+bc3f535f-37f3-458b-8171-b4c5e77a6137`
*/
get streamName(): string;
/**
* Camera media stream as specified by [MediaStream](https://developer.mozilla.org/docs/Web/API/MediaStream)
*/
get stream(): MediaStream | undefined;
/**
* Returns true when streamer is running (between a {@link Streamer.start} and a {@link Streamer.stop})
*/
get running(): boolean;
/**
* Returns the {@link IController} instance when starting with a connector with controllable ability,
* or undefined if stream is not starting or stream is not controllable.
*/
get controller(): IController | undefined;
/**
* Returns the {@link IConnector} instance, or undefined if stream is not starting.
*/
get connector(): IConnector | undefined;
/**
* Last {@link MediaReport} statistics
*/
get mediaReport(): MediaReport | undefined;
/**
* Last {@link RTPProps} statistics
*/
get rtpProps(): RTPProps | undefined;
/**
* Video bitrate configured by the server,
* can be undefined on start or when there is no controllable connector
* @note Use {@link connectionInfos} to get the current precise audio or video bitrate
*/
get videoBitrate(): number | undefined;
/**
* Configure the video bitrate from the server,
* possible only if your {@link Streamer} instance is built with a controllable connector
* Set undefined to remove this configuration
*/
set videoBitrate(value: number | undefined);
/**
* Video bitrate constraint configured by the server,
* can be undefined on start or when there is no controllable connector
* @note Use {@link connectionInfos} to get the current precise audio or video bitrate
*/
get videoBitrateConstraint(): number | undefined;
/**
* The current audio track of the stream, or null if audio track is disabled.
*/
get audioTrack(): MediaStreamTrack | null;
/**
* The current video track of the stream, or null if video track is disabled.
*/
get videoTrack(): MediaStreamTrack | null;
private _connector?;
private _controller?;
private _mediaReport?;
private _videoBitrate?;
private _videoBitrateConstraint?;
private _videoBitrateFixed;
private _rtpProps?;
/**
* Constructs a new Streamer instance, optionally with a custom connector
* This doesn't start the broadcast, you must call start() method
* @param Connector Connector class to use for signaling, can be determined automatically from URL in the start() method
*/
constructor(Connector?: {
new (connectParams: Connect.Params, stream: MediaStream): IConnector;
} | undefined);
/**
* Replace the audio track while streaming.
*
* @param track MediaStreamTrack to set as audio track, or null to remove the audio track
* @returns {Promise<void>} A promise that resolves when the track is set or removed
*/
setAudioTrack(track: MediaStreamTrack | null): Promise<void>;
/**
* Replace the video track while streaming.
*
* @param track MediaStreamTrack to set as video track, or null to remove the video track
* @returns {Promise<void>} A promise that resolves when the track is set or removed
*/
setVideoTrack(track: MediaStreamTrack | null): Promise<void>;
/**
* Sets server properties for packet error (nack) and delayed packet loss (drop)
* and fires an onRTPProps event if changed successfully.
* NOTE: Method can also retrieve current server values if called without arguments.
* @param nack Waiting period before declaring a packet error
* @param drop Waiting period before considering delayed packets as lost
*/
setRTPProps(nack?: number, drop?: number): void;
/**
* Returns connection info, such as round trip time, requests sent and received,
* bytes sent and received, and bitrates
* NOTE: This call is resource-intensive for the CPU.
* @returns {Promise<ConnectionInfos>} A promise for a ConnectionInfos
*/
connectionInfos(): Promise<ConnectionInfos>;
/**
* Starts broadcasting the stream
* The connector is determined automatically from {@link Connect.Params.endPoint} if not forced in the constructor.
*
* The `adaptiveBitrate` option can take three different types of value:
* - A {@link ABRParams} parameters to configure the default ABRLinear implementation
* - undefined to disable ABR management
* - Use a custom {@link ABRAbstract} implementation instance
*
* @param stream {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaStream MediaStream} instance to stream
* @param params Connection parameters
* @param adaptiveBitrate Adaptive bitrate implementation or ABRParams to configure the default implementation
*/
start(stream: MediaStream, params: Connect.Params, adaptiveBitrate?: ABRAbstract | ABRParams | undefined): void;
/**
* Stop streaming the stream
* @param error error description on an improper stop
*/
stop(error?: StreamerError): void;
/**
* Replace a single track while streaming.
*
* - If the underlying connector exposes a `replaceTrack(kind, track)` method,
* this will hot-swap the sender track (no full restart).
* - Otherwise, we update the exposed MediaStream for consistency and throw,
* so callers can decide to stop/start with a new stream.
*
* @param kind 'audio' | 'video'
* @param media A MediaStreamTrack, a MediaStream (first track of kind is used), or null to remove/mute that kind.
*/
private _replaceTrack;
private _computeVideoBitrate;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* SIPConnector is a common abstract class for negotiating a new RTCPeerConnection connection
* with the server.
*
* The child class must implement the _sip method to send the offer to the server and get the answer.
*
*/
declare abstract class SIPConnector extends EventEmitter implements IConnector {
/**
* @override{@inheritDoc IConnector.onOpen}
* @event
*/
onOpen(stream: MediaStream): void;
/**
* @override{@inheritDoc IConnector.onClose}
* @event
*/
onClose(error?: ConnectorError): void;
/**
* @override{@inheritDoc IConnector.opened}
*/
get opened(): boolean;
/**
* @override{@inheritDoc IConnector.closed}
*/
get closed(): boolean;
/**
* @override{@inheritDoc IConnector.stream}
*/
get stream(): MediaStream | undefined;
/**
* @override{@inheritDoc IConnector.streamName}
*/
get streamName(): string;
/**
* @override{@inheritDoc IConnector.codecs}
*/
get codecs(): Set<string>;
private _streamName;
private _endPoint;
private _stream?;
private _peerConnection?;
private _closed;
private _connectionInfos?;
private _connectionInfosTime;
private _codecs;
private _peerConnectionIdleTimeout?;
/**
* Create a new SIPConnector instance. The RTCPeerConnection is created only when calling _open().
*
* By default, a listener channel is negotiated.
* To create a streamer channel, pass a stream parameter.
*/
constructor(connectParams: Connect.Params, stream?: MediaStream);
/**
* Returns connection info, such as round trip time, requests sent and received,
* bytes sent and received, and bitrates
* NOTE: This call is resource-intensive for the CPU.
* @returns {Promise<ConnectionInfos>} A promise that resolves to an RTCStatsReport object
*/
connectionInfos(cacheDuration?: number): Promise<ConnectionInfos>;
/**
* @override{@inheritDoc IConnector.close}
*/
close(error?: ConnectorError): void;
/**
* Operates Session Initiation Protocol, this method implement the logic
* to send the SDP offer to the server and get the SDP answer in response.
*
* @param offer SIP Offer
* @returns a Promise with SIP Answer as result
*/
protected abstract _sip(offer: string): Promise<string>;
/**
* Main function which creates the RTCPeerConnection, creates the offer,
* calls the _sip method, then set the answer and calls onOpen
*/
protected _open(iceServer?: RTCIceServer): void;
/**
* Hot-swap a track on the existing RTCPeerConnection without renegotiation.
* @param kind 'audio' | 'video'
* @param track A MediaStreamTrack to send, or null to stop sending that kind.
*/
replaceTrack(kind: 'audio' | 'video', track: MediaStreamTrack | null): Promise<void>;
/**
* Fill the codecs set with the codecs found in the sdp
*
* @param sdp the sdp to parse
*/
private updateCodecs;
/**
* To call onOpen just once
*/
private _tryToOpen;
private _startPeerConnectionIdleTimeout;
private _clearPeerConnectionIdleTimeout;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* Use HTTPConnector to negotiate a new RTCPeerConnection connection with the server
* using WHIP (WebRTC HTTP Ingest Protocol) or WHEP (WebRTC HTTP Egress Protocol).
* @example
* // Listener channel (no initial 'stream' parameter), listen to a stream without sending data
* const connection = new HTTPConnector({endPoint, streamName});
* // we get the media stream from server
* connection.onOpen = stream => videoElement.srcObject = stream;
*
* // Streamer channel (with initial 'stream' parameter), sends and receives media streams
* const connection = new HTTPConnector({endPoint, streamName}, {stream: await navigator.mediaDevices.getUserMedia()});
* // the media stream here is our local camera as passed in the above constructor
* connection.onOpen = stream => {}
*/
declare class HTTPConnector extends SIPConnector {
private _fetch;
private _url;
/**
* Start the HTTPConnector.
*
* By default, a listener channel is negotiated.
* To create a streamer channel, give a media stream parameter
*/
constructor(connectParams: Connect.Params, stream?: MediaStream);
/**
* @override{@inheritDoc SIPConnector.close}
*/
close(error?: ConnectorError): void;
/**
* @override{@inheritDoc SIPConnector._sip}
*/
protected _sip(offer: string): Promise<string>;
}
/**
* Copyright 2023 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/webrtc-client which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/
/**
* Use WSController to negotiate a new RTCPeerConnection connection with the server
* using WebSocket custom signaling and keep that connection open for communication.
* @example
* // Listener channel (no 'stream' parameter), listen to a stream without sending data
* const connection = new WSController({endPoint, streamName});
* connection.onOpen = stream => videoElement.srcObject = stream;
*
* // Streamer channel (with 'stream' parameter), sends and receives media streams
* const connection = new WSController({endPoint, streamName}, {stream: await navigator.mediaDevices.getUserMedia()});
* connection.onOpen = stream => {}
*/
declare class WSController extends SIPConnector implements IController {
/**
* @override{@inheritDoc IController.onRTPProps}
* @event
*/