mpegtsdemux: Add initial naive seeking support and fix duration query.
authorZaheer Merali <zaheerabbas@merali.org>
Fri, 8 May 2009 17:24:28 +0000 (18:24 +0100)
committerZaheer Merali <zaheerabbas@merali.org>
Fri, 8 May 2009 17:26:43 +0000 (18:26 +0100)
Sync from gst-fluendo-mpegdemux and have seeking/duration query
improvements in. No support however for wrapped around pcrs etc. but a
start nonetheless.
Also fix indentation issues.

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

index 4464cdf..99a499a 100644 (file)
@@ -34,9 +34,9 @@
   *
   * The Original Code is Fluendo MPEG Demuxer plugin.
   *
-  * The Initial Developer of the Original Code is Fluendo, S.L.
-  * Portions created by Fluendo, S.L. are Copyright (C) 2005
-  * Fluendo, S.L. All Rights Reserved.
+  * The Initial Developer of the Original Code is Fluendo, S.A.
+  * Portions created by Fluendo, S.L. are Copyright (C) 2005,2006,2007,2008,2009
+  * Fluendo, S.A. All Rights Reserved.
   *
   * Contributor(s): Wim Taymans <wim@fluendo.com>
   */
@@ -126,6 +126,13 @@ enum
   PROP_M2TS
 };
 
+#define GSTTIME_TO_BYTES(time) \
+  ((time != -1) ? gst_util_uint64_scale (MAX(0,(gint64) ((time))), \
+  demux->bitrate, GST_SECOND) : -1)
+#define BYTES_TO_GSTTIME(bytes) \
+  ((bytes != -1) ? (gst_util_uint64_scale (bytes, GST_SECOND, \
+  demux->bitrate)) : -1)
+
 #define VIDEO_CAPS \
   GST_STATIC_CAPS (\
     "video/mpeg, " \
@@ -189,6 +196,7 @@ static void gst_mpegts_demux_get_property (GObject * object, guint prop_id,
 static gboolean gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID);
 
 static gboolean gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event);
 static GstFlowReturn gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer);
 static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps);
 
@@ -327,7 +335,12 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux)
   demux->m2ts_mode = FALSE;
   demux->sync_lut = NULL;
   demux->sync_lut_len = 0;
-
+  demux->bitrate = -1;
+  demux->num_packets = 0;
+  demux->pcr[0] = -1;
+  demux->pcr[1] = -1;
+  demux->cache_duration = GST_CLOCK_TIME_NONE;
+  demux->base_pts = GST_CLOCK_TIME_NONE;
 #ifdef USE_LIBOIL
   oil_init ();
 #endif
@@ -668,6 +681,8 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
   gst_caps_unref (caps);
   gst_pad_set_query_function (stream->pad,
       GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query));
+  gst_pad_set_event_function (stream->pad,
+      GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event));
   g_free (name);
 
   return TRUE;
@@ -730,7 +745,7 @@ gst_mpegts_demux_send_new_segment (GstMpegTSDemux * demux,
   }
   base_PCR = PCR_stream->base_PCR;
 
-  time = MPEGTIME_TO_GSTTIME (base_PCR);
+  demux->base_pts = time = MPEGTIME_TO_GSTTIME (base_PCR);
 
   GST_DEBUG_OBJECT (demux, "segment PTS to (%" G_GUINT64_FORMAT ") time: %"
       G_GUINT64_FORMAT, base_PCR, time);
@@ -1680,7 +1695,13 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream,
           "valid pcr: %d last PCR difference: %" G_GUINT64_FORMAT, valid_pcr,
           stream->last_PCR_difference);
       if (valid_pcr) {
-
+        if (demux->pcr[0] == -1) {
+          demux->pcr[0] = pcr;
+          demux->num_packets = 0;
+        } /* Considering a difference of 1 sec ie 90000 ticks */
+        else if (demux->pcr[1] == -1 && ((pcr - demux->pcr[0]) >= 90000)) {
+          demux->pcr[1] = pcr;
+        }
         stream->last_PCR = pcr;
 
         if (demux->clock && demux->clock_base != GST_CLOCK_TIME_NONE) {
@@ -1955,15 +1976,20 @@ gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID)
 }
 
 static FORCE_INLINE GstFlowReturn
-gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream)
+gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream, gboolean discard)
 {
   GstFlowReturn ret = GST_FLOW_OK;
 
   if (stream->pes_buffer) {
-    GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used;
-    ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer);
-    if (ret == GST_FLOW_LOST_SYNC)
+    if (discard) {
+      gst_buffer_unref (stream->pes_buffer);
       stream->pes_buffer_in_sync = FALSE;
+    } else {
+      GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used;
+      ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer);
+      if (ret == GST_FLOW_LOST_SYNC)
+        stream->pes_buffer_in_sync = FALSE;
+    }
     stream->pes_buffer = NULL;
   }
   return ret;
@@ -1985,7 +2011,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream,
     if (stream->pes_buffer_size < (MPEGTS_MAX_PES_BUFFER_SIZE >> 1))
       stream->pes_buffer_size <<= 1;
 
-    ret = gst_mpegts_stream_pes_buffer_flush (stream);
+    ret = gst_mpegts_stream_pes_buffer_flush (stream, FALSE);
     if (ret == GST_FLOW_LOST_SYNC)
       goto done;
   }
@@ -2010,7 +2036,7 @@ done:
 }
 
 static FORCE_INLINE GstFlowReturn
-gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux)
+gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux, gboolean discard)
 {
   gint i;
   GstFlowReturn ret = GST_FLOW_OK;
@@ -2018,7 +2044,7 @@ gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux)
   for (i = 0; i < MPEGTS_MAX_PID + 1; i++) {
     GstMpegTSStream *stream = demux->streams[i];
     if (stream && stream->pad) {
-      gst_mpegts_stream_pes_buffer_flush (stream);
+      gst_mpegts_stream_pes_buffer_flush (stream, discard);
       stream->pes_buffer_in_sync = FALSE;
     }
   }
@@ -2233,7 +2259,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream,
               "bytes of %u bytes in the PES buffer",
               PID, stream->pes_buffer_used, stream->pes_buffer_size);
           /* Flush buffered PES data */
-          gst_mpegts_stream_pes_buffer_flush (stream);
+          gst_mpegts_stream_pes_buffer_flush (stream, FALSE);
           gst_pes_filter_drain (&stream->filter);
           /* Resize the buffer to half if no overflow detected and
            * had been used less than half of it */
@@ -2319,12 +2345,138 @@ gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux,
   ret = gst_mpegts_demux_parse_stream (demux, stream, data,
       MPEGTS_NORMAL_TS_PACKETSIZE - 1);
 
+  if (demux->pcr[1] != -1 && demux->bitrate == -1) {
+    GST_DEBUG_OBJECT (demux, "stream->last_PCR_difference: %" G_GINT64_FORMAT
+        ", demux->num_packets %" G_GUINT64_FORMAT,
+        demux->pcr[1] - demux->pcr[0], demux->num_packets);
+    demux->bitrate = gst_util_uint64_scale (GST_SECOND,
+        MPEGTS_NORMAL_TS_PACKETSIZE * demux->num_packets,
+        MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0]));
+    GST_DEBUG_OBJECT (demux, "bitrate is %" G_GINT64_FORMAT
+        " bytes per second", demux->bitrate);
+  }
+  demux->num_packets++;
   return ret;
 
   /* ERRORS */
 }
 
 static gboolean
