UNPKG

mediasoup

Version:

Cutting Edge WebRTC Video Conferencing

365 lines (364 loc) 15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataConsumerImpl = void 0; exports.dataConsumerTypeToFbs = dataConsumerTypeToFbs; exports.parseDataConsumerDumpResponse = parseDataConsumerDumpResponse; const Logger_1 = require("./Logger"); const enhancedEvents_1 = require("./enhancedEvents"); const sctpParametersFbsUtils_1 = require("./sctpParametersFbsUtils"); const fbsUtils = require("./fbsUtils"); const notification_1 = require("./fbs/notification"); const FbsTransport = require("./fbs/transport"); const FbsRequest = require("./fbs/request"); const FbsDataConsumer = require("./fbs/data-consumer"); const FbsDataProducer = require("./fbs/data-producer"); const logger = new Logger_1.Logger('DataConsumer'); class DataConsumerImpl extends enhancedEvents_1.EnhancedEventEmitter { // Internal data. #internal; // DataConsumer data. #data; // Channel instance. #channel; // Closed flag. #closed = false; // Paused flag. #paused = false; // Associated DataProducer paused flag. #dataProducerPaused = false; // Subchannels subscribed to. #subchannels; // Custom app data. #appData; // Observer instance. #observer = new enhancedEvents_1.EnhancedEventEmitter(); constructor({ internal, data, channel, paused, dataProducerPaused, subchannels, appData, }) { super(); logger.debug('constructor()'); this.#internal = internal; this.#data = data; this.#channel = channel; this.#paused = paused; this.#dataProducerPaused = dataProducerPaused; this.#subchannels = subchannels; this.#appData = appData ?? {}; this.handleWorkerNotifications(); this.handleListenerError(); } get id() { return this.#internal.dataConsumerId; } get dataProducerId() { return this.#data.dataProducerId; } get closed() { return this.#closed; } get type() { return this.#data.type; } get sctpStreamParameters() { return this.#data.sctpStreamParameters; } get label() { return this.#data.label; } get protocol() { return this.#data.protocol; } get paused() { return this.#paused; } get dataProducerPaused() { return this.#dataProducerPaused; } get subchannels() { return Array.from(this.#subchannels); } get appData() { return this.#appData; } set appData(appData) { this.#appData = appData; } get observer() { return this.#observer; } close() { if (this.#closed) { return; } logger.debug('close()'); this.#closed = true; // Remove notification subscriptions. this.#channel.removeAllListeners(this.#internal.dataConsumerId); /* Build Request. */ const requestOffset = new FbsTransport.CloseDataConsumerRequestT(this.#internal.dataConsumerId).pack(this.#channel.bufferBuilder); this.#channel .request(FbsRequest.Method.TRANSPORT_CLOSE_DATACONSUMER, FbsRequest.Body.Transport_CloseDataConsumerRequest, requestOffset, this.#internal.transportId) .catch(() => { }); this.emit('@close'); // Emit observer event. this.#observer.safeEmit('close'); } transportClosed() { if (this.#closed) { return; } logger.debug('transportClosed()'); this.#closed = true; // Remove notification subscriptions. this.#channel.removeAllListeners(this.#internal.dataConsumerId); this.safeEmit('transportclose'); // Emit observer event. this.#observer.safeEmit('close'); } async dump() { logger.debug('dump()'); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_DUMP, undefined, undefined, this.#internal.dataConsumerId); /* Decode Response. */ const dumpResponse = new FbsDataConsumer.DumpResponse(); response.body(dumpResponse); return parseDataConsumerDumpResponse(dumpResponse); } async getStats() { logger.debug('getStats()'); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_GET_STATS, undefined, undefined, this.#internal.dataConsumerId); /* Decode Response. */ const data = new FbsDataConsumer.GetStatsResponse(); response.body(data); return [parseDataConsumerStats(data)]; } async pause() { logger.debug('pause()'); await this.#channel.request(FbsRequest.Method.DATACONSUMER_PAUSE, undefined, undefined, this.#internal.dataConsumerId); const wasPaused = this.#paused; this.#paused = true; // Emit observer event. if (!wasPaused && !this.#dataProducerPaused) { this.#observer.safeEmit('pause'); } } async resume() { logger.debug('resume()'); await this.#channel.request(FbsRequest.Method.DATACONSUMER_RESUME, undefined, undefined, this.#internal.dataConsumerId); const wasPaused = this.#paused; this.#paused = false; // Emit observer event. if (wasPaused && !this.#dataProducerPaused) { this.#observer.safeEmit('resume'); } } async setBufferedAmountLowThreshold(threshold) { logger.debug(`setBufferedAmountLowThreshold() [threshold:${threshold}]`); /* Build Request. */ const requestOffset = FbsDataConsumer.SetBufferedAmountLowThresholdRequest.createSetBufferedAmountLowThresholdRequest(this.#channel.bufferBuilder, threshold); await this.#channel.request(FbsRequest.Method.DATACONSUMER_SET_BUFFERED_AMOUNT_LOW_THRESHOLD, FbsRequest.Body.DataConsumer_SetBufferedAmountLowThresholdRequest, requestOffset, this.#internal.dataConsumerId); } async getBufferedAmount() { logger.debug('getBufferedAmount()'); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_GET_BUFFERED_AMOUNT, undefined, undefined, this.#internal.dataConsumerId); const data = new FbsDataConsumer.GetBufferedAmountResponse(); response.body(data); return data.bufferedAmount(); } async send(message, ppid) { if (typeof message !== 'string' && !Buffer.isBuffer(message)) { throw new TypeError('message must be a string or a Buffer'); } /* * +-------------------------------+----------+ * | Value | SCTP | * | | PPID | * +-------------------------------+----------+ * | WebRTC String | 51 | * | WebRTC Binary Partial | 52 | * | (Deprecated) | | * | WebRTC Binary | 53 | * | WebRTC String Partial | 54 | * | (Deprecated) | | * | WebRTC String Empty | 56 | * | WebRTC Binary Empty | 57 | * +-------------------------------+----------+ */ if (typeof ppid !== 'number') { ppid = typeof message === 'string' ? message.length > 0 ? 51 : 56 : message.length > 0 ? 53 : 57; } // Ensure we honor PPIDs. if (ppid === 56) { message = ' '; } else if (ppid === 57) { message = Buffer.alloc(1); } const builder = this.#channel.bufferBuilder; if (typeof message === 'string') { message = Buffer.from(message); } const dataOffset = FbsDataConsumer.SendRequest.createDataVector(builder, message); const requestOffset = FbsDataConsumer.SendRequest.createSendRequest(builder, ppid, dataOffset); await this.#channel.request(FbsRequest.Method.DATACONSUMER_SEND, FbsRequest.Body.DataConsumer_SendRequest, requestOffset, this.#internal.dataConsumerId); } async setSubchannels(subchannels) { logger.debug('setSubchannels()'); /* Build Request. */ const requestOffset = new FbsDataConsumer.SetSubchannelsRequestT(subchannels).pack(this.#channel.bufferBuilder); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_SET_SUBCHANNELS, FbsRequest.Body.DataConsumer_SetSubchannelsRequest, requestOffset, this.#internal.dataConsumerId); /* Decode Response. */ const data = new FbsDataConsumer.SetSubchannelsResponse(); response.body(data); // Update subchannels. this.#subchannels = fbsUtils.parseVector(data, 'subchannels'); } async addSubchannel(subchannel) { logger.debug('addSubchannel()'); /* Build Request. */ const requestOffset = FbsDataConsumer.AddSubchannelRequest.createAddSubchannelRequest(this.#channel.bufferBuilder, subchannel); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_ADD_SUBCHANNEL, FbsRequest.Body.DataConsumer_AddSubchannelRequest, requestOffset, this.#internal.dataConsumerId); /* Decode Response. */ const data = new FbsDataConsumer.AddSubchannelResponse(); response.body(data); // Update subchannels. this.#subchannels = fbsUtils.parseVector(data, 'subchannels'); } async removeSubchannel(subchannel) { logger.debug('removeSubchannel()'); /* Build Request. */ const requestOffset = FbsDataConsumer.RemoveSubchannelRequest.createRemoveSubchannelRequest(this.#channel.bufferBuilder, subchannel); const response = await this.#channel.request(FbsRequest.Method.DATACONSUMER_REMOVE_SUBCHANNEL, FbsRequest.Body.DataConsumer_RemoveSubchannelRequest, requestOffset, this.#internal.dataConsumerId); /* Decode Response. */ const data = new FbsDataConsumer.RemoveSubchannelResponse(); response.body(data); // Update subchannels. this.#subchannels = fbsUtils.parseVector(data, 'subchannels'); } handleWorkerNotifications() { this.#channel.on(this.#internal.dataConsumerId, (event, data) => { switch (event) { case notification_1.Event.DATACONSUMER_DATAPRODUCER_CLOSE: { if (this.#closed) { break; } this.#closed = true; // Remove notification subscriptions. this.#channel.removeAllListeners(this.#internal.dataConsumerId); this.emit('@dataproducerclose'); this.safeEmit('dataproducerclose'); // Emit observer event. this.#observer.safeEmit('close'); break; } case notification_1.Event.DATACONSUMER_DATAPRODUCER_PAUSE: { if (this.#dataProducerPaused) { break; } this.#dataProducerPaused = true; this.safeEmit('dataproducerpause'); // Emit observer event. if (!this.#paused) { this.#observer.safeEmit('pause'); } break; } case notification_1.Event.DATACONSUMER_DATAPRODUCER_RESUME: { if (!this.#dataProducerPaused) { break; } this.#dataProducerPaused = false; this.safeEmit('dataproducerresume'); // Emit observer event. if (!this.#paused) { this.#observer.safeEmit('resume'); } break; } case notification_1.Event.DATACONSUMER_SCTP_SENDBUFFER_FULL: { this.safeEmit('sctpsendbufferfull'); break; } case notification_1.Event.DATACONSUMER_BUFFERED_AMOUNT_LOW: { const notification = new FbsDataConsumer.BufferedAmountLowNotification(); data.body(notification); const bufferedAmount = notification.bufferedAmount(); this.safeEmit('bufferedamountlow', bufferedAmount); break; } case notification_1.Event.DATACONSUMER_MESSAGE: { if (this.#closed) { break; } const notification = new FbsDataConsumer.MessageNotification(); data.body(notification); this.safeEmit('message', Buffer.from(notification.dataArray()), notification.ppid()); break; } default: { logger.error(`ignoring unknown event "${event}"`); } } }); } handleListenerError() { this.on('listenererror', (eventName, error) => { logger.error(`event listener threw an error [eventName:${eventName}]:`, error); }); } } exports.DataConsumerImpl = DataConsumerImpl; function dataConsumerTypeToFbs(type) { switch (type) { case 'sctp': { return FbsDataProducer.Type.SCTP; } case 'direct': { return FbsDataProducer.Type.DIRECT; } default: { throw new TypeError('invalid DataConsumerType: ${type}'); } } } function dataConsumerTypeFromFbs(type) { switch (type) { case FbsDataProducer.Type.SCTP: { return 'sctp'; } case FbsDataProducer.Type.DIRECT: { return 'direct'; } } } function parseDataConsumerDumpResponse(data) { return { id: data.id(), dataProducerId: data.dataProducerId(), type: dataConsumerTypeFromFbs(data.type()), sctpStreamParameters: data.sctpStreamParameters() !== null ? (0, sctpParametersFbsUtils_1.parseSctpStreamParameters)(data.sctpStreamParameters()) : undefined, label: data.label(), protocol: data.protocol(), bufferedAmountLowThreshold: data.bufferedAmountLowThreshold(), paused: data.paused(), dataProducerPaused: data.dataProducerPaused(), subchannels: fbsUtils.parseVector(data, 'subchannels'), }; } function parseDataConsumerStats(binary) { return { type: 'data-consumer', timestamp: Number(binary.timestamp()), label: binary.label(), protocol: binary.protocol(), messagesSent: Number(binary.messagesSent()), bytesSent: Number(binary.bytesSent()), bufferedAmount: binary.bufferedAmount(), }; }