RTP Retransmission (Sender to Janus)

Hello,

I use GStreamer to send an RTP stream from a remote camera to a Janus server (streaming plugin). The camera is connected to a 4G network and I have noticed a ~0.2% packet loss.

Due to the packet loss, the stream on my receiver is very spotty. I can confirm everything works fine on a good (better) internet connection. Reducing the bitrate helps but only reduces the frequency of the issue. Reducing the MTU worsen the problem since the stream stability then relies on a larger number of packets. Increasing the full keyframes frequency seems to improve things as the stream recovers much faster, but that doesn’t seem like a good solution.

I know Janus sends retransmissions when receiving NACKs, but from my understanding that process only happens between Janus and receiver. However in my case, the packet loss occurs between sender and Janus, so missing packets never reach Janus (as a consequence I can see many >> >> Can't retransmit packet X, we don't have it... Janus logs).

I know FCE is not implemented for video, but does Janus provide any alternative to handle video streams from such unstable networks? It seems like the sender RTX object (rtprtxsend) is supposed to tackle this issue but it doesn’t seem like I can make it work with Janus.

Thank you.

That’s incorrect, we support NACK on both directions. This means that if Janus detects missing packets, it will send a NACK to the sender, but of course it’s up to the sender (GStreamer in your case) to honour that and retransmit the packet. That’s confirmed by the “we don’t have it” error you see: we never received the packet, and so the gap is relayed to the receiver.

Notice that retransmissions usually only work for video and not audio, since most implementations only negotiate it for video.

Thank you for clarifying. I set my streaming parameter videortcpport to 5001 (videoport is 5000) and I use the following GStreamer pipeline as my sender:

rtpbin name=rtpbin rtp-profile=avpf videotestsrc is-live=true ! video/x-raw,framerate=15/1  ! x264enc ! h264parse ! rtph264pay ! rtprtxqueue ! rtpbin.send_rtp_sink_0 rtpbin.send_rtp_src_0 ! identity drop-probability=0.01 ! udpsink host=my.janus.server.com port=5000 rtpbin.send_rtcp_src_0 ! udpsink port=5001 host=my.janus.server.com sync=false async=false udpsrc port=5002 ! rtpbin.recv_rtcp_sink_0

Now there are >> >> Scheduling X for retransmission due to NACK and Retransmitted X packets due to NACK (video stream #0), however the stream still drops and there are still many >> >> Can't retransmit packet X, we don't have it... (see pastebin).

Are there any other Janus parameters that I should look at? I must be missing something. I am a bit confused because the examples provided in the rtprtxqueue documentation use 2 ports for the RTCP link, but the Janus parameter videortcpport only allows to specify a single port?

I must be definitely missing something because my GStreamer pipeline stats outputs the following while streaming:

/GstPipeline:pipeline0/GstRtpBin:rtpbin/GstRtpSession:rtpsession0: stats = application/x-rtp-session-stats, rtx-drop-count=(uint)0, sent-nack-count=(uint)0, recv-nack-count=(uint)0, source-stats=(GValueArray)< "application/x-rtp-source-stats\,\ ssrc\=\(uint\)3222232\,\ internal\=\(boolean\)true\,\ validated\=\(boolean\)true\,\ received-bye\=\(boolean\)false\,\ is-csrc\=\(boolean\)false\,\ is-sender\=\(boolean\)true\,\ seqnum-base\=\(int\)16083\,\ clock-rate\=\(int\)90000\,\ octets-sent\=\(guint64\)5739486\,\ packets-sent\=\(guint64\)5959\,\ octets-received\=\(guint64\)5739486\,\ packets-received\=\(guint64\)5959\,\ bitrate\=\(guint64\)813250\,\ packets-lost\=\(int\)-5959\,\ jitter\=\(uint\)0\,\ sent-pli-count\=\(uint\)0\,\ recv-pli-count\=\(uint\)0\,\ sent-fir-count\=\(uint\)0\,\ recv-fir-count\=\(uint\)0\,\ sent-nack-count\=\(uint\)0\,\ recv-nack-count\=\(uint\)0\,\ have-sr\=\(boolean\)true\,\ sr-ntptime\=\(guint64\)16721554919922160287\,\ sr-rtptime\=\(uint\)3272933205\,\ sr-octet-count\=\(uint\)5739486\,\ sr-packet-count\=\(uint\)5959\;" >, rtx-count=(uint)0, recv-rtx-req-count=(uint)0, sent-rtx-req-count=(uint)0;

recv-nack-count=(uint)0 and rtx-count=(uint)0 suggest that my sender does not receive any NACK from Janus and it hasn’t retransmitted any packets. I see that Janus supports RFC4588 so the RTP profile avpf should be handled. Any help or working example would be more than welcome.

You can do a pcap capture of the unencrypted traffic via Admin API: this way,you can have a look the pcap file with Wireshark and check if you see missing packets and/or NACKs in the exchanged RTCP.

Thank you for the suggestion. I am new to this but your blog posts were very helpful, thanks for that.

I can see many RTCP messages indeed. When sending video from a remote network, there are many RTCP messages due to out of order RTP packets. These are immediately followed by H.264 packets, which I understand must be retransmissions from Janus to receiver.

To avoid any confusion and ignore out-of-order packets, I tried sending video locally with a 1% packet loss (GStreamer pipeline I shared earlier). Now I can see RTCP messages referring to the dropped packets, 10 messages for each missing packets but it never seems to be able to retransmit, which I guess confirms Janus never receives these packets.

FYI here’s what the handle info looks like in the Janus Admin API after running the local sender with a 1% packet for a while, and here is the pcap file.

That would confirm my assumption that my sender (GStreamer) never retransmits dropped packets, however I still have no clue how to troubleshoot this (e.g. does my sender receive these RTCP requests?). I believe the pcap file only capture traffic between Janus and the receiver, is there a way to capture traffic between Janus and sender?

If you captured the receiver handle, then yes, the pcap will only include that. You’ll have to start the pcap capture on the publisher handle, if you want those details, since they’re separate legs.

I only see the receiver handle in the Admin API. With the streaming plugin, isn’t a GStreamer sender a non-webrtc source that doesn’t use a publisher handle? I am not too sure how I can do a pcap capture on the publisher handle. Thank you for your help.

Oh then my bad: I was under the assumption the publisher was WebRTC based as well, e.g., via WHIP. In that case, then it’s normal GStreamer doesn’t retransmit anything. Retransmissions are only supported when the source is WebRTC based: in the Streaming plugin, there’s just a plain delivery of RTP packets, with no RTCP support at all.

Thank you for clarifying. I will use the VideoRoom plugin with a WebRTC publisher to solve my problem.

If you want to keep on using GStreamer, then you can either use the whipsink plugin (if available in your distro) or use my Simple WHIP Client, which is based on gstreamer and expects a “regular” pipeline. You’ll need the WHEP server in front of Janus in both cases anyway.