+gst_mpegts_demux_handle_seek_push (GstMpegTSDemux * demux, GstEvent * event)
+{
+  gboolean res = FALSE;
+  gdouble rate;
+  GstFormat format;
+  GstSeekFlags flags;
+  GstSeekType start_type, stop_type;
+  gint64 start, stop, bstart, bstop;
+  GstEvent *bevent;
+
+  gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+      &stop_type, &stop);
+
+  GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT
+      " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
+      GST_TIME_ARGS (stop));
+
+  if (format == GST_FORMAT_BYTES) {
+    GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format);
+    goto beach;
+  }
+
+  GST_DEBUG_OBJECT (demux, "seek - trying directly upstream first");
+
+  /* first try original format seek */
+  res = gst_pad_push_event (demux->sinkpad, gst_event_ref (event));
+  if (res == TRUE)
+    goto beach;
+  GST_DEBUG_OBJECT (demux, "seek - no upstream");
+
+  if (format != GST_FORMAT_TIME) {
+    /* From here down, we only support time based seeks */
+    GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format);
+    goto beach;
+  }
+
+  /* We need to convert to byte based seek and we need a scr_rate for that. */
+  if (demux->bitrate == -1) {
+    GST_DEBUG_OBJECT (demux, "seek not possible, no bitrate");
+    goto beach;
+  }
+
+  GST_DEBUG_OBJECT (demux, "try with bitrate");
+
+  bstart = GSTTIME_TO_BYTES (start);
+  bstop = GSTTIME_TO_BYTES (stop);
+
+  GST_DEBUG_OBJECT (demux, "in bytes bstart %" G_GINT64_FORMAT " bstop %"
+      G_GINT64_FORMAT, bstart, bstop);
+  bevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type,
+      bstart, stop_type, bstop);
+
+  res = gst_pad_push_event (demux->sinkpad, bevent);
+
+beach:
+  gst_event_unref (event);
+  return res;
+}
+
+static gboolean
+gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event)
+{
+  GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad));
+  gboolean res = FALSE;
+
+  GST_DEBUG_OBJECT (demux, "got event %s",
+      gst_event_type_get_name (GST_EVENT_TYPE (event)));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+      res = gst_mpegts_demux_handle_seek_push (demux, event);
+      break;
+    default:
+      res = gst_pad_push_event (demux->sinkpad, event);
+      break;
+  }
+
+  gst_object_unref (demux);
+
+  return res;
+}
+
+static void
+gst_mpegts_demux_flush (GstMpegTSDemux * demux, gboolean discard)
+{
+  GstMpegTSStream *PCR_stream;
+  GstMpegTSStream *PMT_stream;
+
+  GST_DEBUG_OBJECT (demux, "flushing MPEG TS demuxer (discard %d)", discard);
+
+  /* Start by flushing internal buffers */
+  gst_mpegts_demux_pes_buffer_flush (demux, discard);
+
+  /* Clear adapter */
+  gst_adapter_clear (demux->adapter);
+
+  /* Try resetting the last_PCR value as we will have a discont */
+  if (demux->current_PMT == 0)
+    goto beach;
+
+  PMT_stream = demux->streams[demux->current_PMT];
+  if (PMT_stream == NULL)
+    goto beach;
+
+  PCR_stream = demux->streams[PMT_stream->PMT.PCR_PID];
+  if (PCR_stream == NULL)
+    goto beach;
+
+  PCR_stream->last_PCR = -1;
+
+beach:
+  return;
+}
+
+static gboolean
 gst_mpegts_demux_send_event (GstMpegTSDemux * demux, GstEvent * event)
 {
   gint i;
@@ -2347,7 +2499,7 @@ static gboolean
 gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event)
 {
   GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad));
-  gboolean res;
+  gboolean res = FALSE;
 
   GST_DEBUG_OBJECT (demux, "got event %s",
       gst_event_type_get_name (GST_EVENT_TYPE (event)));
@@ -2358,11 +2510,11 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event)
       break;
     case GST_EVENT_FLUSH_STOP:
       gst_adapter_clear (demux->adapter);
+      gst_mpegts_demux_flush (demux, TRUE);
       res = gst_mpegts_demux_send_event (demux, event);
       break;
     case GST_EVENT_EOS:
-      /* Flush buffered PES data */
-      gst_mpegts_demux_pes_buffer_flush (demux);
+      gst_mpegts_demux_flush (demux, FALSE);
       /* Send the EOS event on each stream */
       if (!(res = gst_mpegts_demux_send_event (demux, event))) {
         /* we have no streams */
@@ -2371,15 +2523,46 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event)
       }
       break;
     case GST_EVENT_NEWSEGMENT:
-      res = gst_mpegts_demux_send_event (demux, event);
+    {
+      gboolean update;
+      gdouble rate;
+      GstFormat format;
+      gint64 start, stop, time;
+
+      gst_event_parse_new_segment (event, &update, &rate, &format,
+          &start, &stop, &time);
+
+      gst_event_unref (event);
+      GST_INFO_OBJECT (demux, "received new segment: rate %g "
+          "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT
+          ", time: %" G_GINT64_FORMAT, rate, format, start, stop, time);
+      if (format == GST_FORMAT_BYTES && demux->bitrate != -1) {
+        gint64 tstart = 0, tstop = 0, pos = 0;
+
+        if (demux->base_pts != GST_CLOCK_TIME_NONE) {
+          tstart = tstop = demux->base_pts;
+        }
+        tstart += BYTES_TO_GSTTIME (start);
+        tstop += BYTES_TO_GSTTIME (stop);
+        pos = BYTES_TO_GSTTIME (time);
+
+        event = gst_event_new_new_segment (update, rate,
+            GST_FORMAT_TIME, tstart, tstop, pos);
+        GST_DEBUG_OBJECT (demux, "pushing time newsegment from %"
+            GST_TIME_FORMAT " to %" GST_TIME_FORMAT " pos %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (tstart), GST_TIME_ARGS (tstop), GST_TIME_ARGS (pos));
+
+        res = gst_mpegts_demux_send_event (demux, event);
+      }
       break;
+    }
     default:
       res = gst_mpegts_demux_send_event (demux, event);
       break;
   }
   gst_object_unref (demux);
 
