baseparse: Don't override gst_segment_do_seek()
[platform/upstream/gstreamer.git] / libs / gst / base / gstbaseparse.c
index 861cdf9..df201ab 100644 (file)
  * <listitem>
  *   <itemizedlist><title>Set-up phase</title>
  *   <listitem><para>
- *     GstBaseParse calls @start to inform subclass that data processing is
+ *     #GstBaseParse calls @start to inform subclass that data processing is
  *     about to start now.
  *   </para></listitem>
  *   <listitem><para>
- *     GstBaseParse class calls @set_sink_caps to inform the subclass about
+ *     #GstBaseParse class calls @set_sink_caps to inform the subclass about
  *     incoming sinkpad caps. Subclass could already set the srcpad caps
  *     accordingly, but this might be delayed until calling
  *     gst_base_parse_finish_frame() with a non-queued frame.
  *   </para></listitem>
  *   <listitem><para>
- *      At least at this point subclass needs to tell the GstBaseParse class
+ *      At least at this point subclass needs to tell the #GstBaseParse class
  *      how big data chunks it wants to receive (min_frame_size). It can do
  *      this with gst_base_parse_set_min_frame_size().
  *   </para></listitem>
  *   <listitem><para>
- *      GstBaseParse class sets up appropriate data passing mode (pull/push)
+ *      #GstBaseParse class sets up appropriate data passing mode (pull/push)
  *      and starts to process the data.
  *   </para></listitem>
  *   </itemizedlist>
@@ -74,7 +74,7 @@
  *   <itemizedlist>
  *   <title>Parsing phase</title>
  *     <listitem><para>
- *       GstBaseParse gathers at least min_frame_size bytes of data either
+ *       #GstBaseParse gathers at least min_frame_size bytes of data either
  *       by pulling it from upstream or collecting buffers in an internal
  *       #GstAdapter.
  *     </para></listitem>
  *       </para><para>
  *       Subclass is also responsible for setting the buffer metadata
  *       (e.g. buffer timestamp and duration, or keyframe if applicable).
- *       (although the latter can also be done by GstBaseParse if it is
+ *       (although the latter can also be done by #GstBaseParse if it is
  *       appropriately configured, see below).  Frame is provided with
  *       timestamp derived from upstream (as much as generally possible),
  *       duration obtained from configuration (see below), and offset
  *       events, or to perform custom (segment) filtering.
  *     </para></listitem>
  *     <listitem><para>
- *       During the parsing process GstBaseParseClass will handle both srcpad
+ *       During the parsing process #GstBaseParseClass will handle both srcpad
  *       and sinkpad events. They will be passed to subclass if @event or
  *       @src_event callbacks have been provided.
  *     </para></listitem>
  * <listitem>
  *   <itemizedlist><title>Shutdown phase</title>
  *   <listitem><para>
- *     GstBaseParse class calls @stop to inform the subclass that data
+ *     #GstBaseParse class calls @stop to inform the subclass that data
  *     parsing will be stopped.
  *   </para></listitem>
  *   </itemizedlist>
  * needs to set the fixed caps on srcpad, when the format is ensured (e.g.
  * when base class calls subclass' @set_sink_caps function).
  *
- * This base class uses #GST_FORMAT_DEFAULT as a meaning of frames. So,
+ * This base class uses %GST_FORMAT_DEFAULT as a meaning of frames. So,
  * subclass conversion routine needs to know that conversion from
- * #GST_FORMAT_TIME to #GST_FORMAT_DEFAULT must return the
+ * %GST_FORMAT_TIME to %GST_FORMAT_DEFAULT must return the
  * frame number that can be found from the given byte position.
  *
- * GstBaseParse uses subclasses conversion methods also for seeking (or
+ * #GstBaseParse uses subclasses conversion methods also for seeking (or
  * otherwise uses its own default one, see also below).
  *
  * Subclass @start and @stop functions will be called to inform the beginning
  *   <listitem><para>
  *      In particular, if subclass is unable to determine a duration, but
  *      parsing (or specs) yields a frames per seconds rate, then this can be
- *      provided to GstBaseParse to enable it to cater for
+ *      provided to #GstBaseParse to enable it to cater for
  *      buffer time metadata (which will be taken from upstream as much as
  *      possible). Internally keeping track of frame durations and respective
- *      sizes that have been pushed provides GstBaseParse with an estimated
- *      bitrate. A default @convert (used if not overriden) will then use these
+ *      sizes that have been pushed provides #GstBaseParse with an estimated
+ *      bitrate. A default @convert (used if not overridden) will then use these
  *      rates to perform obvious conversions.  These rates are also used to
  *      update (estimated) duration at regular frame intervals.
  *   </para></listitem>
@@ -246,8 +246,10 @@ struct _GstBaseParsePrivate
   gint64 estimated_drift;
 
   guint min_frame_size;
+  gboolean disable_passthrough;
   gboolean passthrough;
   gboolean pts_interpolate;
+  gboolean infer_ts;
   gboolean syncable;
   gboolean has_timing_info;
   guint fps_num, fps_den;
@@ -260,6 +262,7 @@ struct _GstBaseParsePrivate
   gboolean discont;
   gboolean flushing;
   gboolean drain;
+  gboolean saw_gaps;
 
   gint64 offset;
   gint64 sync_offset;
@@ -303,6 +306,7 @@ struct _GstBaseParsePrivate
   gboolean upstream_seekable;
   gboolean upstream_has_duration;
   gint64 upstream_size;
+  GstFormat upstream_format;
   /* minimum distance between two index entries */
   GstClockTimeDiff idx_interval;
   guint64 idx_byte_interval;
@@ -327,8 +331,10 @@ struct _GstBaseParsePrivate
 
   /* Pending serialized events */
   GList *pending_events;
-  /* Newsegment event to be sent after SEEK */
-  gboolean pending_segment;
+
+  /* If baseparse has checked the caps to identify if it is
+   * handling video or audio */
+  gboolean checked_media;
 
   /* offset of last parsed frame/data */
   gint64 prev_offset;
@@ -345,8 +351,14 @@ struct _GstBaseParsePrivate
   GList *detect_buffers;
   guint detect_buffers_size;
 
+  /* True when no buffers have been received yet */
+  gboolean first_buffer;
+
   /* if TRUE, a STREAM_START event needs to be pushed */
   gboolean push_stream_start;
+
+  /* When we need to skip more data than we have currently */
+  guint skip;
 };
 
 typedef struct _GstBaseParseSeek
@@ -357,6 +369,15 @@ typedef struct _GstBaseParseSeek
   GstClockTime start_ts;
 } GstBaseParseSeek;
 
+#define DEFAULT_DISABLE_PASSTHROUGH        FALSE
+
+enum
+{
+  PROP_0,
+  PROP_DISABLE_PASSTHROUGH,
+  PROP_LAST
+};
+
 #define GST_BASE_PARSE_INDEX_LOCK(parse) \
   g_mutex_lock (&parse->priv->index_lock);
 #define GST_BASE_PARSE_INDEX_UNLOCK(parse) \
