msc-node
Version:
mediasoup client side Node.js library
318 lines (317 loc) • 9.71 kB
JavaScript
"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;