-  return TRUE;
+  return res;
 }
 
 static gboolean
@@ -2455,21 +2638,54 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query)
     case GST_QUERY_DURATION:
     {
       GstFormat format;
-      gint64 duration;
+      GstPad *peer;
 
-      gst_query_parse_duration (query, &format, &duration);
+      gst_query_parse_duration (query, &format, NULL);
 
-      if (format == GST_FORMAT_BYTES) {
-        res = FALSE;
-      } else {
-        res = gst_pad_query_default (pad, query);
+      /* Try query upstream first */
+      peer = gst_pad_get_peer (demux->sinkpad);
+      if (peer) {
+        res = gst_pad_query (peer, query);
+        /* Try doing something with that query if it failed */
+        if (!res && format == GST_FORMAT_TIME && demux->bitrate != -1) {
+          /* Try using cache first */
+          if (GST_CLOCK_TIME_IS_VALID (demux->cache_duration)) {
+            GST_LOG_OBJECT (demux, "replying duration query from cache %"
+                GST_TIME_FORMAT, GST_TIME_ARGS (demux->cache_duration));
+            gst_query_set_duration (query, GST_FORMAT_TIME,
+                demux->cache_duration);
+            res = TRUE;
+          } else {              /* Query upstream and approximate */
+            GstQuery *bquery = gst_query_new_duration (GST_FORMAT_BYTES);
+            gint64 duration = 0;
+
+            /* Query peer for duration in bytes */
+            res = gst_pad_query (peer, bquery);
+            if (res) {
+              /* Convert to time format */
+              gst_query_parse_duration (bquery, &format, &duration);
+              GST_DEBUG_OBJECT (demux, "query on peer pad reported bytes %"
+                  G_GUINT64_FORMAT, duration);
+              demux->cache_duration = BYTES_TO_GSTTIME (duration);
+              GST_DEBUG_OBJECT (demux, "converted to time %" GST_TIME_FORMAT,
+                  GST_TIME_ARGS (demux->cache_duration));
+              gst_query_set_duration (query, GST_FORMAT_TIME,
+                  demux->cache_duration);
+            }
+            gst_query_unref (bquery);
+          }
+        } else {
+          GST_WARNING_OBJECT (demux, "unsupported query format or no bitrate "
+              "yet to approximate duration from bytes");
+        }
+        gst_object_unref (peer);
       }
-
       break;
     }
     default:
       res = gst_pad_query_default (pad, query);
   }
+  gst_object_unref (demux);
 
   return res;
 }
@@ -2554,9 +2770,7 @@ gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer)
   guint sync_count;
 
   if (GST_BUFFER_IS_DISCONT (buffer)) {
-    /* Flush buffered PES data */
-    gst_mpegts_demux_pes_buffer_flush (demux);
-    gst_adapter_clear (demux->adapter);
+    gst_mpegts_demux_flush (demux, FALSE);
   }
   /* first push the new buffer into the adapter */
   gst_adapter_push (demux->adapter, buffer);
index 8a4ca3d..c4a907a 100644 (file)
@@ -34,9 +34,9 @@
  *
  * The Original Code is Fluendo MPEG Demuxer plugin.
  *
- * The Initial Developer of the Original Code is Fluendo, S.L.
- * Portions created by Fluendo, S.L. are Copyright (C) 2005
- * Fluendo, S.L. All Rights Reserved.
+ * The Initial Developer of the Original Code is Fluendo, S.A.
+ * Portions created by Fluendo, S.A. are Copyright (C) 2005,2006,2007,2008,2009
+ * Fluendo, S.A. All Rights Reserved.
  *
  * Contributor(s): Wim Taymans <wim@fluendo.com>
  */
 #include "gstsectionfilter.h"
 
 G_BEGIN_DECLS
-
 #define MPEGTS_MIN_PES_BUFFER_SIZE     4 * 1024
 #define MPEGTS_MAX_PES_BUFFER_SIZE   256 * 1024