@@ -413,6 +434,11 @@ static gboolean gst_base_parse_handle_seek (GstBaseParse * parse,
     GstEvent * event);
 static void gst_base_parse_handle_tag (GstBaseParse * parse, GstEvent * event);
 
+static void gst_base_parse_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_base_parse_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
 static gboolean gst_base_parse_src_event (GstPad * pad, GstObject * parent,
     GstEvent * event);
 static gboolean gst_base_parse_src_query (GstPad * pad, GstObject * parent,
@@ -454,11 +480,14 @@ static GstFlowReturn gst_base_parse_locate_time (GstBaseParse * parse,
 static GstFlowReturn gst_base_parse_start_fragment (GstBaseParse * parse);
 static GstFlowReturn gst_base_parse_finish_fragment (GstBaseParse * parse,
     gboolean prev_head);
+static GstFlowReturn gst_base_parse_send_buffers (GstBaseParse * parse);
 
 static inline GstFlowReturn gst_base_parse_check_sync (GstBaseParse * parse);
 
 static gboolean gst_base_parse_is_seekable (GstBaseParse * parse);
 
+static void gst_base_parse_push_pending_events (GstBaseParse * parse);
+
 static void
 gst_base_parse_clear_queues (GstBaseParse * parse)
 {
@@ -490,7 +519,8 @@ gst_base_parse_clear_queues (GstBaseParse * parse)
   g_list_foreach (parse->priv->pending_events, (GFunc) gst_event_unref, NULL);
   g_list_free (parse->priv->pending_events);
   parse->priv->pending_events = NULL;
-  parse->priv->pending_segment = FALSE;
+
+  parse->priv->checked_media = FALSE;
 }
 
 static void
@@ -500,17 +530,6 @@ gst_base_parse_finalize (GObject * object)
 
   g_object_unref (parse->priv->adapter);
 
-  if (parse->priv->cache) {
-    gst_buffer_unref (parse->priv->cache);
-    parse->priv->cache = NULL;
-  }
-
-  g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
-      NULL);
-  g_list_free (parse->priv->pending_events);
-  parse->priv->pending_events = NULL;
-  parse->priv->pending_segment = FALSE;
-
   if (parse->priv->index) {
     gst_object_unref (parse->priv->index);
     parse->priv->index = NULL;
@@ -531,7 +550,26 @@ gst_base_parse_class_init (GstBaseParseClass * klass)
   gobject_class = G_OBJECT_CLASS (klass);
   g_type_class_add_private (klass, sizeof (GstBaseParsePrivate));
   parent_class = g_type_class_peek_parent (klass);
+
   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize);
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_parse_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_parse_get_property);
+
+  /**
+   * GstBaseParse:disable-passthrough:
+   *
+   * If set to %TRUE, baseparse will unconditionally force parsing of the
+   * incoming data. This can be required in the rare cases where the incoming
+   * side-data (caps, pts, dts, ...) is not trusted by the user and wants to
+   * force validation and parsing of the incoming data.
+   * If set to %FALSE, decision of whether to parse the data or not is up to
+   * the implementation (standard behaviour).
+   */
+  g_object_class_install_property (gobject_class, PROP_DISABLE_PASSTHROUGH,
+      g_param_spec_boolean ("disable-passthrough", "Disable passthrough",
+          "Force processing (disables passthrough)",
+          DEFAULT_DISABLE_PASSTHROUGH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstelement_class = (GstElementClass *) klass;
   gstelement_class->change_state =
@@ -608,6 +646,38 @@ gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass)
   GST_OBJECT_FLAG_SET (parse, GST_ELEMENT_FLAG_INDEXABLE);
 }
 
