diff --git a/node/src/ActiveSpeakerObserver.ts b/node/src/ActiveSpeakerObserver.ts index 609e6581e5..10f6875057 100644 --- a/node/src/ActiveSpeakerObserver.ts +++ b/node/src/ActiveSpeakerObserver.ts @@ -1,57 +1,33 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import { - RtpObserver, - RtpObserverEvents, - RtpObserverObserverEvents, - RtpObserverConstructorOptions, -} from './RtpObserver'; -import { Producer } from './Producer'; + ActiveSpeakerObserverInterface, + ActiveSpeakerObserverDominantSpeaker, + ActiveSpeakerObserverEvents, + ActiveSpeakerObserverObserver, + ActiveSpeakerObserverObserverEvents, +} from './ActiveSpeakerObserverInterface'; +import { RtpObserverInterface } from './RtpObserverInterface'; +import { RtpObserver, RtpObserverConstructorOptions } from './RtpObserver'; import { AppData } from './types'; import { Event, Notification } from './fbs/notification'; import * as FbsActiveSpeakerObserver from './fbs/active-speaker-observer'; -export type ActiveSpeakerObserverOptions< - ActiveSpeakerObserverAppData extends AppData = AppData, -> = { - interval?: number; - - /** - * Custom application data. - */ - appData?: ActiveSpeakerObserverAppData; -}; - -export type ActiveSpeakerObserverDominantSpeaker = { - /** - * The audio Producer instance. - */ - producer: Producer; -}; - -export type ActiveSpeakerObserverEvents = RtpObserverEvents & { - dominantspeaker: [ActiveSpeakerObserverDominantSpeaker]; -}; - -export type ActiveSpeakerObserverObserver = - EnhancedEventEmitter; - -export type ActiveSpeakerObserverObserverEvents = RtpObserverObserverEvents & { - dominantspeaker: [ActiveSpeakerObserverDominantSpeaker]; -}; - type RtpObserverObserverConstructorOptions = RtpObserverConstructorOptions; const logger = new Logger('ActiveSpeakerObserver'); export class ActiveSpeakerObserver< - ActiveSpeakerObserverAppData extends AppData = AppData, -> extends RtpObserver< - ActiveSpeakerObserverAppData, - ActiveSpeakerObserverEvents, - ActiveSpeakerObserverObserver -> { + ActiveSpeakerObserverAppData extends AppData = AppData, + > + extends RtpObserver< + ActiveSpeakerObserverAppData, + ActiveSpeakerObserverEvents, + ActiveSpeakerObserverObserver + > + implements RtpObserverInterface, ActiveSpeakerObserverInterface +{ /** * @private */ diff --git a/node/src/ActiveSpeakerObserverInterface.ts b/node/src/ActiveSpeakerObserverInterface.ts new file mode 100644 index 0000000000..3e8bd0e3d9 --- /dev/null +++ b/node/src/ActiveSpeakerObserverInterface.ts @@ -0,0 +1,47 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + RtpObserverInterface, + RtpObserverEvents, + RtpObserverObserverEvents, +} from './RtpObserverInterface'; +import { ProducerInterface } from './ProducerInterface'; +import { AppData } from './types'; + +export type ActiveSpeakerObserverOptions< + ActiveSpeakerObserverAppData extends AppData = AppData, +> = { + interval?: number; + + /** + * Custom application data. + */ + appData?: ActiveSpeakerObserverAppData; +}; + +export type ActiveSpeakerObserverDominantSpeaker = { + /** + * The audio Producer instance. + */ + producer: ProducerInterface; +}; + +export type ActiveSpeakerObserverEvents = RtpObserverEvents & { + dominantspeaker: [ActiveSpeakerObserverDominantSpeaker]; +}; + +export type ActiveSpeakerObserverObserver = + EnhancedEventEmitter; + +export type ActiveSpeakerObserverObserverEvents = RtpObserverObserverEvents & { + dominantspeaker: [ActiveSpeakerObserverDominantSpeaker]; +}; + +export interface ActiveSpeakerObserverInterface< + ActiveSpeakerObserverAppData extends AppData = AppData, +> extends RtpObserverInterface< + ActiveSpeakerObserverAppData, + ActiveSpeakerObserverEvents, + ActiveSpeakerObserverObserver + > { + get observer(): ActiveSpeakerObserverObserver; +} diff --git a/node/src/AudioLevelObserver.ts b/node/src/AudioLevelObserver.ts index d429933954..bea9bb8a02 100644 --- a/node/src/AudioLevelObserver.ts +++ b/node/src/AudioLevelObserver.ts @@ -1,80 +1,35 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import { - RtpObserver, - RtpObserverEvents, - RtpObserverObserverEvents, - RtpObserverConstructorOptions, -} from './RtpObserver'; -import { Producer } from './Producer'; + AudioLevelObserverInterface, + AudioLevelObserverVolume, + AudioLevelObserverEvents, + AudioLevelObserverObserver, + AudioLevelObserverObserverEvents, +} from './AudioLevelObserverInterface'; +import { RtpObserverInterface } from './RtpObserverInterface'; +import { RtpObserver, RtpObserverConstructorOptions } from './RtpObserver'; +import { ProducerInterface } from './ProducerInterface'; import { AppData } from './types'; import * as utils from './utils'; import { Event, Notification } from './fbs/notification'; import * as FbsAudioLevelObserver from './fbs/audio-level-observer'; -export type AudioLevelObserverOptions< - AudioLevelObserverAppData extends AppData = AppData, -> = { - /** - * Maximum number of entries in the 'volumes”' event. Default 1. - */ - maxEntries?: number; - - /** - * Minimum average volume (in dBvo from -127 to 0) for entries in the - * 'volumes' event. Default -80. - */ - threshold?: number; - - /** - * Interval in ms for checking audio volumes. Default 1000. - */ - interval?: number; - - /** - * Custom application data. - */ - appData?: AudioLevelObserverAppData; -}; - -export type AudioLevelObserverVolume = { - /** - * The audio Producer instance. - */ - producer: Producer; - - /** - * The average volume (in dBvo from -127 to 0) of the audio Producer in the - * last interval. - */ - volume: number; -}; - -export type AudioLevelObserverEvents = RtpObserverEvents & { - volumes: [AudioLevelObserverVolume[]]; - silence: []; -}; - -export type AudioLevelObserverObserver = - EnhancedEventEmitter; - -export type AudioLevelObserverObserverEvents = RtpObserverObserverEvents & { - volumes: [AudioLevelObserverVolume[]]; - silence: []; -}; - type AudioLevelObserverConstructorOptions = RtpObserverConstructorOptions; const logger = new Logger('AudioLevelObserver'); export class AudioLevelObserver< - AudioLevelObserverAppData extends AppData = AppData, -> extends RtpObserver< - AudioLevelObserverAppData, - AudioLevelObserverEvents, - AudioLevelObserverObserver -> { + AudioLevelObserverAppData extends AppData = AppData, + > + extends RtpObserver< + AudioLevelObserverAppData, + AudioLevelObserverEvents, + AudioLevelObserverObserver + > + implements RtpObserverInterface, AudioLevelObserverInterface +{ /** * @private */ @@ -125,7 +80,9 @@ export class AudioLevelObserver< volume, }) ) - .filter(({ producer }: { producer: Producer }) => producer); + .filter( + ({ producer }: { producer: ProducerInterface }) => producer + ); if (volumes.length > 0) { this.safeEmit('volumes', volumes); diff --git a/node/src/AudioLevelObserverInterface.ts b/node/src/AudioLevelObserverInterface.ts new file mode 100644 index 0000000000..ce808b39b6 --- /dev/null +++ b/node/src/AudioLevelObserverInterface.ts @@ -0,0 +1,69 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + RtpObserverInterface, + RtpObserverEvents, + RtpObserverObserverEvents, +} from './RtpObserverInterface'; +import { ProducerInterface } from './ProducerInterface'; +import { AppData } from './types'; + +export type AudioLevelObserverOptions< + AudioLevelObserverAppData extends AppData = AppData, +> = { + /** + * Maximum number of entries in the 'volumes”' event. Default 1. + */ + maxEntries?: number; + + /** + * Minimum average volume (in dBvo from -127 to 0) for entries in the + * 'volumes' event. Default -80. + */ + threshold?: number; + + /** + * Interval in ms for checking audio volumes. Default 1000. + */ + interval?: number; + + /** + * Custom application data. + */ + appData?: AudioLevelObserverAppData; +}; + +export type AudioLevelObserverVolume = { + /** + * The audio Producer instance. + */ + producer: ProducerInterface; + + /** + * The average volume (in dBvo from -127 to 0) of the audio Producer in the + * last interval. + */ + volume: number; +}; + +export type AudioLevelObserverEvents = RtpObserverEvents & { + volumes: [AudioLevelObserverVolume[]]; + silence: []; +}; + +export type AudioLevelObserverObserver = + EnhancedEventEmitter; + +export type AudioLevelObserverObserverEvents = RtpObserverObserverEvents & { + volumes: [AudioLevelObserverVolume[]]; + silence: []; +}; + +export interface AudioLevelObserverInterface< + AudioLevelObserverAppData extends AppData = AppData, +> extends RtpObserverInterface< + AudioLevelObserverAppData, + AudioLevelObserverEvents, + AudioLevelObserverObserver + > { + get observer(): AudioLevelObserverObserver; +} diff --git a/node/src/Consumer.ts b/node/src/Consumer.ts index 5945f8993b..31bb0d0736 100644 --- a/node/src/Consumer.ts +++ b/node/src/Consumer.ts @@ -1,17 +1,37 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; +import { + ConsumerInterface, + ConsumerType, + ConsumerScore, + ConsumerLayers, + ConsumerDump, + SimpleConsumerDump, + SimulcastConsumerDump, + SvcConsumerDump, + PipeConsumerDump, + BaseConsumerDump, + RtpStreamDump, + RtpStreamParametersDump, + RtxStreamDump, + RtxStreamParameters, + ConsumerStat, + ConsumerTraceEventType, + ConsumerTraceEventData, + ConsumerEvents, + ConsumerObserver, + ConsumerObserverEvents, +} from './ConsumerInterface'; import { Channel } from './Channel'; import { TransportInternal } from './Transport'; -import { ProducerStat } from './Producer'; +import { ProducerStat } from './ProducerInterface'; import { MediaKind, - RtpCapabilities, - RtpEncodingParameters, RtpParameters, parseRtpEncodingParameters, parseRtpParameters, } from './RtpParameters'; -import { parseRtpStreamStats, RtpStreamSendStats } from './RtpStream'; +import { parseRtpStreamStats } from './RtpStream'; import { AppData } from './types'; import * as utils from './utils'; import { Event, Notification } from './fbs/notification'; @@ -25,200 +45,6 @@ import * as FbsRtxStream from './fbs/rtx-stream'; import { Type as FbsRtpParametersType } from './fbs/rtp-parameters'; import * as FbsRtpParameters from './fbs/rtp-parameters'; -export type ConsumerOptions = { - /** - * The id of the Producer to consume. - */ - producerId: string; - - /** - * RTP capabilities of the consuming endpoint. - */ - rtpCapabilities: RtpCapabilities; - - /** - * Whether the consumer must start in paused mode. Default false. - * - * When creating a video Consumer, it's recommended to set paused to true, - * then transmit the Consumer parameters to the consuming endpoint and, once - * the consuming endpoint has created its local side Consumer, unpause the - * server side Consumer using the resume() method. This is an optimization - * to make it possible for the consuming endpoint to render the video as far - * as possible. If the server side Consumer was created with paused: false, - * mediasoup will immediately request a key frame to the remote Producer and - * suych a key frame may reach the consuming endpoint even before it's ready - * to consume it, generating “black” video until the device requests a keyframe - * by itself. - */ - paused?: boolean; - - /** - * The MID for the Consumer. If not specified, a sequentially growing - * number will be assigned. - */ - mid?: string; - - /** - * Preferred spatial and temporal layer for simulcast or SVC media sources. - * If unset, the highest ones are selected. - */ - preferredLayers?: ConsumerLayers; - - /** - * Whether this Consumer should enable RTP retransmissions, storing sent RTP - * and processing the incoming RTCP NACK from the remote Consumer. If not set - * it's true by default for video codecs and false for audio codecs. If set - * to true, NACK will be enabled if both endpoints (mediasoup and the remote - * Consumer) support NACK for this codec. When it comes to audio codecs, just - * OPUS supports NACK. - */ - enableRtx?: boolean; - - /** - * Whether this Consumer should ignore DTX packets (only valid for Opus codec). - * If set, DTX packets are not forwarded to the remote Consumer. - */ - ignoreDtx?: boolean; - - /** - * Whether this Consumer should consume all RTP streams generated by the - * Producer. - */ - pipe?: boolean; - - /** - * Custom application data. - */ - appData?: ConsumerAppData; -}; - -/** - * Valid types for 'trace' event. - */ -export type ConsumerTraceEventType = - | 'rtp' - | 'keyframe' - | 'nack' - | 'pli' - | 'fir'; - -/** - * 'trace' event data. - */ -export type ConsumerTraceEventData = { - /** - * Trace type. - */ - type: ConsumerTraceEventType; - - /** - * Event timestamp. - */ - timestamp: number; - - /** - * Event direction. - */ - direction: 'in' | 'out'; - - /** - * Per type information. - */ - info: any; -}; - -export type ConsumerScore = { - /** - * The score of the RTP stream of the consumer. - */ - score: number; - - /** - * The score of the currently selected RTP stream of the producer. - */ - producerScore: number; - - /** - * The scores of all RTP streams in the producer ordered by encoding (just - * useful when the producer uses simulcast). - */ - producerScores: number[]; -}; - -export type ConsumerLayers = { - /** - * The spatial layer index (from 0 to N). - */ - spatialLayer: number; - - /** - * The temporal layer index (from 0 to N). - */ - temporalLayer?: number; -}; - -export type ConsumerStat = RtpStreamSendStats; - -/** - * Consumer type. - */ -export type ConsumerType = 'simple' | 'simulcast' | 'svc' | 'pipe'; - -export type ConsumerEvents = { - transportclose: []; - producerclose: []; - producerpause: []; - producerresume: []; - score: [ConsumerScore]; - layerschange: [ConsumerLayers?]; - trace: [ConsumerTraceEventData]; - rtp: [Buffer]; - listenererror: [string, Error]; - // Private events. - '@close': []; - '@producerclose': []; -}; - -export type ConsumerObserver = EnhancedEventEmitter; - -export type ConsumerObserverEvents = { - close: []; - pause: []; - resume: []; - score: [ConsumerScore]; - layerschange: [ConsumerLayers?]; - trace: [ConsumerTraceEventData]; -}; - -export type SimpleConsumerDump = BaseConsumerDump & { - type: string; - rtpStream: RtpStreamDump; -}; - -export type SimulcastConsumerDump = BaseConsumerDump & { - type: string; - rtpStream: RtpStreamDump; - preferredSpatialLayer: number; - targetSpatialLayer: number; - currentSpatialLayer: number; - preferredTemporalLayer: number; - targetTemporalLayer: number; - currentTemporalLayer: number; -}; - -export type SvcConsumerDump = SimulcastConsumerDump; - -export type PipeConsumerDump = BaseConsumerDump & { - type: string; - rtpStreams: RtpStreamDump[]; -}; - -export type ConsumerDump = - | SimpleConsumerDump - | SimulcastConsumerDump - | SvcConsumerDump - | PipeConsumerDump; - type ConsumerInternal = TransportInternal & { consumerId: string; }; @@ -230,62 +56,12 @@ type ConsumerData = { type: ConsumerType; }; -type BaseConsumerDump = { - id: string; - producerId: string; - kind: MediaKind; - rtpParameters: RtpParameters; - consumableRtpEncodings?: RtpEncodingParameters[]; - supportedCodecPayloadTypes: number[]; - traceEventTypes: string[]; - paused: boolean; - producerPaused: boolean; - priority: number; -}; - -type RtpStreamParameters = { - encodingIdx: number; - ssrc: number; - payloadType: number; - mimeType: string; - clockRate: number; - rid?: string; - cname: string; - rtxSsrc?: number; - rtxPayloadType?: number; - useNack: boolean; - usePli: boolean; - useFir: boolean; - useInBandFec: boolean; - useDtx: boolean; - spatialLayers: number; - temporalLayers: number; -}; - -type RtpStreamDump = { - params: RtpStreamParameters; - score: number; - rtxStream?: RtxStreamDump; -}; - -type RtxStreamParameters = { - ssrc: number; - payloadType: number; - mimeType: string; - clockRate: number; - rrid?: string; - cname: string; -}; - -type RtxStreamDump = { - params: RtxStreamParameters; -}; - const logger = new Logger('Consumer'); -export class Consumer< - ConsumerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class Consumer + extends EnhancedEventEmitter + implements ConsumerInterface +{ // Internal data. readonly #internal: ConsumerInternal; @@ -1000,9 +776,25 @@ function parseConsumerLayers(data: FbsConsumer.ConsumerLayers): ConsumerLayers { }; } +function parseRtpStream(data: FbsRtpStream.Dump): RtpStreamDump { + const params = parseRtpStreamParameters(data.params()!); + + let rtxStream: RtxStreamDump | undefined; + + if (data.rtxStream()) { + rtxStream = parseRtxStream(data.rtxStream()!); + } + + return { + params, + score: data.score(), + rtxStream, + }; +} + function parseRtpStreamParameters( data: FbsRtpStream.Params -): RtpStreamParameters { +): RtpStreamParametersDump { return { encodingIdx: data.encodingIdx(), ssrc: data.ssrc(), @@ -1024,6 +816,14 @@ function parseRtpStreamParameters( }; } +function parseRtxStream(data: FbsRtxStream.RtxDump): RtxStreamDump { + const params = parseRtxStreamParameters(data.params()!); + + return { + params, + }; +} + function parseRtxStreamParameters( data: FbsRtxStream.Params ): RtxStreamParameters { @@ -1037,30 +837,6 @@ function parseRtxStreamParameters( }; } -function parseRtxStream(data: FbsRtxStream.RtxDump): RtxStreamDump { - const params = parseRtxStreamParameters(data.params()!); - - return { - params, - }; -} - -function parseRtpStream(data: FbsRtpStream.Dump): RtpStreamDump { - const params = parseRtpStreamParameters(data.params()!); - - let rtxStream: RtxStreamDump | undefined; - - if (data.rtxStream()) { - rtxStream = parseRtxStream(data.rtxStream()!); - } - - return { - params, - score: data.score(), - rtxStream, - }; -} - function parseBaseConsumerDump( data: FbsConsumer.BaseConsumerDump ): BaseConsumerDump { diff --git a/node/src/ConsumerInterface.ts b/node/src/ConsumerInterface.ts new file mode 100644 index 0000000000..2bd9ef9486 --- /dev/null +++ b/node/src/ConsumerInterface.ts @@ -0,0 +1,318 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { ProducerStat } from './ProducerInterface'; +import { + MediaKind, + RtpCapabilities, + RtpEncodingParameters, + RtpParameters, +} from './RtpParameters'; +import { RtpStreamSendStats } from './RtpStream'; +import { AppData } from './types'; + +export type ConsumerOptions = { + /** + * The id of the Producer to consume. + */ + producerId: string; + + /** + * RTP capabilities of the consuming endpoint. + */ + rtpCapabilities: RtpCapabilities; + + /** + * Whether the consumer must start in paused mode. Default false. + * + * When creating a video Consumer, it's recommended to set paused to true, + * then transmit the Consumer parameters to the consuming endpoint and, once + * the consuming endpoint has created its local side Consumer, unpause the + * server side Consumer using the resume() method. This is an optimization + * to make it possible for the consuming endpoint to render the video as far + * as possible. If the server side Consumer was created with paused: false, + * mediasoup will immediately request a key frame to the remote Producer and + * suych a key frame may reach the consuming endpoint even before it's ready + * to consume it, generating “black” video until the device requests a keyframe + * by itself. + */ + paused?: boolean; + + /** + * The MID for the Consumer. If not specified, a sequentially growing + * number will be assigned. + */ + mid?: string; + + /** + * Preferred spatial and temporal layer for simulcast or SVC media sources. + * If unset, the highest ones are selected. + */ + preferredLayers?: ConsumerLayers; + + /** + * Whether this Consumer should enable RTP retransmissions, storing sent RTP + * and processing the incoming RTCP NACK from the remote Consumer. If not set + * it's true by default for video codecs and false for audio codecs. If set + * to true, NACK will be enabled if both endpoints (mediasoup and the remote + * Consumer) support NACK for this codec. When it comes to audio codecs, just + * OPUS supports NACK. + */ + enableRtx?: boolean; + + /** + * Whether this Consumer should ignore DTX packets (only valid for Opus codec). + * If set, DTX packets are not forwarded to the remote Consumer. + */ + ignoreDtx?: boolean; + + /** + * Whether this Consumer should consume all RTP streams generated by the + * Producer. + */ + pipe?: boolean; + + /** + * Custom application data. + */ + appData?: ConsumerAppData; +}; + +/** + * Consumer type. + */ +export type ConsumerType = 'simple' | 'simulcast' | 'svc' | 'pipe'; + +export type ConsumerScore = { + /** + * The score of the RTP stream of the consumer. + */ + score: number; + + /** + * The score of the currently selected RTP stream of the producer. + */ + producerScore: number; + + /** + * The scores of all RTP streams in the producer ordered by encoding (just + * useful when the producer uses simulcast). + */ + producerScores: number[]; +}; + +export type ConsumerLayers = { + /** + * The spatial layer index (from 0 to N). + */ + spatialLayer: number; + + /** + * The temporal layer index (from 0 to N). + */ + temporalLayer?: number; +}; + +export type ConsumerDump = + | SimpleConsumerDump + | SimulcastConsumerDump + | SvcConsumerDump + | PipeConsumerDump; + +export type SimpleConsumerDump = BaseConsumerDump & { + type: string; + rtpStream: RtpStreamDump; +}; + +export type SimulcastConsumerDump = BaseConsumerDump & { + type: string; + rtpStream: RtpStreamDump; + preferredSpatialLayer: number; + targetSpatialLayer: number; + currentSpatialLayer: number; + preferredTemporalLayer: number; + targetTemporalLayer: number; + currentTemporalLayer: number; +}; + +export type SvcConsumerDump = SimulcastConsumerDump; + +export type PipeConsumerDump = BaseConsumerDump & { + type: string; + rtpStreams: RtpStreamDump[]; +}; + +export type BaseConsumerDump = { + id: string; + producerId: string; + kind: MediaKind; + rtpParameters: RtpParameters; + consumableRtpEncodings?: RtpEncodingParameters[]; + supportedCodecPayloadTypes: number[]; + traceEventTypes: string[]; + paused: boolean; + producerPaused: boolean; + priority: number; +}; + +export type RtpStreamDump = { + params: RtpStreamParametersDump; + score: number; + rtxStream?: RtxStreamDump; +}; + +export type RtpStreamParametersDump = { + encodingIdx: number; + ssrc: number; + payloadType: number; + mimeType: string; + clockRate: number; + rid?: string; + cname: string; + rtxSsrc?: number; + rtxPayloadType?: number; + useNack: boolean; + usePli: boolean; + useFir: boolean; + useInBandFec: boolean; + useDtx: boolean; + spatialLayers: number; + temporalLayers: number; +}; + +export type RtxStreamDump = { + params: RtxStreamParameters; +}; + +export type RtxStreamParameters = { + ssrc: number; + payloadType: number; + mimeType: string; + clockRate: number; + rrid?: string; + cname: string; +}; + +export type ConsumerStat = RtpStreamSendStats; + +/** + * Valid types for 'trace' event. + */ +export type ConsumerTraceEventType = + | 'rtp' + | 'keyframe' + | 'nack' + | 'pli' + | 'fir'; + +/** + * 'trace' event data. + */ +export type ConsumerTraceEventData = { + /** + * Trace type. + */ + type: ConsumerTraceEventType; + + /** + * Event timestamp. + */ + timestamp: number; + + /** + * Event direction. + */ + direction: 'in' | 'out'; + + /** + * Per type information. + */ + info: any; +}; + +export type ConsumerEvents = { + transportclose: []; + producerclose: []; + producerpause: []; + producerresume: []; + score: [ConsumerScore]; + layerschange: [ConsumerLayers?]; + trace: [ConsumerTraceEventData]; + rtp: [Buffer]; + listenererror: [string, Error]; + // Private events. + '@close': []; + '@producerclose': []; +}; + +export type ConsumerObserver = EnhancedEventEmitter; + +export type ConsumerObserverEvents = { + close: []; + pause: []; + resume: []; + score: [ConsumerScore]; + layerschange: [ConsumerLayers?]; + trace: [ConsumerTraceEventData]; +}; + +export interface ConsumerInterface + extends EnhancedEventEmitter { + get id(): string; + + get producerId(): string; + + get closed(): boolean; + + get kind(): MediaKind; + + get rtpParameters(): RtpParameters; + + get type(): ConsumerType; + + get paused(): boolean; + + get producerPaused(): boolean; + + get priority(): number; + + get score(): ConsumerScore; + + get preferredLayers(): ConsumerLayers | undefined; + + get currentLayers(): ConsumerLayers | undefined; + + get appData(): ConsumerAppData; + + set appData(appData: ConsumerAppData); + + get observer(): ConsumerObserver; + + close(): void; + + /** + * Transport was closed. + * + * @private + */ + transportClosed(): void; + + dump(): Promise; + + getStats(): Promise<(ConsumerStat | ProducerStat)[]>; + + pause(): Promise; + + resume(): Promise; + + setPreferredLayers({ + spatialLayer, + temporalLayer, + }: ConsumerLayers): Promise; + + setPriority(priority: number): Promise; + + unsetPriority(): Promise; + + requestKeyFrame(): Promise; + + enableTraceEvent(types?: ConsumerTraceEventType[]): Promise; +} diff --git a/node/src/DataConsumer.ts b/node/src/DataConsumer.ts index f05b998d17..256232b7e9 100644 --- a/node/src/DataConsumer.ts +++ b/node/src/DataConsumer.ts @@ -1,5 +1,14 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; +import { + DataConsumerInterface, + DataConsumerType, + DataConsumerDump, + DataConsumerStat, + DataConsumerEvents, + DataConsumerObserver, + DataConsumerObserverEvents, +} from './DataConsumerInterface'; import { Channel } from './Channel'; import { TransportInternal } from './Transport'; import { @@ -14,100 +23,6 @@ import * as FbsRequest from './fbs/request'; import * as FbsDataConsumer from './fbs/data-consumer'; import * as FbsDataProducer from './fbs/data-producer'; -export type DataConsumerOptions = - { - /** - * The id of the DataProducer to consume. - */ - dataProducerId: string; - - /** - * Just if consuming over SCTP. - * Whether data messages must be received in order. If true the messages will - * be sent reliably. Defaults to the value in the DataProducer if it has type - * 'sctp' or to true if it has type 'direct'. - */ - ordered?: boolean; - - /** - * Just if consuming over SCTP. - * When ordered is false indicates the time (in milliseconds) after which a - * SCTP packet will stop being retransmitted. Defaults to the value in the - * DataProducer if it has type 'sctp' or unset if it has type 'direct'. - */ - maxPacketLifeTime?: number; - - /** - * Just if consuming over SCTP. - * When ordered is false indicates the maximum number of times a packet will - * be retransmitted. Defaults to the value in the DataProducer if it has type - * 'sctp' or unset if it has type 'direct'. - */ - maxRetransmits?: number; - - /** - * Whether the data consumer must start in paused mode. Default false. - */ - paused?: boolean; - - /** - * Subchannels this data consumer initially subscribes to. - * Only used in case this data consumer receives messages from a local data - * producer that specifies subchannel(s) when calling send(). - */ - subchannels?: number[]; - - /** - * Custom application data. - */ - appData?: DataConsumerAppData; - }; - -export type DataConsumerStat = { - type: string; - timestamp: number; - label: string; - protocol: string; - messagesSent: number; - bytesSent: number; - bufferedAmount: number; -}; - -/** - * DataConsumer type. - */ -export type DataConsumerType = 'sctp' | 'direct'; - -export type DataConsumerEvents = { - transportclose: []; - dataproducerclose: []; - dataproducerpause: []; - dataproducerresume: []; - message: [Buffer, number]; - sctpsendbufferfull: []; - bufferedamountlow: [number]; - listenererror: [string, Error]; - // Private events. - '@close': []; - '@dataproducerclose': []; -}; - -export type DataConsumerObserver = - EnhancedEventEmitter; - -export type DataConsumerObserverEvents = { - close: []; - pause: []; - resume: []; -}; - -type DataConsumerDump = DataConsumerData & { - id: string; - paused: boolean; - dataProducerPaused: boolean; - subchannels: number[]; -}; - type DataConsumerInternal = TransportInternal & { dataConsumerId: string; }; @@ -123,9 +38,10 @@ type DataConsumerData = { const logger = new Logger('DataConsumer'); -export class DataConsumer< - DataConsumerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class DataConsumer + extends EnhancedEventEmitter + implements DataConsumerInterface +{ // Internal data. readonly #internal: DataConsumerInternal; @@ -447,6 +363,26 @@ export class DataConsumer< ); } + /** + * Get buffered amount size. + */ + async getBufferedAmount(): Promise { + 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(); + } + /** * Send data. */ @@ -513,26 +449,6 @@ export class DataConsumer< ); } - /** - * Get buffered amount size. - */ - async getBufferedAmount(): Promise { - 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(); - } - /** * Set subchannels. */ diff --git a/node/src/DataConsumerInterface.ts b/node/src/DataConsumerInterface.ts new file mode 100644 index 0000000000..bc74283c51 --- /dev/null +++ b/node/src/DataConsumerInterface.ts @@ -0,0 +1,162 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { SctpStreamParameters } from './SctpParameters'; +import { AppData } from './types'; + +export type DataConsumerOptions = + { + /** + * The id of the DataProducer to consume. + */ + dataProducerId: string; + + /** + * Just if consuming over SCTP. + * Whether data messages must be received in order. If true the messages will + * be sent reliably. Defaults to the value in the DataProducer if it has type + * 'sctp' or to true if it has type 'direct'. + */ + ordered?: boolean; + + /** + * Just if consuming over SCTP. + * When ordered is false indicates the time (in milliseconds) after which a + * SCTP packet will stop being retransmitted. Defaults to the value in the + * DataProducer if it has type 'sctp' or unset if it has type 'direct'. + */ + maxPacketLifeTime?: number; + + /** + * Just if consuming over SCTP. + * When ordered is false indicates the maximum number of times a packet will + * be retransmitted. Defaults to the value in the DataProducer if it has type + * 'sctp' or unset if it has type 'direct'. + */ + maxRetransmits?: number; + + /** + * Whether the data consumer must start in paused mode. Default false. + */ + paused?: boolean; + + /** + * Subchannels this data consumer initially subscribes to. + * Only used in case this data consumer receives messages from a local data + * producer that specifies subchannel(s) when calling send(). + */ + subchannels?: number[]; + + /** + * Custom application data. + */ + appData?: DataConsumerAppData; + }; + +/** + * DataConsumer type. + */ +export type DataConsumerType = 'sctp' | 'direct'; + +export type DataConsumerDump = { + id: string; + paused: boolean; + dataProducerPaused: boolean; + subchannels: number[]; + dataProducerId: string; + type: DataConsumerType; + sctpStreamParameters?: SctpStreamParameters; + label: string; + protocol: string; + bufferedAmountLowThreshold: number; +}; + +export type DataConsumerStat = { + type: string; + timestamp: number; + label: string; + protocol: string; + messagesSent: number; + bytesSent: number; + bufferedAmount: number; +}; + +export type DataConsumerEvents = { + transportclose: []; + dataproducerclose: []; + dataproducerpause: []; + dataproducerresume: []; + message: [Buffer, number]; + sctpsendbufferfull: []; + bufferedamountlow: [number]; + listenererror: [string, Error]; + // Private events. + '@close': []; + '@dataproducerclose': []; +}; + +export type DataConsumerObserver = + EnhancedEventEmitter; + +export type DataConsumerObserverEvents = { + close: []; + pause: []; + resume: []; +}; + +export interface DataConsumerInterface< + DataConsumerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { + get id(): string; + + get dataProducerId(): string; + + get closed(): boolean; + + get type(): DataConsumerType; + + get sctpStreamParameters(): SctpStreamParameters | undefined; + + get label(): string; + + get protocol(): string; + + get paused(): boolean; + + get dataProducerPaused(): boolean; + + get subchannels(): number[]; + + get appData(): DataConsumerAppData; + + set appData(appData: DataConsumerAppData); + + get observer(): DataConsumerObserver; + + close(): void; + + /** + * Transport was closed. + * + * @private + */ + transportClosed(): void; + + dump(): Promise; + + getStats(): Promise; + + pause(): Promise; + + resume(): Promise; + + setBufferedAmountLowThreshold(threshold: number): Promise; + + getBufferedAmount(): Promise; + + send(message: string | Buffer, ppid?: number): Promise; + + setSubchannels(subchannels: number[]): Promise; + + addSubchannel(subchannel: number): Promise; + + removeSubchannel(subchannel: number): Promise; +} diff --git a/node/src/DataProducer.ts b/node/src/DataProducer.ts index e4ac42884e..3a5df11969 100644 --- a/node/src/DataProducer.ts +++ b/node/src/DataProducer.ts @@ -1,5 +1,14 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; +import { + DataProducerInterface, + DataProducerType, + DataProducerDump, + DataProducerStat, + DataProducerEvents, + DataProducerObserver, + DataProducerObserverEvents, +} from './DataProducerInterface'; import { Channel } from './Channel'; import { TransportInternal } from './Transport'; import { @@ -12,75 +21,6 @@ import * as FbsNotification from './fbs/notification'; import * as FbsRequest from './fbs/request'; import * as FbsDataProducer from './fbs/data-producer'; -export type DataProducerOptions = - { - /** - * DataProducer id (just for Router.pipeToRouter() method). - */ - id?: string; - - /** - * SCTP parameters defining how the endpoint is sending the data. - * Just if messages are sent over SCTP. - */ - sctpStreamParameters?: SctpStreamParameters; - - /** - * A label which can be used to distinguish this DataChannel from others. - */ - label?: string; - - /** - * Name of the sub-protocol used by this DataChannel. - */ - protocol?: string; - - /** - * Whether the data producer must start in paused mode. Default false. - */ - paused?: boolean; - - /** - * Custom application data. - */ - appData?: DataProducerAppData; - }; - -export type DataProducerStat = { - type: string; - timestamp: number; - label: string; - protocol: string; - messagesReceived: number; - bytesReceived: number; -}; - -/** - * DataProducer type. - */ -export type DataProducerType = 'sctp' | 'direct'; - -export type DataProducerEvents = { - transportclose: []; - listenererror: [string, Error]; - // Private events. - '@close': []; -}; - -export type DataProducerObserver = - EnhancedEventEmitter; - -export type DataProducerObserverEvents = { - close: []; - pause: []; - resume: []; -}; - -type DataProducerDump = DataProducerData & { - id: string; - paused: boolean; -}; - type DataProducerInternal = TransportInternal & { dataProducerId: string; }; @@ -94,9 +34,10 @@ type DataProducerData = { const logger = new Logger('DataProducer'); -export class DataProducer< - DataProducerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class DataProducer + extends EnhancedEventEmitter + implements DataProducerInterface +{ // Internal data. readonly #internal: DataProducerInternal; diff --git a/node/src/DataProducerInterface.ts b/node/src/DataProducerInterface.ts new file mode 100644 index 0000000000..e84650d892 --- /dev/null +++ b/node/src/DataProducerInterface.ts @@ -0,0 +1,124 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { SctpStreamParameters } from './SctpParameters'; +import { AppData } from './types'; + +export type DataProducerOptions = + { + /** + * DataProducer id (just for Router.pipeToRouter() method). + */ + id?: string; + + /** + * SCTP parameters defining how the endpoint is sending the data. + * Just if messages are sent over SCTP. + */ + sctpStreamParameters?: SctpStreamParameters; + + /** + * A label which can be used to distinguish this DataChannel from others. + */ + label?: string; + + /** + * Name of the sub-protocol used by this DataChannel. + */ + protocol?: string; + + /** + * Whether the data producer must start in paused mode. Default false. + */ + paused?: boolean; + + /** + * Custom application data. + */ + appData?: DataProducerAppData; + }; + +/** + * DataProducer type. + */ +export type DataProducerType = 'sctp' | 'direct'; + +export type DataProducerDump = { + id: string; + paused: boolean; + type: DataProducerType; + sctpStreamParameters?: SctpStreamParameters; + label: string; + protocol: string; +}; + +export type DataProducerStat = { + type: string; + timestamp: number; + label: string; + protocol: string; + messagesReceived: number; + bytesReceived: number; +}; + +export type DataProducerEvents = { + transportclose: []; + listenererror: [string, Error]; + // Private events. + '@close': []; +}; + +export type DataProducerObserver = + EnhancedEventEmitter; + +export type DataProducerObserverEvents = { + close: []; + pause: []; + resume: []; +}; + +export interface DataProducerInterface< + DataProducerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + get type(): DataProducerType; + + get sctpStreamParameters(): SctpStreamParameters | undefined; + + get label(): string; + + get protocol(): string; + + get paused(): boolean; + + get appData(): DataProducerAppData; + + set appData(appData: DataProducerAppData); + + get observer(): DataProducerObserver; + + close(): void; + + /** + * Transport was closed. + * + * @private + */ + transportClosed(): void; + + dump(): Promise; + + getStats(): Promise; + + pause(): Promise; + + resume(): Promise; + + send( + message: string | Buffer, + ppid?: number, + subchannels?: number[], + requiredSubchannel?: number + ): void; +} diff --git a/node/src/DirectTransport.ts b/node/src/DirectTransport.ts index 044dd2d443..db4616bf48 100644 --- a/node/src/DirectTransport.ts +++ b/node/src/DirectTransport.ts @@ -1,57 +1,30 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; -import { UnsupportedError } from './errors'; import { - BaseTransportDump, - BaseTransportStats, + DirectTransportInterface, + DirectTransportDump, + DirectTransportStat, + DirectTransportEvents, + DirectTransportObserver, + DirectTransportObserverEvents, +} from './DirectTransportInterface'; +import { TransportInterface, BaseTransportDump } from './TransportInterface'; +import { + Transport, + TransportConstructorOptions, parseBaseTransportDump, parseBaseTransportStats, parseTransportTraceEventData, - Transport, - TransportEvents, - TransportObserverEvents, - TransportConstructorOptions, } from './Transport'; import { SctpParameters } from './SctpParameters'; import { AppData } from './types'; +import { UnsupportedError } from './errors'; import { Event, Notification } from './fbs/notification'; import * as FbsDirectTransport from './fbs/direct-transport'; import * as FbsTransport from './fbs/transport'; import * as FbsNotification from './fbs/notification'; import * as FbsRequest from './fbs/request'; -export type DirectTransportOptions< - DirectTransportAppData extends AppData = AppData, -> = { - /** - * Maximum allowed size for direct messages sent from DataProducers. - * Default 262144. - */ - maxMessageSize: number; - - /** - * Custom application data. - */ - appData?: DirectTransportAppData; -}; - -export type DirectTransportDump = BaseTransportDump; - -export type DirectTransportStat = BaseTransportStats & { - type: string; -}; - -export type DirectTransportEvents = TransportEvents & { - rtcp: [Buffer]; -}; - -export type DirectTransportObserver = - EnhancedEventEmitter; - -export type DirectTransportObserverEvents = TransportObserverEvents & { - rtcp: [Buffer]; -}; - type DirectTransportConstructorOptions = TransportConstructorOptions & { data: DirectTransportData; @@ -63,13 +36,14 @@ export type DirectTransportData = { const logger = new Logger('DirectTransport'); -export class DirectTransport< - DirectTransportAppData extends AppData = AppData, -> extends Transport< - DirectTransportAppData, - DirectTransportEvents, - DirectTransportObserver -> { +export class DirectTransport + extends Transport< + DirectTransportAppData, + DirectTransportEvents, + DirectTransportObserver + > + implements TransportInterface, DirectTransportInterface +{ // DirectTransport data. // eslint-disable-next-line no-unused-private-class-members readonly #data: DirectTransportData; @@ -131,7 +105,7 @@ export class DirectTransport< } /** - * Dump Transport. + * Dump DirectTransport. */ async dump(): Promise { logger.debug('dump()'); diff --git a/node/src/DirectTransportInterface.ts b/node/src/DirectTransportInterface.ts new file mode 100644 index 0000000000..8bfcb29153 --- /dev/null +++ b/node/src/DirectTransportInterface.ts @@ -0,0 +1,59 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + TransportInterface, + BaseTransportDump, + BaseTransportStats, + TransportEvents, + TransportObserverEvents, +} from './TransportInterface'; +import { AppData } from './types'; + +export type DirectTransportOptions< + DirectTransportAppData extends AppData = AppData, +> = { + /** + * Maximum allowed size for direct messages sent from DataProducers. + * Default 262144. + */ + maxMessageSize: number; + + /** + * Custom application data. + */ + appData?: DirectTransportAppData; +}; + +export type DirectTransportDump = BaseTransportDump; + +export type DirectTransportStat = BaseTransportStats & { + type: string; +}; + +export type DirectTransportEvents = TransportEvents & { + rtcp: [Buffer]; +}; + +export type DirectTransportObserver = + EnhancedEventEmitter; + +export type DirectTransportObserverEvents = TransportObserverEvents & { + rtcp: [Buffer]; +}; + +export interface DirectTransportInterface< + DirectTransportAppData extends AppData = AppData, +> extends TransportInterface< + DirectTransportAppData, + DirectTransportEvents, + DirectTransportObserver + > { + get observer(): DirectTransportObserver; + + dump(): Promise; + + getStats(): Promise; + + connect(): Promise; + + sendRtcp(rtcpPacket: Buffer): void; +} diff --git a/node/src/PipeTransport.ts b/node/src/PipeTransport.ts index d15dd5025a..2a0526c5b7 100644 --- a/node/src/PipeTransport.ts +++ b/node/src/PipeTransport.ts @@ -3,36 +3,43 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import * as ortc from './ortc'; import { - BaseTransportDump, - BaseTransportStats, + PipeTransportInterface, + PipeConsumerOptions, + PipeTransportDump, + PipeTransportStat, + PipeTransportEvents, + PipeTransportObserver, + PipeTransportObserverEvents, +} from './PipeTransportInterface'; +import { + TransportInterface, + TransportTuple, + SctpState, +} from './TransportInterface'; +import { + Transport, + TransportConstructorOptions, parseBaseTransportDump, parseBaseTransportStats, parseSctpState, parseTuple, parseTransportTraceEventData, - Transport, - TransportListenInfo, - TransportListenIp, - TransportTuple, - TransportEvents, - TransportObserverEvents, - TransportConstructorOptions, - SctpState, } from './Transport'; -import { Consumer, ConsumerType } from './Consumer'; -import { Producer } from './Producer'; +import { ProducerInterface } from './ProducerInterface'; +import { ConsumerInterface, ConsumerType } from './ConsumerInterface'; +import { Consumer } from './Consumer'; import { RtpParameters, serializeRtpEncodingParameters, serializeRtpParameters, } from './RtpParameters'; -import { SctpParameters, NumSctpStreams } from './SctpParameters'; +import { SctpParameters } from './SctpParameters'; import { parseSrtpParameters, serializeSrtpParameters, SrtpParameters, } from './SrtpParameters'; -import { AppData, Either } from './types'; +import { AppData } from './types'; import { generateUUIDv4 } from './utils'; import { MediaKind as FbsMediaKind } from './fbs/rtp-parameters/media-kind'; import * as FbsRtpParameters from './fbs/rtp-parameters'; @@ -41,104 +48,6 @@ import * as FbsRequest from './fbs/request'; import * as FbsTransport from './fbs/transport'; import * as FbsPipeTransport from './fbs/pipe-transport'; -type PipeTransportListenInfo = { - /** - * Listening info. - */ - listenInfo: TransportListenInfo; -}; - -type PipeTransportListenIp = { - /** - * Listening IP address. - */ - listenIp: TransportListenIp | string; - - /** - * Fixed port to listen on instead of selecting automatically from Worker's port - * range. - */ - port?: number; -}; - -type PipeTransportListen = Either< - PipeTransportListenInfo, - PipeTransportListenIp ->; - -export type PipeTransportOptions< - PipeTransportAppData extends AppData = AppData, -> = { - /** - * Create a SCTP association. Default false. - */ - enableSctp?: boolean; - - /** - * SCTP streams number. - */ - numSctpStreams?: NumSctpStreams; - - /** - * Maximum allowed size for SCTP messages sent by DataProducers. - * Default 268435456. - */ - maxSctpMessageSize?: number; - - /** - * Maximum SCTP send buffer used by DataConsumers. - * Default 268435456. - */ - sctpSendBufferSize?: number; - - /** - * Enable RTX and NACK for RTP retransmission. Useful if both Routers are - * located in different hosts and there is packet lost in the link. For this - * to work, both PipeTransports must enable this setting. Default false. - */ - enableRtx?: boolean; - - /** - * Enable SRTP. Useful to protect the RTP and RTCP traffic if both Routers - * are located in different hosts. For this to work, connect() must be called - * with remote SRTP parameters. Default false. - */ - enableSrtp?: boolean; - - /** - * Custom application data. - */ - appData?: PipeTransportAppData; -} & PipeTransportListen; - -export type PipeTransportStat = BaseTransportStats & { - type: string; - tuple: TransportTuple; -}; - -export type PipeConsumerOptions = { - /** - * The id of the Producer to consume. - */ - producerId: string; - - /** - * Custom application data. - */ - appData?: ConsumerAppData; -}; - -export type PipeTransportEvents = TransportEvents & { - sctpstatechange: [SctpState]; -}; - -export type PipeTransportObserver = - EnhancedEventEmitter; - -export type PipeTransportObserverEvents = TransportObserverEvents & { - sctpstatechange: [SctpState]; -}; - type PipeTransportConstructorOptions = TransportConstructorOptions & { data: PipeTransportData; @@ -152,21 +61,16 @@ export type PipeTransportData = { srtpParameters?: SrtpParameters; }; -export type PipeTransportDump = BaseTransportDump & { - tuple: TransportTuple; - rtx: boolean; - srtpParameters?: SrtpParameters; -}; - const logger = new Logger('PipeTransport'); -export class PipeTransport< - PipeTransportAppData extends AppData = AppData, -> extends Transport< - PipeTransportAppData, - PipeTransportEvents, - PipeTransportObserver -> { +export class PipeTransport + extends Transport< + PipeTransportAppData, + PipeTransportEvents, + PipeTransportObserver + > + implements TransportInterface, PipeTransportInterface +{ // PipeTransport data. readonly #data: PipeTransportData; @@ -289,6 +193,27 @@ export class PipeTransport< return [parseGetStatsResponse(data)]; } + /** + * Dump PipeTransport. + */ + async dump(): Promise { + logger.debug('dump()'); + + const response = await this.channel.request( + FbsRequest.Method.TRANSPORT_DUMP, + undefined, + undefined, + this.internal.transportId + ); + + /* Decode Response. */ + const data = new FbsPipeTransport.DumpResponse(); + + response.body(data); + + return parsePipeTransportDumpResponse(data); + } + /** * Provide the PipeTransport remote parameters. * @@ -339,7 +264,9 @@ export class PipeTransport< async consume({ producerId, appData, - }: PipeConsumerOptions): Promise> { + }: PipeConsumerOptions): Promise< + ConsumerInterface + > { logger.debug('consume()'); if (!producerId || typeof producerId !== 'string') { @@ -390,7 +317,7 @@ export class PipeTransport< type: 'pipe' as ConsumerType, }; - const consumer: Consumer = new Consumer({ + const consumer: ConsumerInterface = new Consumer({ internal: { ...this.internal, consumerId, @@ -505,7 +432,7 @@ function createConsumeRequest({ }: { builder: flatbuffers.Builder; consumerId: string; - producer: Producer; + producer: ProducerInterface; rtpParameters: RtpParameters; }): number { // Build the request. diff --git a/node/src/PipeTransportInterface.ts b/node/src/PipeTransportInterface.ts new file mode 100644 index 0000000000..c690ecae6e --- /dev/null +++ b/node/src/PipeTransportInterface.ts @@ -0,0 +1,173 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + TransportInterface, + TransportListenInfo, + TransportListenIp, + TransportTuple, + SctpState, + BaseTransportDump, + BaseTransportStats, + TransportEvents, + TransportObserverEvents, +} from './TransportInterface'; +import { ConsumerInterface } from './ConsumerInterface'; +import { SrtpParameters } from './SrtpParameters'; +import { SctpParameters, NumSctpStreams } from './SctpParameters'; +import { Either, AppData } from './types'; + +export type PipeTransportOptions< + PipeTransportAppData extends AppData = AppData, +> = { + /** + * Create a SCTP association. Default false. + */ + enableSctp?: boolean; + + /** + * SCTP streams number. + */ + numSctpStreams?: NumSctpStreams; + + /** + * Maximum allowed size for SCTP messages sent by DataProducers. + * Default 268435456. + */ + maxSctpMessageSize?: number; + + /** + * Maximum SCTP send buffer used by DataConsumers. + * Default 268435456. + */ + sctpSendBufferSize?: number; + + /** + * Enable RTX and NACK for RTP retransmission. Useful if both Routers are + * located in different hosts and there is packet lost in the link. For this + * to work, both PipeTransports must enable this setting. Default false. + */ + enableRtx?: boolean; + + /** + * Enable SRTP. Useful to protect the RTP and RTCP traffic if both Routers + * are located in different hosts. For this to work, connect() must be called + * with remote SRTP parameters. Default false. + */ + enableSrtp?: boolean; + + /** + * Custom application data. + */ + appData?: PipeTransportAppData; +} & PipeTransportListen; + +type PipeTransportListen = Either< + PipeTransportListenInfo, + PipeTransportListenIp +>; + +type PipeTransportListenInfo = { + /** + * Listening info. + */ + listenInfo: TransportListenInfo; +}; + +type PipeTransportListenIp = { + /** + * Listening IP address. + */ + listenIp: TransportListenIp | string; + + /** + * Fixed port to listen on instead of selecting automatically from Worker's port + * range. + */ + port?: number; +}; + +export type PipeConsumerOptions = { + /** + * The id of the Producer to consume. + */ + producerId: string; + + /** + * Custom application data. + */ + appData?: ConsumerAppData; +}; + +export type PipeTransportDump = BaseTransportDump & { + tuple: TransportTuple; + rtx: boolean; + srtpParameters?: SrtpParameters; +}; + +export type PipeTransportStat = BaseTransportStats & { + type: string; + tuple: TransportTuple; +}; + +export type PipeTransportEvents = TransportEvents & { + sctpstatechange: [SctpState]; +}; + +export type PipeTransportObserver = + EnhancedEventEmitter; + +export type PipeTransportObserverEvents = TransportObserverEvents & { + sctpstatechange: [SctpState]; +}; + +export interface PipeTransportInterface< + PipeTransportAppData extends AppData = AppData, +> extends TransportInterface< + PipeTransportAppData, + PipeTransportEvents, + PipeTransportObserver + > { + get observer(): PipeTransportObserver; + + get tuple(): TransportTuple; + + /** + * SCTP parameters. + */ + get sctpParameters(): SctpParameters | undefined; + + /** + * SCTP state. + */ + get sctpState(): SctpState | undefined; + + /** + * SRTP parameters. + */ + get srtpParameters(): SrtpParameters | undefined; + + dump(): Promise; + + getStats(): Promise; + + connect({ + ip, + port, + srtpParameters, + }: { + ip: string; + port: number; + srtpParameters?: SrtpParameters; + }): Promise; + + /** + * Create a pipe Consumer. + * + * @override + */ + consume({ + producerId, + appData, + }: PipeConsumerOptions): Promise< + ConsumerInterface + >; +} diff --git a/node/src/PlainTransport.ts b/node/src/PlainTransport.ts index e1eabcac62..8776132f24 100644 --- a/node/src/PlainTransport.ts +++ b/node/src/PlainTransport.ts @@ -2,144 +2,39 @@ import * as flatbuffers from 'flatbuffers'; import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import { + PlainTransportInterface, + PlainTransportDump, + PlainTransportStat, + PlainTransportEvents, + PlainTransportObserver, + PlainTransportObserverEvents, +} from './PlainTransportInterface'; +import { + TransportInterface, + TransportTuple, + SctpState, +} from './TransportInterface'; +import { + Transport, + TransportConstructorOptions, parseSctpState, - BaseTransportDump, - BaseTransportStats, parseTuple, parseBaseTransportDump, parseBaseTransportStats, parseTransportTraceEventData, - Transport, - TransportListenInfo, - TransportListenIp, - TransportTuple, - TransportEvents, - TransportObserverEvents, - TransportConstructorOptions, - SctpState, } from './Transport'; -import { SctpParameters, NumSctpStreams } from './SctpParameters'; +import { SctpParameters } from './SctpParameters'; import { parseSrtpParameters, serializeSrtpParameters, SrtpParameters, - SrtpCryptoSuite, } from './SrtpParameters'; -import { AppData, Either } from './types'; +import { AppData } from './types'; import { Event, Notification } from './fbs/notification'; import * as FbsRequest from './fbs/request'; import * as FbsTransport from './fbs/transport'; import * as FbsPlainTransport from './fbs/plain-transport'; -type PlainTransportListenInfo = { - /** - * Listening info. - */ - listenInfo: TransportListenInfo; - - /** - * Optional listening info for RTCP. - */ - rtcpListenInfo?: TransportListenInfo; -}; - -type PlainTransportListenIp = { - /** - * Listening IP address. - */ - listenIp: TransportListenIp | string; - - /** - * Fixed port to listen on instead of selecting automatically from Worker's port - * range. - */ - port?: number; -}; - -type PlainTransportListen = Either< - PlainTransportListenInfo, - PlainTransportListenIp ->; - -export type PlainTransportOptions< - PlainTransportAppData extends AppData = AppData, -> = { - /** - * Use RTCP-mux (RTP and RTCP in the same port). Default true. - */ - rtcpMux?: boolean; - - /** - * Whether remote IP:port should be auto-detected based on first RTP/RTCP - * packet received. If enabled, connect() method must not be called unless - * SRTP is enabled. If so, it must be called with just remote SRTP parameters. - * Default false. - */ - comedia?: boolean; - - /** - * Create a SCTP association. Default false. - */ - enableSctp?: boolean; - - /** - * SCTP streams number. - */ - numSctpStreams?: NumSctpStreams; - - /** - * Maximum allowed size for SCTP messages sent by DataProducers. - * Default 262144. - */ - maxSctpMessageSize?: number; - - /** - * Maximum SCTP send buffer used by DataConsumers. - * Default 262144. - */ - sctpSendBufferSize?: number; - - /** - * Enable SRTP. For this to work, connect() must be called - * with remote SRTP parameters. Default false. - */ - enableSrtp?: boolean; - - /** - * The SRTP crypto suite to be used if enableSrtp is set. Default - * 'AES_CM_128_HMAC_SHA1_80'. - */ - srtpCryptoSuite?: SrtpCryptoSuite; - - /** - * Custom application data. - */ - appData?: PlainTransportAppData; -} & PlainTransportListen; - -export type PlainTransportStat = BaseTransportStats & { - type: string; - rtcpMux: boolean; - comedia: boolean; - tuple: TransportTuple; - rtcpTuple?: TransportTuple; -}; - -export type PlainTransportEvents = TransportEvents & { - tuple: [TransportTuple]; - rtcptuple: [TransportTuple]; - sctpstatechange: [SctpState]; -}; - -export type PlainTransportObserver = - EnhancedEventEmitter; - -export type PlainTransportObserverEvents = TransportObserverEvents & { - tuple: [TransportTuple]; - rtcptuple: [TransportTuple]; - sctpstatechange: [SctpState]; -}; - type PlainTransportConstructorOptions = TransportConstructorOptions & { data: PlainTransportData; @@ -155,23 +50,16 @@ export type PlainTransportData = { srtpParameters?: SrtpParameters; }; -type PlainTransportDump = BaseTransportDump & { - rtcpMux: boolean; - comedia: boolean; - tuple: TransportTuple; - rtcpTuple?: TransportTuple; - srtpParameters?: SrtpParameters; -}; - const logger = new Logger('PlainTransport'); -export class PlainTransport< - PlainTransportAppData extends AppData = AppData, -> extends Transport< - PlainTransportAppData, - PlainTransportEvents, - PlainTransportObserver -> { +export class PlainTransport + extends Transport< + PlainTransportAppData, + PlainTransportEvents, + PlainTransportObserver + > + implements TransportInterface, PlainTransportInterface +{ // PlainTransport data. readonly #data: PlainTransportData; @@ -283,7 +171,7 @@ export class PlainTransport< } /** - * Dump Transport. + * Dump PlainTransport. */ async dump(): Promise { logger.debug('dump()'); diff --git a/node/src/PlainTransportInterface.ts b/node/src/PlainTransportInterface.ts new file mode 100644 index 0000000000..595930debf --- /dev/null +++ b/node/src/PlainTransportInterface.ts @@ -0,0 +1,180 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + TransportInterface, + TransportListenInfo, + TransportListenIp, + TransportTuple, + SctpState, + BaseTransportDump, + BaseTransportStats, + TransportEvents, + TransportObserverEvents, +} from './TransportInterface'; +import { SrtpParameters, SrtpCryptoSuite } from './SrtpParameters'; +import { SctpParameters, NumSctpStreams } from './SctpParameters'; +import { Either, AppData } from './types'; + +export type PlainTransportOptions< + PlainTransportAppData extends AppData = AppData, +> = { + /** + * Use RTCP-mux (RTP and RTCP in the same port). Default true. + */ + rtcpMux?: boolean; + + /** + * Whether remote IP:port should be auto-detected based on first RTP/RTCP + * packet received. If enabled, connect() method must not be called unless + * SRTP is enabled. If so, it must be called with just remote SRTP parameters. + * Default false. + */ + comedia?: boolean; + + /** + * Create a SCTP association. Default false. + */ + enableSctp?: boolean; + + /** + * SCTP streams number. + */ + numSctpStreams?: NumSctpStreams; + + /** + * Maximum allowed size for SCTP messages sent by DataProducers. + * Default 262144. + */ + maxSctpMessageSize?: number; + + /** + * Maximum SCTP send buffer used by DataConsumers. + * Default 262144. + */ + sctpSendBufferSize?: number; + + /** + * Enable SRTP. For this to work, connect() must be called + * with remote SRTP parameters. Default false. + */ + enableSrtp?: boolean; + + /** + * The SRTP crypto suite to be used if enableSrtp is set. Default + * 'AES_CM_128_HMAC_SHA1_80'. + */ + srtpCryptoSuite?: SrtpCryptoSuite; + + /** + * Custom application data. + */ + appData?: PlainTransportAppData; +} & PlainTransportListen; + +type PlainTransportListen = Either< + PlainTransportListenInfo, + PlainTransportListenIp +>; + +type PlainTransportListenInfo = { + /** + * Listening info. + */ + listenInfo: TransportListenInfo; + + /** + * Optional listening info for RTCP. + */ + rtcpListenInfo?: TransportListenInfo; +}; + +type PlainTransportListenIp = { + /** + * Listening IP address. + */ + listenIp: TransportListenIp | string; + + /** + * Fixed port to listen on instead of selecting automatically from Worker's port + * range. + */ + port?: number; +}; + +export type PlainTransportDump = BaseTransportDump & { + rtcpMux: boolean; + comedia: boolean; + tuple: TransportTuple; + rtcpTuple?: TransportTuple; + srtpParameters?: SrtpParameters; +}; + +export type PlainTransportStat = BaseTransportStats & { + type: string; + rtcpMux: boolean; + comedia: boolean; + tuple: TransportTuple; + rtcpTuple?: TransportTuple; +}; + +export type PlainTransportEvents = TransportEvents & { + tuple: [TransportTuple]; + rtcptuple: [TransportTuple]; + sctpstatechange: [SctpState]; +}; + +export type PlainTransportObserver = + EnhancedEventEmitter; + +export type PlainTransportObserverEvents = TransportObserverEvents & { + tuple: [TransportTuple]; + rtcptuple: [TransportTuple]; + sctpstatechange: [SctpState]; +}; + +export interface PlainTransportInterface< + PlainTransportAppData extends AppData = AppData, +> extends TransportInterface< + PlainTransportAppData, + PlainTransportEvents, + PlainTransportObserver + > { + get observer(): PlainTransportObserver; + + get tuple(): TransportTuple; + + /** + * Transport RTCP tuple. + */ + get rtcpTuple(): TransportTuple | undefined; + + /** + * SCTP parameters. + */ + get sctpParameters(): SctpParameters | undefined; + + /** + * SCTP state. + */ + get sctpState(): SctpState | undefined; + + /** + * SRTP parameters. + */ + get srtpParameters(): SrtpParameters | undefined; + + dump(): Promise; + + getStats(): Promise; + + connect({ + ip, + port, + rtcpPort, + srtpParameters, + }: { + ip?: string; + port?: number; + rtcpPort?: number; + srtpParameters?: SrtpParameters; + }): Promise; +} diff --git a/node/src/Producer.ts b/node/src/Producer.ts index 42e2d2b1ee..673cd32c43 100644 --- a/node/src/Producer.ts +++ b/node/src/Producer.ts @@ -1,10 +1,23 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; +import { + ProducerInterface, + ProducerType, + ProducerScore, + ProducerVideoOrientation, + ProducerDump, + ProducerStat, + ProducerTraceEventType, + ProducerTraceEventData, + ProducerEvents, + ProducerObserver, + ProducerObserverEvents, +} from './ProducerInterface'; import { Channel } from './Channel'; import { TransportInternal } from './Transport'; import { MediaKind, RtpParameters, parseRtpParameters } from './RtpParameters'; import { Event, Notification } from './fbs/notification'; -import { parseRtpStreamRecvStats, RtpStreamRecvStats } from './RtpStream'; +import { parseRtpStreamRecvStats } from './RtpStream'; import { AppData } from './types'; import * as utils from './utils'; import { TraceDirection as FbsTraceDirection } from './fbs/common'; @@ -15,157 +28,12 @@ import * as FbsProducer from './fbs/producer'; import * as FbsProducerTraceInfo from './fbs/producer/trace-info'; import * as FbsRtpParameters from './fbs/rtp-parameters'; -export type ProducerOptions = { - /** - * Producer id (just for Router.pipeToRouter() method). - */ - id?: string; - - /** - * Media kind ('audio' or 'video'). - */ - kind: MediaKind; - - /** - * RTP parameters defining what the endpoint is sending. - */ - rtpParameters: RtpParameters; - - /** - * Whether the producer must start in paused mode. Default false. - */ - paused?: boolean; - - /** - * Just for video. Time (in ms) before asking the sender for a new key frame - * after having asked a previous one. Default 0. - */ - keyFrameRequestDelay?: number; - - /** - * Custom application data. - */ - appData?: ProducerAppData; -}; - -/** - * Valid types for 'trace' event. - */ -export type ProducerTraceEventType = - | 'rtp' - | 'keyframe' - | 'nack' - | 'pli' - | 'fir' - | 'sr'; - -/** - * 'trace' event data. - */ -export type ProducerTraceEventData = { - /** - * Trace type. - */ - type: ProducerTraceEventType; - - /** - * Event timestamp. - */ - timestamp: number; - - /** - * Event direction. - */ - direction: 'in' | 'out'; - - /** - * Per type information. - */ - info: any; -}; - -export type ProducerScore = { - /** - * Index of the RTP stream in the rtpParameters.encodings array. - */ - encodingIdx: number; - - /** - * SSRC of the RTP stream. - */ - ssrc: number; - - /** - * RID of the RTP stream. - */ - rid?: string; - - /** - * The score of the RTP stream. - */ - score: number; -}; - -export type ProducerVideoOrientation = { - /** - * Whether the source is a video camera. - */ - camera: boolean; - - /** - * Whether the video source is flipped. - */ - flip: boolean; - - /** - * Rotation degrees (0, 90, 180 or 270). - */ - rotation: number; -}; - -export type ProducerStat = RtpStreamRecvStats; - -/** - * Producer type. - */ -export type ProducerType = 'simple' | 'simulcast' | 'svc'; - -export type ProducerEvents = { - transportclose: []; - score: [ProducerScore[]]; - videoorientationchange: [ProducerVideoOrientation]; - trace: [ProducerTraceEventData]; - listenererror: [string, Error]; - // Private events. - '@close': []; -}; - -export type ProducerObserver = EnhancedEventEmitter; - -export type ProducerObserverEvents = { - close: []; - pause: []; - resume: []; - score: [ProducerScore[]]; - videoorientationchange: [ProducerVideoOrientation]; - trace: [ProducerTraceEventData]; -}; - -type ProducerDump = { - id: string; - kind: string; - type: ProducerType; - rtpParameters: RtpParameters; - rtpMapping: any; - rtpStreams: any; - traceEventTypes: string[]; - paused: boolean; -}; - type ProducerInternal = TransportInternal & { producerId: string; }; +const logger = new Logger('Producer'); + type ProducerData = { kind: MediaKind; rtpParameters: RtpParameters; @@ -173,11 +41,10 @@ type ProducerData = { consumableRtpParameters: RtpParameters; }; -const logger = new Logger('Producer'); - -export class Producer< - ProducerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class Producer + extends EnhancedEventEmitter + implements ProducerInterface +{ // Internal data. readonly #internal: ProducerInternal; @@ -312,8 +179,9 @@ export class Producer< } /** - * @private * Just for testing purposes. + * + * @private */ get channelForTesting(): Channel { return this.#channel; diff --git a/node/src/ProducerInterface.ts b/node/src/ProducerInterface.ts new file mode 100644 index 0000000000..d4786cd340 --- /dev/null +++ b/node/src/ProducerInterface.ts @@ -0,0 +1,202 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { MediaKind, RtpParameters } from './RtpParameters'; +import { RtpStreamRecvStats } from './RtpStream'; +import { AppData } from './types'; + +export type ProducerOptions = { + /** + * Producer id (just for Router.pipeToRouter() method). + */ + id?: string; + + /** + * Media kind ('audio' or 'video'). + */ + kind: MediaKind; + + /** + * RTP parameters defining what the endpoint is sending. + */ + rtpParameters: RtpParameters; + + /** + * Whether the producer must start in paused mode. Default false. + */ + paused?: boolean; + + /** + * Just for video. Time (in ms) before asking the sender for a new key frame + * after having asked a previous one. Default 0. + */ + keyFrameRequestDelay?: number; + + /** + * Custom application data. + */ + appData?: ProducerAppData; +}; + +/** + * Producer type. + */ +export type ProducerType = 'simple' | 'simulcast' | 'svc'; + +export type ProducerScore = { + /** + * Index of the RTP stream in the rtpParameters.encodings array. + */ + encodingIdx: number; + + /** + * SSRC of the RTP stream. + */ + ssrc: number; + + /** + * RID of the RTP stream. + */ + rid?: string; + + /** + * The score of the RTP stream. + */ + score: number; +}; + +export type ProducerVideoOrientation = { + /** + * Whether the source is a video camera. + */ + camera: boolean; + + /** + * Whether the video source is flipped. + */ + flip: boolean; + + /** + * Rotation degrees (0, 90, 180 or 270). + */ + rotation: number; +}; + +export type ProducerDump = { + id: string; + kind: string; + type: ProducerType; + rtpParameters: RtpParameters; + rtpMapping: any; + rtpStreams: any; + traceEventTypes: string[]; + paused: boolean; +}; + +export type ProducerStat = RtpStreamRecvStats; + +/** + * Valid types for 'trace' event. + */ +export type ProducerTraceEventType = + | 'rtp' + | 'keyframe' + | 'nack' + | 'pli' + | 'fir' + | 'sr'; + +/** + * 'trace' event data. + */ +export type ProducerTraceEventData = { + /** + * Trace type. + */ + type: ProducerTraceEventType; + + /** + * Event timestamp. + */ + timestamp: number; + + /** + * Event direction. + */ + direction: 'in' | 'out'; + + /** + * Per type information. + */ + info: any; +}; + +export type ProducerEvents = { + transportclose: []; + score: [ProducerScore[]]; + videoorientationchange: [ProducerVideoOrientation]; + trace: [ProducerTraceEventData]; + listenererror: [string, Error]; + // Private events. + '@close': []; +}; + +export type ProducerObserver = EnhancedEventEmitter; + +export type ProducerObserverEvents = { + close: []; + pause: []; + resume: []; + score: [ProducerScore[]]; + videoorientationchange: [ProducerVideoOrientation]; + trace: [ProducerTraceEventData]; +}; + +export interface ProducerInterface + extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + get kind(): MediaKind; + + get rtpParameters(): RtpParameters; + + get type(): ProducerType; + + /** + * Consumable RTP parameters. + * + * @private + */ + get consumableRtpParameters(): RtpParameters; + + get paused(): boolean; + + get score(): ProducerScore[]; + + get appData(): ProducerAppData; + + set appData(appData: ProducerAppData); + + get observer(): ProducerObserver; + + close(): void; + + /** + * Transport was closed. + * + * @private + */ + transportClosed(): void; + + dump(): Promise; + + getStats(): Promise; + + pause(): Promise; + + resume(): Promise; + + enableTraceEvent(types?: ProducerTraceEventType[]): Promise; + + send(rtpPacket: Buffer): void; +} diff --git a/node/src/Router.ts b/node/src/Router.ts index 755d579f72..ce3bab5239 100644 --- a/node/src/Router.ts +++ b/node/src/Router.ts @@ -4,50 +4,68 @@ import * as ortc from './ortc'; import { InvalidStateError } from './errors'; import { Channel } from './Channel'; import { - Transport, - TransportListenInfo, + RouterInterface, + PipeToRouterOptions, + PipeToRouterResult, + PipeTransportPair, + RouterDump, + RouterEvents, + RouterObserver, + RouterObserverEvents, +} from './RouterInterface'; +import { + TransportInterface, TransportListenIp, TransportProtocol, - portRangeToFbs, - socketFlagsToFbs, -} from './Transport'; +} from './TransportInterface'; +import { portRangeToFbs, socketFlagsToFbs } from './Transport'; import { - WebRtcTransport, + WebRtcTransportInterface, WebRtcTransportOptions, +} from './WebRtcTransportInterface'; +import { + WebRtcTransport, parseWebRtcTransportDumpResponse, } from './WebRtcTransport'; import { - PlainTransport, + PlainTransportInterface, PlainTransportOptions, +} from './PlainTransportInterface'; +import { + PlainTransport, parsePlainTransportDumpResponse, } from './PlainTransport'; import { - PipeTransport, + PipeTransportInterface, PipeTransportOptions, - parsePipeTransportDumpResponse, -} from './PipeTransport'; +} from './PipeTransportInterface'; +import { PipeTransport, parsePipeTransportDumpResponse } from './PipeTransport'; import { - DirectTransport, + DirectTransportInterface, DirectTransportOptions, +} from './DirectTransportInterface'; +import { + DirectTransport, parseDirectTransportDumpResponse, } from './DirectTransport'; -import { Producer } from './Producer'; -import { Consumer } from './Consumer'; -import { DataProducer } from './DataProducer'; -import { DataConsumer } from './DataConsumer'; -import { RtpObserver } from './RtpObserver'; +import { ProducerInterface } from './ProducerInterface'; +import { ConsumerInterface } from './ConsumerInterface'; +import { DataProducerInterface } from './DataProducerInterface'; +import { DataConsumerInterface } from './DataConsumerInterface'; +import { RtpObserverInterface } from './RtpObserverInterface'; import { - ActiveSpeakerObserver, + ActiveSpeakerObserverInterface, ActiveSpeakerObserverOptions, -} from './ActiveSpeakerObserver'; +} from './ActiveSpeakerObserverInterface'; +import { ActiveSpeakerObserver } from './ActiveSpeakerObserver'; import { - AudioLevelObserver, + AudioLevelObserverInterface, AudioLevelObserverOptions, -} from './AudioLevelObserver'; -import { RtpCapabilities, RtpCodecCapability } from './RtpParameters'; +} from './AudioLevelObserverInterface'; +import { AudioLevelObserver } from './AudioLevelObserver'; +import { RtpCapabilities } from './RtpParameters'; import { cryptoSuiteToFbs } from './SrtpParameters'; -import { NumSctpStreams } from './SctpParameters'; -import { AppData, Either } from './types'; +import { AppData } from './types'; import { clone, generateUUIDv4, @@ -68,144 +86,6 @@ import * as FbsPipeTransport from './fbs/pipe-transport'; import * as FbsDirectTransport from './fbs/direct-transport'; import * as FbsSctpParameters from './fbs/sctp-parameters'; -export type RouterOptions = { - /** - * Router media codecs. - */ - mediaCodecs?: RtpCodecCapability[]; - - /** - * Custom application data. - */ - appData?: RouterAppData; -}; - -type PipeToRouterListenInfo = { - listenInfo: TransportListenInfo; -}; - -type PipeToRouterListenIp = { - /** - * IP used in the PipeTransport pair. Default '127.0.0.1'. - */ - listenIp?: TransportListenIp | string; -}; - -type PipeToRouterListen = Either; - -export type PipeToRouterOptions = { - /** - * The id of the Producer to consume. - */ - producerId?: string; - - /** - * The id of the DataProducer to consume. - */ - dataProducerId?: string; - - /** - * Target Router instance. - */ - router: Router; - - /** - * Create a SCTP association. Default true. - */ - enableSctp?: boolean; - - /** - * SCTP streams number. - */ - numSctpStreams?: NumSctpStreams; - - /** - * Enable RTX and NACK for RTP retransmission. - */ - enableRtx?: boolean; - - /** - * Enable SRTP. - */ - enableSrtp?: boolean; -} & PipeToRouterListen; - -export type PipeToRouterResult = { - /** - * The Consumer created in the current Router. - */ - pipeConsumer?: Consumer; - - /** - * The Producer created in the target Router. - */ - pipeProducer?: Producer; - - /** - * The DataConsumer created in the current Router. - */ - pipeDataConsumer?: DataConsumer; - - /** - * The DataProducer created in the target Router. - */ - pipeDataProducer?: DataProducer; -}; - -export type RouterDump = { - /** - * The Router id. - */ - id: string; - /** - * Id of Transports. - */ - transportIds: string[]; - /** - * Id of RtpObservers. - */ - rtpObserverIds: string[]; - /** - * Array of Producer id and its respective Consumer ids. - */ - mapProducerIdConsumerIds: { key: string; values: string[] }[]; - /** - * Array of Consumer id and its Producer id. - */ - mapConsumerIdProducerId: { key: string; value: string }[]; - /** - * Array of Producer id and its respective Observer ids. - */ - mapProducerIdObserverIds: { key: string; values: string[] }[]; - /** - * Array of Producer id and its respective DataConsumer ids. - */ - mapDataProducerIdDataConsumerIds: { key: string; values: string[] }[]; - /** - * Array of DataConsumer id and its DataProducer id. - */ - mapDataConsumerIdDataProducerId: { key: string; value: string }[]; -}; - -type PipeTransportPair = { - [key: string]: PipeTransport; -}; - -export type RouterEvents = { - workerclose: []; - listenererror: [string, Error]; - // Private events. - '@close': []; -}; - -export type RouterObserver = EnhancedEventEmitter; - -export type RouterObserverEvents = { - close: []; - newtransport: [Transport]; - newrtpobserver: [RtpObserver]; -}; - export type RouterInternal = { routerId: string; }; @@ -216,9 +96,10 @@ type RouterData = { const logger = new Logger('Router'); -export class Router< - RouterAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class Router + extends EnhancedEventEmitter + implements RouterInterface +{ // Internal data. readonly #internal: RouterInternal; @@ -235,16 +116,16 @@ export class Router< #appData: RouterAppData; // Transports map. - readonly #transports: Map = new Map(); + readonly #transports: Map = new Map(); // Producers map. - readonly #producers: Map = new Map(); + readonly #producers: Map = new Map(); // RtpObservers map. - readonly #rtpObservers: Map = new Map(); + readonly #rtpObservers: Map = new Map(); // DataProducers map. - readonly #dataProducers: Map = new Map(); + readonly #dataProducers: Map = new Map(); // Map of PipeTransport pair Promises indexed by the id of the Router in // which pipeToRouter() was called. @@ -327,7 +208,7 @@ export class Router< * @private * Just for testing purposes. */ - get transportsForTesting(): Map { + get transportsForTesting(): Map { return this.#transports; } @@ -461,7 +342,7 @@ export class Router< iceConsentTimeout = 30, appData, }: WebRtcTransportOptions): Promise< - WebRtcTransport + WebRtcTransportInterface > { logger.debug('createWebRtcTransport()'); @@ -627,7 +508,7 @@ export class Router< const webRtcTransportData = parseWebRtcTransportDumpResponse(data); - const transport: WebRtcTransport = + const transport: WebRtcTransportInterface = new WebRtcTransport({ internal: { ...this.#internal, @@ -638,11 +519,12 @@ export class Router< appData, getRouterRtpCapabilities: (): RtpCapabilities => this.#data.rtpCapabilities, - getProducerById: (producerId: string): Producer | undefined => + getProducerById: (producerId: string): ProducerInterface | undefined => this.#producers.get(producerId), getDataProducerById: ( dataProducerId: string - ): DataProducer | undefined => this.#dataProducers.get(dataProducerId), + ): DataProducerInterface | undefined => + this.#dataProducers.get(dataProducerId), }); this.#transports.set(transport.id, transport); @@ -650,16 +532,16 @@ export class Router< transport.on('@listenserverclose', () => this.#transports.delete(transport.id) ); - transport.on('@newproducer', (producer: Producer) => + transport.on('@newproducer', (producer: ProducerInterface) => this.#producers.set(producer.id, producer) ); - transport.on('@producerclose', (producer: Producer) => + transport.on('@producerclose', (producer: ProducerInterface) => this.#producers.delete(producer.id) ); - transport.on('@newdataproducer', (dataProducer: DataProducer) => + transport.on('@newdataproducer', (dataProducer: DataProducerInterface) => this.#dataProducers.set(dataProducer.id, dataProducer) ); - transport.on('@dataproducerclose', (dataProducer: DataProducer) => + transport.on('@dataproducerclose', (dataProducer: DataProducerInterface) => this.#dataProducers.delete(dataProducer.id) ); @@ -691,7 +573,7 @@ export class Router< srtpCryptoSuite = 'AES_CM_128_HMAC_SHA1_80', appData, }: PlainTransportOptions): Promise< - PlainTransport + PlainTransportInterface > { logger.debug('createPlainTransport()'); @@ -799,8 +681,8 @@ export class Router< const plainTransportData = parsePlainTransportDumpResponse(data); - const transport: PlainTransport = new PlainTransport( - { + const transport: PlainTransportInterface = + new PlainTransport({ internal: { ...this.#internal, transportId: transportId, @@ -810,29 +692,29 @@ export class Router< appData, getRouterRtpCapabilities: (): RtpCapabilities => this.#data.rtpCapabilities, - getProducerById: (producerId: string): Producer | undefined => + getProducerById: (producerId: string): ProducerInterface | undefined => this.#producers.get(producerId), getDataProducerById: ( dataProducerId: string - ): DataProducer | undefined => this.#dataProducers.get(dataProducerId), - } - ); + ): DataProducerInterface | undefined => + this.#dataProducers.get(dataProducerId), + }); this.#transports.set(transport.id, transport); transport.on('@close', () => this.#transports.delete(transport.id)); transport.on('@listenserverclose', () => this.#transports.delete(transport.id) ); - transport.on('@newproducer', (producer: Producer) => + transport.on('@newproducer', (producer: ProducerInterface) => this.#producers.set(producer.id, producer) ); - transport.on('@producerclose', (producer: Producer) => + transport.on('@producerclose', (producer: ProducerInterface) => this.#producers.delete(producer.id) ); - transport.on('@newdataproducer', (dataProducer: DataProducer) => + transport.on('@newdataproducer', (dataProducer: DataProducerInterface) => this.#dataProducers.set(dataProducer.id, dataProducer) ); - transport.on('@dataproducerclose', (dataProducer: DataProducer) => + transport.on('@dataproducerclose', (dataProducer: DataProducerInterface) => this.#dataProducers.delete(dataProducer.id) ); @@ -857,7 +739,7 @@ export class Router< enableSrtp = false, appData, }: PipeTransportOptions): Promise< - PipeTransport + PipeTransportInterface > { logger.debug('createPipeTransport()'); @@ -940,37 +822,40 @@ export class Router< const plainTransportData = parsePipeTransportDumpResponse(data); - const transport: PipeTransport = new PipeTransport({ - internal: { - ...this.#internal, - transportId, - }, - data: plainTransportData, - channel: this.#channel, - appData, - getRouterRtpCapabilities: (): RtpCapabilities => - this.#data.rtpCapabilities, - getProducerById: (producerId: string): Producer | undefined => - this.#producers.get(producerId), - getDataProducerById: (dataProducerId: string): DataProducer | undefined => - this.#dataProducers.get(dataProducerId), - }); + const transport: PipeTransportInterface = + new PipeTransport({ + internal: { + ...this.#internal, + transportId, + }, + data: plainTransportData, + channel: this.#channel, + appData, + getRouterRtpCapabilities: (): RtpCapabilities => + this.#data.rtpCapabilities, + getProducerById: (producerId: string): ProducerInterface | undefined => + this.#producers.get(producerId), + getDataProducerById: ( + dataProducerId: string + ): DataProducerInterface | undefined => + this.#dataProducers.get(dataProducerId), + }); this.#transports.set(transport.id, transport); transport.on('@close', () => this.#transports.delete(transport.id)); transport.on('@listenserverclose', () => this.#transports.delete(transport.id) ); - transport.on('@newproducer', (producer: Producer) => + transport.on('@newproducer', (producer: ProducerInterface) => this.#producers.set(producer.id, producer) ); - transport.on('@producerclose', (producer: Producer) => + transport.on('@producerclose', (producer: ProducerInterface) => this.#producers.delete(producer.id) ); - transport.on('@newdataproducer', (dataProducer: DataProducer) => + transport.on('@newdataproducer', (dataProducer: DataProducerInterface) => this.#dataProducers.set(dataProducer.id, dataProducer) ); - transport.on('@dataproducerclose', (dataProducer: DataProducer) => + transport.on('@dataproducerclose', (dataProducer: DataProducerInterface) => this.#dataProducers.delete(dataProducer.id) ); @@ -990,7 +875,7 @@ export class Router< }: DirectTransportOptions = { maxMessageSize: 262144, } - ): Promise> { + ): Promise> { logger.debug('createDirectTransport()'); if (typeof maxMessageSize !== 'number' || maxMessageSize < 0) { @@ -1035,7 +920,7 @@ export class Router< const directTransportData = parseDirectTransportDumpResponse(data); - const transport: DirectTransport = + const transport: DirectTransportInterface = new DirectTransport({ internal: { ...this.#internal, @@ -1046,11 +931,12 @@ export class Router< appData, getRouterRtpCapabilities: (): RtpCapabilities => this.#data.rtpCapabilities, - getProducerById: (producerId: string): Producer | undefined => + getProducerById: (producerId: string): ProducerInterface | undefined => this.#producers.get(producerId), getDataProducerById: ( dataProducerId: string - ): DataProducer | undefined => this.#dataProducers.get(dataProducerId), + ): DataProducerInterface | undefined => + this.#dataProducers.get(dataProducerId), }); this.#transports.set(transport.id, transport); @@ -1058,16 +944,16 @@ export class Router< transport.on('@listenserverclose', () => this.#transports.delete(transport.id) ); - transport.on('@newproducer', (producer: Producer) => + transport.on('@newproducer', (producer: ProducerInterface) => this.#producers.set(producer.id, producer) ); - transport.on('@producerclose', (producer: Producer) => + transport.on('@producerclose', (producer: ProducerInterface) => this.#producers.delete(producer.id) ); - transport.on('@newdataproducer', (dataProducer: DataProducer) => + transport.on('@newdataproducer', (dataProducer: DataProducerInterface) => this.#dataProducers.set(dataProducer.id, dataProducer) ); - transport.on('@dataproducerclose', (dataProducer: DataProducer) => + transport.on('@dataproducerclose', (dataProducer: DataProducerInterface) => this.#dataProducers.delete(dataProducer.id) ); @@ -1126,8 +1012,8 @@ export class Router< }; } - let producer: Producer | undefined; - let dataProducer: DataProducer | undefined; + let producer: ProducerInterface | undefined; + let dataProducer: DataProducerInterface | undefined; if (producerId) { producer = this.#producers.get(producerId); @@ -1147,8 +1033,8 @@ export class Router< let pipeTransportPairPromise = this.#mapRouterPairPipeTransportPairPromise.get(pipeTransportPairKey); let pipeTransportPair: PipeTransportPair; - let localPipeTransport: PipeTransport; - let remotePipeTransport: PipeTransport; + let localPipeTransport: PipeTransportInterface; + let remotePipeTransport: PipeTransportInterface; if (pipeTransportPairPromise) { pipeTransportPair = await pipeTransportPairPromise; @@ -1239,8 +1125,8 @@ export class Router< } if (producer) { - let pipeConsumer: Consumer | undefined; - let pipeProducer: Producer | undefined; + let pipeConsumer: ConsumerInterface | undefined; + let pipeProducer: ProducerInterface | undefined; try { pipeConsumer = await localPipeTransport!.consume({ @@ -1296,8 +1182,8 @@ export class Router< throw error; } } else if (dataProducer) { - let pipeDataConsumer: DataConsumer | undefined; - let pipeDataProducer: DataProducer | undefined; + let pipeDataConsumer: DataConsumerInterface | undefined; + let pipeDataProducer: DataProducerInterface | undefined; try { pipeDataConsumer = await localPipeTransport!.consumeData({ @@ -1386,7 +1272,7 @@ export class Router< interval = 300, appData, }: ActiveSpeakerObserverOptions = {}): Promise< - ActiveSpeakerObserver + ActiveSpeakerObserverInterface > { logger.debug('createActiveSpeakerObserver()'); @@ -1414,7 +1300,7 @@ export class Router< this.#internal.routerId ); - const activeSpeakerObserver: ActiveSpeakerObserver = + const activeSpeakerObserver: ActiveSpeakerObserverInterface = new ActiveSpeakerObserver({ internal: { ...this.#internal, @@ -1422,7 +1308,7 @@ export class Router< }, channel: this.#channel, appData, - getProducerById: (producerId: string): Producer | undefined => + getProducerById: (producerId: string): ProducerInterface | undefined => this.#producers.get(producerId), }); @@ -1448,7 +1334,7 @@ export class Router< interval = 1000, appData, }: AudioLevelObserverOptions = {}): Promise< - AudioLevelObserver + AudioLevelObserverInterface > { logger.debug('createAudioLevelObserver()'); @@ -1490,7 +1376,7 @@ export class Router< this.#internal.routerId ); - const audioLevelObserver: AudioLevelObserver = + const audioLevelObserver: AudioLevelObserverInterface = new AudioLevelObserver({ internal: { ...this.#internal, @@ -1498,7 +1384,7 @@ export class Router< }, channel: this.#channel, appData, - getProducerById: (producerId: string): Producer | undefined => + getProducerById: (producerId: string): ProducerInterface | undefined => this.#producers.get(producerId), }); diff --git a/node/src/RouterInterface.ts b/node/src/RouterInterface.ts new file mode 100644 index 0000000000..a3e2a10498 --- /dev/null +++ b/node/src/RouterInterface.ts @@ -0,0 +1,258 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + TransportInterface, + TransportListenInfo, + TransportListenIp, +} from './TransportInterface'; +import { + WebRtcTransportInterface, + WebRtcTransportOptions, +} from './WebRtcTransportInterface'; +import { + PlainTransportInterface, + PlainTransportOptions, +} from './PlainTransportInterface'; +import { + PipeTransportInterface, + PipeTransportOptions, +} from './PipeTransportInterface'; +import { + DirectTransportInterface, + DirectTransportOptions, +} from './DirectTransportInterface'; +import { ProducerInterface } from './ProducerInterface'; +import { ConsumerInterface } from './ConsumerInterface'; +import { DataProducerInterface } from './DataProducerInterface'; +import { DataConsumerInterface } from './DataConsumerInterface'; +import { RtpObserverInterface } from './RtpObserverInterface'; +import { + ActiveSpeakerObserverInterface, + ActiveSpeakerObserverOptions, +} from './ActiveSpeakerObserverInterface'; +import { + AudioLevelObserverInterface, + AudioLevelObserverOptions, +} from './AudioLevelObserverInterface'; +import { RtpCapabilities, RtpCodecCapability } from './RtpParameters'; +import { NumSctpStreams } from './SctpParameters'; +import { Either, AppData } from './types'; + +export type RouterOptions = { + /** + * Router media codecs. + */ + mediaCodecs?: RtpCodecCapability[]; + + /** + * Custom application data. + */ + appData?: RouterAppData; +}; + +export type PipeToRouterOptions = { + /** + * The id of the Producer to consume. + */ + producerId?: string; + + /** + * The id of the DataProducer to consume. + */ + dataProducerId?: string; + + /** + * Target Router instance. + */ + router: RouterInterface; + + /** + * Create a SCTP association. Default true. + */ + enableSctp?: boolean; + + /** + * SCTP streams number. + */ + numSctpStreams?: NumSctpStreams; + + /** + * Enable RTX and NACK for RTP retransmission. + */ + enableRtx?: boolean; + + /** + * Enable SRTP. + */ + enableSrtp?: boolean; +} & PipeToRouterListen; + +type PipeToRouterListen = Either; + +type PipeToRouterListenInfo = { + listenInfo: TransportListenInfo; +}; + +type PipeToRouterListenIp = { + /** + * IP used in the PipeTransport pair. Default '127.0.0.1'. + */ + listenIp?: TransportListenIp | string; +}; + +export type PipeToRouterResult = { + /** + * The Consumer created in the current Router. + */ + pipeConsumer?: ConsumerInterface; + + /** + * The Producer created in the target Router. + */ + pipeProducer?: ProducerInterface; + + /** + * The DataConsumer created in the current Router. + */ + pipeDataConsumer?: DataConsumerInterface; + + /** + * The DataProducer created in the target Router. + */ + pipeDataProducer?: DataProducerInterface; +}; + +export type PipeTransportPair = { + [key: string]: PipeTransportInterface; +}; + +export type RouterDump = { + /** + * The Router id. + */ + id: string; + /** + * Id of Transports. + */ + transportIds: string[]; + /** + * Id of RtpObservers. + */ + rtpObserverIds: string[]; + /** + * Array of Producer id and its respective Consumer ids. + */ + mapProducerIdConsumerIds: { key: string; values: string[] }[]; + /** + * Array of Consumer id and its Producer id. + */ + mapConsumerIdProducerId: { key: string; value: string }[]; + /** + * Array of Producer id and its respective Observer ids. + */ + mapProducerIdObserverIds: { key: string; values: string[] }[]; + /** + * Array of Producer id and its respective DataConsumer ids. + */ + mapDataProducerIdDataConsumerIds: { key: string; values: string[] }[]; + /** + * Array of DataConsumer id and its DataProducer id. + */ + mapDataConsumerIdDataProducerId: { key: string; value: string }[]; +}; + +export type RouterEvents = { + workerclose: []; + listenererror: [string, Error]; + // Private events. + '@close': []; +}; + +export type RouterObserver = EnhancedEventEmitter; + +export type RouterObserverEvents = { + close: []; + newtransport: [TransportInterface]; + newrtpobserver: [RtpObserverInterface]; +}; + +export interface RouterInterface + extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + /** + * RTP capabilities of the Router. + */ + get rtpCapabilities(): RtpCapabilities; + + /** + * App custom data. + */ + get appData(): RouterAppData; + + /** + * App custom data setter. + */ + set appData(appData: RouterAppData); + + /** + * Observer. + */ + get observer(): RouterObserver; + + close(): void; + + /** + * Worker was closed. + * + * @private + */ + workerClosed(): void; + + dump(): Promise; + + createWebRtcTransport( + options: WebRtcTransportOptions + ): Promise>; + + createPlainTransport( + options: PlainTransportOptions + ): Promise>; + + createPipeTransport( + options: PipeTransportOptions + ): Promise>; + + createDirectTransport( + options?: DirectTransportOptions + ): Promise>; + + pipeToRouter(options: PipeToRouterOptions): Promise; + + /** + * @private + */ + addPipeTransportPair( + pipeTransportPairKey: string, + pipeTransportPairPromise: Promise + ): void; + + createActiveSpeakerObserver< + ActiveSpeakerObserverAppData extends AppData = AppData, + >( + options?: ActiveSpeakerObserverOptions + ): Promise>; + + createAudioLevelObserver( + options?: AudioLevelObserverOptions + ): Promise>; + + canConsume({ + producerId, + rtpCapabilities, + }: { + producerId: string; + rtpCapabilities: RtpCapabilities; + }): boolean; +} diff --git a/node/src/RtpObserver.ts b/node/src/RtpObserver.ts index a64abc3747..ba07f2af3d 100644 --- a/node/src/RtpObserver.ts +++ b/node/src/RtpObserver.ts @@ -1,51 +1,27 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; +import { RtpObserverEvents, RtpObserverObserver } from './RtpObserverInterface'; import { Channel } from './Channel'; import { RouterInternal } from './Router'; -import { Producer } from './Producer'; +import { ProducerInterface } from './ProducerInterface'; import { AppData } from './types'; import * as FbsRequest from './fbs/request'; import * as FbsRouter from './fbs/router'; import * as FbsRtpObserver from './fbs/rtp-observer'; -export type RtpObserverEvents = { - routerclose: []; - listenererror: [string, Error]; - // Private events. - '@close': []; -}; - -export type RtpObserverObserver = - EnhancedEventEmitter; - -export type RtpObserverObserverEvents = { - close: []; - pause: []; - resume: []; - addproducer: [Producer]; - removeproducer: [Producer]; -}; - export type RtpObserverConstructorOptions = { internal: RtpObserverObserverInternal; channel: Channel; appData?: RtpObserverAppData; - getProducerById: (producerId: string) => Producer | undefined; + getProducerById: (producerId: string) => ProducerInterface | undefined; }; -export type RtpObserverObserverInternal = RouterInternal & { +type RtpObserverObserverInternal = RouterInternal & { rtpObserverId: string; }; const logger = new Logger('RtpObserver'); -export type RtpObserverAddRemoveProducerOptions = { - /** - * The id of the Producer to be added or removed. - */ - producerId: string; -}; - export abstract class RtpObserver< RtpObserverAppData extends AppData = AppData, Events extends RtpObserverEvents = RtpObserverEvents, @@ -69,7 +45,7 @@ export abstract class RtpObserver< // Method to retrieve a Producer. protected readonly getProducerById: ( producerId: string - ) => Producer | undefined; + ) => ProducerInterface | undefined; // Observer instance. readonly #observer: Observer; @@ -247,9 +223,7 @@ export abstract class RtpObserver< /** * Add a Producer to the RtpObserver. */ - async addProducer({ - producerId, - }: RtpObserverAddRemoveProducerOptions): Promise { + async addProducer({ producerId }: { producerId: string }): Promise { logger.debug('addProducer()'); const producer = this.getProducerById(producerId); @@ -276,9 +250,7 @@ export abstract class RtpObserver< /** * Remove a Producer from the RtpObserver. */ - async removeProducer({ - producerId, - }: RtpObserverAddRemoveProducerOptions): Promise { + async removeProducer({ producerId }: { producerId: string }): Promise { logger.debug('removeProducer()'); const producer = this.getProducerById(producerId); diff --git a/node/src/RtpObserverInterface.ts b/node/src/RtpObserverInterface.ts new file mode 100644 index 0000000000..8f047bcf1c --- /dev/null +++ b/node/src/RtpObserverInterface.ts @@ -0,0 +1,56 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { ProducerInterface } from './ProducerInterface'; +import { AppData } from './types'; + +export type RtpObserverEvents = { + routerclose: []; + listenererror: [string, Error]; + // Private events. + '@close': []; +}; + +export type RtpObserverObserver = + EnhancedEventEmitter; + +export type RtpObserverObserverEvents = { + close: []; + pause: []; + resume: []; + addproducer: [ProducerInterface]; + removeproducer: [ProducerInterface]; +}; + +export interface RtpObserverInterface< + RtpObserverAppData extends AppData = AppData, + Events extends RtpObserverEvents = RtpObserverEvents, + Observer extends RtpObserverObserver = RtpObserverObserver, +> extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + get paused(): boolean; + + get appData(): RtpObserverAppData; + + set appData(appData: RtpObserverAppData); + + get observer(): Observer; + + close(): void; + + /** + * Router was closed. + * + * @private + */ + routerClosed(): void; + + pause(): Promise; + + resume(): Promise; + + addProducer({ producerId }: { producerId: string }): Promise; + + removeProducer({ producerId }: { producerId: string }): Promise; +} diff --git a/node/src/Transport.ts b/node/src/Transport.ts index 7448e8a438..2794ad6665 100644 --- a/node/src/Transport.ts +++ b/node/src/Transport.ts @@ -2,35 +2,55 @@ import * as flatbuffers from 'flatbuffers'; import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import * as ortc from './ortc'; +import { + TransportInterface, + TransportProtocol, + TransportPortRange, + TransportSocketFlags, + TransportTuple, + SctpState, + RtpListenerDump, + SctpListenerDump, + RecvRtpHeaderExtensions, + BaseTransportDump, + BaseTransportStats, + TransportTraceEventType, + TransportTraceEventData, + TransportEvents, + TransportObserver, +} from './TransportInterface'; import { Channel } from './Channel'; import { RouterInternal } from './Router'; import { WebRtcTransportData } from './WebRtcTransport'; import { PlainTransportData } from './PlainTransport'; import { PipeTransportData } from './PipeTransport'; import { DirectTransportData } from './DirectTransport'; +import { ProducerInterface, ProducerOptions } from './ProducerInterface'; +import { Producer, producerTypeFromFbs, producerTypeToFbs } from './Producer'; import { - Producer, - ProducerOptions, - producerTypeFromFbs, - producerTypeToFbs, -} from './Producer'; -import { - Consumer, + ConsumerInterface, ConsumerOptions, ConsumerType, ConsumerLayers, -} from './Consumer'; +} from './ConsumerInterface'; +import { Consumer } from './Consumer'; import { - DataProducer, + DataProducerInterface, DataProducerOptions, DataProducerType, +} from './DataProducerInterface'; +import { + DataProducer, dataProducerTypeToFbs, parseDataProducerDumpResponse, } from './DataProducer'; import { - DataConsumer, + DataConsumerInterface, DataConsumerOptions, DataConsumerType, +} from './DataConsumerInterface'; +import { + DataConsumer, dataConsumerTypeToFbs, parseDataConsumerDumpResponse, } from './DataConsumer'; @@ -60,270 +80,38 @@ import * as FbsRouter from './fbs/router'; import * as FbsRtpParameters from './fbs/rtp-parameters'; import { SctpState as FbsSctpState } from './fbs/sctp-association/sctp-state'; -export type TransportListenInfo = { - /** - * Network protocol. - */ - protocol: TransportProtocol; - - /** - * Listening IPv4 or IPv6. - */ - ip: string; - - /** - * @deprecated Use |announcedAddress| instead. - * - * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT - * with private IP). - */ - announcedIp?: string; - - /** - * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT - * with private IP). - */ - announcedAddress?: string; - - /** - * Listening port. - */ - port?: number; - - /** - * Listening port range. If given then |port| will be ignored. - */ - portRange?: TransportPortRange; - - /** - * Socket flags. - */ - flags?: TransportSocketFlags; - - /** - * Send buffer size (bytes). - */ - sendBufferSize?: number; - - /** - * Recv buffer size (bytes). - */ - recvBufferSize?: number; -}; - -/** - * Use TransportListenInfo instead. - * @deprecated - */ -export type TransportListenIp = { - /** - * Listening IPv4 or IPv6. - */ - ip: string; - - /** - * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT - * with private IP). - */ - announcedIp?: string; -}; - -/** - * Transport protocol. - */ -export type TransportProtocol = 'udp' | 'tcp'; - -/** - * Port range.. - */ -export type TransportPortRange = { - /** - * Lowest port in the range. - */ - min: number; - /** - * Highest port in the range. - */ - max: number; -}; - -/** - * UDP/TCP socket flags. - */ -export type TransportSocketFlags = { - /** - * Disable dual-stack support so only IPv6 is used (only if |ip| is IPv6). - */ - ipv6Only?: boolean; - /** - * Make different transports bind to the same IP and port (only for UDP). - * Useful for multicast scenarios with plain transport. Use with caution. - */ - udpReusePort?: boolean; -}; - -export type TransportTuple = { - // @deprecated Use localAddress instead. - localIp: string; - localAddress: string; - localPort: number; - remoteIp?: string; - remotePort?: number; - protocol: TransportProtocol; -}; - -/** - * Valid types for 'trace' event. - */ -export type TransportTraceEventType = 'probation' | 'bwe'; - -/** - * 'trace' event data. - */ -export type TransportTraceEventData = { - /** - * Trace type. - */ - type: TransportTraceEventType; - - /** - * Event timestamp. - */ - timestamp: number; - - /** - * Event direction. - */ - direction: 'in' | 'out'; - - /** - * Per type information. - */ - info: any; -}; - -export type SctpState = - | 'new' - | 'connecting' - | 'connected' - | 'failed' - | 'closed'; - -export type TransportEvents = { - routerclose: []; - listenserverclose: []; - trace: [TransportTraceEventData]; - listenererror: [string, Error]; - // Private events. - '@close': []; - '@newproducer': [Producer]; - '@producerclose': [Producer]; - '@newdataproducer': [DataProducer]; - '@dataproducerclose': [DataProducer]; - '@listenserverclose': []; -}; - -export type TransportObserver = EnhancedEventEmitter; - -export type TransportObserverEvents = { - close: []; - newproducer: [Producer]; - newconsumer: [Consumer]; - newdataproducer: [DataProducer]; - newdataconsumer: [DataConsumer]; - trace: [TransportTraceEventData]; -}; - export type TransportConstructorOptions = { internal: TransportInternal; data: TransportData; channel: Channel; appData?: TransportAppData; getRouterRtpCapabilities: () => RtpCapabilities; - getProducerById: (producerId: string) => Producer | undefined; - getDataProducerById: (dataProducerId: string) => DataProducer | undefined; + getProducerById: (producerId: string) => ProducerInterface | undefined; + getDataProducerById: ( + dataProducerId: string + ) => DataProducerInterface | undefined; }; export type TransportInternal = RouterInternal & { transportId: string; }; -export type BaseTransportDump = { - id: string; - direct: boolean; - producerIds: string[]; - consumerIds: string[]; - mapSsrcConsumerId: { key: number; value: string }[]; - mapRtxSsrcConsumerId: { key: number; value: string }[]; - recvRtpHeaderExtensions: RecvRtpHeaderExtensions; - rtpListener: RtpListenerDump; - maxMessageSize: number; - dataProducerIds: string[]; - dataConsumerIds: string[]; - sctpParameters?: SctpParameters; - sctpState?: SctpState; - sctpListener?: SctpListenerDump; - traceEventTypes?: string[]; -}; - -export type BaseTransportStats = { - transportId: string; - timestamp: number; - sctpState?: SctpState; - bytesReceived: number; - recvBitrate: number; - bytesSent: number; - sendBitrate: number; - rtpBytesReceived: number; - rtpRecvBitrate: number; - rtpBytesSent: number; - rtpSendBitrate: number; - rtxBytesReceived: number; - rtxRecvBitrate: number; - rtxBytesSent: number; - rtxSendBitrate: number; - probationBytesSent: number; - probationSendBitrate: number; - availableOutgoingBitrate?: number; - availableIncomingBitrate?: number; - maxIncomingBitrate?: number; - maxOutgoingBitrate?: number; - minOutgoingBitrate?: number; - rtpPacketLossReceived?: number; - rtpPacketLossSent?: number; -}; - type TransportData = | WebRtcTransportData | PlainTransportData | PipeTransportData | DirectTransportData; -type RtpListenerDump = { - ssrcTable: { key: number; value: string }[]; - midTable: { key: number; value: string }[]; - ridTable: { key: number; value: string }[]; -}; - -type SctpListenerDump = { - streamIdTable: { key: number; value: string }[]; -}; - -type RecvRtpHeaderExtensions = { - mid?: number; - rid?: number; - rrid?: number; - absSendTime?: number; - transportWideCc01?: number; -}; - const logger = new Logger('Transport'); export abstract class Transport< - TransportAppData extends AppData = AppData, - Events extends TransportEvents = TransportEvents, - Observer extends TransportObserver = TransportObserver, -> extends EnhancedEventEmitter { + TransportAppData extends AppData = AppData, + Events extends TransportEvents = TransportEvents, + Observer extends TransportObserver = TransportObserver, + > + extends EnhancedEventEmitter + implements TransportInterface +{ // Internal data. protected readonly internal: TransportInternal; @@ -345,24 +133,26 @@ export abstract class Transport< // Method to retrieve a Producer. protected readonly getProducerById: ( producerId: string - ) => Producer | undefined; + ) => ProducerInterface | undefined; // Method to retrieve a DataProducer. protected readonly getDataProducerById: ( dataProducerId: string - ) => DataProducer | undefined; + ) => DataProducerInterface | undefined; // Producers map. - readonly #producers: Map = new Map(); + readonly #producers: Map = new Map(); // Consumers map. - protected readonly consumers: Map = new Map(); + protected readonly consumers: Map = new Map(); // DataProducers map. - protected readonly dataProducers: Map = new Map(); + protected readonly dataProducers: Map = + new Map(); // DataConsumers map. - protected readonly dataConsumers: Map = new Map(); + protected readonly dataConsumers: Map = + new Map(); // RTCP CNAME for Producers. #cnameForProducers?: string; @@ -732,7 +522,9 @@ export abstract class Transport< paused = false, keyFrameRequestDelay, appData, - }: ProducerOptions): Promise> { + }: ProducerOptions): Promise< + ProducerInterface + > { logger.debug('produce()'); if (id && this.#producers.has(id)) { @@ -825,7 +617,7 @@ export abstract class Transport< consumableRtpParameters, }; - const producer: Producer = new Producer({ + const producer: ProducerInterface = new Producer({ internal: { ...this.internal, producerId, @@ -865,7 +657,9 @@ export abstract class Transport< enableRtx, pipe = false, appData, - }: ConsumerOptions): Promise> { + }: ConsumerOptions): Promise< + ConsumerInterface + > { logger.debug('consume()'); if (!producerId || typeof producerId !== 'string') { @@ -952,7 +746,7 @@ export abstract class Transport< type: pipe ? 'pipe' : (producer.type as ConsumerType), }; - const consumer: Consumer = new Consumer({ + const consumer: ConsumerInterface = new Consumer({ internal: { ...this.internal, consumerId, @@ -992,7 +786,7 @@ export abstract class Transport< paused = false, appData, }: DataProducerOptions = {}): Promise< - DataProducer + DataProducerInterface > { logger.debug('produceData()'); @@ -1054,21 +848,22 @@ export abstract class Transport< const dump = parseDataProducerDumpResponse(produceDataResponse); - const dataProducer: DataProducer = new DataProducer({ - internal: { - ...this.internal, - dataProducerId, - }, - data: { - type: dump.type, - sctpStreamParameters: dump.sctpStreamParameters, - label: dump.label, - protocol: dump.protocol, - }, - channel: this.channel, - paused, - appData, - }); + const dataProducer: DataProducerInterface = + new DataProducer({ + internal: { + ...this.internal, + dataProducerId, + }, + data: { + type: dump.type, + sctpStreamParameters: dump.sctpStreamParameters, + label: dump.label, + protocol: dump.protocol, + }, + channel: this.channel, + paused, + appData, + }); this.dataProducers.set(dataProducer.id, dataProducer); dataProducer.on('@close', () => { @@ -1096,7 +891,7 @@ export abstract class Transport< subchannels, appData, }: DataConsumerOptions): Promise< - DataConsumer + DataConsumerInterface > { logger.debug('consumeData()'); @@ -1189,25 +984,26 @@ export abstract class Transport< const dump = parseDataConsumerDumpResponse(consumeDataResponse); - const dataConsumer: DataConsumer = new DataConsumer({ - internal: { - ...this.internal, - dataConsumerId, - }, - data: { - dataProducerId: dump.dataProducerId, - type: dump.type, - sctpStreamParameters: dump.sctpStreamParameters, - label: dump.label, - protocol: dump.protocol, - bufferedAmountLowThreshold: dump.bufferedAmountLowThreshold, - }, - channel: this.channel, - paused: dump.paused, - subchannels: dump.subchannels, - dataProducerPaused: dump.dataProducerPaused, - appData, - }); + const dataConsumer: DataConsumerInterface = + new DataConsumer({ + internal: { + ...this.internal, + dataConsumerId, + }, + data: { + dataProducerId: dump.dataProducerId, + type: dump.type, + sctpStreamParameters: dump.sctpStreamParameters, + label: dump.label, + protocol: dump.protocol, + bufferedAmountLowThreshold: dump.bufferedAmountLowThreshold, + }, + channel: this.channel, + paused: dump.paused, + subchannels: dump.subchannels, + dataProducerPaused: dump.dataProducerPaused, + appData, + }); this.dataConsumers.set(dataConsumer.id, dataConsumer); dataConsumer.on('@close', () => { @@ -1617,7 +1413,7 @@ function createConsumeRequest({ pipe, }: { builder: flatbuffers.Builder; - producer: Producer; + producer: ProducerInterface; consumerId: string; rtpParameters: RtpParameters; paused: boolean; diff --git a/node/src/TransportInterface.ts b/node/src/TransportInterface.ts new file mode 100644 index 0000000000..65cb0b1616 --- /dev/null +++ b/node/src/TransportInterface.ts @@ -0,0 +1,307 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { ProducerInterface, ProducerOptions } from './ProducerInterface'; +import { ConsumerInterface, ConsumerOptions } from './ConsumerInterface'; +import { + DataProducerInterface, + DataProducerOptions, +} from './DataProducerInterface'; +import { + DataConsumerInterface, + DataConsumerOptions, +} from './DataConsumerInterface'; +import { SctpParameters } from './SctpParameters'; +import { AppData } from './types'; + +export type TransportListenInfo = { + /** + * Network protocol. + */ + protocol: TransportProtocol; + + /** + * Listening IPv4 or IPv6. + */ + ip: string; + + /** + * @deprecated Use |announcedAddress| instead. + * + * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT + * with private IP). + */ + announcedIp?: string; + + /** + * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT + * with private IP). + */ + announcedAddress?: string; + + /** + * Listening port. + */ + port?: number; + + /** + * Listening port range. If given then |port| will be ignored. + */ + portRange?: TransportPortRange; + + /** + * Socket flags. + */ + flags?: TransportSocketFlags; + + /** + * Send buffer size (bytes). + */ + sendBufferSize?: number; + + /** + * Recv buffer size (bytes). + */ + recvBufferSize?: number; +}; + +/** + * Use TransportListenInfo instead. + * @deprecated + */ +export type TransportListenIp = { + /** + * Listening IPv4 or IPv6. + */ + ip: string; + + /** + * Announced IPv4, IPv6 or hostname (useful when running mediasoup behind NAT + * with private IP). + */ + announcedIp?: string; +}; + +/** + * Transport protocol. + */ +export type TransportProtocol = 'udp' | 'tcp'; + +/** + * Port range.. + */ +export type TransportPortRange = { + /** + * Lowest port in the range. + */ + min: number; + /** + * Highest port in the range. + */ + max: number; +}; + +/** + * UDP/TCP socket flags. + */ +export type TransportSocketFlags = { + /** + * Disable dual-stack support so only IPv6 is used (only if |ip| is IPv6). + */ + ipv6Only?: boolean; + /** + * Make different transports bind to the same IP and port (only for UDP). + * Useful for multicast scenarios with plain transport. Use with caution. + */ + udpReusePort?: boolean; +}; + +export type TransportTuple = { + // @deprecated Use localAddress instead. + localIp: string; + localAddress: string; + localPort: number; + remoteIp?: string; + remotePort?: number; + protocol: TransportProtocol; +}; + +export type SctpState = + | 'new' + | 'connecting' + | 'connected' + | 'failed' + | 'closed'; + +export type RtpListenerDump = { + ssrcTable: { key: number; value: string }[]; + midTable: { key: number; value: string }[]; + ridTable: { key: number; value: string }[]; +}; + +export type SctpListenerDump = { + streamIdTable: { key: number; value: string }[]; +}; + +export type RecvRtpHeaderExtensions = { + mid?: number; + rid?: number; + rrid?: number; + absSendTime?: number; + transportWideCc01?: number; +}; + +export type BaseTransportDump = { + id: string; + direct: boolean; + producerIds: string[]; + consumerIds: string[]; + mapSsrcConsumerId: { key: number; value: string }[]; + mapRtxSsrcConsumerId: { key: number; value: string }[]; + recvRtpHeaderExtensions: RecvRtpHeaderExtensions; + rtpListener: RtpListenerDump; + maxMessageSize: number; + dataProducerIds: string[]; + dataConsumerIds: string[]; + sctpParameters?: SctpParameters; + sctpState?: SctpState; + sctpListener?: SctpListenerDump; + traceEventTypes?: string[]; +}; + +export type BaseTransportStats = { + transportId: string; + timestamp: number; + sctpState?: SctpState; + bytesReceived: number; + recvBitrate: number; + bytesSent: number; + sendBitrate: number; + rtpBytesReceived: number; + rtpRecvBitrate: number; + rtpBytesSent: number; + rtpSendBitrate: number; + rtxBytesReceived: number; + rtxRecvBitrate: number; + rtxBytesSent: number; + rtxSendBitrate: number; + probationBytesSent: number; + probationSendBitrate: number; + availableOutgoingBitrate?: number; + availableIncomingBitrate?: number; + maxIncomingBitrate?: number; + maxOutgoingBitrate?: number; + minOutgoingBitrate?: number; + rtpPacketLossReceived?: number; + rtpPacketLossSent?: number; +}; + +/** + * Valid types for 'trace' event. + */ +export type TransportTraceEventType = 'probation' | 'bwe'; + +/** + * 'trace' event data. + */ +export type TransportTraceEventData = { + /** + * Trace type. + */ + type: TransportTraceEventType; + + /** + * Event timestamp. + */ + timestamp: number; + + /** + * Event direction. + */ + direction: 'in' | 'out'; + + /** + * Per type information. + */ + info: any; +}; + +export type TransportEvents = { + routerclose: []; + listenserverclose: []; + trace: [TransportTraceEventData]; + listenererror: [string, Error]; + // Private events. + '@close': []; + '@newproducer': [ProducerInterface]; + '@producerclose': [ProducerInterface]; + '@newdataproducer': [DataProducerInterface]; + '@dataproducerclose': [DataProducerInterface]; + '@listenserverclose': []; +}; + +export type TransportObserver = EnhancedEventEmitter; + +export type TransportObserverEvents = { + close: []; + newproducer: [ProducerInterface]; + newconsumer: [ConsumerInterface]; + newdataproducer: [DataProducerInterface]; + newdataconsumer: [DataConsumerInterface]; + trace: [TransportTraceEventData]; +}; + +export interface TransportInterface< + TransportAppData extends AppData = AppData, + Events extends TransportEvents = TransportEvents, + Observer extends TransportObserver = TransportObserver, +> extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + get appData(): TransportAppData; + + set appData(appData: TransportAppData); + + get observer(): Observer; + + close(): void; + + /** + * Router was closed. + * + * @private + */ + routerClosed(): void; + + /** + * Listen server was closed (this just happens in WebRtcTransports when their + * associated WebRtcServer is closed). + * + * @private + */ + listenServerClosed(): void; + + setMaxIncomingBitrate(bitrate: number): Promise; + + setMaxOutgoingBitrate(bitrate: number): Promise; + + setMinOutgoingBitrate(bitrate: number): Promise; + + produce( + options: ProducerOptions + ): Promise>; + + consume( + options: ConsumerOptions + ): Promise>; + + produceData( + options?: DataProducerOptions + ): Promise>; + + consumeData( + options: DataConsumerOptions + ): Promise>; + + enableTraceEvent(types?: TransportTraceEventType[]): Promise; +} diff --git a/node/src/WebRtcServer.ts b/node/src/WebRtcServer.ts index 385a49e638..1822492171 100644 --- a/node/src/WebRtcServer.ts +++ b/node/src/WebRtcServer.ts @@ -1,81 +1,33 @@ import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import { Channel } from './Channel'; -import { TransportListenInfo } from './Transport'; -import { WebRtcTransport } from './WebRtcTransport'; +import { + WebRtcServerInterface, + IpPort, + IceUserNameFragment, + TupleHash, + WebRtcServerDump, + WebRtcServerEvents, + WebRtcServerObserver, + WebRtcServerObserverEvents, +} from './WebRtcServerInterface'; +import { WebRtcTransportInterface } from './WebRtcTransportInterface'; import { AppData } from './types'; import * as utils from './utils'; import { Body as RequestBody, Method } from './fbs/request'; import * as FbsWorker from './fbs/worker'; import * as FbsWebRtcServer from './fbs/web-rtc-server'; -export type WebRtcServerOptions = - { - /** - * Listen infos. - */ - listenInfos: TransportListenInfo[]; - - /** - * Custom application data. - */ - appData?: WebRtcServerAppData; - }; - -/** - * @deprecated Use TransportListenInfo instead. - */ -export type WebRtcServerListenInfo = TransportListenInfo; - -export type WebRtcServerEvents = { - workerclose: []; - listenererror: [string, Error]; - // Private events. - '@close': []; -}; - -export type WebRtcServerObserver = - EnhancedEventEmitter; - -export type WebRtcServerObserverEvents = { - close: []; - webrtctransporthandled: [WebRtcTransport]; - webrtctransportunhandled: [WebRtcTransport]; -}; - -export type WebRtcServerDump = { - id: string; - udpSockets: IpPort[]; - tcpServers: IpPort[]; - webRtcTransportIds: string[]; - localIceUsernameFragments: IceUserNameFragment[]; - tupleHashes: TupleHash[]; -}; - -type IpPort = { - ip: string; - port: number; -}; - -type IceUserNameFragment = { - localIceUsernameFragment: string; - webRtcTransportId: string; -}; - -type TupleHash = { - tupleHash: number; - webRtcTransportId: string; -}; - type WebRtcServerInternal = { webRtcServerId: string; }; const logger = new Logger('WebRtcServer'); -export class WebRtcServer< - WebRtcServerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class WebRtcServer + extends EnhancedEventEmitter + implements WebRtcServerInterface +{ // Internal data. readonly #internal: WebRtcServerInternal; @@ -89,7 +41,7 @@ export class WebRtcServer< #appData: WebRtcServerAppData; // Transports map. - readonly #webRtcTransports: Map = new Map(); + readonly #webRtcTransports: Map = new Map(); // Observer instance. readonly #observer: WebRtcServerObserver = @@ -155,7 +107,7 @@ export class WebRtcServer< * @private * Just for testing purposes. */ - get webRtcTransportsForTesting(): Map { + get webRtcTransportsForTesting(): Map { return this.#webRtcTransports; } @@ -247,7 +199,7 @@ export class WebRtcServer< /** * @private */ - handleWebRtcTransport(webRtcTransport: WebRtcTransport): void { + handleWebRtcTransport(webRtcTransport: WebRtcTransportInterface): void { this.#webRtcTransports.set(webRtcTransport.id, webRtcTransport); // Emit observer event. diff --git a/node/src/WebRtcServerInterface.ts b/node/src/WebRtcServerInterface.ts new file mode 100644 index 0000000000..fc3b527b27 --- /dev/null +++ b/node/src/WebRtcServerInterface.ts @@ -0,0 +1,98 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { TransportListenInfo } from './TransportInterface'; +import { WebRtcTransportInterface } from './WebRtcTransportInterface'; +import { AppData } from './types'; + +export type WebRtcServerOptions = + { + /** + * Listen infos. + */ + listenInfos: TransportListenInfo[]; + + /** + * Custom application data. + */ + appData?: WebRtcServerAppData; + }; + +/** + * @deprecated Use TransportListenInfo instead. + */ +export type WebRtcServerListenInfo = TransportListenInfo; + +export type IpPort = { + ip: string; + port: number; +}; + +export type IceUserNameFragment = { + localIceUsernameFragment: string; + webRtcTransportId: string; +}; + +export type TupleHash = { + tupleHash: number; + webRtcTransportId: string; +}; + +export type WebRtcServerDump = { + id: string; + udpSockets: IpPort[]; + tcpServers: IpPort[]; + webRtcTransportIds: string[]; + localIceUsernameFragments: IceUserNameFragment[]; + tupleHashes: TupleHash[]; +}; + +export type WebRtcServerEvents = { + workerclose: []; + listenererror: [string, Error]; + // Private events. + '@close': []; +}; + +export type WebRtcServerObserver = + EnhancedEventEmitter; + +export type WebRtcServerObserverEvents = { + close: []; + webrtctransporthandled: [WebRtcTransportInterface]; + webrtctransportunhandled: [WebRtcTransportInterface]; +}; + +export interface WebRtcServerInterface< + WebRtcServerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { + get id(): string; + + get closed(): boolean; + + get appData(): WebRtcServerAppData; + + /** + * App custom data setter. + */ + set appData(appData: WebRtcServerAppData); + + /** + * Observer. + */ + get observer(): WebRtcServerObserver; + + close(): void; + + /** + * Worker was closed. + * + * @private + */ + workerClosed(): void; + + dump(): Promise; + + /** + * @private + */ + handleWebRtcTransport(webRtcTransport: WebRtcTransportInterface): void; +} diff --git a/node/src/WebRtcTransport.ts b/node/src/WebRtcTransport.ts index 9f908ec19c..14a4fde516 100644 --- a/node/src/WebRtcTransport.ts +++ b/node/src/WebRtcTransport.ts @@ -2,27 +2,41 @@ import * as flatbuffers from 'flatbuffers'; import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import { + WebRtcTransportInterface, + IceParameters, + IceCandidate, + DtlsParameters, + FingerprintAlgorithm, + DtlsFingerprint, + IceRole, + IceState, + IceCandidateType, + IceCandidateTcpType, + DtlsRole, + DtlsState, + WebRtcTransportDump, + WebRtcTransportStat, + WebRtcTransportEvents, + WebRtcTransportObserver, + WebRtcTransportObserverEvents, +} from './WebRtcTransportInterface'; +import { + TransportInterface, + TransportTuple, + SctpState, +} from './TransportInterface'; +import { + Transport, + TransportConstructorOptions, parseSctpState, parseBaseTransportDump, parseBaseTransportStats, parseProtocol, parseTransportTraceEventData, parseTuple, - BaseTransportDump, - BaseTransportStats, - Transport, - TransportListenInfo, - TransportListenIp, - TransportProtocol, - TransportTuple, - TransportEvents, - TransportObserverEvents, - TransportConstructorOptions, - SctpState, } from './Transport'; -import { WebRtcServer } from './WebRtcServer'; -import { SctpParameters, NumSctpStreams } from './SctpParameters'; -import { AppData, Either } from './types'; +import { SctpParameters } from './SctpParameters'; +import { AppData } from './types'; import { parseVector } from './utils'; import { Event, Notification } from './fbs/notification'; import * as FbsRequest from './fbs/request'; @@ -36,196 +50,6 @@ import { IceRole as FbsIceRole } from './fbs/web-rtc-transport/ice-role'; import { IceCandidateType as FbsIceCandidateType } from './fbs/web-rtc-transport/ice-candidate-type'; import { IceCandidateTcpType as FbsIceCandidateTcpType } from './fbs/web-rtc-transport/ice-candidate-tcp-type'; -export type WebRtcTransportOptions< - WebRtcTransportAppData extends AppData = AppData, -> = WebRtcTransportOptionsBase & WebRtcTransportListen; - -type WebRtcTransportListenIndividualListenInfo = { - /** - * Listening info. - */ - listenInfos: TransportListenInfo[]; -}; - -type WebRtcTransportListenIndividualListenIp = { - /** - * Listening IP address or addresses in order of preference (first one is the - * preferred one). - */ - listenIps: (TransportListenIp | string)[]; - - /** - * Fixed port to listen on instead of selecting automatically from Worker's port - * range. - */ - port?: number; -}; - -type WebRtcTransportListenServer = { - /** - * Instance of WebRtcServer. - */ - webRtcServer: WebRtcServer; -}; - -type WebRtcTransportListen = Either< - Either< - WebRtcTransportListenIndividualListenInfo, - WebRtcTransportListenIndividualListenIp - >, - WebRtcTransportListenServer ->; - -export type WebRtcTransportOptionsBase = { - /** - * Listen in UDP. Default true. - */ - enableUdp?: boolean; - - /** - * Listen in TCP. Default true if webrtcServer is given, false otherwise. - */ - enableTcp?: boolean; - - /** - * Prefer UDP. Default false. - */ - preferUdp?: boolean; - - /** - * Prefer TCP. Default false. - */ - preferTcp?: boolean; - - /** - * ICE consent timeout (in seconds). If 0 it is disabled. Default 30. - */ - iceConsentTimeout?: number; - - /** - * Initial available outgoing bitrate (in bps). Default 600000. - */ - initialAvailableOutgoingBitrate?: number; - - /** - * Create a SCTP association. Default false. - */ - enableSctp?: boolean; - - /** - * SCTP streams number. - */ - numSctpStreams?: NumSctpStreams; - - /** - * Maximum allowed size for SCTP messages sent by DataProducers. - * Default 262144. - */ - maxSctpMessageSize?: number; - - /** - * Maximum SCTP send buffer used by DataConsumers. - * Default 262144. - */ - sctpSendBufferSize?: number; - - /** - * Custom application data. - */ - appData?: WebRtcTransportAppData; -}; - -export type IceParameters = { - usernameFragment: string; - password: string; - iceLite?: boolean; -}; - -export type IceCandidate = { - foundation: string; - priority: number; - // @deprecated Use |address| instead. - ip: string; - address: string; - protocol: TransportProtocol; - port: number; - type: IceCandidateType; - tcpType?: IceCandidateTcpType; -}; - -export type DtlsParameters = { - role?: DtlsRole; - fingerprints: DtlsFingerprint[]; -}; - -/** - * The hash function algorithm (as defined in the "Hash function Textual Names" - * registry initially specified in RFC 4572 Section 8). - */ -export type FingerprintAlgorithm = - | 'sha-1' - | 'sha-224' - | 'sha-256' - | 'sha-384' - | 'sha-512'; - -/** - * The hash function algorithm and its corresponding certificate fingerprint - * value (in lowercase hex string as expressed utilizing the syntax of - * "fingerprint" in RFC 4572 Section 5). - */ -export type DtlsFingerprint = { - algorithm: FingerprintAlgorithm; - value: string; -}; - -export type IceRole = 'controlled' | 'controlling'; - -export type IceState = - | 'new' - | 'connected' - | 'completed' - | 'disconnected' - | 'closed'; - -export type IceCandidateType = 'host'; - -export type IceCandidateTcpType = 'passive'; - -export type DtlsRole = 'auto' | 'client' | 'server'; - -export type DtlsState = - | 'new' - | 'connecting' - | 'connected' - | 'failed' - | 'closed'; - -export type WebRtcTransportStat = BaseTransportStats & { - type: string; - iceRole: string; - iceState: IceState; - iceSelectedTuple?: TransportTuple; - dtlsState: DtlsState; -}; - -export type WebRtcTransportEvents = TransportEvents & { - icestatechange: [IceState]; - iceselectedtuplechange: [TransportTuple]; - dtlsstatechange: [DtlsState]; - sctpstatechange: [SctpState]; -}; - -export type WebRtcTransportObserver = - EnhancedEventEmitter; - -export type WebRtcTransportObserverEvents = TransportObserverEvents & { - icestatechange: [IceState]; - iceselectedtuplechange: [TransportTuple]; - dtlsstatechange: [DtlsState]; - sctpstatechange: [SctpState]; -}; - type WebRtcTransportConstructorOptions = TransportConstructorOptions & { data: WebRtcTransportData; @@ -244,26 +68,16 @@ export type WebRtcTransportData = { sctpState?: SctpState; }; -type WebRtcTransportDump = BaseTransportDump & { - iceRole: 'controlled'; - iceParameters: IceParameters; - iceCandidates: IceCandidate[]; - iceState: IceState; - iceSelectedTuple?: TransportTuple; - dtlsParameters: DtlsParameters; - dtlsState: DtlsState; - dtlsRemoteCert?: string; -}; - const logger = new Logger('WebRtcTransport'); -export class WebRtcTransport< - WebRtcTransportAppData extends AppData = AppData, -> extends Transport< - WebRtcTransportAppData, - WebRtcTransportEvents, - WebRtcTransportObserver -> { +export class WebRtcTransport + extends Transport< + WebRtcTransportAppData, + WebRtcTransportEvents, + WebRtcTransportObserver + > + implements TransportInterface, WebRtcTransportInterface +{ // WebRtcTransport data. readonly #data: WebRtcTransportData; @@ -442,7 +256,7 @@ export class WebRtcTransport< } /** - * Dump Transport. + * Dump WebRtcTransport. */ async dump(): Promise { logger.debug('dump()'); diff --git a/node/src/WebRtcTransportInterface.ts b/node/src/WebRtcTransportInterface.ts new file mode 100644 index 0000000000..1b95cd68cc --- /dev/null +++ b/node/src/WebRtcTransportInterface.ts @@ -0,0 +1,277 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + TransportInterface, + TransportListenInfo, + TransportListenIp, + TransportProtocol, + TransportTuple, + SctpState, + BaseTransportDump, + BaseTransportStats, + TransportEvents, + TransportObserverEvents, +} from './TransportInterface'; +import { WebRtcServerInterface } from './WebRtcServerInterface'; +import { SctpParameters, NumSctpStreams } from './SctpParameters'; +import { Either, AppData } from './types'; + +export type WebRtcTransportOptions< + WebRtcTransportAppData extends AppData = AppData, +> = WebRtcTransportOptionsBase & WebRtcTransportListen; + +type WebRtcTransportOptionsBase = { + /** + * Listen in UDP. Default true. + */ + enableUdp?: boolean; + + /** + * Listen in TCP. Default true if webrtcServer is given, false otherwise. + */ + enableTcp?: boolean; + + /** + * Prefer UDP. Default false. + */ + preferUdp?: boolean; + + /** + * Prefer TCP. Default false. + */ + preferTcp?: boolean; + + /** + * ICE consent timeout (in seconds). If 0 it is disabled. Default 30. + */ + iceConsentTimeout?: number; + + /** + * Initial available outgoing bitrate (in bps). Default 600000. + */ + initialAvailableOutgoingBitrate?: number; + + /** + * Create a SCTP association. Default false. + */ + enableSctp?: boolean; + + /** + * SCTP streams number. + */ + numSctpStreams?: NumSctpStreams; + + /** + * Maximum allowed size for SCTP messages sent by DataProducers. + * Default 262144. + */ + maxSctpMessageSize?: number; + + /** + * Maximum SCTP send buffer used by DataConsumers. + * Default 262144. + */ + sctpSendBufferSize?: number; + + /** + * Custom application data. + */ + appData?: WebRtcTransportAppData; +}; + +type WebRtcTransportListen = Either< + Either< + WebRtcTransportListenIndividualListenInfo, + WebRtcTransportListenIndividualListenIp + >, + WebRtcTransportListenServer +>; + +type WebRtcTransportListenIndividualListenInfo = { + /** + * Listening info. + */ + listenInfos: TransportListenInfo[]; +}; + +type WebRtcTransportListenIndividualListenIp = { + /** + * Listening IP address or addresses in order of preference (first one is the + * preferred one). + */ + listenIps: (TransportListenIp | string)[]; + + /** + * Fixed port to listen on instead of selecting automatically from Worker's port + * range. + */ + port?: number; +}; + +type WebRtcTransportListenServer = { + /** + * Instance of WebRtcServer. + */ + webRtcServer: WebRtcServerInterface; +}; + +export type IceParameters = { + usernameFragment: string; + password: string; + iceLite?: boolean; +}; + +export type IceCandidate = { + foundation: string; + priority: number; + // @deprecated Use |address| instead. + ip: string; + address: string; + protocol: TransportProtocol; + port: number; + type: IceCandidateType; + tcpType?: IceCandidateTcpType; +}; + +export type DtlsParameters = { + role?: DtlsRole; + fingerprints: DtlsFingerprint[]; +}; + +/** + * The hash function algorithm (as defined in the "Hash function Textual Names" + * registry initially specified in RFC 4572 Section 8). + */ +export type FingerprintAlgorithm = + | 'sha-1' + | 'sha-224' + | 'sha-256' + | 'sha-384' + | 'sha-512'; + +/** + * The hash function algorithm and its corresponding certificate fingerprint + * value (in lowercase hex string as expressed utilizing the syntax of + * "fingerprint" in RFC 4572 Section 5). + */ +export type DtlsFingerprint = { + algorithm: FingerprintAlgorithm; + value: string; +}; + +export type IceRole = 'controlled' | 'controlling'; + +export type IceState = + | 'new' + | 'connected' + | 'completed' + | 'disconnected' + | 'closed'; + +export type IceCandidateType = 'host'; + +export type IceCandidateTcpType = 'passive'; + +export type DtlsRole = 'auto' | 'client' | 'server'; + +export type DtlsState = + | 'new' + | 'connecting' + | 'connected' + | 'failed' + | 'closed'; + +export type WebRtcTransportDump = BaseTransportDump & { + iceRole: 'controlled'; + iceParameters: IceParameters; + iceCandidates: IceCandidate[]; + iceState: IceState; + iceSelectedTuple?: TransportTuple; + dtlsParameters: DtlsParameters; + dtlsState: DtlsState; + dtlsRemoteCert?: string; +}; + +export type WebRtcTransportStat = BaseTransportStats & { + type: string; + iceRole: string; + iceState: IceState; + iceSelectedTuple?: TransportTuple; + dtlsState: DtlsState; +}; + +export type WebRtcTransportEvents = TransportEvents & { + icestatechange: [IceState]; + iceselectedtuplechange: [TransportTuple]; + dtlsstatechange: [DtlsState]; + sctpstatechange: [SctpState]; +}; + +export type WebRtcTransportObserver = + EnhancedEventEmitter; + +export type WebRtcTransportObserverEvents = TransportObserverEvents & { + icestatechange: [IceState]; + iceselectedtuplechange: [TransportTuple]; + dtlsstatechange: [DtlsState]; + sctpstatechange: [SctpState]; +}; + +export interface WebRtcTransportInterface< + WebRtcTransportAppData extends AppData = AppData, +> extends TransportInterface< + WebRtcTransportAppData, + WebRtcTransportEvents, + WebRtcTransportObserver + > { + get observer(): WebRtcTransportObserver; + + get iceRole(): 'controlled'; + + get iceParameters(): IceParameters; + + get iceCandidates(): IceCandidate[]; + + get iceState(): IceState; + + /** + * ICE selected tuple. + */ + get iceSelectedTuple(): TransportTuple | undefined; + + /** + * DTLS parameters. + */ + get dtlsParameters(): DtlsParameters; + + /** + * DTLS state. + */ + get dtlsState(): DtlsState; + + /** + * Remote certificate in PEM format. + */ + get dtlsRemoteCert(): string | undefined; + + /** + * SCTP parameters. + */ + get sctpParameters(): SctpParameters | undefined; + + /** + * SCTP state. + */ + get sctpState(): SctpState | undefined; + + dump(): Promise; + + getStats(): Promise; + + connect({ + dtlsParameters, + }: { + dtlsParameters: DtlsParameters; + }): Promise; + + restartIce(): Promise; +} diff --git a/node/src/Worker.ts b/node/src/Worker.ts index c1d2e20f7c..08fda4bd2a 100644 --- a/node/src/Worker.ts +++ b/node/src/Worker.ts @@ -5,9 +5,24 @@ import { version } from './'; import { Logger } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; import * as ortc from './ortc'; +import { + WorkerInterface, + WorkerSettings, + WorkerUpdateableSettings, + WorkerResourceUsage, + WorkerDump, + WorkerEvents, + WorkerObserver, + WorkerObserverEvents, +} from './WorkerInterface'; import { Channel } from './Channel'; -import { Router, RouterOptions } from './Router'; -import { WebRtcServer, WebRtcServerOptions } from './WebRtcServer'; +import { + WebRtcServerInterface, + WebRtcServerOptions, +} from './WebRtcServerInterface'; +import { WebRtcServer } from './WebRtcServer'; +import { RouterInterface, RouterOptions } from './RouterInterface'; +import { Router } from './Router'; import { portRangeToFbs, socketFlagsToFbs } from './Transport'; import { RtpCodecCapability } from './RtpParameters'; import { AppData } from './types'; @@ -18,207 +33,6 @@ import * as FbsWorker from './fbs/worker'; import * as FbsTransport from './fbs/transport'; import { Protocol as FbsTransportProtocol } from './fbs/transport/protocol'; -export type WorkerLogLevel = 'debug' | 'warn' | 'error' | 'none'; - -export type WorkerLogTag = - | 'info' - | 'ice' - | 'dtls' - | 'rtp' - | 'srtp' - | 'rtcp' - | 'rtx' - | 'bwe' - | 'score' - | 'simulcast' - | 'svc' - | 'sctp' - | 'message'; - -export type WorkerSettings = { - /** - * Logging level for logs generated by the media worker subprocesses (check - * the Debugging documentation). Valid values are 'debug', 'warn', 'error' and - * 'none'. Default 'error'. - */ - logLevel?: WorkerLogLevel; - - /** - * Log tags for debugging. Check the meaning of each available tag in the - * Debugging documentation. - */ - logTags?: WorkerLogTag[]; - - /** - * Minimun RTC port for ICE, DTLS, RTP, etc. Default 10000. - * @deprecated Use |portRange| in TransportListenInfo object instead. - */ - rtcMinPort?: number; - - /** - * Maximum RTC port for ICE, DTLS, RTP, etc. Default 59999. - * @deprecated Use |portRange| in TransportListenInfo object instead. - */ - rtcMaxPort?: number; - - /** - * Path to the DTLS public certificate file in PEM format. If unset, a - * certificate is dynamically created. - */ - dtlsCertificateFile?: string; - - /** - * Path to the DTLS certificate private key file in PEM format. If unset, a - * certificate is dynamically created. - */ - dtlsPrivateKeyFile?: string; - - /** - * Field trials for libwebrtc. - * @private - * - * NOTE: For advanced users only. An invalid value will make the worker crash. - * Default value is - * "WebRTC-Bwe-AlrLimitedBackoff/Enabled/". - */ - libwebrtcFieldTrials?: string; - - /** - * Disable liburing (io_uring) despite it's supported in current host. - */ - disableLiburing?: boolean; - - /** - * Custom application data. - */ - appData?: WorkerAppData; -}; - -export type WorkerUpdateableSettings = Pick< - WorkerSettings, - 'logLevel' | 'logTags' ->; - -/** - * An object with the fields of the uv_rusage_t struct. - * - * - http://docs.libuv.org/en/v1.x/misc.html#c.uv_rusage_t - * - https://linux.die.net/man/2/getrusage - */ -export type WorkerResourceUsage = { - /** - * User CPU time used (in ms). - */ - ru_utime: number; - - /** - * System CPU time used (in ms). - */ - ru_stime: number; - - /** - * Maximum resident set size. - */ - ru_maxrss: number; - - /** - * Integral shared memory size. - */ - ru_ixrss: number; - - /** - * Integral unshared data size. - */ - ru_idrss: number; - - /** - * Integral unshared stack size. - */ - ru_isrss: number; - - /** - * Page reclaims (soft page faults). - */ - ru_minflt: number; - - /** - * Page faults (hard page faults). - */ - ru_majflt: number; - - /** - * Swaps. - */ - ru_nswap: number; - - /** - * Block input operations. - */ - ru_inblock: number; - - /** - * Block output operations. - */ - ru_oublock: number; - - /** - * IPC messages sent. - */ - ru_msgsnd: number; - - /** - * IPC messages received. - */ - ru_msgrcv: number; - - /** - * Signals received. - */ - ru_nsignals: number; - - /** - * Voluntary context switches. - */ - ru_nvcsw: number; - - /** - * Involuntary context switches. - */ - ru_nivcsw: number; -}; - -export type WorkerDump = { - pid: number; - webRtcServerIds: string[]; - routerIds: string[]; - channelMessageHandlers: { - channelRequestHandlers: string[]; - channelNotificationHandlers: string[]; - }; - liburing?: { - sqeProcessCount: number; - sqeMissCount: number; - userDataMissCount: number; - }; -}; - -export type WorkerEvents = { - died: [Error]; - subprocessclose: []; - listenererror: [string, Error]; - // Private events. - '@success': []; - '@failure': [Error]; -}; - -export type WorkerObserver = EnhancedEventEmitter; - -export type WorkerObserverEvents = { - close: []; - newwebrtcserver: [WebRtcServer]; - newrouter: [Router]; -}; - // If env MEDIASOUP_WORKER_BIN is given, use it as worker binary. // Otherwise if env MEDIASOUP_BUILDTYPE is 'Debug' use the Debug binary. // Otherwise use the Release binary. @@ -247,9 +61,10 @@ export const workerBin = process.env.MEDIASOUP_WORKER_BIN const logger = new Logger('Worker'); const workerLogger = new Logger('Worker'); -export class Worker< - WorkerAppData extends AppData = AppData, -> extends EnhancedEventEmitter { +export class Worker + extends EnhancedEventEmitter + implements WorkerInterface +{ // mediasoup-worker child process. #child: ChildProcess; @@ -272,10 +87,10 @@ export class Worker< #appData: WorkerAppData; // WebRtcServers set. - readonly #webRtcServers: Set = new Set(); + readonly #webRtcServers: Set = new Set(); // Routers set. - readonly #routers: Set = new Set(); + readonly #routers: Set = new Set(); // Observer instance. readonly #observer: WorkerObserver = @@ -541,18 +356,20 @@ export class Worker< } /** - * @private * Just for testing purposes. + * + * @private */ - get webRtcServersForTesting(): Set { + get webRtcServersForTesting(): Set { return this.#webRtcServers; } /** - * @private * Just for testing purposes. + * + * @private */ - get routersForTesting(): Set { + get routersForTesting(): Set { return this.#routers; } @@ -673,7 +490,7 @@ export class Worker< listenInfos, appData, }: WebRtcServerOptions): Promise< - WebRtcServer + WebRtcServerInterface > { logger.debug('createWebRtcServer()'); @@ -715,11 +532,12 @@ export class Worker< createWebRtcServerRequestOffset ); - const webRtcServer: WebRtcServer = new WebRtcServer({ - internal: { webRtcServerId }, - channel: this.#channel, - appData, - }); + const webRtcServer: WebRtcServerInterface = + new WebRtcServer({ + internal: { webRtcServerId }, + channel: this.#channel, + appData, + }); this.#webRtcServers.add(webRtcServer); webRtcServer.on('@close', () => this.#webRtcServers.delete(webRtcServer)); @@ -736,7 +554,9 @@ export class Worker< async createRouter({ mediaCodecs, appData, - }: RouterOptions = {}): Promise> { + }: RouterOptions = {}): Promise< + RouterInterface + > { logger.debug('createRouter()'); if (appData && typeof appData !== 'object') { @@ -766,7 +586,7 @@ export class Worker< ); const data = { rtpCapabilities }; - const router: Router = new Router({ + const router: RouterInterface = new Router({ internal: { routerId, }, diff --git a/node/src/WorkerInterface.ts b/node/src/WorkerInterface.ts new file mode 100644 index 0000000000..30b3696546 --- /dev/null +++ b/node/src/WorkerInterface.ts @@ -0,0 +1,255 @@ +import { EnhancedEventEmitter } from './enhancedEvents'; +import { + WebRtcServerInterface, + WebRtcServerOptions, +} from './WebRtcServerInterface'; +import { RouterInterface, RouterOptions } from './RouterInterface'; +import { AppData } from './types'; + +export type WorkerLogLevel = 'debug' | 'warn' | 'error' | 'none'; + +export type WorkerLogTag = + | 'info' + | 'ice' + | 'dtls' + | 'rtp' + | 'srtp' + | 'rtcp' + | 'rtx' + | 'bwe' + | 'score' + | 'simulcast' + | 'svc' + | 'sctp' + | 'message'; + +export type WorkerSettings = { + /** + * Logging level for logs generated by the media worker subprocesses (check + * the Debugging documentation). Valid values are 'debug', 'warn', 'error' and + * 'none'. Default 'error'. + */ + logLevel?: WorkerLogLevel; + + /** + * Log tags for debugging. Check the meaning of each available tag in the + * Debugging documentation. + */ + logTags?: WorkerLogTag[]; + + /** + * Minimun RTC port for ICE, DTLS, RTP, etc. Default 10000. + * @deprecated Use |portRange| in TransportListenInfo object instead. + */ + rtcMinPort?: number; + + /** + * Maximum RTC port for ICE, DTLS, RTP, etc. Default 59999. + * @deprecated Use |portRange| in TransportListenInfo object instead. + */ + rtcMaxPort?: number; + + /** + * Path to the DTLS public certificate file in PEM format. If unset, a + * certificate is dynamically created. + */ + dtlsCertificateFile?: string; + + /** + * Path to the DTLS certificate private key file in PEM format. If unset, a + * certificate is dynamically created. + */ + dtlsPrivateKeyFile?: string; + + /** + * Field trials for libwebrtc. + * @private + * + * NOTE: For advanced users only. An invalid value will make the worker crash. + * Default value is + * "WebRTC-Bwe-AlrLimitedBackoff/Enabled/". + */ + libwebrtcFieldTrials?: string; + + /** + * Disable liburing (io_uring) despite it's supported in current host. + */ + disableLiburing?: boolean; + + /** + * Custom application data. + */ + appData?: WorkerAppData; +}; + +export type WorkerUpdateableSettings = Pick< + WorkerSettings, + 'logLevel' | 'logTags' +>; + +/** + * An object with the fields of the uv_rusage_t struct. + * + * - http://docs.libuv.org/en/v1.x/misc.html#c.uv_rusage_t + * - https://linux.die.net/man/2/getrusage + */ +export type WorkerResourceUsage = { + /** + * User CPU time used (in ms). + */ + ru_utime: number; + + /** + * System CPU time used (in ms). + */ + ru_stime: number; + + /** + * Maximum resident set size. + */ + ru_maxrss: number; + + /** + * Integral shared memory size. + */ + ru_ixrss: number; + + /** + * Integral unshared data size. + */ + ru_idrss: number; + + /** + * Integral unshared stack size. + */ + ru_isrss: number; + + /** + * Page reclaims (soft page faults). + */ + ru_minflt: number; + + /** + * Page faults (hard page faults). + */ + ru_majflt: number; + + /** + * Swaps. + */ + ru_nswap: number; + + /** + * Block input operations. + */ + ru_inblock: number; + + /** + * Block output operations. + */ + ru_oublock: number; + + /** + * IPC messages sent. + */ + ru_msgsnd: number; + + /** + * IPC messages received. + */ + ru_msgrcv: number; + + /** + * Signals received. + */ + ru_nsignals: number; + + /** + * Voluntary context switches. + */ + ru_nvcsw: number; + + /** + * Involuntary context switches. + */ + ru_nivcsw: number; +}; + +export type WorkerDump = { + pid: number; + webRtcServerIds: string[]; + routerIds: string[]; + channelMessageHandlers: { + channelRequestHandlers: string[]; + channelNotificationHandlers: string[]; + }; + liburing?: { + sqeProcessCount: number; + sqeMissCount: number; + userDataMissCount: number; + }; +}; + +export type WorkerEvents = { + died: [Error]; + subprocessclose: []; + listenererror: [string, Error]; + // Private events. + '@success': []; + '@failure': [Error]; +}; + +export type WorkerObserver = EnhancedEventEmitter; + +export type WorkerObserverEvents = { + close: []; + newwebrtcserver: [WebRtcServerInterface]; + newrouter: [RouterInterface]; +}; + +export interface WorkerInterface + extends EnhancedEventEmitter { + get pid(): number; + + get closed(): boolean; + + get died(): boolean; + + /** + * Whether the Worker subprocess is closed. + */ + get subprocessClosed(): boolean; + + /** + * App custom data. + */ + get appData(): WorkerAppData; + + /** + * App custom data setter. + */ + set appData(appData: WorkerAppData); + + /** + * Observer. + */ + get observer(): WorkerObserver; + + close(): void; + + dump(): Promise; + + getResourceUsage(): Promise; + + updateSettings( + options?: WorkerUpdateableSettings + ): Promise; + + createWebRtcServer( + options: WebRtcServerOptions + ): Promise>; + + createRouter( + options?: RouterOptions + ): Promise>; +} diff --git a/node/src/index.ts b/node/src/index.ts index 9239dc77ac..a781d44ddb 100644 --- a/node/src/index.ts +++ b/node/src/index.ts @@ -1,6 +1,7 @@ import { Logger, LoggerEmitter } from './Logger'; import { EnhancedEventEmitter } from './enhancedEvents'; -import { workerBin, Worker, WorkerSettings } from './Worker'; +import { WorkerInterface, WorkerSettings } from './WorkerInterface'; +import { Worker, workerBin } from './Worker'; import * as utils from './utils'; import { supportedRtpCapabilities } from './supportedRtpCapabilities'; import { RtpCapabilities } from './RtpParameters'; @@ -26,7 +27,7 @@ export { parse as parseScalabilityMode } from './scalabilityModes'; export type Observer = EnhancedEventEmitter; export type ObserverEvents = { - newworker: [Worker]; + newworker: [WorkerInterface]; }; const observer: Observer = new EnhancedEventEmitter(); @@ -116,14 +117,16 @@ export async function createWorker< libwebrtcFieldTrials, disableLiburing, appData, -}: WorkerSettings = {}): Promise> { +}: WorkerSettings = {}): Promise< + WorkerInterface +> { logger.debug('createWorker()'); if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } - const worker: Worker = new Worker({ + const worker: WorkerInterface = new Worker({ logLevel, logTags, rtcMinPort, diff --git a/node/src/test/test-ActiveSpeakerObserver.ts b/node/src/test/test-ActiveSpeakerObserver.ts index aaa922390d..f0d6c04d48 100644 --- a/node/src/test/test-ActiveSpeakerObserver.ts +++ b/node/src/test/test-ActiveSpeakerObserver.ts @@ -5,8 +5,8 @@ import * as utils from '../utils'; type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; }; const ctx: TestContext = { @@ -85,8 +85,8 @@ test('activeSpeakerObserver.pause() and resume() succeed', async () => { }, 2000); test('activeSpeakerObserver.close() succeeds', async () => { - const activeSpeakerObserver = await ctx.router!.createAudioLevelObserver({ - maxEntries: 8, + const activeSpeakerObserver = await ctx.router!.createActiveSpeakerObserver({ + interval: 500, }); let dump = await ctx.router!.dump(); @@ -103,7 +103,7 @@ test('activeSpeakerObserver.close() succeeds', async () => { }, 2000); test('ActiveSpeakerObserver emits "routerclose" if Router is closed', async () => { - const activeSpeakerObserver = await ctx.router!.createAudioLevelObserver(); + const activeSpeakerObserver = await ctx.router!.createActiveSpeakerObserver(); const promise = enhancedOnce( activeSpeakerObserver, @@ -117,7 +117,7 @@ test('ActiveSpeakerObserver emits "routerclose" if Router is closed', async () = }, 2000); test('ActiveSpeakerObserver emits "routerclose" if Worker is closed', async () => { - const activeSpeakerObserver = await ctx.router!.createAudioLevelObserver(); + const activeSpeakerObserver = await ctx.router!.createActiveSpeakerObserver(); const promise = enhancedOnce( activeSpeakerObserver, diff --git a/node/src/test/test-AudioLevelObserver.ts b/node/src/test/test-AudioLevelObserver.ts index f1a1a5fb17..4f3f716c0c 100644 --- a/node/src/test/test-AudioLevelObserver.ts +++ b/node/src/test/test-AudioLevelObserver.ts @@ -5,8 +5,8 @@ import * as utils from '../utils'; type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; }; const ctx: TestContext = { diff --git a/node/src/test/test-Consumer.ts b/node/src/test/test-Consumer.ts index aad102f089..f190163f92 100644 --- a/node/src/test/test-Consumer.ts +++ b/node/src/test/test-Consumer.ts @@ -2,6 +2,7 @@ import * as flatbuffers from 'flatbuffers'; import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; import { WorkerEvents, ConsumerEvents } from '../types'; +import { Consumer } from '../Consumer'; import { UnsupportedError } from '../errors'; import * as utils from '../utils'; import { @@ -16,12 +17,12 @@ type TestContext = { audioProducerOptions: mediasoup.types.ProducerOptions; videoProducerOptions: mediasoup.types.ProducerOptions; consumerDeviceCapabilities: mediasoup.types.RtpCapabilities; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - webRtcTransport1?: mediasoup.types.WebRtcTransport; - webRtcTransport2?: mediasoup.types.WebRtcTransport; - audioProducer?: mediasoup.types.Producer; - videoProducer?: mediasoup.types.Producer; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + webRtcTransport1?: mediasoup.types.WebRtcTransportInterface; + webRtcTransport2?: mediasoup.types.WebRtcTransportInterface; + audioProducer?: mediasoup.types.ProducerInterface; + videoProducer?: mediasoup.types.ProducerInterface; }; const ctx: TestContext = { @@ -975,7 +976,7 @@ test('consumer.enableTraceEvent() succeed', async () => { expect(dump1.traceEventTypes).toEqual(expect.arrayContaining(['rtp', 'pli'])); - await audioConsumer.enableTraceEvent([]); + await audioConsumer.enableTraceEvent(); const dump2 = await audioConsumer.dump(); @@ -1052,8 +1053,8 @@ test('Consumer emits "score"', async () => { rtpCapabilities: ctx.consumerDeviceCapabilities, }); - // Private API. - const channel = audioConsumer.channelForTesting; + // API not exposed in the interface. + const channel = (audioConsumer as Consumer).channelForTesting; const onScore = jest.fn(); audioConsumer.on('score', onScore); diff --git a/node/src/test/test-DataConsumer.ts b/node/src/test/test-DataConsumer.ts index f61bb8291c..7b3c91a2d0 100644 --- a/node/src/test/test-DataConsumer.ts +++ b/node/src/test/test-DataConsumer.ts @@ -5,12 +5,12 @@ import * as utils from '../utils'; type TestContext = { dataProducerOptions: mediasoup.types.DataProducerOptions; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - webRtcTransport1?: mediasoup.types.WebRtcTransport; - webRtcTransport2?: mediasoup.types.WebRtcTransport; - directTransport?: mediasoup.types.DirectTransport; - dataProducer?: mediasoup.types.DataProducer; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + webRtcTransport1?: mediasoup.types.WebRtcTransportInterface; + webRtcTransport2?: mediasoup.types.WebRtcTransportInterface; + directTransport?: mediasoup.types.DirectTransportInterface; + dataProducer?: mediasoup.types.DataProducerInterface; }; const ctx: TestContext = { diff --git a/node/src/test/test-DataProducer.ts b/node/src/test/test-DataProducer.ts index 4f490792ce..61d6394768 100644 --- a/node/src/test/test-DataProducer.ts +++ b/node/src/test/test-DataProducer.ts @@ -6,10 +6,10 @@ import * as utils from '../utils'; type TestContext = { dataProducerOptions1: mediasoup.types.DataProducerOptions; dataProducerOptions2: mediasoup.types.DataProducerOptions; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - webRtcTransport1?: mediasoup.types.WebRtcTransport; - webRtcTransport2?: mediasoup.types.WebRtcTransport; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + webRtcTransport1?: mediasoup.types.WebRtcTransportInterface; + webRtcTransport2?: mediasoup.types.WebRtcTransportInterface; }; const ctx: TestContext = { diff --git a/node/src/test/test-DirectTransport.ts b/node/src/test/test-DirectTransport.ts index e8812257b4..cb8784f3bb 100644 --- a/node/src/test/test-DirectTransport.ts +++ b/node/src/test/test-DirectTransport.ts @@ -1,10 +1,11 @@ import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; -import { WorkerEvents, DirectTransportEvents } from '../types'; +import { DirectTransportEvents } from '../DirectTransportInterface'; +import { WorkerEvents } from '../types'; type TestContext = { - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; }; const ctx: TestContext = {}; diff --git a/node/src/test/test-PipeTransport.ts b/node/src/test/test-PipeTransport.ts index 854636ffa4..86603da30f 100644 --- a/node/src/test/test-PipeTransport.ts +++ b/node/src/test/test-PipeTransport.ts @@ -15,17 +15,17 @@ type TestContext = { videoProducerOptions: mediasoup.types.ProducerOptions; dataProducerOptions: mediasoup.types.DataProducerOptions; consumerDeviceCapabilities: mediasoup.types.RtpCapabilities; - worker1?: mediasoup.types.Worker; - worker2?: mediasoup.types.Worker; - router1?: mediasoup.types.Router; - router2?: mediasoup.types.Router; - webRtcTransport1?: mediasoup.types.WebRtcTransport; - webRtcTransport2?: mediasoup.types.WebRtcTransport; - audioProducer?: mediasoup.types.Producer; - videoProducer?: mediasoup.types.Producer; - videoConsumer?: mediasoup.types.Consumer; - dataProducer?: mediasoup.types.DataProducer; - dataConsumer?: mediasoup.types.DataConsumer; + worker1?: mediasoup.types.WorkerInterface; + worker2?: mediasoup.types.WorkerInterface; + router1?: mediasoup.types.RouterInterface; + router2?: mediasoup.types.RouterInterface; + webRtcTransport1?: mediasoup.types.WebRtcTransportInterface; + webRtcTransport2?: mediasoup.types.WebRtcTransportInterface; + audioProducer?: mediasoup.types.ProducerInterface; + videoProducer?: mediasoup.types.ProducerInterface; + videoConsumer?: mediasoup.types.ConsumerInterface; + dataProducer?: mediasoup.types.DataProducerInterface; + dataConsumer?: mediasoup.types.DataConsumerInterface; }; const ctx: TestContext = { @@ -221,8 +221,8 @@ test('router.pipeToRouter() succeeds with audio', async () => { producerId: ctx.audioProducer!.id, router: ctx.router2!, })) as { - pipeConsumer: mediasoup.types.Consumer; - pipeProducer: mediasoup.types.Producer; + pipeConsumer: mediasoup.types.ConsumerInterface; + pipeProducer: mediasoup.types.ProducerInterface; }; const dump1 = await ctx.router1!.dump(); @@ -336,8 +336,8 @@ test('router.pipeToRouter() succeeds with video', async () => { producerId: ctx.videoProducer!.id, router: ctx.router2!, })) as { - pipeConsumer: mediasoup.types.Consumer; - pipeProducer: mediasoup.types.Producer; + pipeConsumer: mediasoup.types.ConsumerInterface; + pipeProducer: mediasoup.types.ProducerInterface; }; const dump1 = await ctx.router1!.dump(); @@ -937,8 +937,8 @@ test('router.pipeToRouter() succeeds with data', async () => { dataProducerId: ctx.dataProducer!.id, router: ctx.router2!, })) as { - pipeDataConsumer: mediasoup.types.DataConsumer; - pipeDataProducer: mediasoup.types.DataProducer; + pipeDataConsumer: mediasoup.types.DataConsumerInterface; + pipeDataProducer: mediasoup.types.DataProducerInterface; }; const dump1 = await ctx.router1!.dump(); diff --git a/node/src/test/test-PlainTransport.ts b/node/src/test/test-PlainTransport.ts index abd7c326b9..c3c91b3b57 100644 --- a/node/src/test/test-PlainTransport.ts +++ b/node/src/test/test-PlainTransport.ts @@ -9,8 +9,8 @@ const IS_WINDOWS = os.platform() === 'win32'; type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; }; const ctx: TestContext = { diff --git a/node/src/test/test-Producer.ts b/node/src/test/test-Producer.ts index 661c60faf1..6587ae781c 100644 --- a/node/src/test/test-Producer.ts +++ b/node/src/test/test-Producer.ts @@ -2,6 +2,7 @@ import * as flatbuffers from 'flatbuffers'; import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; import { WorkerEvents, ProducerEvents } from '../types'; +import { Producer } from '../Producer'; import { UnsupportedError } from '../errors'; import * as utils from '../utils'; import { @@ -15,10 +16,10 @@ type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; audioProducerOptions: mediasoup.types.ProducerOptions; videoProducerOptions: mediasoup.types.ProducerOptions; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - webRtcTransport1?: mediasoup.types.WebRtcTransport; - webRtcTransport2?: mediasoup.types.WebRtcTransport; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + webRtcTransport1?: mediasoup.types.WebRtcTransportInterface; + webRtcTransport2?: mediasoup.types.WebRtcTransportInterface; }; const ctx: TestContext = { @@ -680,7 +681,7 @@ test('producer.enableTraceEvent() succeed', async () => { expect(dump1.traceEventTypes).toEqual(expect.arrayContaining(['rtp', 'pli'])); - await audioProducer.enableTraceEvent([]); + await audioProducer.enableTraceEvent(); const dump2 = await audioProducer.dump(); @@ -726,8 +727,8 @@ test('Producer emits "score"', async () => { ctx.videoProducerOptions ); - // Private API. - const channel = videoProducer.channelForTesting; + // API not exposed in the interface. + const channel = (videoProducer as Producer).channelForTesting; const onScore = jest.fn(); videoProducer.on('score', onScore); diff --git a/node/src/test/test-Router.ts b/node/src/test/test-Router.ts index 615ff4176c..81804755cd 100644 --- a/node/src/test/test-Router.ts +++ b/node/src/test/test-Router.ts @@ -1,12 +1,13 @@ import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; +import { Worker } from '../Worker'; import { WorkerEvents, RouterEvents } from '../types'; import { InvalidStateError } from '../errors'; import * as utils from '../utils'; type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; - worker?: mediasoup.types.Worker; + worker?: mediasoup.types.WorkerInterface; }; const ctx: TestContext = { @@ -94,15 +95,15 @@ test('worker.createRouter() succeeds', async () => { mapDataConsumerIdDataProducerId: {}, }); - // Private API. - expect(ctx.worker!.routersForTesting.size).toBe(1); + // API not exposed in the interface. + expect((ctx.worker! as Worker).routersForTesting.size).toBe(1); ctx.worker!.close(); expect(router.closed).toBe(true); - // Private API. - expect(ctx.worker!.routersForTesting.size).toBe(0); + // API not exposed in the interface. + expect((ctx.worker! as Worker).routersForTesting.size).toBe(0); }, 2000); test('worker.createRouter() with wrong arguments rejects with TypeError', async () => { diff --git a/node/src/test/test-WebRtcServer.ts b/node/src/test/test-WebRtcServer.ts index a6c5e8bec0..9dff5aa1df 100644 --- a/node/src/test/test-WebRtcServer.ts +++ b/node/src/test/test-WebRtcServer.ts @@ -1,11 +1,14 @@ import { pickPort } from 'pick-port'; import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; +import { Worker } from '../Worker'; import { WorkerEvents, WebRtcServerEvents } from '../types'; +import { WebRtcServer } from '../WebRtcServer'; +import { Router } from '../Router'; import { InvalidStateError } from '../errors'; type TestContext = { - worker?: mediasoup.types.Worker; + worker?: mediasoup.types.WorkerInterface; }; const ctx: TestContext = {}; @@ -80,15 +83,15 @@ test('worker.createWebRtcServer() succeeds', async () => { tupleHashes: [], }); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(1); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(1); ctx.worker!.close(); expect(webRtcServer.closed).toBe(true); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(0); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(0); }, 2000); test('worker.createWebRtcServer() with portRange succeeds', async () => { @@ -149,15 +152,15 @@ test('worker.createWebRtcServer() with portRange succeeds', async () => { tupleHashes: [], }); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(1); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(1); ctx.worker!.close(); expect(webRtcServer.closed).toBe(true); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(0); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(0); }, 2000); test('worker.createWebRtcServer() without specifying port/portRange succeeds', async () => { @@ -205,15 +208,15 @@ test('worker.createWebRtcServer() without specifying port/portRange succeeds', a tupleHashes: [], }); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(1); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(1); ctx.worker!.close(); expect(webRtcServer.closed).toBe(true); - // Private API. - expect(ctx.worker!.webRtcServersForTesting.size).toBe(0); + // API not exposed in the interface. + expect((ctx.worker! as Worker).webRtcServersForTesting.size).toBe(0); }, 2000); test('worker.createWebRtcServer() with wrong arguments rejects with TypeError', async () => { @@ -435,8 +438,12 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and transport is expect(transport.iceState).toBe('new'); expect(transport.iceSelectedTuple).toBeUndefined(); - expect(webRtcServer.webRtcTransportsForTesting.size).toBe(1); - expect(router.transportsForTesting.size).toBe(1); + // API not exposed in the interface. + expect((webRtcServer as WebRtcServer).webRtcTransportsForTesting.size).toBe( + 1 + ); + // API not exposed in the interface. + expect((router as Router).transportsForTesting.size).toBe(1); await expect(webRtcServer.dump()).resolves.toMatchObject({ id: webRtcServer.id, @@ -454,8 +461,12 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and transport is expect(transport.closed).toBe(true); expect(onObserverWebRtcTransportUnhandled).toHaveBeenCalledTimes(1); expect(onObserverWebRtcTransportUnhandled).toHaveBeenCalledWith(transport); - expect(webRtcServer.webRtcTransportsForTesting.size).toBe(0); - expect(router.transportsForTesting.size).toBe(0); + // API not exposed in the interface. + expect((webRtcServer as WebRtcServer).webRtcTransportsForTesting.size).toBe( + 0 + ); + // API not exposed in the interface. + expect((router as Router).transportsForTesting.size).toBe(0); await expect(webRtcServer.dump()).resolves.toMatchObject({ id: webRtcServer.id, @@ -531,8 +542,12 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and webRtcServer expect(transport.iceState).toBe('new'); expect(transport.iceSelectedTuple).toBeUndefined(); - expect(webRtcServer.webRtcTransportsForTesting.size).toBe(1); - expect(router.transportsForTesting.size).toBe(1); + // API not exposed in the interface. + expect((webRtcServer as WebRtcServer).webRtcTransportsForTesting.size).toBe( + 1 + ); + // API not exposed in the interface. + expect((router as Router).transportsForTesting.size).toBe(1); await expect(webRtcServer.dump()).resolves.toMatchObject({ id: webRtcServer.id, @@ -581,8 +596,12 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and webRtcServer expect(transport.iceSelectedTuple).toBe(undefined); expect(transport.dtlsState).toBe('closed'); expect(transport.sctpState).toBe(undefined); - expect(webRtcServer.webRtcTransportsForTesting.size).toBe(0); - expect(router.transportsForTesting.size).toBe(0); + // API not exposed in the interface. + expect((webRtcServer as WebRtcServer).webRtcTransportsForTesting.size).toBe( + 0 + ); + // API not exposed in the interface. + expect((router as Router).transportsForTesting.size).toBe(0); await expect(ctx.worker!.dump()).resolves.toMatchObject({ pid: ctx.worker!.pid, diff --git a/node/src/test/test-WebRtcTransport.ts b/node/src/test/test-WebRtcTransport.ts index 400f25c11e..f43849f8a8 100644 --- a/node/src/test/test-WebRtcTransport.ts +++ b/node/src/test/test-WebRtcTransport.ts @@ -3,8 +3,10 @@ import * as flatbuffers from 'flatbuffers'; import * as mediasoup from '../'; import { enhancedOnce } from '../enhancedEvents'; import { WorkerEvents, WebRtcTransportEvents } from '../types'; +import { WebRtcTransport } from '../WebRtcTransport'; +import { TransportTuple } from '../TransportInterface'; +import { serializeProtocol } from '../Transport'; import * as utils from '../utils'; -import { serializeProtocol, TransportTuple } from '../Transport'; import { Notification, Body as NotificationBody, @@ -15,8 +17,8 @@ import * as FbsWebRtcTransport from '../fbs/web-rtc-transport'; type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; }; const ctx: TestContext = { @@ -620,7 +622,7 @@ test('transport.enableTraceEvent() succeed', async () => { traceEventTypes: ['probation'], }); - await webRtcTransport.enableTraceEvent([]); + await webRtcTransport.enableTraceEvent(); await expect(webRtcTransport.dump()).resolves.toMatchObject({ traceEventTypes: [], }); @@ -667,8 +669,8 @@ test('WebRtcTransport events succeed', async () => { ], }); - // Private API. - const channel = webRtcTransport.channelForTesting; + // API not exposed in the interface. + const channel = (webRtcTransport as WebRtcTransport).channelForTesting; const onIceStateChange = jest.fn(); webRtcTransport.on('icestatechange', onIceStateChange); diff --git a/node/src/test/test-multiopus.ts b/node/src/test/test-multiopus.ts index 4b8ab50450..22b9484cc0 100644 --- a/node/src/test/test-multiopus.ts +++ b/node/src/test/test-multiopus.ts @@ -8,9 +8,9 @@ type TestContext = { mediaCodecs: mediasoup.types.RtpCodecCapability[]; audioProducerOptions: mediasoup.types.ProducerOptions; consumerDeviceCapabilities: mediasoup.types.RtpCapabilities; - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - webRtcTransport?: mediasoup.types.WebRtcTransport; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + webRtcTransport?: mediasoup.types.WebRtcTransportInterface; }; const ctx: TestContext = { diff --git a/node/src/test/test-node-sctp.ts b/node/src/test/test-node-sctp.ts index e344c1a484..cb4fb14f64 100644 --- a/node/src/test/test-node-sctp.ts +++ b/node/src/test/test-node-sctp.ts @@ -6,11 +6,11 @@ import { enhancedOnce } from '../enhancedEvents'; import { WorkerEvents } from '../types'; type TestContext = { - worker?: mediasoup.types.Worker; - router?: mediasoup.types.Router; - plainTransport?: mediasoup.types.PlainTransport; - dataProducer?: mediasoup.types.DataProducer; - dataConsumer?: mediasoup.types.DataConsumer; + worker?: mediasoup.types.WorkerInterface; + router?: mediasoup.types.RouterInterface; + plainTransport?: mediasoup.types.PlainTransportInterface; + dataProducer?: mediasoup.types.DataProducerInterface; + dataConsumer?: mediasoup.types.DataConsumerInterface; udpSocket?: dgram.Socket; sctpSocket?: any; sctpSendStreamId?: number; diff --git a/node/src/types.ts b/node/src/types.ts index 14c9e4a0c1..cfe07698fb 100644 --- a/node/src/types.ts +++ b/node/src/types.ts @@ -4,27 +4,23 @@ export type * from './SctpParameters'; export type * from './SrtpParameters'; export type * from './scalabilityModes'; -export * from './Worker'; -export * from './WebRtcServer'; -export * from './Router'; -export * from './Transport'; -export * from './WebRtcTransport'; -export * from './PlainTransport'; -export * from './PipeTransport'; -export * from './DirectTransport'; -export * from './Producer'; -export * from './Consumer'; -export * from './DataProducer'; -export * from './DataConsumer'; -export * from './RtpObserver'; -export * from './ActiveSpeakerObserver'; -export * from './AudioLevelObserver'; +export * from './WorkerInterface'; +export * from './WebRtcServerInterface'; +export * from './RouterInterface'; +export * from './TransportInterface'; +export * from './WebRtcTransportInterface'; +export * from './PlainTransportInterface'; +export * from './PipeTransportInterface'; +export * from './DirectTransportInterface'; +export * from './ProducerInterface'; +export * from './ConsumerInterface'; +export * from './DataProducerInterface'; +export * from './DataConsumerInterface'; +export * from './RtpObserverInterface'; +export * from './ActiveSpeakerObserverInterface'; +export * from './AudioLevelObserverInterface'; export * from './errors'; -export type AppData = { - [key: string]: unknown; -}; - type Only = { [P in keyof T]: T[P]; } & { @@ -32,3 +28,7 @@ type Only = { }; export type Either = Only | Only; + +export type AppData = { + [key: string]: unknown; +}; diff --git a/node/src/utils.ts b/node/src/utils.ts index f7c8e8c8b9..c6e1d9748d 100644 --- a/node/src/utils.ts +++ b/node/src/utils.ts @@ -1,5 +1,5 @@ import { randomUUID, randomInt } from 'node:crypto'; -import { ProducerType } from './Producer'; +import { ProducerType } from './ProducerInterface'; import { Type as FbsRtpParametersType } from './fbs/rtp-parameters'; /**