-
 #define MPEGTS_MAX_PID 0x1fff
 #define MPEGTS_NORMAL_TS_PACKETSIZE  188
 #define MPEGTS_M2TS_TS_PACKETSIZE    192
-
 #define IS_MPEGTS_SYNC(data) (((data)[0] == 0x47) && \
                                     (((data)[1] & 0x80) == 0x00) && \
                                     (((data)[3] & 0x10) == 0x10))
-
 #define GST_TYPE_MPEGTS_DEMUX              (gst_mpegts_demux_get_type())
 #define GST_MPEGTS_DEMUX(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),\
                                             GST_TYPE_MPEGTS_DEMUX,GstMpegTSDemux))
@@ -75,7 +71,6 @@ G_BEGIN_DECLS
                                             GST_TYPE_MPEGTS_DEMUX))
 #define GST_IS_MPEGTS_DEMUX_CLASS(obj)     (G_TYPE_CHECK_CLASS_TYPE((klass),\
                                             GST_TYPE_MPEGTS_DEMUX))
-
 typedef struct _GstMpegTSStream GstMpegTSStream;
 typedef struct _GstMpegTSPMTEntry GstMpegTSPMTEntry;
 typedef struct _GstMpegTSPMT GstMpegTSPMT;
@@ -84,142 +79,157 @@ typedef struct _GstMpegTSPAT GstMpegTSPAT;
 typedef struct _GstMpegTSDemux GstMpegTSDemux;
 typedef struct _GstMpegTSDemuxClass GstMpegTSDemuxClass;
 
