UNPKG

@ceeblue/webrtc-client

Version:
1,409 lines (1,383 loc) 61.4 kB
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 */