NACK and REMB with SIP plugin

I have been using Janus as a WebRTC server with the SIP plugin. My setup is pretty simple, client establishes a WebRTC session with Janus which in turn establishes a SIP session with my SIP server. It’s Audio only. I was checking the SDP and nackCount and realized there’s no nack negotiation or actual NACK packets initiated from Janus. How do I configure Janus to both negotiate and use NACK.

I tried customizing the SDP on the client side to insert NACK negotiation rtcp-fb lines for every audio codec negotiated, but nothing changed. Janus just relays the SIP ANSWER received from the SIP server which of course doesn’t contain any NACK related stuff and it nevers requests any retransmissions.

My Janus version is 1.1.1.

1 Like

Browsers don’t negotiate NACK support for audio, but even if they did, Janus would terminate and handle it at the WebRTC level, and not forward NACK requests on the SIP side. REMB should already be relayed instead. Closing as not a code issue: discussion can continue on the group, if needed.

@lorenzo Following up on your reply in the github issue, this is not the behavior I’m experiencing at all. It seems JANUS is completely ignoring both NACK and REMB (neither relaying them nor handling them on the WebRTC level). I did a basic test using tc to simulate aggressive packet loss and initiated a session both on Google Meet and with Janus. The Googe Meet session as apparent from chrome://webrtc-internals, utilizes NACK (both nack count and number of retransmitted packets increases) while Janus doesn’t.

To take a step back, the main motive here is making sure to deliver the best experience under poor network conditions. Currently I’m using OPUS codec with FEC which seems to improve things but there’s still huge chunks of audio completely gone when experiencing burst packet loss. If you think there’s a better approach possible with Janus or something I’m missing your recommendations are very welcome.

As I said on Github, it’s the browsers that by default don’t negotiate NACKs for audio, which is why by default we don’t either, and that’s true for all plugins, not just the SIP one. I guess Meet forces NACKs for audio too by munging the SDP or something like that. I can’t remember if we have fences in the code that still prevent audio NACKs, though. For bursts of packet loss, browsers usually perform packet-loss concealment (PLC) whn FEC on its own is not enough.

Hmmm, my experiment included modifying the SDP to negotiate NACK on the INVITE to Janus. Is that normally not enough to overcome the issue of browsers not negotiating by default?

That depends what you mean by modifying the SDP; you need to edit SDPs before a call to setLocalDescription to make sure browsers do NACK themselves, otherwise it’s useless. Then it’s a matter of seeing what Janus returns back. You should check what appears in the SDP offer and answer that Janus sees on the WebRTC side, and what the Admin API says with respect to NACK support in the audio medium.

Here’s the local SDP from the setLocalDescription:

{"type":"offer","sdp":"v=0\r\no=- 2306892980781731547 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS c055b2d1-a9ed-4501-9488-67743d12b395\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:Yt61\r\na=ice-pwd:wy3/H7UTXXGeMuxPeyhDdcRx\r\na=ice-options:trickle\r\na=fingerprint:sha-256 88:1B:37:A1:4D:9A:E0:F8:82:B2:00:CA:9E:B7:59:AE:9A:2F:F8:39:44:65:52:02:90:9A:08:C0:21:DE:B0:A5\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:c055b2d1-a9ed-4501-9488-67743d12b395 24d990b1-dec9-4a91-b6fc-249bd015335f\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 nack 500\na=rtcp-fb:111 nack pli\na=rtcp-fb:111 goog-remb\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=rtcp-fb:63 nack 500\na=rtcp-fb:63 nack pli\na=rtcp-fb:63 goog-remb\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtcp-fb:9 nack 500\na=rtcp-fb:9 nack pli\na=rtcp-fb:9 goog-remb\na=rtpmap:0 PCMU/8000\r\na=rtcp-fb:0 nack 500\na=rtcp-fb:0 nack pli\na=rtcp-fb:0 goog-remb\na=rtpmap:8 PCMA/8000\r\na=rtcp-fb:8 nack 500\na=rtcp-fb:8 nack pli\na=rtcp-fb:8 goog-remb\na=rtpmap:13 CN/8000\r\na=rtcp-fb:13 nack 500\na=rtcp-fb:13 nack pli\na=rtcp-fb:13 goog-remb\na=rtpmap:110 telephone-event/48000\r\na=rtcp-fb:110 nack 500\na=rtcp-fb:110 nack pli\na=rtcp-fb:110 goog-remb\na=rtpmap:126 telephone-event/8000\r\na=rtcp-fb:126 nack 500\na=rtcp-fb:126 nack pli\na=rtcp-fb:126 goog-remb\na=ssrc:180501267 cname:HoXrGlh5LsBihUGC\r\na=ssrc:180501267 msid:c055b2d1-a9ed-4501-9488-67743d12b395 24d990b1-dec9-4a91-b6fc-249bd015335f\r\n"}

Here’s the Janus response:

