feat: Support different rtp header extentions and payload for each consumer#1783
Open
jiyeyuran wants to merge 4 commits into
Open
feat: Support different rtp header extentions and payload for each consumer#1783jiyeyuran wants to merge 4 commits into
jiyeyuran wants to merge 4 commits into
Conversation
…nsumer 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.
5b5c28b to
45b06b3
Compare
Author
Member
|
Thanks a lot @jiyeyuran. Please let us get the proper time to review this in depth. It's gonna be long due to the heavy implications of this sensitive change. |
added 3 commits
April 23, 2026 19:57
This update modifies the handling of rtcpFeedback in both TypeScript and Rust implementations to ensure that the final Consumer RTP parameters adhere to RFC 4585 §4.2.2. The changes preserve the caller-advertised rtcpFeedback list, preventing the Router's consumable feedback from leaking into WHEP answers. The implementation includes filtering logic to maintain compatibility with the enableRtx setting.
This commit enhances the readability of the test code by formatting the codec lookups for H264 and RTX into multi-line statements. Additionally, it updates the error type thrown in the test for invalid RTP parameters from TypeError to UnsupportedError, ensuring more accurate error handling in the test cases.
Member
|
Definitely it's gonna take longer than expected for us to review this PR. Very busy. But will do in next weeks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Per-Consumer egress PT / header-extension-id remap
Closes #1126.
Motivation
Today
Consumer.rtpParametersare fully derived fromproducer.consumableRtpParameters(the Router's canonical RTP space). In scenarios where the signaling answer dictates the wire-level RTP shape — most prominently WHEP, where the server sends the offer and the client's answer may choose different PT / header-extension-id values — there is no way to make a single Consumer emit RTP whose PT / extension ids differ from the Router's canonical ones. Rewriting the answer SDP is not an option because the offer is alreadysetLocalDescription'd.This PR adds that capability while preserving full backward compatibility and without touching ingress, routing, RTCP or pipe semantics.
Design
Public API: a new optional
rtpParametersfield onConsumerOptions(exposed identically in Node and Rust):Consumer.rtpParameters. The signaling layer validates compatibility against the Producer'sconsumableRtpParameters:profile-level-idfamily, VP9profile-id, RTXapt).aptmust resolve to a primary codec in the same list.headerExtensions[].urimust be a subset of the consumable ones.rtpParameters.encodings[].ssrcis ignored on input and overwritten.pipe=trueor on aPipeTransport(explicit error).IPC: the signaling layer ships down a new
ConsumerRtpMappingalongside the Consumer'srtpParameters:Worker — O(1) in-place egress rewrite: each
Consumerprecomputes two tiny dense lookup tables from the mapping:egressPayloadTypeMap: std::array<uint8_t, 128>(producer PT → consumer PT; 0 = identity).egressHeaderExtensionIdMap: std::array<uint8_t, 15>(producer 1B ext id → consumer 1B ext id; 0 = identity).Only populated when
rtpParametersoverride is present. On the send path:EgressRewriteUndocaptures the original PT, extension-offset tables andHeaderExtensionIds, so the packet is restored byte-for-byte after the Consumer is done, leaving fan-out to other Consumers unaffected.Consumervalidation guarantees new ext ids are in1..14, so the hot path has no range checks.This is applied in all four Consumer flavors:
SimpleConsumer,SimulcastConsumer,SvcConsumer,PipeConsumer(the pipe case is kept as an error at API level but the rewrite machinery lives on the base class for uniformity).NACK/RTX correctness: retransmission buffers must contain packets that match what went out on the wire. For remap Consumers we use a per-call local
SharedPacketthat receives the rewritten (wire-level) clone, and it is this local clone thatRtpStreamSendstores for retransmission. The Router-levelsharedPacketremains untouched, so non-remap Consumers on the same fan-out still share a single clone — there is no extra cost for the existing default path. Remap Consumers pay exactly one additionalClone()per stored packet, bounded by the retransmission window.What changes / what does not
rtpParameters)rtpParametersset)Produceringress, RTP forwarding, RTCP, pipe-to-routerConsumer.rtpParametersreturned to the appBindings
Implemented end-to-end in Node and Rust with matching validation, error messages, types and serialization. Default callers (
mediasoup-client,libmediasoupclient, pipe consumers, existing tests) are unaffected because the new field is optional.Backward compatibility
Fully backward compatible. No FBS field is required on the ingress side; the new
ConsumerRtpMappingis only emitted when the caller opts in viaConsumerOptions.rtpParameters. All existing callers — includingmediasoup-client,libmediasoupclientand pipe producers/consumers — retain their current behavior byte-for-byte.