Skip to content

Commit 45b06b3

Browse files
author
wuxinfei
committed
feat: Support different rtp header extentions and payload for each consumer
This commit introduces the ability to specify custom RTP parameters for consumers, allowing for advanced scenarios where the wire-level payload types and header-extension IDs differ from the router's canonical values. The implementation includes validation against the producer's consumable RTP parameters and ensures compatibility. Additionally, tests have been added to verify the correct behavior of this feature, including rejection of overrides in pipe transports and validation of codec mappings.
1 parent edb8c72 commit 45b06b3

20 files changed

Lines changed: 1786 additions & 132 deletions

node/src/ConsumerTypes.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,25 @@ export type ConsumerOptions<ConsumerAppData extends AppData = AppData> = {
7070
*/
7171
pipe?: boolean;
7272

73+
/**
74+
* When set, bypasses ortc.getConsumerRtpParameters and is used verbatim
75+
* as the Consumer's rtpParameters. The payload types and header-extension
76+
* ids declared here become the on-wire values for this Consumer.
77+
*
78+
* mediasoup validates compatibility against the Producer's
79+
* consumableRtpParameters (1:1 codec mapping, compatible fmtp, matching
80+
* layer/simulcast structure, header-extension URIs being a subset of the
81+
* consumable ones) and asks the worker to rewrite outgoing RTP headers
82+
* accordingly.
83+
*
84+
* Intended for advanced scenarios (e.g. WHEP) where the answer SDP
85+
* dictates wire-level PT / ext-id values that differ from the Router's
86+
* canonical ones.
87+
*
88+
* Not supported together with pipe=true.
89+
*/
90+
rtpParameters?: RtpParameters;
91+
7392
/**
7493
* Custom application data.
7594
*/

node/src/Transport.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ export abstract class TransportImpl<
585585
ignoreDtx = false,
586586
enableRtx,
587587
pipe = false,
588+
rtpParameters: overrideRtpParameters,
588589
appData,
589590
}: ConsumerOptions<ConsumerAppData>): Promise<Consumer<ConsumerAppData>> {
590591
logger.debug('consume()');
@@ -595,6 +596,10 @@ export abstract class TransportImpl<
595596
throw new TypeError('if given, appData must be an object');
596597
} else if (mid && (typeof mid !== 'string' || mid.length === 0)) {
597598
throw new TypeError('if given, mid must be non empty string');
599+
} else if (overrideRtpParameters && pipe) {
600+
throw new TypeError(
601+
'rtpParameters override is not supported together with pipe=true'
602+
);
598603
}
599604

600605
// Clone given RTP capabilities to not modify input data.
@@ -614,19 +619,28 @@ export abstract class TransportImpl<
614619
enableRtx = producer.kind === 'video';
615620
}
616621

622+
let consumerRtpMapping: ortc.ConsumerRtpMapping | undefined;
623+
617624
// This may throw.
618-
const rtpParameters = ortc.getConsumerRtpParameters({
625+
const rtpParameters: RtpParameters = ortc.getConsumerRtpParameters({
619626
consumableRtpParameters: producer.consumableRtpParameters,
620-
remoteRtpCapabilities: clonedRtpCapabilities,
627+
remoteRtpCapabilities: overrideRtpParameters ?? clonedRtpCapabilities,
621628
pipe,
622629
enableRtx,
623630
});
624631

632+
if (overrideRtpParameters) {
633+
consumerRtpMapping = ortc.getConsumerRtpMapping(
634+
producer.consumableRtpParameters,
635+
rtpParameters
636+
);
637+
}
638+
625639
// Set MID.
626640
if (!pipe) {
627641
if (mid) {
628642
rtpParameters.mid = mid;
629-
} else {
643+
} else if (!rtpParameters.mid) {
630644
rtpParameters.mid = `${this.#nextMidForConsumers++}`;
631645

632646
// We use up to 8 bytes for MID (string).
@@ -650,6 +664,7 @@ export abstract class TransportImpl<
650664
preferredLayers,
651665
ignoreDtx,
652666
pipe,
667+
consumerRtpMapping,
653668
});
654669

655670
const response = await this.channel.request(
@@ -1348,6 +1363,7 @@ function createConsumeRequest({
13481363
preferredLayers,
13491364
ignoreDtx,
13501365
pipe,
1366+
consumerRtpMapping,
13511367
}: {
13521368
builder: flatbuffers.Builder;
13531369
producer: Producer;
@@ -1357,6 +1373,7 @@ function createConsumeRequest({
13571373
preferredLayers?: ConsumerLayers;
13581374
ignoreDtx?: boolean;
13591375
pipe: boolean;
1376+
consumerRtpMapping?: ortc.ConsumerRtpMapping;
13601377
}): number {
13611378
const rtpParametersOffset = serializeRtpParameters(builder, rtpParameters);
13621379
const consumerIdOffset = builder.createString(consumerId);
@@ -1389,6 +1406,15 @@ function createConsumeRequest({
13891406
FbsConsumer.ConsumerLayers.endConsumerLayers(builder);
13901407
}
13911408

1409+
let consumerRtpMappingOffset: number | undefined;
1410+
1411+
if (consumerRtpMapping) {
1412+
consumerRtpMappingOffset = ortc.serializeConsumerRtpMapping(
1413+
builder,
1414+
consumerRtpMapping
1415+
);
1416+
}
1417+
13921418
const ConsumeRequest = FbsTransport.ConsumeRequest;
13931419

13941420
// Create Consume Request.
@@ -1420,6 +1446,10 @@ function createConsumeRequest({
14201446

14211447
ConsumeRequest.addIgnoreDtx(builder, Boolean(ignoreDtx));
14221448

1449+
if (consumerRtpMappingOffset !== undefined) {
1450+
ConsumeRequest.addConsumerRtpMapping(builder, consumerRtpMappingOffset);
1451+
}
1452+
14231453
return ConsumeRequest.endConsumeRequest(builder);
14241454
}
14251455

0 commit comments

Comments
 (0)