{"type":"answer","sdp":"v=0\r\no=root 266025454 266025454 IN IP4 3.78.135.122\r\ns=Asterisk PBX 18.17.1\r\nt=0 0\r\na=group:BUNDLE 0\r\na=ice-options:trickle\r\na=fingerprint:sha-256 82:4A:69:9A:D6:46:02:20:C3:3E:88:67:8F:C1:25:54:11:E1:1E:A9:1C:CE:05:FA:59:ED:30:02:B2:77:95:8C\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS *\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 0 8 126\r\nc=IN IP4 3.78.135.122\r\na=sendrecv\r\na=mid:0\r\na=rtcp-mux\r\na=ice-ufrag:5evU\r\na=ice-pwd:HErkfm+PSdrCRkTQXaYX5Y\r\na=ice-options:trickle\r\na=setup:active\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 maxaveragebitrate=8000;cbr=1;useinbandfec=1\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:126 telephone-event/8000\r\na=fmtp:126 0-16\r\na=ptime:20\r\na=maxptime:20\r\na=msid:janus janus0\r\na=ssrc:3319877820 cname:janus\r\na=candidate:1 1 udp 2013266431 3.78.135.122 55469 typ host\r\na=end-of-candidates\r\n"}

You’re using \n instead of \r\n, and that’s broken SDP already. That said, you’re not negotiating NACK at all, that way: I don’t see rtx payload types being offered. No idea why you’re adding REMB and transport-cc too, which have nothing to do with NACK, or pli, which only makes sense for video.

@lorenzo Thanks for your feedback. I re-read the RFC and made some changes. Now I’m facing the following error (on Chrome): “Failed to execute ‘setLocalDescription’ on ‘RTCPeerConnection’: Failed to parse SessionDescription. Encountered webrtc error in janus”. Here’s the local SDP:

'v=0\r\no=- 3126595281767327042 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS 8f1bb9ba-67b4-4e0c-a5ee-a89e57546a29\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:YXhf\r\na=ice-pwd:+QxX2jOa/SVH/H2Hz77yEz5k\r\na=ice-options:trickle\r\na=fingerprint:sha-256 2A:10:BC:20:B5:BE:EF:18:C4:C1:C3:54:55:68:83:25:D7:65:9E:15:63:AD:70:C8:8F:EF:33:F3:8E:9D:B6:F7\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:8f1bb9ba-67b4-4e0c-a5ee-a89e57546a29 888695ba-68fd-47c2-94bd-4868ac4d0baa\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtpmap:923 rtx/48000\r\na=fmtp:923 apt=111\r\na=rtcp-fb:111 nack 500\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=rtpmap:929 rtx/48000\r\na=fmtp:929 apt=63\r\na=rtcp-fb:63 nack 500\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:934 rtx/8000\r\na=fmtp:934 apt=9\r\na=rtcp-fb:9 nack 500\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:938 rtx/8000\r\na=fmtp:938 apt=0\r\na=rtcp-fb:0 nack 500\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:942 rtx/8000\r\na=fmtp:942 apt=8\r\na=rtcp-fb:8 nack 500\r\na=rtpmap:13 CN/8000\r\na=rtpmap:946 rtx/8000\r\na=fmtp:946 apt=13\r\na=rtcp-fb:13 nack 500\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:950 rtx/48000\r\na=fmtp:950 apt=110\r\na=rtcp-fb:110 nack 500\r\na=rtpmap:126 telephone-event/8000\r\na=rtpmap:954 rtx/8000\r\na=fmtp:954 apt=126\r\na=rtcp-fb:126 nack 500\r\na=ssrc:95988370 cname:q/Ly4vWC+w0YjH19\r\na=ssrc:95988370 msid:8f1bb9ba-67b4-4e0c-a5ee-a89e57546a29 888695ba-68fd-47c2-94bd-4868ac4d0baa\r\n'

I can see that there’s someone else that’s facing the same issue but I couldn’t progress any further. Do you mind taking a quick look on the SDP and letting me know if I missed something.

After some reading, it looks like neither chrome nor firefox support audio RTX. According to this.

Yeah, which is what I told you about browsers not negotiating it. There may be ways to force it, though (maybe by munging both local and remote SDP, before setLocalDescription and setRemoteDescription?), especially if you say Meet does use them.

That turned out to be an overlook by me, they use it in their video stream only. Sorry about that. Thank you for your help.

Looking at the bigger picture my initial goal is to mitigate burst packet loss as much as possible. Are you aware of any other measures I could try, other than NACK. (I’m using OPUS with FEC, CBR and a JitterBuffer)

But is this a custom client? That is, not a browser? Then you could look into how to use Packet Loss Concealment (PLC) in Opus to smoothen out losses. Check the libopus documentation for more info.

It’s not a custom client sadly (for now at least). Janus is relaying media directly to an Asterisk server which is supposed to have its own PLC implementation. I’ll look more into optimizing that. Thanks a lot for your support