UNPKG

mediasoup-client

Version:

mediasoup client side TypeScript library

315 lines (314 loc) 8.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Producer = void 0; const Logger_1 = require("./Logger"); const enhancedEvents_1 = require("./enhancedEvents"); const errors_1 = require("./errors"); const logger = new Logger_1.Logger('Producer'); class Producer extends enhancedEvents_1.EnhancedEventEmitter { // Id. _id; // Local id. _localId; // Closed flag. _closed = false; // Associated RTCRtpSender. _rtpSender; // Local track. _track; // Producer kind. _kind; // RTP parameters. _rtpParameters; // Paused flag. _paused; // Video max spatial layer. _maxSpatialLayer; // Whether the Producer should call stop() in given tracks. _stopTracks; // Whether the Producer should set track.enabled = false when paused. _disableTrackOnPause; // Whether we should mark the transceiver as inactive when paused. _zeroRtpOnPause; // App custom data. _appData; // Observer instance. _observer = new enhancedEvents_1.EnhancedEventEmitter(); constructor({ id, localId, rtpSender, track, rtpParameters, stopTracks, disableTrackOnPause, zeroRtpOnPause, appData, }) { super(); logger.debug('constructor()'); this._id = id; this._localId = localId; this._rtpSender = rtpSender; this._track = track; this._kind = track.kind; this._rtpParameters = rtpParameters; this._paused = disableTrackOnPause ? !track.enabled : false; this._maxSpatialLayer = undefined; this._stopTracks = stopTracks; this._disableTrackOnPause = disableTrackOnPause; this._zeroRtpOnPause = zeroRtpOnPause; this._appData = appData ?? {}; this.onTrackEnded = this.onTrackEnded.bind(this); // NOTE: Minor issue. If zeroRtpOnPause is true, we cannot emit the // '@replacetrack' event here. this.handleTrack(); } /** * Producer id. */ get id() { return this._id; } /** * Local id. */ get localId() { return this._localId; } /** * Whether the Producer is closed. */ get closed() { return this._closed; } /** * Media kind. */ get kind() { return this._kind; } /** * Associated RTCRtpSender. */ get rtpSender() { return this._rtpSender; } /** * The associated track. */ get track() { return this._track; } /** * RTP parameters. */ get rtpParameters() { return this._rtpParameters; } /** * Whether the Producer is paused. */ get paused() { return this._paused; } /** * Max spatial layer. * * @type {Number | undefined} */ get maxSpatialLayer() { return this._maxSpatialLayer; } /** * App custom data. */ get appData() { return this._appData; } /** * App custom data setter. */ set appData(appData) { this._appData = appData; } get observer() { return this._observer; } /** * Closes the Producer. */ close() { if (this._closed) { return; } logger.debug('close()'); this._closed = true; this.destroyTrack(); this.emit('@close'); // Emit observer event. this._observer.safeEmit('close'); // Invoke close() in EnhancedEventEmitter classes. super.close(); this._observer.close(); } /** * Transport was closed. */ transportClosed() { if (this._closed) { return; } logger.debug('transportClosed()'); this._closed = true; this.destroyTrack(); this.safeEmit('transportclose'); // Emit observer event. this._observer.safeEmit('close'); } /** * Get associated RTCRtpSender stats. */ async getStats() { if (this._closed) { throw new errors_1.InvalidStateError('closed'); } return new Promise((resolve, reject) => { this.safeEmit('@getstats', resolve, reject); }); } /** * Pauses sending media. */ pause() { logger.debug('pause()'); if (this._closed) { logger.error('pause() | Producer closed'); return; } this._paused = true; if (this._track && this._disableTrackOnPause) { this._track.enabled = false; } if (this._zeroRtpOnPause) { new Promise((resolve, reject) => { this.safeEmit('@pause', resolve, reject); }).catch(() => { }); } // Emit observer event. this._observer.safeEmit('pause'); } /** * Resumes sending media. */ resume() { logger.debug('resume()'); if (this._closed) { logger.error('resume() | Producer closed'); return; } this._paused = false; if (this._track && this._disableTrackOnPause) { this._track.enabled = true; } if (this._zeroRtpOnPause) { new Promise((resolve, reject) => { this.safeEmit('@resume', resolve, reject); }).catch(() => { }); } // Emit observer event. this._observer.safeEmit('resume'); } /** * Replaces the current track with a new one or null. */ async replaceTrack({ track, }) { logger.debug('replaceTrack() [track:%o]', track); if (this._closed) { // This must be done here. Otherwise there is no chance to stop the given // track. if (track && this._stopTracks) { try { track.stop(); } catch (error) { } } throw new errors_1.InvalidStateError('closed'); } else if (track?.readyState === 'ended') { throw new errors_1.InvalidStateError('track ended'); } // Do nothing if this is the same track as the current handled one. if (track === this._track) { logger.debug('replaceTrack() | same track, ignored'); return; } await new Promise((resolve, reject) => { this.safeEmit('@replacetrack', track, resolve, reject); }); // Destroy the previous track. this.destroyTrack(); // Set the new track. this._track = track; // If this Producer was paused/resumed and the state of the new // track does not match, fix it. if (this._track && this._disableTrackOnPause) { if (!this._paused) { this._track.enabled = true; } else if (this._paused) { this._track.enabled = false; } } // Handle the effective track. this.handleTrack(); } /** * Sets the video max spatial layer to be sent. */ async setMaxSpatialLayer(spatialLayer) { if (this._closed) { throw new errors_1.InvalidStateError('closed'); } else if (this._kind !== 'video') { throw new errors_1.UnsupportedError('not a video Producer'); } else if (typeof spatialLayer !== 'number') { throw new TypeError('invalid spatialLayer'); } if (spatialLayer === this._maxSpatialLayer) { return; } await new Promise((resolve, reject) => { this.safeEmit('@setmaxspatiallayer', spatialLayer, resolve, reject); }).catch(() => { }); this._maxSpatialLayer = spatialLayer; } async setRtpEncodingParameters(params) { if (this._closed) { throw new errors_1.InvalidStateError('closed'); } else if (typeof params !== 'object') { throw new TypeError('invalid params'); } await new Promise((resolve, reject) => { this.safeEmit('@setrtpencodingparameters', params, resolve, reject); }); } onTrackEnded() { logger.debug('track "ended" event'); this.safeEmit('trackended'); // Emit observer event. this._observer.safeEmit('trackended'); } handleTrack() { if (!this._track) { return; } this._track.addEventListener('ended', this.onTrackEnded); } destroyTrack() { if (!this._track) { return; } try { this._track.removeEventListener('ended', this.onTrackEnded); // Just stop the track unless the app set stopTracks: false. if (this._stopTracks) { this._track.stop(); } } catch (error) { } } } exports.Producer = Producer;