+static void
+gst_base_parse_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstBaseParse *parse = GST_BASE_PARSE (object);
+
+  switch (prop_id) {
+    case PROP_DISABLE_PASSTHROUGH:
+      parse->priv->disable_passthrough = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_base_parse_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstBaseParse *parse = GST_BASE_PARSE (object);
+
+  switch (prop_id) {
+    case PROP_DISABLE_PASSTHROUGH:
+      g_value_set_boolean (value, parse->priv->disable_passthrough);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
 static GstBaseParseFrame *
 gst_base_parse_frame_copy (GstBaseParseFrame * frame)
 {
@@ -690,11 +760,8 @@ gst_base_parse_frame_new (GstBuffer * buffer, GstBaseParseFrameFlags flags,
 }
 
 static inline void
-gst_base_parse_frame_update (GstBaseParse * parse, GstBaseParseFrame * frame,
-    GstBuffer * buf)
+gst_base_parse_update_flags (GstBaseParse * parse)
 {
-  gst_buffer_replace (&frame->buffer, buf);
-
   parse->flags = 0;
 
   /* set flags one by one for clarity */
@@ -706,6 +773,22 @@ gst_base_parse_frame_update (GstBaseParse * parse, GstBaseParseFrame * frame,
     parse->flags |= GST_BASE_PARSE_FLAG_LOST_SYNC;
 }
 
+static inline void
+gst_base_parse_update_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+  if (G_UNLIKELY (parse->priv->discont)) {
+    GST_DEBUG_OBJECT (parse, "marking DISCONT");
+    GST_BUFFER_FLAG_SET (frame->buffer, GST_BUFFER_FLAG_DISCONT);
+  }
+
+  if (parse->priv->prev_offset != parse->priv->offset || parse->priv->new_frame) {
+    GST_LOG_OBJECT (parse, "marking as new frame");
+    frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME;
+  }
+
+  frame->offset = parse->priv->prev_offset = parse->priv->offset;
+}
+
 static void
 gst_base_parse_reset (GstBaseParse * parse)
 {
@@ -715,6 +798,7 @@ gst_base_parse_reset (GstBaseParse * parse)
   parse->priv->min_frame_size = 1;
   parse->priv->discont = TRUE;
   parse->priv->flushing = FALSE;
+  parse->priv->saw_gaps = FALSE;
   parse->priv->offset = 0;
   parse->priv->sync_offset = 0;
   parse->priv->update_interval = -1;
@@ -734,8 +818,10 @@ gst_base_parse_reset (GstBaseParse * parse)
   parse->priv->next_pts = GST_CLOCK_TIME_NONE;
   parse->priv->next_dts = 0;
   parse->priv->syncable = TRUE;
+  parse->priv->disable_passthrough = DEFAULT_DISABLE_PASSTHROUGH;
   parse->priv->passthrough = FALSE;
   parse->priv->pts_interpolate = TRUE;
+  parse->priv->infer_ts = TRUE;
   parse->priv->has_timing_info = FALSE;
   parse->priv->post_min_bitrate = TRUE;
   parse->priv->post_avg_bitrate = TRUE;
@@ -751,20 +837,23 @@ gst_base_parse_reset (GstBaseParse * parse)
   parse->priv->upstream_seekable = FALSE;
   parse->priv->upstream_size = 0;
   parse->priv->upstream_has_duration = FALSE;
+  parse->priv->upstream_format = GST_FORMAT_UNDEFINED;
   parse->priv->idx_interval = 0;
   parse->priv->idx_byte_interval = 0;
   parse->priv->exact_position = TRUE;
   parse->priv->seen_keyframe = FALSE;
+  parse->priv->checked_media = FALSE;
 
   parse->priv->last_dts = GST_CLOCK_TIME_NONE;
   parse->priv->last_pts = GST_CLOCK_TIME_NONE;
   parse->priv->last_offset = 0;
 
+  parse->priv->skip = 0;
+
   g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
       NULL);
   g_list_free (parse->priv->pending_events);
   parse->priv->pending_events = NULL;
-  parse->priv->pending_segment = FALSE;
 
   if (parse->priv->cache) {
     gst_buffer_unref (parse->priv->cache);
@@ -780,6 +869,8 @@ gst_base_parse_reset (GstBaseParse * parse)
 
   parse->priv->new_frame = TRUE;
 
+  parse->priv->first_buffer = TRUE;
+
   g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, NULL);
   g_list_free (parse->priv->detect_buffers);
   parse->priv->detect_buffers = NULL;
@@ -822,7 +913,7 @@ gst_base_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
  *
  * Converts using configured "convert" vmethod in #GstBaseParse class.
  *
- * Returns: TRUE if conversion was successful.
+ * Returns: %TRUE if conversion was successful.
  */
 static gboolean
 gst_base_parse_convert (GstBaseParse * parse,
@@ -873,7 +964,7 @@ gst_base_parse_convert (GstBaseParse * parse,
  *
  * Handler for sink pad events.
  *
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
  */
 static gboolean
 gst_base_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
@@ -932,12 +1023,16 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
       const GstSegment *in_segment;
       GstSegment out_segment;
       gint64 offset = 0, next_dts;
+      guint32 seqnum = gst_event_get_seqnum (event);
 
       gst_event_parse_segment (event, &in_segment);
       gst_segment_init (&out_segment, GST_FORMAT_TIME);
+      out_segment.rate = in_segment->rate;
+      out_segment.applied_rate = in_segment->applied_rate;
 
       GST_DEBUG_OBJECT (parse, "segment %" GST_SEGMENT_FORMAT, in_segment);
 
+      parse->priv->upstream_format = in_segment->format;
       if (in_segment->format == GST_FORMAT_BYTES) {
         GstBaseParseSeek *seek = NULL;
         GSList *node;
@@ -987,6 +1082,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
         gst_event_unref (event);
 
         event = gst_event_new_segment (&out_segment);
+        gst_event_set_seqnum (event, seqnum);
 
         GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. %"
             GST_SEGMENT_FORMAT, in_segment);
@@ -1001,6 +1097,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
         out_segment.time = 0;
 
         event = gst_event_new_segment (&out_segment);
+        gst_event_set_seqnum (event, seqnum);
 
         next_dts = 0;
       } else {
@@ -1008,6 +1105,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
          * whatever else it might claim */
         parse->priv->upstream_seekable = FALSE;
         next_dts = in_segment->start;
+        gst_event_copy_segment (event, &out_segment);
       }
 
       memcpy (&parse->segment, &out_segment, sizeof (GstSegment));
@@ -1017,13 +1115,11 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
          applied_rate, format, start, stop, start);
        */
 
-      /* save the segment for later, right before we push a new buffer so that
-       * the caps are fixed and the next linked element can receive
-       * the segment. */
-      parse->priv->pending_segment = TRUE;
       ret = TRUE;
 
-      /* but finish the current segment */
+      /* save the segment for later, right before we push a new buffer so that
+       * the caps are fixed and the next linked element can receive
+       * the segment but finish the current segment */
       GST_DEBUG_OBJECT (parse, "draining current segment");
       if (in_segment->rate > 0.0)
         gst_base_parse_drain (parse);
@@ -1034,13 +1130,31 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
       parse->priv->offset = offset;
       parse->priv->sync_offset = offset;
       parse->priv->next_dts = next_dts;
+      parse->priv->next_pts = GST_CLOCK_TIME_NONE;
       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
+      parse->priv->prev_pts = GST_CLOCK_TIME_NONE;
+      parse->priv->prev_dts = GST_CLOCK_TIME_NONE;
       parse->priv->discont = TRUE;
       parse->priv->seen_keyframe = FALSE;
+      parse->priv->skip = 0;
       break;
     }
 
+    case GST_EVENT_SEGMENT_DONE:
+      /* need to drain now, rather than upon a new segment,
+       * since that would have SEGMENT_DONE come before potential
+       * delayed last part of the current segment */
+      GST_DEBUG_OBJECT (parse, "draining current segment");
+      if (parse->segment.rate > 0.0)
+        gst_base_parse_drain (parse);
+      else
+        gst_base_parse_finish_fragment (parse, FALSE);
+      /* Also forward event immediately, there might be no new data
+       * coming afterwards that would allow us to forward it later */
+      forward_immediate = TRUE;
+      break;
+
     case GST_EVENT_FLUSH_START:
       GST_OBJECT_LOCK (parse);
       parse->priv->flushing = TRUE;
@@ -1055,6 +1169,7 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
       parse->priv->new_frame = TRUE;
+      parse->priv->skip = 0;
 
       forward_immediate = TRUE;
       break;
@@ -1066,22 +1181,16 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
         gst_base_parse_finish_fragment (parse, TRUE);
 
       /* If we STILL have zero frames processed, fire an error */
-      if (parse->priv->framecount == 0) {
+      if (parse->priv->framecount == 0 && !parse->priv->saw_gaps &&
+          !parse->priv->first_buffer) {
         GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE,
             ("No valid frames found before end of stream"), (NULL));
       }
       /* newsegment and other serialized events before eos */
-      if (G_UNLIKELY (parse->priv->pending_events)) {
-        GList *l;
+      gst_base_parse_push_pending_events (parse);
 
-        for (l = parse->priv->pending_events; l != NULL; l = l->next) {
-          gst_pad_push_event (parse->srcpad, GST_EVENT (l->data));
-        }
-        g_list_free (parse->priv->pending_events);
-        parse->priv->pending_events = NULL;
-        parse->priv->pending_segment = FALSE;
-      }
-      if (parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) {
+      if (!parse->priv->saw_gaps
+          && parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) {
         /* We've not posted bitrate tags yet - do so now */
         gst_base_parse_post_bitrates (parse, TRUE, TRUE, TRUE);
       }
@@ -1114,11 +1223,15 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
     case GST_EVENT_GAP:
     {
       GST_DEBUG_OBJECT (parse, "draining current data due to gap event");
+
+      gst_base_parse_push_pending_events (parse);
+
       if (parse->segment.rate > 0.0)
         gst_base_parse_drain (parse);
       else
         gst_base_parse_finish_fragment (parse, TRUE);
       forward_immediate = TRUE;
+      parse->priv->saw_gaps = TRUE;
       break;
     }
     case GST_EVENT_TAG:
@@ -1150,10 +1263,8 @@ gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
     if (!GST_EVENT_IS_SERIALIZED (event) || forward_immediate) {
       ret = gst_pad_push_event (parse->srcpad, event);
     } else {
-      // GST_VIDEO_DECODER_STREAM_LOCK (decoder);
       parse->priv->pending_events =
           g_list_prepend (parse->priv->pending_events, event);
-      // GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
       ret = TRUE;
     }
   }
@@ -1272,7 +1383,7 @@ gst_base_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
  *
  * Handler for source pad events.
  *
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
  */
 static gboolean
 gst_base_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
@@ -1309,7 +1420,7 @@ gst_base_parse_is_seekable (GstBaseParse * parse)
  *
  * Default srcpad event handler.
  *
- * Returns: TRUE if the event was handled and can be dropped.
+ * Returns: %TRUE if the event was handled and can be dropped.
  */
 static gboolean
 gst_base_parse_src_event_default (GstBaseParse * parse, GstEvent * event)
@@ -1340,7 +1451,7 @@ gst_base_parse_src_event_default (GstBaseParse * parse, GstEvent * event)
  *
  * Default implementation of "convert" vmethod in #GstBaseParse class.
  *
- * Returns: TRUE if conversion was successful.
+ * Returns: %TRUE if conversion was successful.
  */
 gboolean
 gst_base_parse_convert_default (GstBaseParse * parse,
@@ -1453,16 +1564,18 @@ gst_base_parse_update_duration (GstBaseParse * baseparse)
         /* inform if duration changed, but try to avoid spamming */
         parse->priv->estimated_drift +=
             dest_value - parse->priv->estimated_duration;
+
+        parse->priv->estimated_duration = dest_value;
+        GST_LOG_OBJECT (parse,
+            "updated estimated duration to %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (dest_value));
+
         if (parse->priv->estimated_drift > GST_SECOND ||
             parse->priv->estimated_drift < -GST_SECOND) {
           gst_element_post_message (GST_ELEMENT (parse),
               gst_message_new_duration_changed (GST_OBJECT (parse)));
           parse->priv->estimated_drift = 0;
         }
-        parse->priv->estimated_duration = dest_value;
-        GST_LOG_OBJECT (parse,
-            "updated estimated duration to %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (dest_value));
       }
     }
   }
@@ -1781,6 +1894,7 @@ gst_base_parse_check_media (GstBaseParse * parse)
   if (caps)
     gst_caps_unref (caps);
 
+  parse->priv->checked_media = TRUE;
   GST_DEBUG_OBJECT (parse, "media is video: %d", parse->priv->is_video);
 }
 
@@ -1819,27 +1933,17 @@ gst_base_parse_prepare_frame (GstBaseParse * parse, GstBuffer * buffer)
       GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET (buffer),
       gst_buffer_get_size (buffer));
 
