mpegdemux: catch smaller PTS dicontinuities
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Tue, 30 Aug 2011 13:50:52 +0000 (14:50 +0100)
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Mon, 28 Nov 2011 13:57:29 +0000 (13:57 +0000)
In a test stream, I get one buffer with a PTS of about 15 seconds
in the future compared to the previous one, and next buffers with
timestamps continuing where the original ones left off.

This caused the sink to wait 15 seconds to display the frame while
more frames queued up, and then dump all the subsequent frames as
they "arrived too late".

Maybe that threshold should be made configurable, but for now,
make it more smaller to catch more of these.

https://bugzilla.gnome.org/show_bug.cgi?id=655804

Non AV streams keep using the larger threshold (10 minutes), as
subtitles may arrive only every so often.

gst/mpegdemux/gstmpegtsdemux.c
gst/mpegdemux/gstmpegtsdemux.h

index b3b8249..f720c07 100644 (file)
@@ -88,6 +88,10 @@ enum
 /* latency in mseconds */
 #define TS_LATENCY 700
 
+/* threshold at which we deem PTS difference to be a discontinuity */
+#define DISCONT_THRESHOLD_AV (GST_SECOND * 2)   /* 2 seconds */
+#define DISCONT_THRESHOLD_OTHER (GST_SECOND * 60 * 10)  /* 10 minutes */
+
 enum
 {
   PROP_0,
@@ -539,6 +543,20 @@ gst_mpegts_stream_is_video (GstMpegTSStream * stream)
   return FALSE;
 }
 
+static FORCE_INLINE gboolean
+gst_mpegts_stream_is_audio (GstMpegTSStream * stream)
+{
+  switch (stream->stream_type) {
+    case ST_AUDIO_MPEG1:
+    case ST_AUDIO_MPEG2:
+    case ST_AUDIO_AAC_ADTS:
+    case ST_AUDIO_AAC_LOAS:
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
 static gboolean
 gst_mpegts_demux_is_reserved_PID (GstMpegTSDemux * demux, guint16 PID)
 {
@@ -1040,16 +1058,21 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
   demux = stream->demux;
   srcpad = stream->pad;
 
-  GST_DEBUG_OBJECT (demux, "got data on PID 0x%04x", stream->PID);
+  GST_DEBUG_OBJECT (demux, "got data on PID 0x%04x (flags %x)", stream->PID,
+      stream->flags);
 
   if (first && filter->pts != -1) {
+    gint64 discont_threshold =
+        ((stream->flags & (MPEGTS_STREAM_FLAG_IS_AUDIO |
+                MPEGTS_STREAM_FLAG_IS_VIDEO))) ? DISCONT_THRESHOLD_AV :
+        DISCONT_THRESHOLD_OTHER;
     pts = filter->pts;
     time = MPEGTIME_TO_GSTTIME (pts) + stream->base_time;
 
     if ((stream->last_time > 0 && stream->last_time < time &&
-            time - stream->last_time > GST_SECOND * 60 * 10)
+            time - stream->last_time > discont_threshold)
         || (stream->last_time > time
-            && stream->last_time - time > GST_SECOND * 60 * 10)) {
+            && stream->last_time - time > discont_threshold)) {
       /* check first to see if we're in middle of detecting a discont in PCR.
        * if we are we're not sure what timestamp the buffer should have, best
        * to drop. */
@@ -1065,8 +1088,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
           stream->last_time - time > MPEGTIME_TO_GSTTIME (G_MAXUINT32)) {
         /* wrap around occurred */
         if (stream->base_time + MPEGTIME_TO_GSTTIME ((guint64) (1) << 33) +
-            MPEGTIME_TO_GSTTIME (pts) >
-            stream->last_time + GST_SECOND * 60 * 10) {
+            MPEGTIME_TO_GSTTIME (pts) > stream->last_time + discont_threshold) {
           GST_DEBUG_OBJECT (demux,
               "looks like we have a corrupt packet because its pts is a lot lower than"
               " the previous pts but not a wraparound");
@@ -1100,7 +1122,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
           stream->base_time > 0) {
         /* had a previous wrap around */
         if (time - MPEGTIME_TO_GSTTIME ((guint64) (1) << 33) +
-            GST_SECOND * 60 * 10 < stream->last_time) {
+            discont_threshold < stream->last_time) {
           GST_DEBUG_OBJECT (demux,
               "looks like we have a corrupt packet because its pts is a lot higher than"
               " the previous pts but not because of a wraparound or pcr discont");
@@ -1202,7 +1224,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
     gst_mpegts_demux_send_tags_for_stream (demux, stream);
   }
 
-  GST_DEBUG_OBJECT (srcpad, "pushing buffer");
+  GST_DEBUG_OBJECT (srcpad, "pushing buffer ts %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
   gst_buffer_set_caps (buffer, GST_PAD_CAPS (srcpad));
   if (stream->discont) {
     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
@@ -1479,6 +1502,9 @@ gst_mpegts_stream_parse_pmt (GstMpegTSStream * stream,
         /* Recognise video streams based on stream_type */
         if (gst_mpegts_stream_is_video (ES_stream))
           ES_stream->flags |= MPEGTS_STREAM_FLAG_IS_VIDEO;
+        /* likewise for audio */
+        if (gst_mpegts_stream_is_audio (ES_stream))
+          ES_stream->flags |= MPEGTS_STREAM_FLAG_IS_AUDIO;
 
         /* set adaptor */
         GST_LOG ("Initializing PES filter for PID %u", ES_stream->PID);
index c3732ba..2dddfe0 100644 (file)
@@ -121,7 +121,8 @@ struct _GstMpegTSPAT  {
 typedef enum _MpegTsStreamFlags {
   MPEGTS_STREAM_FLAG_STREAM_TYPE_UNKNOWN = 0x01,
   MPEGTS_STREAM_FLAG_PMT_VALID = 0x02,
-  MPEGTS_STREAM_FLAG_IS_VIDEO  = 0x04
+  MPEGTS_STREAM_FLAG_IS_VIDEO  = 0x04,
+  MPEGTS_STREAM_FLAG_IS_AUDIO  = 0x08
 } MpegTsStreamFlags;
 
 /* Information associated to a single MPEG stream. */