rtspsrc: stream freezes on dropped packets when using ntp-sync
Using rtsprc
with ntp-sync=true
and ntp-time-source=running-time
causes rtpjitterbuffer
to wait for (literally) a hundred years for the next packet whenever a packet is dropped.
Steps to reproduce
This should fail on any reasonably recent version of gst on (at least) linux. I see the same behaviour on 1.14, 1.15.0, and master (as of March 6th 2019). See below for exact commits and distro.
First we need some rtsp server, preferrably on localhost. I'm using the test-launch
example of gst-rtsp-server
, but really any rtsp server should do.
gst-rtsp-server/examples/test-launch "( videotestsrc is-live=true ! video/x-raw, framerate=30/1, width=1920, height=1080 ! timeoverlay ! videoconvert ! x264enc bitrate=2000 tune=zerolatency speed-preset=ultrafast key-int-max=10 ! rtph264pay name=pay0 pt=96 )"
Next, start a pipeline with rtsprc
with ntp-sync=true
and ntp-time-source=running-time
:
gst-launch-1.0 rtspsrc location="rtsp://localhost:8554/test" latency=0 ntp-sync=true ntp-time-source=running-time ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false
A window with the test video should appear immediately.
Then make sure we drop packets for a few ms. If you're running against a server on localhost the following should work:
sudo tc qdisc add dev lo root netem loss 100%
sleep 0.01
sudo tc qdisc del dev lo root netem
Expected behaviour: Video stutters for a few ms, then continues to play normally.
Actual behaviour: Video freezes (seemingly) indefinitely.
My test setup
I'm currently running Ubuntu 18.04 on amd_64.
GStreamer built from source, using master branches:
gstreamer
: dfd9fd7f82d0c480581f7659d8c3442be9fadb84
gst-plugins-base
: d58b7cf75c19e03d4fde9f32bb57f357f8c92ab9
gst-plugins-good
: 7f95a809e9009720cedf97cbb89fb0d8293a42d4
gst-plugins-bad
: 919cd44f4722b48d83728a501ea8949d71fdd555
gst-plugins-ugly
: ab8cc537aad620f518d22acbdce185a82258c93e
rtpjitterbuffer
Faulty code in I'm not sufficiently familiar with rtpjitterbuffer
to suggest a good fix for this, but I have identified the cause of the issue.
Looking at gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c
on commit 7f95a809e9009720cedf97cbb89fb0d8293a42d4
. The freeze happens within wait_next_timeout()
due to test_timeout
getting set to 3.76e+18 (which is time since NTP epoch in nanoseconds) on line 4082.
I think the correct thing to do whenever ntp-time-source=running-time
is used is to subtract the offset from the timeout. I hacked together a fix by subtracting the offset if test_timeout
is really far into the future. This can be done by adding the following on line 4083:
const GstClockTime unix_epoch_in_ntp = (2208988800LL * GST_SECOND);
if (test_timeout - now > unix_epoch_in_ntp/2)
{
test_timeout -= jitterbuffer->priv->ts_offset;
}
This resolves the issue, but I'm not convinced it doesn't break anything else. I also suspect there is a more elegant way to detect that NTP timestamps are being used.