-  if (parse->priv->discont) {
-    GST_DEBUG_OBJECT (parse, "marking DISCONT");
-    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
-    parse->priv->discont = FALSE;
-  }
-
   GST_BUFFER_OFFSET (buffer) = parse->priv->offset;
 
-  frame = gst_base_parse_frame_new (buffer, 0, 0);
+  gst_base_parse_update_flags (parse);
 
-  /* also ensure to update state flags */
-  gst_base_parse_frame_update (parse, frame, buffer);
+  frame = gst_base_parse_frame_new (buffer, 0, 0);
   gst_buffer_unref (buffer);
+  gst_base_parse_update_frame (parse, frame);
 
-  if (parse->priv->prev_offset != parse->priv->offset || parse->priv->new_frame) {
-    GST_LOG_OBJECT (parse, "marking as new frame");
-    parse->priv->new_frame = FALSE;
-    frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME;
-  }
-
-  frame->offset = parse->priv->prev_offset = parse->priv->offset;
+  /* clear flags for next frame */
+  parse->priv->discont = FALSE;
+  parse->priv->new_frame = FALSE;
 
   /* use default handler to provide initial (upstream) metadata */
   gst_base_parse_parse_frame (parse, frame);
@@ -1908,7 +2012,20 @@ gst_base_parse_handle_buffer (GstBaseParse * parse, GstBuffer * buffer,
           g_slist_prepend (parse->priv->buffers_head, outbuf);
       outbuf = NULL;
     } else {
-      gst_adapter_flush (parse->priv->adapter, *skip);
+      /* If we're asked to skip more than is available in the adapter,
+         we need to remember what we need to skip for next iteration */
+      gsize av = gst_adapter_available (parse->priv->adapter);
+      GST_DEBUG ("Asked to skip %u (%" G_GSIZE_FORMAT " available)", *skip, av);
+      if (av >= *skip) {
+        gst_adapter_flush (parse->priv->adapter, *skip);
+      } else {
+        GST_DEBUG
+            ("This is more than available, flushing %" G_GSIZE_FORMAT
+            ", storing %u to skip", av, (guint) (*skip - av));
+        parse->priv->skip = *skip - av;
+        gst_adapter_flush (parse->priv->adapter, av);
+        *skip = av;
+      }
     }
     if (!parse->priv->discont)
       parse->priv->sync_offset = parse->priv->offset;
@@ -1930,7 +2047,27 @@ gst_base_parse_handle_buffer (GstBaseParse * parse, GstBuffer * buffer,
   return ret;
 }
 
