Add SSRC option to force a fixed egress SSRC - #2166
Conversation
Add a new `SSRC` dictionary to the NG control protocol with the keys `egress-to-offerer` and `egress-to-answerer`. Each takes an SSRC value which rtpengine then writes into the SSRC field of every RTP packet it sends towards that side of the call. The forced SSRC is stored against the call leg it applies to as soon as the offer or answer carrying it is processed, so it can be set in either message and stays in effect for the rest of the call. The rewrite itself happens in media_packet_encrypt(), the single point all outgoing RTP passes through just before SRTP encryption. It therefore covers media that is relayed unchanged, transcoded media and media generated by rtpengine itself, for plain RTP as well as SRTP, and the SRTP authentication tag is computed over the rewritten header. RTCP is left untouched. Because the rewrite is only implemented in userspace, streams towards a party with a forced SSRC are excluded from kernel packet forwarding. This allows a controlling application to make a downstream receiver, such as a WebRTC selective forwarding unit, expect a known SSRC instead of having to discover it from the media or from RTCP.
23e4043 to
ecd4088
Compare
rfuchs
left a comment
There was a problem hiding this comment.
The obvious question of course is: what if two ingress SSRCs appear? They would be rewritten to the same egress SSRC, which may have undesirable side effects for sequence and timestamp tracking, or SRTP contexts.
Also the a=ssrc attribute comes to mind - if we have a fixed egress SSRC, that would lend itself to their usage, no?
| // forced egress SSRC is only implemented in userspace | ||
| if (sh->sink->media && sh->sink->media->monologue | ||
| && sh->sink->media->monologue->force_egress_ssrc) | ||
| goto no_kernel; |
There was a problem hiding this comment.
We do have support for SSRC substitution in the kernel module (see ssrc_out and ssrc_subst)
There was a problem hiding this comment.
Agreed. ssrc_subst and ssrc_out[] are already wired up from media_socket.c:1863,1882
off ssrc_map_out, with the substitution itself at nft_rtpengine.c:6745, so once this
moves onto ssrc_map_out the bailout serves no purpose and forced-SSRC streams can stay in
kernel forwarding. Removing it.
| if (!mp->rtcp && out->media && out->media->monologue && out->media->monologue->force_egress_ssrc) { | ||
| uint32_t ssrc = htonl(out->media->monologue->force_egress_ssrc); | ||
| IQUEUE_FOREACH(&mp->packets_out, p) { | ||
| str payload; | ||
| struct rtp_header *rh = rtp_payload(&payload, &p->s, NULL); | ||
| if (rh) | ||
| rh->ssrc = ssrc; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
This whole thing should really be handled by the existing SSRC substitution mechanism (see handler_func_passthrough_ssrc in codec.c) instead of adding another loop over the already-processed output packets (and even parsing the RTP header again)
There was a problem hiding this comment.
Agreed. I will rework this onto __stream_ssrc_out() and ssrc_map_out rather than adding a
second pass over the already-processed output packets, which also does away with parsing the
RTP header again.
| case CSH_LOOKUP("egress-to-offerer"): | ||
| case CSH_LOOKUP("egress to offerer"): | ||
| out->ssrc_force.egress_to_offerer = call_ng_parse_ssrc(parser, value); | ||
| break; | ||
| case CSH_LOOKUP("egress-to-answerer"): | ||
| case CSH_LOOKUP("egress to answerer"): | ||
| out->ssrc_force.egress_to_answerer = call_ng_parse_ssrc(parser, value); |
There was a problem hiding this comment.
This mechanism would also be relevant to methods which don't involve two separate parties for offerer and answerer (e.g. publish/subscribe), so a syntax that is less specific to offer/answer would be beneficial.
There was a problem hiding this comment.
Fair point. source-tag and from-tags look like the established pattern, so keying the
value by tag rather than by offerer or answerer role would cover publish/subscribe alongside
offer/answer.
This has to move from the monologue to the media in any case to handle bundle, so the
addressing is going to change regardless of what the keys end up being called. Unless you
would prefer a different shape, I will use a tag-keyed dictionary and drop
egress-to-offerer and egress-to-answerer.
|
Two ingress SSRCs That is the case the current patch does not handle, and it needs the option to be narrower The deployment this came from involves a sequential succession of SSRCs rather than Collapsing that succession onto one stable egress SSRC only works if For genuinely concurrent ingress SSRCs I do not think there is a defensible answer, so I That also means the value wants to be scoped per media rather than per monologue as it is at
It does follow from a fixed egress SSRC, but it is a larger change than it first appears. |
|
Holding the rework here until you have had a chance to look at the two open points above, |
This adds a new
SSRCdictionary to the NG control protocol with two keys,egress-to-offererandegress-to-answerer. Each takes an SSRC value (aninteger, or a decimal or
0x-prefixed hex string). rtpengine then rewritesthe SSRC field of every RTP packet it sends towards that side of the call
to the given value.
Motivation: when rtpengine feeds media into a downstream system such as a
WebRTC selective forwarding unit (SFU), that system needs to know the SSRC
of the incoming stream up front in order to bind a receiver to it. Today
the controlling application can only learn rtpengine's outgoing SSRC after
the fact, from RTCP or from a
query. With this option the control planepicks the SSRC itself and tells both sides.
Implementation notes:
the offer or answer carrying it is processed. It can be set in either
message and stays in effect for the rest of the call.
just before SRTP encryption (
media_packet_encrypt()). It thereforecovers media that is relayed unchanged, media that is transcoded, and
media generated by rtpengine itself, for plain RTP as well as SRTP, and
the SRTP authentication tag is computed over the rewritten header. RTCP
is left untouched.
with a forced SSRC are excluded from kernel packet forwarding. Streams
in the other direction are unaffected.
value as well, I am happy to look into feeding it through the existing
per-stream output SSRC mapping that RTCP generation already uses, which
would also allow the kernel module to apply it.
Tests:
t/auto-daemon-tests-ssrc.plcovers integer, hex string and decimalstring values, setting the value in the offer and in the answer, the flat
string flag syntax used by the SIP proxy modules, and that a value of
0is ignored.
Example in string syntax:
SSRC=[egress-to-answerer=0x12345678]