mpegtsdemux: Handle PTS/DTS wraparound with ignore-pcr=true
authorSebastian Dröge <sebastian@centricular.com>
Mon, 30 Sep 2024 12:51:04 +0000 (15:51 +0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 29 Oct 2024 17:04:09 +0000 (17:04 +0000)
The wraparound handling code assumes that the PCR gets updated regularly for
being able to detect wraparounds. With ignore-pcr=true that was not the case and
it stayed initialized at 1h forever.

To avoid this problem, update the fake PCR whenever the PTS advanced by more
than 5s, and also detect wraparounds in these fake PCRs.

Problem can be reproduced with

  $ gst-launch-1.0 videotestsrc pattern=black ! video/x-raw,framerate=1/5 ! \
    x264enc speed-preset=ultrafast tune=zerolatency ! mpegtsmux ! \
    tsdemux ignore-pcr=true ! fakesink

which restarts timestamps at 0 after around 26h30m.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7763>

subprojects/gst-plugins-bad/gst/mpegtsdemux/mpegtspacketizer.c

index fdc7851dcd1e1d6d8faabaca36beec8639a05b47..c50dec31e080c7083419afbc8b7e117f0cc80ea5 100644 (file)
@@ -2261,10 +2261,24 @@ mpegts_packetizer_pts_to_ts_internal (MpegTSPacketizer2 * packetizer,
   PACKETIZER_GROUP_LOCK (packetizer);
   pcrtable = get_pcr_table (packetizer, pcr_pid);
 
-  if (!GST_CLOCK_TIME_IS_VALID (pcrtable->base_time) && pcr_pid == 0x1fff &&
-      GST_CLOCK_TIME_IS_VALID (packetizer->last_in_time)) {
-    pcrtable->base_time = packetizer->last_in_time;
-    pcrtable->base_pcrtime = pts;
+  if (pcr_pid == 0x1fff && GST_CLOCK_TIME_IS_VALID (packetizer->last_in_time)) {
+    if (!GST_CLOCK_TIME_IS_VALID (pcrtable->base_time)) {
+      pcrtable->base_time = packetizer->last_in_time;
+      pcrtable->base_pcrtime = pts;
+    } else if (check_diff) {
+      /* Handle discont and wraparound */
+      guint64 tmp_pts = pts + pcrtable->pcroffset + packetizer->extra_shift;
+      if (pcrtable->base_pcrtime < tmp_pts
+          && tmp_pts - pcrtable->base_pcrtime >= 5 * GST_SECOND) {
+        guint64 diff = tmp_pts - pcrtable->base_pcrtime - 2 * GST_SECOND;
+
+        pcrtable->base_time += diff;
+        pcrtable->base_pcrtime += diff;
+      } else if (pcrtable->base_pcrtime > tmp_pts
+          && pcrtable->base_pcrtime > PCR_GST_MAX_VALUE / 2) {
+        pcrtable->pcroffset += PCR_GST_MAX_VALUE;
+      }
+    }
   }
 
   /* Use clock skew if present */