-/* gst_base_parse_handle_and_push_buffer:
+/* gst_base_parse_push_pending_events:
+ * @parse: #GstBaseParse
+ *
+ * Pushes the pending events
+ */
+static void
+gst_base_parse_push_pending_events (GstBaseParse * parse)
+{
+  if (G_UNLIKELY (parse->priv->pending_events)) {
+    GList *r = g_list_reverse (parse->priv->pending_events);
+    GList *l;
+
+    parse->priv->pending_events = NULL;
+    for (l = r; l != NULL; l = l->next) {
+      gst_pad_push_event (parse->srcpad, GST_EVENT_CAST (l->data));
+    }
+    g_list_free (r);
+  }
+}
+
+/* gst_base_parse_handle_and_push_frame:
  * @parse: #GstBaseParse.
  * @klass: #GstBaseParseClass.
  * @frame: (transfer full): a #GstBaseParseFrame
@@ -1982,10 +2119,16 @@ gst_base_parse_handle_and_push_frame (GstBaseParse * parse,
     }
   }
 
+  /* track upstream time if provided, not subclass' internal notion of it */
+  if (parse->priv->upstream_format == GST_FORMAT_TIME) {
+    GST_BUFFER_PTS (frame->buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DTS (frame->buffer) = GST_CLOCK_TIME_NONE;
+  }
+
   /* interpolating and no valid pts yet,
    * start with dts and carry on from there */
-  if (parse->priv->pts_interpolate &&
-      !GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts))
+  if (parse->priv->infer_ts && parse->priv->pts_interpolate
+      && !GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts))
     parse->priv->next_pts = parse->priv->next_dts;
 
   /* again use default handler to add missing metadata;
@@ -2021,6 +2164,7 @@ gst_base_parse_handle_and_push_frame (GstBaseParse * parse,
 
     while ((queued_frame = g_queue_pop_head (&parse->priv->queued_frames))) {
       gst_base_parse_push_frame (parse, queued_frame);
+      gst_base_parse_frame_free (queued_frame);
     }
   }
 
@@ -2090,23 +2234,13 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
   if (!gst_pad_has_current_caps (parse->srcpad))
     goto no_caps;
 
-  if (G_UNLIKELY (parse->priv->pending_segment)) {
+  if (G_UNLIKELY (!parse->priv->checked_media)) {
     /* have caps; check identity */
     gst_base_parse_check_media (parse);
   }
 
   /* Push pending events, including SEGMENT events */
-  if (G_UNLIKELY (parse->priv->pending_events)) {
-    GList *r = g_list_reverse (parse->priv->pending_events);
-    GList *l;
-
-    parse->priv->pending_events = NULL;
-    for (l = r; l != NULL; l = l->next) {
-      gst_pad_push_event (parse->srcpad, GST_EVENT (l->data));
-    }
-    g_list_free (r);
-    parse->priv->pending_segment = FALSE;
-  }
+  gst_base_parse_push_pending_events (parse);
 
   /* segment adjustment magic; only if we are running the whole show */
   if (!parse->priv->passthrough && parse->segment.rate > 0.0 &&
@@ -2204,6 +2338,34 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
           size);
       ret = gst_pad_push (parse->srcpad, buffer);
       GST_LOG_OBJECT (parse, "frame pushed, flow %s", gst_flow_get_name (ret));
+    } else if (!parse->priv->disable_passthrough && parse->priv->passthrough) {
+
+      /* in backwards playback mode, if on passthrough we need to push buffers
+       * directly without accumulating them into the buffers_queued as baseparse
+       * will never check for a DISCONT while on passthrough and those buffers
+       * will never be pushed.
+       *
+       * also, as we are on reverse playback, it might be possible that
+       * passthrough might have just been enabled, so make sure to drain the
+       * buffers_queued list */
+      if (G_UNLIKELY (parse->priv->buffers_queued != NULL)) {
+        gst_base_parse_finish_fragment (parse, TRUE);
+        ret = gst_base_parse_send_buffers (parse);
+      }
+
+      if (ret == GST_FLOW_OK) {
+        GST_LOG_OBJECT (parse,
+            "pushing frame (%" G_GSIZE_FORMAT " bytes) now..", size);
+        ret = gst_pad_push (parse->srcpad, buffer);
+        GST_LOG_OBJECT (parse, "frame pushed, flow %s",
+            gst_flow_get_name (ret));
+      } else {
+        GST_LOG_OBJECT (parse,
+            "frame (%" G_GSIZE_FORMAT " bytes) not pushed: %s", size,
+            gst_flow_get_name (ret));
+        gst_buffer_unref (buffer);
+      }
+
     } else {
       GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) queued for now",
           size);
@@ -2216,7 +2378,7 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
         size, gst_flow_get_name (ret));
     gst_buffer_unref (buffer);
     /* if we are not sufficiently in control, let upstream decide on EOS */
-    if (ret == GST_FLOW_EOS &&
+    if (ret == GST_FLOW_EOS && !parse->priv->disable_passthrough &&
         (parse->priv->passthrough ||
             (parse->priv->pad_mode == GST_PAD_MODE_PUSH &&
                 !parse->priv->upstream_seekable)))
@@ -2233,6 +2395,9 @@ gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
   /* ERRORS */
 no_caps:
   {
+    if (GST_PAD_IS_FLUSHING (parse->srcpad))
+      return GST_FLOW_FLUSHING;
+
     GST_ELEMENT_ERROR (parse, STREAM, DECODE, ("No caps set"), (NULL));
     return GST_FLOW_ERROR;
   }
@@ -2379,6 +2544,7 @@ gst_base_parse_send_buffers (GstBaseParse * parse)
   GSList *send = NULL;
   GstBuffer *buf;
   GstFlowReturn ret = GST_FLOW_OK;
+  gboolean first = TRUE;
 
   send = parse->priv->buffers_send;
 
@@ -2392,6 +2558,13 @@ gst_base_parse_send_buffers (GstBaseParse * parse)
         GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
         GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf));
 
+    /* Make sure the first buffer is always DISCONT. If we split
+     * GOPs inside the parser this is otherwise not guaranteed */
+    if (first) {
+      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+      first = FALSE;
+    }
+
     /* iterate output queue an push downstream */
     ret = gst_pad_push (parse->srcpad, buf);
     send = g_slist_delete_link (send, send);
@@ -2547,8 +2720,10 @@ gst_base_parse_finish_fragment (GstBaseParse * parse, gboolean prev_head)
         parse->priv->buffers_queued);
   }
 
-  /* audio may have all marked as keyframe, so arrange to send here */
-  if (!seen_delta)
+  /* audio may have all marked as keyframe, so arrange to send here. Also
+   * we might have ended the loop above on a keyframe, in which case we
+   * should */
+  if (!seen_delta || seen_key)
     ret = gst_base_parse_send_buffers (parse);
 
   /* any trailing unused no longer usable (ideally none) */
@@ -2576,20 +2751,111 @@ gst_base_parse_check_sync (GstBaseParse * parse)
 }
 
 static GstFlowReturn
+gst_base_parse_process_streamheader (GstBaseParse * parse)
+{
+  GstCaps *caps;
+  GstStructure *str;
+  const GValue *value;
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
+  if (caps == NULL)
+    goto notfound;
+
+  str = gst_caps_get_structure (caps, 0);
+  value = gst_structure_get_value (str, "streamheader");
+  if (value == NULL)
+    goto notfound;
+
+  GST_DEBUG_OBJECT (parse, "Found streamheader field on input caps");
+
+  if (GST_VALUE_HOLDS_ARRAY (value)) {
+    gint i;
+    gsize len = gst_value_array_get_size (value);
+
+    for (i = 0; i < len; i++) {
+      GstBuffer *buffer =
+          gst_value_get_buffer (gst_value_array_get_value (value, i));
+      ret =
+          gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse),
+          GST_OBJECT_CAST (parse), gst_buffer_ref (buffer));
+    }
+
+  } else if (GST_VALUE_HOLDS_BUFFER (value)) {
+    GstBuffer *buffer = gst_value_get_buffer (value);
+    ret =
+        gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse),
+        GST_OBJECT_CAST (parse), gst_buffer_ref (buffer));
+  }
+
+  gst_caps_unref (caps);
+
+  return ret;
+
+notfound:
+  {
+    if (caps) {
+      gst_caps_unref (caps);
+    }
+
+    GST_DEBUG_OBJECT (parse, "No streamheader on caps");
+    return GST_FLOW_OK;
+  }
+}
+
+static GstFlowReturn
 gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
   GstBaseParseClass *bclass;
   GstBaseParse *parse;
   GstFlowReturn ret = GST_FLOW_OK;
