-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New APIs for cascading VideoRoom publishers #3014
Conversation
Sounds exciting - it could be the solution for a question we had some months ago - the "dynamic" streaming plugin. Since currently streaming plugin has a fixed amount of port/slots and when new publishers come we either need to recreate/destroy streaming plugin or use a huge amount of predefined slots, which potentially wastes ports. With this new API we can add/remove dynamically streams for cascading on other servers. Is our understanding correct? Although not sure about performance, since our impression is Streaming plugin is a little more lean/performant than VideoRoom? |
Yes, that's the idea, even though it's worth pointing out that at the moment the VideoRoom is not optimized for broadcasting as the Streaming plugin. There's no such thing as helper threads, for instance. As such, this effort is helpful to distribute the same publisher across mutliple servers (or multiple rooms), but not really to enhance the performance of a publisher in a specific Janus instance (the VideoRoom plugin is limited in that regard). While we may address that in the future, it's not part of this effort. |
I took care of most of the things in the "what's missing?" list from the original description, so the lack of support for data channels, RTP extensions, and proper RTCP demultiplexing of PLIs. The only thing I didn't work on was making the SSRC arithmetic configurable, as that's way less important at the moment (it's more on the "nice to have"). As such, I consider this effort complete, and plan to merge it soon: please do test and provide feedback before that happens. |
Merging. |
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [meetecho/janus-gateway](https://github.com/meetecho/janus-gateway) | minor | `v1.0.4` -> `v1.1.0` | --- ### Release Notes <details> <summary>meetecho/janus-gateway</summary> ### [`v1.1.0`](https://github.com/meetecho/janus-gateway/blob/HEAD/CHANGELOG.md#v110---2022-10-03) [Compare Source](meetecho/janus-gateway@v1.0.4...v1.1.0) - Added versioning to .so files \[[PR-3075](meetecho/janus-gateway#3075)] - Allow plugins to specify msid in SDPs \[[PR-2998](meetecho/janus-gateway#2998)] - Fixed broken RTCP timestamp on 32bit architectures \[[Issue-3045](meetecho/janus-gateway#3045)] - Fixed problems compiling against recent versions of libwebsockets \[[Issue-3039](meetecho/janus-gateway#3039)] - Updated deprecated DTLS functions to OpenSSL v3.0 [PR-3048](meetecho/janus-gateway#3048)] - Switched to SHA256 for signing self signed DTLS certificates (thanks [@​tgabi333](https://github.com/tgabi333)!) \[[PR-3069](meetecho/janus-gateway#3069)] - Started using strnlen to optimize performance of some strlen calls (thanks [@​tmatth](https://github.com/tmatth)!) \[[PR-3059](meetecho/janus-gateway#3059)] - Added checks to avoid RTX payload type collisions \[[PR-3080](meetecho/janus-gateway#3080)] - Added new APIs for cascading VideoRoom publishers \[[PR-3014](meetecho/janus-gateway#3014)] - Fixed deadlock when using legacy switch in VideoRoom \[[Issue-3066](meetecho/janus-gateway#3066)] - Fixed disabled property not being advertized to subscribers when VideoRoom publishers removed tracks - Fixed occasional deadlock when using G.711 in the AudioBridge \[[Issue-3062](meetecho/janus-gateway#3062)] - Added new way of capturing devices/tracks in janus.js \[[PR-3003](meetecho/janus-gateway#3003)] - Removed call to .stop() for remote tracks in demos \[[PR-3056](meetecho/janus-gateway#3056)] - Fixed missing message/info/transfer buttons in SIP demo page - Fixed postprocessing compilation issue on older FFmpeg versions \[[PR-3064](meetecho/janus-gateway#3064)] - Other smaller fixes and improvements (thanks to all who contributed pull requests and reported issues!) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzMi4yMTMuMCIsInVwZGF0ZWRJblZlciI6IjMyLjIxMy4wIn0=--> Reviewed-on: https://git.walbeck.it/walbeck-it/docker-janus-gateway/pulls/91 Co-authored-by: renovate-bot <bot@walbeck.it> Co-committed-by: renovate-bot <bot@walbeck.it>
Does the VideoRoom cascading support SRTP? If not, does it mean the streams are sent not encrypted during the forward transit from Janus Instance A to Janus Instance B? In streaming plugin SRTP seems to be supported. Thanks for any suggestions! |
No, at the moment it doesn't. I don't even know if anyone is actually using the feature as it is. There are other things that may be added too, so maybe in the future, but not in the short term I think. |
@lminiero I tested this pr and most of the things work great, but i am curious, what is required for simulcasting in terms of cascading api payload anything specific is needed to pass in |
As the title says, this PR adds new APIs to cascade VideoRoom publishers: this means it allows you to publish in room A on Janus X, and be available as a publisher in room B on Janus Y as well. We've actually been doing something like this for a long time, even though we always used VideoRoom RTP forwarders and Streaming mountpoints for scalability purposes (see this presentation for more information): this PR has the key difference that we're not using different plugins, but only the VideoRoom, meaning that remote publishers created this way can be addressed exactly as local publishers (so as a participant you can be notified about them, subscribe to them, etc.). This also means these remote publishers can be served within the context of multistream subscriptions, which is something that with our previous approach cannot be done.
If you're wondering how this is supposed to work. it's pretty much like this:
add_remote_publisher
to create a fake publisher there, which will return connectivity information;publish_remotely
to instruct the plugin to automatically relay all streams to the remote instance;This is it in a nutshell, even though it's a bit more complex than that of course. First of all, you may have noticed that the process is only partly automated, since you still have to invoke APIs yourself to actually tie the source to the remote sink: this was done on purpose, since while I had considered somehow making the VideoRoom able to do this automatically on its own, for this initial integration it made much more sense to rely on simple APIs an orchestrator could take care of instead. This will allow us to experiment with this form or cascading, and in case it makes sense (which may not necessarily be true, since a custom orchestrator can sometimes be better in terms of flexibility) come up with a more integrated approach.
The APIs we've added are the following:
add_remote_publisher
: as the title says, this is what you can on the target server to setup a fake publisher on behalf of the real one; this API expects the list of streams that should be offered, and their characteristics (e.g., first m-line is an Opus stereo stream, second m-line is a VP8 simulcast stream, etc.), and returns the single port it will bind to for receiving RTP traffic from the source; other users in the room will be notified as if a new PeerConnection is available;update_remote_publisher
: this API can be used to update a remote publisher, e.g,, whenever the actual publisher is updated (e.g., audio was removed, a new video stream was added, etc.); this API only expects the list of changes, and doesn't return anything new, since the connectivity info will remain the same; other users in the room will be notified as if a local publisher just renegotiated their PeerConnection;remove_remote_publisher
: quite simply, this gets rid of a remote publisher, and is the equivalent of a local publisher hanging up their PeerConnection; as a result, other users in the room will be notified accordingly;publish_remotely
: this is used at the source to feed a remote publisher somewhere else, and mostly only needs the IP/port to send RTP packets to; it will automatically create forwarders for all active streams, and automatically do the same when a PeerConnection is renegotiated;listremotes
: you can use this API to list all the remotizations in use for a specific publisher (since you can actually make the same publisher available in multiple rooms/servers at the same time);unpublish_remotely
: this automatically destroys all forwarders tied to this remotization.As you can evince from these descriptions,
add_remote_publisher
,update_remote_publisher
andremove_remote_publisher
are APIs you use on the targets, whilepublish_remotely
,unpublish_remotely
andlistremotes
on the source. You always configure a target first, and the source next, since the source needs to know where to send packets to. All these APIs are synchronous, which means they're easy to send via Admin API as well (which makes them easy to script in an orchestrator component).Examples
Here's a simple example of how we can create a remote publisher on a target:
We'll receive a unique publisher ID (e.g.,
6261586119881072
, which again will be valid in the specified room), a single port for RTP (as unlike the Streaming plugin, all packets will be multiplexed), and one for RTCP. Assuming the port for RTP is10000
and RTCP is10001
, and that the original publisher we want to remotize has ID98765432
in room1234
, this is how we can configure the source accordingly:If the source publisher is renegotiated to add a new video stream (e.g., a screensharing) beyond the existing audio and video ones we added already, the source will automatically start sending the packets for the new stream to the port, but the target will ignore them until we notify it accordingly, as this would be needed to also notify subscribers of the new stream being published. As such we can do something like this on the target:
Notice that
update_remote_publisher
isn't only used for new streams, but also to notify about streams being gone. In that case, we'd need to send an object that references an existing mindex/mid, and adisabled: true
property.When we want to get rid of the remote publisher, we have to notify both source and target. On the source we'll have to send
unpublish_remotely
to stop sending packets to the target, using theremote_id
we received when we publisher remotely:We'll also have to send a
remove_remote_publisher
on the target, though, otherwise the PeerConnection there will remain alive but without any packets coming in anymore:As you can see, it's no rocket science, so it should be easy enough to manage. It's easy to also experiment with in the context of a single room on a single server, e.g., to duplicate a publisher in the same room they're in: silly and useless, but helpful to figure out how the APIs work.
What's missing?
This is still experimental (hence the draft PR) but functionally more or less complete. There are a few things that should be improved before it's ready, though.
First of all, data channels don't currently work: while they may be remotized, they will not be sent by a remote publisher. This is because of how we currently structured the usage of the single RTP port for multiplexing: more specifically, we perform some arithmetic on SSRCs to know which m-index an incoming RTP packet is, so that we can relay it accordingly. That said, SSRCs only exist for RTP, which data channels don't use. A simple approach may be "relay anything you don't recognize as RTP via data channels" but that may be frail and prone to abuse. Another possible approach may be prefixing a data channel payload with an RTP header wihere we can put the SSRC we need, but that may risk getting the packet to exceed the MTU due to the RTP header overhead. As such I haven't decided how to handle that yet.This should now be fixed, using the fake RTP prefix that I described as a potential approach.RTP extensions don't currently work with remote publishers either. Due to the way the core now works, extensions are automatically stripped from RTP packets to relay, meaning it's up to plugins to fill in a struct with the extension properties to set in outgoing packets. Considering the intermediate plain RTP layer added for this remote publisher mechanism, we'd have to do something like the core does to turn extensions we can parse in packets to the struct we need to fill, which while doable is a bit of a pain. As such, for this initial version I chose to skip extensions altogether.This should now be fixed, as long as you specify the right extension IDs when adding a remote publisher.The remotization of RTCP for video (e.g., keyframe requests) will probably not work when there are multiple video streams being published and remotized. This is because we again use a single port for RTCP, and due to how forwarders (which we're reusing internally) work with latching, we may forward an incoming PLI from a target to the wrong m-line. I plan to fix this by using the same SSRC arithmetic I mentioned above.This should now be fixed, as we now check the SSRC in the PLI and find the right publisher stream it's meant for.1000
) and a fixed step (10
). This means that m-line 0 will always have SSRC1000
, m-line 11010
, etc. Simulcast substreams are additions to the m-line SSRC, so if m-line 1 is simulcast, then there will be three different SSRCs (1010
,1011
,1012
) for the different substreams. Since the source and target agree on this convention, it's easy to demultiplex an incoming packet and know how to relay it. That said, it may make sense to make both the starting point and the step dynamic instead: in that case, we'll need to update the APIs so that both source and target know which values are used, so that demultiplexing can take place.There's probably more that's slipping my mind, now, but I think these are the major things that need to be addressed before we can merge this. That said, please don't wait until that happens before testing this: as anticipated, it already does what it's supposed to do, and the APIs are mostly consolidated, so there's a good chance that when we'll merge very little will change there.
Feedback welcome!