-struct _GstMpegTSPMTEntry {
-  guint16           PID;
+struct _GstMpegTSPMTEntry
+{
+  guint16 PID;
 };
 
-struct _GstMpegTSPMT {
-  guint16           program_number;
-  guint8            version_number;
-  gboolean          current_next_indicator;
-  guint8            section_number;
-  guint8            last_section_number;
-  guint16           PCR_PID;
-  guint16           program_info_length;
-  GstMPEGDescriptor * program_info;
-
-  GArray            * entries;
+struct _GstMpegTSPMT
+{
+  guint16 program_number;
+  guint8 version_number;
+  gboolean current_next_indicator;
+  guint8 section_number;
+  guint8 last_section_number;
+  guint16 PCR_PID;
+  guint16 program_info_length;
+  GstMPEGDescriptor *program_info;
+
+  GArray *entries;
 };
 
-struct _GstMpegTSPATEntry {
-  guint16           program_number;
-  guint16           PID;
+struct _GstMpegTSPATEntry
+{
+  guint16 program_number;
+  guint16 PID;
 };
 
-struct _GstMpegTSPAT  {
-  guint16           transport_stream_id;
-  guint8            version_number;
-  gboolean          current_next_indicator;
-  guint8            section_number;
-  guint8            last_section_number;
+struct _GstMpegTSPAT
+{
+  guint16 transport_stream_id;
+  guint8 version_number;
+  gboolean current_next_indicator;
+  guint8 section_number;
+  guint8 last_section_number;
 
-  GArray            * entries;
+  GArray *entries;
 };
 
-typedef enum _MpegTsStreamFlags {
+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
 } MpegTsStreamFlags;
 
 /* Information associated to a single MPEG stream. */
-struct _GstMpegTSStream {
-  GstMpegTSDemux     * demux;
+struct _GstMpegTSStream
+{
+  GstMpegTSDemux *demux;
 
-  MpegTsStreamFlags  flags;
+  MpegTsStreamFlags flags;
 
   /* PID and type */
-  guint16           PID;
-  guint8            PID_type;
+  guint16 PID;
+  guint8 PID_type;
 
   /* adaptation_field data */
-  guint64           last_PCR;
-  guint64           base_PCR;
-  guint64           last_OPCR;
-  guint64           last_PCR_difference;
-  gboolean          discont_PCR;
-  GstClockTimeDiff  discont_difference;
+  guint64 last_PCR;
+  guint64 base_PCR;
+  guint64 last_OPCR;
+  guint64 last_PCR_difference;
+  gboolean discont_PCR;
+  GstClockTimeDiff discont_difference;
 
   /* for PAT streams */
-  GstMpegTSPAT       PAT;
+  GstMpegTSPAT PAT;
 
   /* for PMT streams */
-  GstMpegTSPMT       PMT;
+  GstMpegTSPMT PMT;
 
   /* for CA streams */
 
   /* for PAT, PMT, CA and private streams */
-  GstSectionFilter  section_filter;
+  GstSectionFilter section_filter;
 
   /* for PES streams */
-  guint8            id;
-  guint8            stream_type;
-  GstBuffer         * pes_buffer;
-  guint32           pes_buffer_size;
-  guint32           pes_buffer_used;
-  gboolean          pes_buffer_overflow;
-  gboolean          pes_buffer_in_sync;
-  GstPESFilter      filter;
-  GstPad            * pad;
-  GstFlowReturn     last_ret;
+  guint8 id;
+  guint8 stream_type;
+  GstBuffer *pes_buffer;
+  guint32 pes_buffer_size;
+  guint32 pes_buffer_used;
+  gboolean pes_buffer_overflow;
+  gboolean pes_buffer_in_sync;
+  GstPESFilter filter;
+  GstPad *pad;
+  GstFlowReturn last_ret;
   GstMPEGDescriptor *ES_info;
   /* needed because 33bit mpeg timestamps wrap around every (approx) 26.5 hrs */
-  GstClockTimeDiff  base_time;
-  GstClockTime      last_time;
+  GstClockTimeDiff base_time;
+  GstClockTime last_time;
   /* pid of PMT that this stream belongs to */
-  guint16           PMT_pid;
+  guint16 PMT_pid;
 };
 
-struct _GstMpegTSDemux {
-  GstElement        parent;
+struct _GstMpegTSDemux
+{
+  GstElement parent;
 
   /* properties */
-  gboolean          check_crc;
+  gboolean check_crc;
 
   /* sink pad and adapter */
-  GstPad            * sinkpad;
-  GstAdapter        * adapter;
-  guint8            ** sync_lut;
-  guint             sync_lut_len;
+  GstPad *sinkpad;
+  GstAdapter *adapter;
+  guint8 **sync_lut;
+  guint sync_lut_len;
 
   /* current PMT PID */
-  guint16           current_PMT;
+  guint16 current_PMT;
 
   /* Array of MPEGTS_MAX_PID + 1 stream entries */
-  GstMpegTSStream    **  streams;
+  GstMpegTSStream **streams;
   /* Array to perform pmts checks at gst_mpegts_demux_parse_adaptation_field */
-  gboolean          pmts_checked[MPEGTS_MAX_PID + 1];
-  
+  gboolean pmts_checked[MPEGTS_MAX_PID + 1];
+
   /* Array of Elementary Stream pids for ts with PMT */
-  guint16           * elementary_pids;
-  guint             nb_elementary_pids;
+  guint16 *elementary_pids;
+  guint nb_elementary_pids;
 
   /* Program number to use */
-  gint              program_number;
+  gint program_number;
 
   /* indicates that we need to close our pad group, because we've added
    * at least one pad */
-  gboolean          need_no_more_pads;
-  guint16           packetsize;
-  gboolean          m2ts_mode;
+  gboolean need_no_more_pads;
+  guint16 packetsize;
+  gboolean m2ts_mode;
   /* clocking */
-  GstClock          * clock;
-  GstClockTime      clock_base;
+  GstClock *clock;
+  GstClockTime clock_base;
+  /* Additional information required for seeking */
+  guint64 num_packets;
+  gint64 bitrate;
+  /* Two PCRs observations to calculate bitrate */
+  gint64 pcr[2];
+  GstClockTime cache_duration;
+  /* Cached base_PCR in GStreamer time. */
+  GstClockTime base_pts;
 };
 
-struct _GstMpegTSDemuxClass {
-  GstElementClass   parent_class;
+struct _GstMpegTSDemuxClass
+{
+  GstElementClass parent_class;
 
-  GstPadTemplate    * sink_template;
-  GstPadTemplate    * video_template;
-  GstPadTemplate    * audio_template;
-  GstPadTemplate    * private_template;
+  GstPadTemplate *sink_template;
+  GstPadTemplate *video_template;
+  GstPadTemplate *audio_template;
+  GstPadTemplate *private_template;
 };
 
-GType     gst_mpegts_demux_get_type (void);
+GType gst_mpegts_demux_get_type (void);
 
-gboolean  gst_mpegts_demux_plugin_init (GstPlugin *plugin);
+gboolean gst_mpegts_demux_plugin_init (GstPlugin * plugin);
 
 G_END_DECLS
-
 #endif /* __GST_MPEGTS_DEMUX_H__ */