+  GstFlowReturn old_ret = GST_FLOW_OK;
   GstBuffer *tmpbuf = NULL;
   guint fsize = 1;
   gint skip = -1;
-  const guint8 *data;
   guint min_size, av;
   GstClockTime pts, dts;
 
   parse = GST_BASE_PARSE (parent);
   bclass = GST_BASE_PARSE_GET_CLASS (parse);
+  GST_DEBUG_OBJECT (parent, "chain");
+
+  /* early out for speed, if we need to skip */
+  if (buffer && GST_BUFFER_IS_DISCONT (buffer))
+    parse->priv->skip = 0;
+  if (parse->priv->skip > 0) {
+    gsize bsize = gst_buffer_get_size (buffer);
+    GST_DEBUG ("Got %" G_GSIZE_FORMAT " buffer, need to skip %u", bsize,
+        parse->priv->skip);
+    if (parse->priv->skip >= bsize) {
+      parse->priv->skip -= bsize;
+      GST_DEBUG ("All the buffer is skipped");
+      parse->priv->offset += bsize;
+      parse->priv->sync_offset = parse->priv->offset;
+      return GST_FLOW_OK;
+    }
+    buffer = gst_buffer_make_writable (buffer);
+    gst_buffer_resize (buffer, parse->priv->skip, bsize - parse->priv->skip);
+    parse->priv->offset += parse->priv->skip;
+    GST_DEBUG ("Done skipping, we have %u left on this buffer",
+        (unsigned) (bsize - parse->priv->skip));
+    parse->priv->skip = 0;
+    parse->priv->discont = TRUE;
+  }
+
+  if (G_UNLIKELY (parse->priv->first_buffer)) {
+    parse->priv->first_buffer = FALSE;
+    if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) {
+      /* this stream has no header buffers, check if we just prepend the
+       * streamheader from caps to the stream */
+      GST_DEBUG_OBJECT (parse, "Looking for streamheader field on caps to "
+          "prepend to the stream");
+      gst_base_parse_process_streamheader (parse);
+    } else {
+      GST_DEBUG_OBJECT (parse, "Stream has header buffers, not prepending "
+          "streamheader from caps");
+    }
+  }
 
   if (parse->priv->detecting) {
     GstBuffer *detect_buf;
@@ -2674,7 +2940,8 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
         GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
         GST_TIME_ARGS (GST_BUFFER_PTS (buffer)));
 
-    if (G_UNLIKELY (parse->priv->passthrough)) {
+    if (G_UNLIKELY (!parse->priv->disable_passthrough
+            && parse->priv->passthrough)) {
       GstBaseParseFrame frame;
 
       gst_base_parse_frame_init (&frame);
@@ -2683,13 +2950,17 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
       gst_base_parse_frame_free (&frame);
       return ret;
     }
-    /* upstream feeding us in reverse playback;
-     * finish previous fragment and start new upon DISCONT */
-    if (parse->segment.rate < 0.0) {
-      if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) {
+    if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) {
+      /* upstream feeding us in reverse playback;
+       * finish previous fragment and start new upon DISCONT */
+      if (parse->segment.rate < 0.0) {
         GST_DEBUG_OBJECT (parse, "buffer starts new reverse playback fragment");
         ret = gst_base_parse_finish_fragment (parse, TRUE);
         gst_base_parse_start_fragment (parse);
+      } else {
+        /* discont in the stream, drain and mark discont for next output */
+        gst_base_parse_drain (parse);
+        parse->priv->discont = TRUE;
       }
     }
     gst_adapter_push (parse->priv->adapter, buffer);
@@ -2729,28 +3000,43 @@ gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
     if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts))
       parse->priv->prev_dts = parse->priv->next_dts = dts;
 
+    /* we can mess with, erm interpolate, timestamps,
+     * and incoming stuff has PTS but no DTS seen so far,
+     * then pick up DTS from PTS and hope for the best ... */
+    if (parse->priv->infer_ts &&
+        parse->priv->pts_interpolate &&
+        !GST_CLOCK_TIME_IS_VALID (dts) &&
+        !GST_CLOCK_TIME_IS_VALID (parse->priv->prev_dts) &&
+        GST_CLOCK_TIME_IS_VALID (pts))
+      parse->priv->next_dts = pts;
+
     /* always pass all available data */
-    data = gst_adapter_map (parse->priv->adapter, av);
-    /* arrange for actual data to be copied if subclass tries to,
-     * since what is passed is tied to the adapter */
-    tmpbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY |
-        GST_MEMORY_FLAG_NO_SHARE, (gpointer) data, av, 0, av, NULL, NULL);
+    tmpbuf = gst_adapter_get_buffer (parse->priv->adapter, av);
+
+    /* already inform subclass what timestamps we have planned,
+     * at least if provided by time-based upstream */
+    if (parse->priv->upstream_format == GST_FORMAT_TIME) {
+      tmpbuf = gst_buffer_make_writable (tmpbuf);
+      GST_BUFFER_PTS (tmpbuf) = parse->priv->next_pts;
+      GST_BUFFER_DTS (tmpbuf) = parse->priv->next_dts;
+      GST_BUFFER_DURATION (tmpbuf) = GST_CLOCK_TIME_NONE;
+    }
 
     /* keep the adapter mapped, so keep track of what has to be flushed */
     ret = gst_base_parse_handle_buffer (parse, tmpbuf, &skip, &flush);
     tmpbuf = NULL;
 
-    /* probably already implicitly unmapped due to adapter operation,
-     * but for good measure ... */
-    gst_adapter_unmap (parse->priv->adapter);
     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
       goto done;
     }
     if (skip == 0 && flush == 0) {
       GST_LOG_OBJECT (parse, "nothing skipped and no frames finished, "
           "breaking to get more data");
+      /* ignore this return as it produced no data */
+      ret = old_ret;
       goto done;
     }
+    old_ret = ret;
   }
 
 done:
@@ -2962,6 +3248,12 @@ gst_base_parse_scan_frame (GstBaseParse * parse, GstBaseParseClass * klass)
     if (ret != GST_FLOW_OK)
       break;
 
