diff --git a/src/Consumer.ts b/src/Consumer.ts index eb0a901a..76698d82 100644 --- a/src/Consumer.ts +++ b/src/Consumer.ts @@ -12,9 +12,16 @@ export type ConsumerOptions = { kind?: 'audio' | 'video'; rtpParameters: RtpParameters; streamId?: string; + onRtpReceiver?: OnRtpReceiverCallback; appData?: ConsumerAppData; }; +/** + * Invoked synchronously immediately after a new RTCRtpReceiver is created. + * This allows for creating encoded streams in chromium browsers. + */ +export type OnRtpReceiverCallback = (rtpReceiver: RTCRtpReceiver) => void; + export type ConsumerEvents = { transportclose: []; trackended: []; diff --git a/src/Producer.ts b/src/Producer.ts index 4be5666a..3e9e05c3 100644 --- a/src/Producer.ts +++ b/src/Producer.ts @@ -19,9 +19,16 @@ export type ProducerOptions = { stopTracks?: boolean; disableTrackOnPause?: boolean; zeroRtpOnPause?: boolean; + onRtpSender?: OnRtpSenderCallback; appData?: ProducerAppData; }; +/** + * Invoked synchronously immediately after a new RTCRtpSender is created. + * This allows for creating encoded streams in chromium browsers. + */ +export type OnRtpSenderCallback = (rtpSender: RTCRtpSender) => void; + // https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerCodecOptions export type ProducerCodecOptions = { opusStereo?: boolean; diff --git a/src/Transport.ts b/src/Transport.ts index 49fc14aa..fe4dbf93 100644 --- a/src/Transport.ts +++ b/src/Transport.ts @@ -490,6 +490,7 @@ export class Transport< stopTracks = true, disableTrackOnPause = true, zeroRtpOnPause = false, + onRtpSender, appData = {} as ProducerAppData, }: ProducerOptions = {}): Promise< Producer @@ -570,6 +571,7 @@ export class Transport< encodings: normalizedEncodings, codecOptions, codec, + onRtpSender, }); try { @@ -639,6 +641,7 @@ export class Transport< kind, rtpParameters, streamId, + onRtpReceiver, appData = {} as ConsumerAppData, }: ConsumerOptions): Promise> { logger.debug('consume()'); @@ -681,6 +684,7 @@ export class Transport< kind, rtpParameters: clonedRtpParameters, streamId, + onRtpReceiver, appData, }); @@ -876,13 +880,15 @@ export class Transport< const optionsList: HandlerReceiveOptions[] = []; for (const task of pendingConsumerTasks) { - const { id, kind, rtpParameters, streamId } = task.consumerOptions; + const { id, kind, rtpParameters, streamId, onRtpReceiver } = + task.consumerOptions; optionsList.push({ trackId: id!, kind: kind as MediaKind, rtpParameters, streamId, + onRtpReceiver, }); } diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index 186a5552..75c82264 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -321,6 +321,7 @@ export class Chrome111 extends HandlerInterface { encodings, codecOptions, codec, + onRtpSender, }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -378,6 +379,11 @@ export class Chrome111 extends HandlerInterface { streams: [this._sendStream], sendEncodings: encodings, }); + + if (onRtpSender) { + onRtpSender(transceiver.sender); + } + const offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); @@ -826,6 +832,23 @@ export class Chrome111 extends HandlerInterface { await this._pc.setRemoteDescription(offer); + for (const options of optionsList) { + const { trackId, onRtpReceiver } = options; + + if (onRtpReceiver) { + const localId = mapLocalId.get(trackId); + const transceiver = this._pc + .getTransceivers() + .find((t: RTCRtpTransceiver) => t.mid === localId); + + if (!transceiver) { + throw new Error('transceiver not found'); + } + + onRtpReceiver(transceiver.receiver); + } + } + let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); diff --git a/src/handlers/Firefox120.ts b/src/handlers/Firefox120.ts index 76bf0d62..e956992f 100644 --- a/src/handlers/Firefox120.ts +++ b/src/handlers/Firefox120.ts @@ -340,6 +340,7 @@ export class Firefox120 extends HandlerInterface { encodings, codecOptions, codec, + onRtpSender, }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -384,6 +385,11 @@ export class Firefox120 extends HandlerInterface { streams: [this._sendStream], sendEncodings: encodings, }); + + if (onRtpSender) { + onRtpSender(transceiver.sender); + } + const offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); @@ -851,6 +857,23 @@ export class Firefox120 extends HandlerInterface { await this._pc.setRemoteDescription(offer); + for (const options of optionsList) { + const { trackId, onRtpReceiver } = options; + + if (onRtpReceiver) { + const localId = mapLocalId.get(trackId); + const transceiver = this._pc + .getTransceivers() + .find((t: RTCRtpTransceiver) => t.mid === localId); + + if (!transceiver) { + throw new Error('transceiver not found'); + } + + onRtpReceiver(transceiver.receiver); + } + } + let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); diff --git a/src/handlers/HandlerInterface.ts b/src/handlers/HandlerInterface.ts index 8dd7dbf1..db495435 100644 --- a/src/handlers/HandlerInterface.ts +++ b/src/handlers/HandlerInterface.ts @@ -1,5 +1,4 @@ import { EnhancedEventEmitter } from '../EnhancedEventEmitter'; -import { ProducerCodecOptions } from '../Producer'; import { IceParameters, IceCandidate, @@ -7,6 +6,8 @@ import { IceGatheringState, ConnectionState, } from '../Transport'; +import { ProducerCodecOptions, OnRtpSenderCallback } from '../Producer'; +import { OnRtpReceiverCallback } from '../Consumer'; import { RtpCapabilities, RtpCodecCapability, @@ -39,6 +40,7 @@ export type HandlerSendOptions = { encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; codec?: RtpCodecCapability; + onRtpSender?: OnRtpSenderCallback; }; export type HandlerSendResult = { @@ -58,6 +60,7 @@ export type HandlerReceiveOptions = { * can just synchronize up to one audio stream with one video stream. */ streamId?: string; + onRtpReceiver?: OnRtpReceiverCallback; }; export type HandlerReceiveResult = { diff --git a/src/handlers/ReactNativeUnifiedPlan.ts b/src/handlers/ReactNativeUnifiedPlan.ts index efd8f15b..08928e56 100644 --- a/src/handlers/ReactNativeUnifiedPlan.ts +++ b/src/handlers/ReactNativeUnifiedPlan.ts @@ -330,6 +330,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface { encodings, codecOptions, codec, + onRtpSender, }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -368,6 +369,11 @@ export class ReactNativeUnifiedPlan extends HandlerInterface { streams: [this._sendStream], sendEncodings: encodings, }); + + if (onRtpSender) { + onRtpSender(transceiver.sender); + } + let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; @@ -884,6 +890,23 @@ export class ReactNativeUnifiedPlan extends HandlerInterface { await this._pc.setRemoteDescription(offer); + for (const options of optionsList) { + const { trackId, onRtpReceiver } = options; + + if (onRtpReceiver) { + const localId = mapLocalId.get(trackId); + const transceiver = this._pc + .getTransceivers() + .find((t: RTCRtpTransceiver) => t.mid === localId); + + if (!transceiver) { + throw new Error('transceiver not found'); + } + + onRtpReceiver(transceiver.receiver); + } + } + let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index 1b8892f7..144957e5 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -319,6 +319,7 @@ export class Safari12 extends HandlerInterface { encodings, codecOptions, codec, + onRtpSender, }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -350,6 +351,11 @@ export class Safari12 extends HandlerInterface { direction: 'sendonly', streams: [this._sendStream], }); + + if (onRtpSender) { + onRtpSender(transceiver.sender); + } + let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; @@ -822,6 +828,23 @@ export class Safari12 extends HandlerInterface { await this._pc.setRemoteDescription(offer); + for (const options of optionsList) { + const { trackId, onRtpReceiver } = options; + + if (onRtpReceiver) { + const localId = mapLocalId.get(trackId); + const transceiver = this._pc + .getTransceivers() + .find((t: RTCRtpTransceiver) => t.mid === localId); + + if (!transceiver) { + throw new Error('transceiver not found'); + } + + onRtpReceiver(transceiver.receiver); + } + } + let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp);