UNPKG

msc-node

Version:

mediasoup client side Node.js library

318 lines (317 loc) 9.71 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Producer = void 0; const Logger_1 = require("./Logger"); const EnhancedEventEmitter_1 = require("./EnhancedEventEmitter"); const errors_1 = require("./errors"); const logger = new Logger_1.Logger('Producer'); class Producer extends EnhancedEventEmitter_1.EnhancedEventEmitter { /** * @emits transportclose * @emits trackended * @emits @replacetrack - (track: MediaStreamTrack | null) * @emits @setmaxspatiallayer - (spatialLayer: string) * @emits @setrtpencodingparameters - (params: any) * @emits @getstats * @emits @close */ constructor({ id, localId, rtpSender, track, rtpParameters, stopTracks, disableTrackOnPause, zeroRtpOnPause, appData }) { super(); // Closed flag. this._closed = false; // Observer instance. this._observer = new EnhancedEventEmitter_1.EnhancedEventEmitter(); 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, so RTCRtpSender.track won't be null. 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; } /** * Invalid setter. */ set appData(appData) { throw new Error('cannot override appData object'); } /** * Observer. * * @emits close * @emits pause * @emits resume * @emits trackended */ 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'); } /** * 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. */ getStats() { return __awaiter(this, void 0, void 0, function* () { if (this._closed) { throw new errors_1.InvalidStateError('closed'); } return this.safeEmitAsPromise('@getstats'); }); } /** * 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) { this.safeEmitAsPromise('@replacetrack', null) .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) { this.safeEmitAsPromise('@replacetrack', this._track) .catch(() => { }); } // Emit observer event. this._observer.safeEmit('resume'); } /** * Replaces the current track with a new one or null. */ replaceTrack({ track }) { return __awaiter(this, void 0, void 0, function* () { 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 && track.readyState === 'ended') // { // throw new 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; } if (!this._zeroRtpOnPause || !this._paused) { yield this.safeEmitAsPromise('@replacetrack', track); } // 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. */ setMaxSpatialLayer(spatialLayer) { return __awaiter(this, void 0, void 0, function* () { 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; } yield this.safeEmitAsPromise('@setmaxspatiallayer', spatialLayer); this._maxSpatialLayer = spatialLayer; }); } /** * Sets the DSCP value. */ setRtpEncodingParameters(params) { return __awaiter(this, void 0, void 0, function* () { if (this._closed) { throw new errors_1.InvalidStateError('closed'); } else if (typeof params !== 'object') { throw new TypeError('invalid params'); } yield this.safeEmitAsPromise('@setrtpencodingparameters', params); }); } _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;