+    /* If a large amount of data was requested to be skipped, _handle_buffer
+       might have set the priv->skip flag to an extra amount on top of skip.
+       In pull mode, we can just pull from the new offset directly. */
+    parse->priv->offset += parse->priv->skip;
+    parse->priv->skip = 0;
+
     /* something flushed means something happened,
      * and we should bail out of this loop so as not to occupy
      * the task thread indefinitely */
@@ -3004,13 +3296,17 @@ gst_base_parse_loop (GstPad * pad)
 
   if (G_UNLIKELY (parse->priv->push_stream_start)) {
     gchar *stream_id;
+    GstEvent *event;
 
     stream_id =
         gst_pad_create_stream_id (parse->srcpad, GST_ELEMENT_CAST (parse),
         NULL);
 
+    event = gst_event_new_stream_start (stream_id);
+    gst_event_set_group_id (event, gst_util_group_id_next ());
+
     GST_DEBUG_OBJECT (parse, "Pushing STREAM_START");
-    gst_pad_push_event (parse->srcpad, gst_event_new_stream_start (stream_id));
+    gst_pad_push_event (parse->srcpad, event);
     parse->priv->push_stream_start = FALSE;
     g_free (stream_id);
   }
@@ -3028,8 +3324,6 @@ gst_base_parse_loop (GstPad * pad)
   }
 
   ret = gst_base_parse_scan_frame (parse, klass);
-  if (ret != GST_FLOW_OK)
-    goto done;
 
   /* eat expected eos signalling past segment in reverse playback */
   if (parse->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
@@ -3039,9 +3333,12 @@ gst_base_parse_loop (GstPad * pad)
     gst_base_parse_finish_fragment (parse, FALSE);
     /* force previous fragment */
     parse->priv->offset = -1;
-    ret = GST_FLOW_OK;
+    goto eos;
   }
 
+  if (ret != GST_FLOW_OK)
+    goto done;
+
 done:
   if (ret == GST_FLOW_EOS)
     goto eos;
@@ -3099,18 +3396,11 @@ pause:
       push_eos = TRUE;
     }
     if (push_eos) {
-      /* Push pending events, including SEGMENT events */
-      if (G_UNLIKELY (parse->priv->pending_events)) {
-        GList *r = g_list_reverse (parse->priv->pending_events);
-        GList *l;
-
-        parse->priv->pending_events = NULL;
-        for (l = r; l != NULL; l = l->next) {
-          gst_pad_push_event (parse->srcpad, GST_EVENT (l->data));
-        }
-        g_list_free (r);
-        parse->priv->pending_segment = FALSE;
+      if (parse->priv->estimated_duration <= 0) {
+        gst_base_parse_update_duration (parse);
       }
+      /* Push pending events, including SEGMENT events */
+      gst_base_parse_push_pending_events (parse);
 
       gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
     }
@@ -3215,7 +3505,6 @@ gst_base_parse_sink_activate_mode (GstPad * pad, GstObject * parent,
         parse->priv->pending_events =
             g_list_prepend (parse->priv->pending_events,
             gst_event_new_segment (&parse->segment));
-        parse->priv->pending_segment = TRUE;
         result = TRUE;
       } else {
         result = gst_pad_stop_task (pad);
@@ -3450,6 +3739,23 @@ gst_base_parse_set_pts_interpolation (GstBaseParse * parse,
 }
 
 /**
+ * gst_base_parse_set_infer_ts:
+ * @parse: a #GstBaseParse
+ * @infer_ts: %TRUE if parser should infer DTS/PTS from each other
+ *
+ * By default, the base class might try to infer PTS from DTS and vice
+ * versa.  While this is generally correct for audio data, it may not
+ * be otherwise. Sub-classes implementing such formats should disable
+ * timestamp inferring.
+ */
+void
+gst_base_parse_set_infer_ts (GstBaseParse * parse, gboolean infer_ts)
+{
+  parse->priv->infer_ts = infer_ts;
+  GST_INFO_OBJECT (parse, "TS inferring: %s", (infer_ts) ? "yes" : "no");
+}
+
+/**
  * gst_base_parse_set_latency:
  * @parse: a #GstBaseParse
  * @min_latency: minimum parse latency
@@ -3463,6 +3769,9 @@ void
 gst_base_parse_set_latency (GstBaseParse * parse, GstClockTime min_latency,
     GstClockTime max_latency)
 {
+  g_return_if_fail (min_latency != GST_CLOCK_TIME_NONE);
+  g_return_if_fail (min_latency <= max_latency);
+
   GST_OBJECT_LOCK (parse);
   parse->priv->min_latency = min_latency;
   parse->priv->max_latency = max_latency;
@@ -3632,9 +3941,10 @@ gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query)
 
         GST_OBJECT_LOCK (parse);
         /* add our latency */
-        if (min_latency != -1)
-          min_latency += parse->priv->min_latency;
-        if (max_latency != -1)
+        min_latency += parse->priv->min_latency;
+        if (max_latency == -1 || parse->priv->max_latency == -1)
+          max_latency = -1;
+        else
           max_latency += parse->priv->max_latency;
         GST_OBJECT_UNLOCK (parse);
 
@@ -3642,6 +3952,25 @@ gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query)
       }
       break;
     }
+    case GST_QUERY_SEGMENT:
+    {
+      GstFormat format;
+      gint64 start, stop;
+
+      format = parse->segment.format;
+
+      start =
+          gst_segment_to_stream_time (&parse->segment, format,
+          parse->segment.start);
+      if ((stop = parse->segment.stop) == -1)
+        stop = parse->segment.duration;
+      else
+        stop = gst_segment_to_stream_time (&parse->segment, format, stop);
+
+      gst_query_set_segment (query, parse->segment.rate, format, start, stop);
+      res = TRUE;
+      break;
+    }
     default:
       res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query);
       break;
@@ -3759,6 +4088,9 @@ gst_base_parse_locate_time (GstBaseParse * parse, GstClockTime * _time,
   /* need initial positions; start and end */
   lpos = parse->priv->first_frame_offset;
   ltime = parse->priv->first_frame_pts;
+  /* try other one if no luck */
+  if (!GST_CLOCK_TIME_IS_VALID (ltime))
+    ltime = parse->priv->first_frame_dts;
   if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &htime)) {
     GST_DEBUG_OBJECT (parse, "Unknown time duration, cannot bisect");
     return GST_FLOW_ERROR;
@@ -3767,7 +4099,7 @@ gst_base_parse_locate_time (GstBaseParse * parse, GstClockTime * _time,
 
   GST_DEBUG_OBJECT (parse,
       "Bisection initial bounds: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
-      ", times %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, lpos, htime,
+      ", times %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, lpos, hpos,
       GST_TIME_ARGS (ltime), GST_TIME_ARGS (htime));
 
   /* check preconditions are satisfied;
@@ -3923,6 +4255,8 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
   gint64 start, stop, seekpos, seekstop;
   GstSegment seeksegment = { 0, };
   GstClockTime start_ts;
+  guint32 seqnum;
+  GstEvent *segment_event;
 
   /* try upstream first, unless we're driving the streaming thread ourselves */
   if (parse->priv->pad_mode != GST_PAD_MODE_PULL) {
@@ -3933,6 +4267,7 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
 
   gst_event_parse_seek (event, &rate, &format, &flags,
       &start_type, &start, &stop_type, &stop);
+  seqnum = gst_event_get_seqnum (event);
 
   GST_DEBUG_OBJECT (parse, "seek to format %s, rate %f, "
       "start type %d at %" GST_TIME_FORMAT ", end type %d at %"
@@ -3960,9 +4295,6 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
   if (rate < 0.0 && parse->priv->pad_mode == GST_PAD_MODE_PUSH)
     goto negative_rate;
 
-  if (rate < 0.0 && parse->priv->pad_mode == GST_PAD_MODE_PULL)
-    goto negative_rate_pull_mode;
-
   if (start_type != GST_SEEK_TYPE_SET ||
       (stop_type != GST_SEEK_TYPE_SET && stop_type != GST_SEEK_TYPE_NONE))
     goto wrong_type;
@@ -3988,20 +4320,36 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
     GST_DEBUG_OBJECT (parse, "accurate seek possible");
     accurate = TRUE;
   }
+
   if (accurate) {
-    GstClockTime startpos = seeksegment.position;
+    GstClockTime startpos;
+    if (rate >= 0)
+      startpos = seeksegment.position;
+    else
+      startpos = start;
 
     /* accurate requested, so ... seek a bit before target */
     if (startpos < parse->priv->lead_in_ts)
       startpos = 0;
     else
       startpos -= parse->priv->lead_in_ts;
+
+    if (seeksegment.stop == -1 && seeksegment.duration != -1)
+      seeksegment.stop = seeksegment.start + seeksegment.duration;
+
     seekpos = gst_base_parse_find_offset (parse, startpos, TRUE, &start_ts);
     seekstop = gst_base_parse_find_offset (parse, seeksegment.stop, FALSE,
         NULL);
   } else {
-    start_ts = seeksegment.position;
-    if (!gst_base_parse_convert (parse, format, seeksegment.position,
+    if (rate >= 0)
+      start_ts = seeksegment.position;
+    else
+      start_ts = start;
+
+    if (seeksegment.stop == -1 && seeksegment.duration != -1)
+      seeksegment.stop = seeksegment.start + seeksegment.duration;
+
+    if (!gst_base_parse_convert (parse, format, start_ts,
             GST_FORMAT_BYTES, &seekpos))
       goto convert_failed;
     if (!gst_base_parse_convert (parse, format, seeksegment.stop,
@@ -4023,10 +4371,14 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
 
     if (flush) {
       if (parse->srcpad) {
+        GstEvent *fevent = gst_event_new_flush_start ();
         GST_DEBUG_OBJECT (parse, "sending flush start");
-        gst_pad_push_event (parse->srcpad, gst_event_new_flush_start ());
+
+        gst_event_set_seqnum (fevent, seqnum);
+
+        gst_pad_push_event (parse->srcpad, gst_event_ref (fevent));
         /* unlock upstream pull_range */
-        gst_pad_push_event (parse->sinkpad, gst_event_new_flush_start ());
+        gst_pad_push_event (parse->sinkpad, fevent);
       }
     } else {
       gst_pad_pause_task (parse->sinkpad);
@@ -4045,9 +4397,11 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
 
     /* prepare for streaming again */
     if (flush) {
+      GstEvent *fevent = gst_event_new_flush_stop (TRUE);
       GST_DEBUG_OBJECT (parse, "sending flush stop");
-      gst_pad_push_event (parse->srcpad, gst_event_new_flush_stop (TRUE));
-      gst_pad_push_event (parse->sinkpad, gst_event_new_flush_stop (TRUE));
+      gst_event_set_seqnum (fevent, seqnum);
+      gst_pad_push_event (parse->srcpad, gst_event_ref (fevent));
+      gst_pad_push_event (parse->sinkpad, fevent);
       gst_base_parse_clear_queues (parse);
     } else {
       /* keep track of our position */
@@ -4059,10 +4413,10 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
 
     /* store the newsegment event so it can be sent from the streaming thread. */
     /* This will be sent later in _loop() */
-    parse->priv->pending_segment = TRUE;
+    segment_event = gst_event_new_segment (&parse->segment);
+    gst_event_set_seqnum (segment_event, seqnum);
     parse->priv->pending_events =
-        g_list_prepend (parse->priv->pending_events,
-        gst_event_new_segment (&parse->segment));
+        g_list_prepend (parse->priv->pending_events, segment_event);
 
     GST_DEBUG_OBJECT (parse, "Created newseg format %d, "
         "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
@@ -4098,6 +4452,7 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
       parse->priv->seen_keyframe = FALSE;
       parse->priv->discont = TRUE;
       parse->priv->next_dts = start_ts;
+      parse->priv->next_pts = GST_CLOCK_TIME_NONE;
       parse->priv->last_dts = GST_CLOCK_TIME_NONE;
       parse->priv->last_pts = GST_CLOCK_TIME_NONE;
       parse->priv->sync_offset = seekpos;
@@ -4125,6 +4480,7 @@ gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
       seekstop = seekpos;
     new_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
         GST_SEEK_TYPE_SET, seekpos, stop_type, seekstop);
+    gst_event_set_seqnum (new_event, seqnum);
 
     /* store segment info so its precise details can be reconstructed when
      * receiving newsegment;
@@ -4157,12 +4513,6 @@ done:
   return res;
 
   /* ERRORS */
-negative_rate_pull_mode:
-  {
-    GST_FIXME_OBJECT (parse, "negative playback in pull mode needs fixing");
-    res = FALSE;
-    goto done;
-  }
 negative_rate:
   {
     GST_DEBUG_OBJECT (parse, "negative playback rates delegated upstream.");
@@ -4308,7 +4658,7 @@ gst_base_parse_change_state (GstElement * element, GstStateChange transition)
  *
  * This function should only be called from a @handle_frame implementation.
  *
- * GstBaseParse creates initial timestamps for frames by using the last
+ * #GstBaseParse creates initial timestamps for frames by using the last
  * timestamp seen in the stream before the frame starts.  In certain
  * cases, the correct timestamps will occur in the stream after the
  * start of the frame, but before the start of the actual picture data.
@@ -4323,7 +4673,6 @@ gst_base_parse_set_ts_at_offset (GstBaseParse * parse, gsize offset)
   GstClockTime pts, dts;
 
   g_return_if_fail (GST_IS_BASE_PARSE (parse));
-  g_return_if_fail (offset >= 0);
 
   pts = gst_adapter_prev_pts_at_offset (parse->priv->adapter, offset, NULL);
   dts = gst_adapter_prev_dts_at_offset (parse->priv->adapter, offset, NULL);