gst/: Handle and adjust new-segment events so that downstream really sees a stream...
authorJan Schmidt <thaytan@mad.scientist.com>
Fri, 25 May 2007 10:44:12 +0000 (10:44 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Fri, 25 May 2007 10:44:12 +0000 (10:44 +0000)
Original commit message from CVS:
* gst/id3demux/gstid3demux.c: (gst_id3demux_reset),
(gst_id3demux_send_new_segment), (gst_id3demux_chain),
(gst_id3demux_sink_event):
* gst/id3demux/gstid3demux.h:
* gst/apetag/gsttagdemux.c: (gst_tag_demux_reset),
(gst_tag_demux_chain), (gst_tag_demux_sink_event),
(gst_tag_demux_send_new_segment):
Handle and adjust new-segment events so that downstream really
sees a stream with the tag pieces stripped off the front and back.
Fixes strangeness in seeking when mp3 decoders use the new-segment
byte position to estimate their current playback position timestamp
and then the arriving buffers don't match up.

ChangeLog
gst/apetag/gsttagdemux.c
gst/id3demux/gstid3demux.c
gst/id3demux/gstid3demux.h

index f883165..03e761f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2007-05-25  Jan Schmidt  <thaytan@mad.scientist.com>
 
+       * gst/id3demux/gstid3demux.c: (gst_id3demux_reset),
+       (gst_id3demux_send_new_segment), (gst_id3demux_chain),
+       (gst_id3demux_sink_event):
+       * gst/id3demux/gstid3demux.h:
+       * gst/apetag/gsttagdemux.c: (gst_tag_demux_reset),
+       (gst_tag_demux_chain), (gst_tag_demux_sink_event),
+       (gst_tag_demux_send_new_segment):
+       Handle and adjust new-segment events so that downstream really
+       sees a stream with the tag pieces stripped off the front and back.
+       Fixes strangeness in seeking when mp3 decoders use the new-segment
+       byte position to estimate their current playback position timestamp
+       and then the arriving buffers don't match up.
+
+2007-05-25  Jan Schmidt  <thaytan@mad.scientist.com>
+
        * gst/autodetect/gstautoaudiosink.c: (gst_auto_audio_sink_detect):
          Don't unnecessarily perform a READY->NULL->READY transition on the
          detected audio sink when starting up. Fixes: #440127
index fde7e52..8587159 100644 (file)
@@ -107,6 +107,10 @@ struct _GstTagDemuxPrivate
   GstTagList *event_tags;
   GstTagList *parsed_tags;
   gboolean send_tag_event;
+
+  GstSegment segment;
+  gboolean need_newseg;
+  gboolean newseg_update;
 };
 
 /* Require at least 8kB of data before we attempt typefind. 
@@ -151,6 +155,7 @@ static gboolean gst_tag_demux_pad_query (GstPad * pad, GstQuery * query);
 static const GstQueryType *gst_tag_demux_get_query_types (GstPad * pad);
 static gboolean gst_tag_demux_get_upstream_size (GstTagDemux * tagdemux);
 static void gst_tag_demux_send_tag_event (GstTagDemux * tagdemux);
+static gboolean gst_tag_demux_send_new_segment (GstTagDemux * tagdemux);
 
 static void gst_tag_demux_base_init (gpointer g_class);
 static void gst_tag_demux_class_init (gpointer g_class, gpointer d);
@@ -236,6 +241,10 @@ gst_tag_demux_reset (GstTagDemux * tagdemux)
     gst_tag_list_free (tagdemux->priv->parsed_tags);
     tagdemux->priv->parsed_tags = NULL;
   }
+
+  gst_segment_init (&tagdemux->priv->segment, GST_FORMAT_UNDEFINED);
+  tagdemux->priv->need_newseg = TRUE;
+  tagdemux->priv->newseg_update = FALSE;
 }
 
 static void
@@ -548,6 +557,18 @@ gst_tag_demux_chain (GstPad * pad, GstBuffer * buf)
   demux = GST_TAG_DEMUX (GST_PAD_PARENT (pad));
   g_return_val_if_fail (GST_IS_TAG_DEMUX (demux), GST_FLOW_ERROR);
 
+  /* Update our segment last_stop info */
+  if (demux->priv->segment.format == GST_FORMAT_BYTES) {
+    if (GST_BUFFER_OFFSET_IS_VALID (buf))
+      demux->priv->segment.last_stop = GST_BUFFER_OFFSET (buf);
+    demux->priv->segment.last_stop += GST_BUFFER_SIZE (buf);
+  } else if (demux->priv->segment.format == GST_FORMAT_TIME) {
+    if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
+      demux->priv->segment.last_stop = GST_BUFFER_TIMESTAMP (buf);
+    if (GST_BUFFER_DURATION_IS_VALID (buf))
+      demux->priv->segment.last_stop += GST_BUFFER_DURATION (buf);
+  }
+
   if (demux->priv->collect == NULL) {
     demux->priv->collect = buf;
   } else {
@@ -647,6 +668,15 @@ gst_tag_demux_chain (GstPad * pad, GstBuffer * buf)
         outbuf = gst_buffer_make_metadata_writable (outbuf);
         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (demux->priv->srcpad));
 
+        /* Might need a new segment before the buffer */
+        if (demux->priv->need_newseg) {
+          if (!gst_tag_demux_send_new_segment (demux)) {
+            GST_DEBUG_OBJECT (demux, "Failed to send new segment event");
+            goto error;
+          }
+          demux->priv->need_newseg = FALSE;
+        }
+
         return gst_pad_push (demux->priv->srcpad, outbuf);
       }
     }
@@ -675,6 +705,22 @@ gst_tag_demux_sink_event (GstPad * pad, GstEvent * event)
       }
       ret = gst_pad_event_default (pad, event);
       break;
+    case GST_EVENT_NEWSEGMENT:{
+      gboolean update;
+      gdouble rate, arate;
+      GstFormat format;
+      gint64 start, stop, position;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate,
+          &format, &start, &stop, &position);
+
+      gst_segment_set_newsegment_full (&demux->priv->segment, update, rate,
+          arate, format, start, stop, position);
+      demux->priv->newseg_update = update;
+      demux->priv->need_newseg = TRUE;
+      ret = TRUE;
+      break;
+    }
     default:
       ret = gst_pad_event_default (pad, event);
       break;
@@ -1343,3 +1389,83 @@ gst_tag_demux_send_tag_event (GstTagDemux * demux)
     gst_pad_push_event (demux->priv->srcpad, event);
   }
 }
+
+static gboolean
+gst_tag_demux_send_new_segment (GstTagDemux * tagdemux)
+{
+  GstEvent *event;
+  gint64 start, stop, position;
+  GstSegment *seg = &tagdemux->priv->segment;
+
+  if (seg->format == GST_FORMAT_UNDEFINED) {
+    GST_LOG_OBJECT (tagdemux,
+        "No new segment received before first buffer. Using default");
+    gst_segment_set_newsegment (seg, FALSE, 1.0,
+        GST_FORMAT_BYTES, tagdemux->priv->strip_start, -1,
+        tagdemux->priv->strip_start);
+  }
+
+  /* Can't adjust segments in non-BYTES formats */
+  if (tagdemux->priv->segment.format != GST_FORMAT_BYTES) {
+    event = gst_event_new_new_segment_full (tagdemux->priv->newseg_update,
+        seg->rate, seg->applied_rate, seg->format, seg->start,
+        seg->stop, seg->time);
+    return gst_pad_push_event (tagdemux->priv->srcpad, event);
+  }
+
+  start = seg->start;
+  stop = seg->stop;
+  position = seg->time;
+
+  g_return_val_if_fail (start != -1, FALSE);
+  g_return_val_if_fail (position != -1, FALSE);
+
+  if (tagdemux->priv->strip_end > 0) {
+    if (gst_tag_demux_get_upstream_size (tagdemux)) {
+      guint64 v1tag_offset =
+          tagdemux->priv->upstream_size - tagdemux->priv->strip_end;
+
+      if (start >= v1tag_offset) {
+        /* Segment is completely within the ID3v1 tag, output an open-ended
+         * segment, even though all the buffers will get trimmed away */
+        start = v1tag_offset;
+        stop = -1;
+      }
+
+      if (stop != -1 && stop >= v1tag_offset) {
+        GST_DEBUG_OBJECT (tagdemux,
+            "Segment crosses the ID3v1 tag. Trimming end");
+        stop = v1tag_offset;
+      }
+    }
+  }
+
+  if (tagdemux->priv->strip_start > 0) {
+    if (start > tagdemux->priv->strip_start)
+      start -= tagdemux->priv->strip_start;
+    else
+      start = 0;
+
+    if (position > tagdemux->priv->strip_start)
+      position -= tagdemux->priv->strip_start;
+    else
+      position = 0;
+
+    if (stop != -1) {
+      if (stop > tagdemux->priv->strip_start)
+        stop -= tagdemux->priv->strip_start;
+      else
+        stop = 0;
+    }
+  }
+
+  GST_DEBUG_OBJECT (tagdemux,
+      "Sending new segment update %d, rate %g, format %d, "
+      "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %"
+      G_GINT64_FORMAT, tagdemux->priv->newseg_update, seg->rate, seg->format,
+      start, stop, position);
+
+  event = gst_event_new_new_segment_full (tagdemux->priv->newseg_update,
+      seg->rate, seg->applied_rate, seg->format, start, stop, position);
+  return gst_pad_push_event (tagdemux->priv->srcpad, event);
+}
index 6fec9db..77bf0e4 100644 (file)
@@ -117,6 +117,7 @@ static gboolean gst_id3demux_pad_query (GstPad * pad, GstQuery * query);
 static const GstQueryType *gst_id3demux_get_query_types (GstPad * pad);
 static gboolean id3demux_get_upstream_size (GstID3Demux * id3demux);
 static void gst_id3demux_send_tag_event (GstID3Demux * id3demux);
+static gboolean gst_id3demux_send_new_segment (GstID3Demux * id3demux);
 
 static GstElementClass *parent_class = NULL;
 
@@ -206,6 +207,88 @@ gst_id3demux_reset (GstID3Demux * id3demux)
     id3demux->parsed_tags = NULL;
   }
 
+  gst_segment_init (&id3demux->segment, GST_FORMAT_UNDEFINED);
+  id3demux->need_newseg = TRUE;
+  id3demux->newseg_update = FALSE;
+}
+
+static gboolean
+gst_id3demux_send_new_segment (GstID3Demux * id3demux)
+{
+  GstEvent *event;
+  gint64 start, stop, position;
+  GstSegment *seg = &id3demux->segment;
+
+  if (seg->format == GST_FORMAT_UNDEFINED) {
+    GST_LOG_OBJECT (id3demux,
+        "No new segment received before first buffer. Using default");
+    gst_segment_set_newsegment (seg, FALSE, 1.0,
+        GST_FORMAT_BYTES, id3demux->strip_start, -1, id3demux->strip_start);
+  }
+
+  /* Can't adjust segments in non-BYTES formats */
+  if (id3demux->segment.format != GST_FORMAT_BYTES) {
+    event = gst_event_new_new_segment_full (id3demux->newseg_update,
+        seg->rate, seg->applied_rate, seg->format, seg->start,
+        seg->stop, seg->time);
+    return gst_pad_push_event (id3demux->srcpad, event);
+  }
+
+  start = seg->start;
+  stop = seg->stop;
+  position = seg->time;
+
+  g_return_val_if_fail (start != -1, FALSE);
+  g_return_val_if_fail (position != -1, FALSE);
+
+  if (id3demux->strip_end > 0) {
+    if (id3demux_get_upstream_size (id3demux)) {
+      guint64 v1tag_offset = id3demux->upstream_size - id3demux->strip_end;
+
+      if (start >= v1tag_offset) {
+        /* Segment is completely within the ID3v1 tag, output an open-ended
+         * segment, even though all the buffers will get trimmed away */
+        start = v1tag_offset;
+        stop = -1;
+      }
+
+      if (stop != -1 && stop >= v1tag_offset) {
+        GST_DEBUG_OBJECT (id3demux,
+            "Segment crosses the ID3v1 tag. Trimming end");
+        stop = v1tag_offset;
+      }
+    }
+  }
+
+  if (id3demux->strip_start > 0) {
+    if (start > id3demux->strip_start)
+      start -= id3demux->strip_start;
+    else
+      start = 0;
+
+    if (position > id3demux->strip_start)
+      position -= id3demux->strip_start;
+    else
+      position = 0;
+
+    if (stop != -1) {
+      if (stop > id3demux->strip_start)
+        stop -= id3demux->strip_start;
+      else
+        stop = 0;
+    }
+  }
+
+  GST_DEBUG_OBJECT (id3demux,
+      "Sending new segment update %d, rate %g, format %d, "
+      "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %"
+      G_GINT64_FORMAT, id3demux->newseg_update, seg->rate, seg->format,
+      start, stop, position);
+
+  event = gst_event_new_new_segment_full (id3demux->newseg_update,
+      seg->rate, seg->applied_rate, seg->format, start, stop, position);
+
+  return gst_pad_push_event (id3demux->srcpad, event);
 }
 
 static void
@@ -409,6 +492,18 @@ gst_id3demux_chain (GstPad * pad, GstBuffer * buf)
   id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
   g_return_val_if_fail (GST_IS_ID3DEMUX (id3demux), GST_FLOW_ERROR);
 
+  /* Update our segment last_stop info */
+  if (id3demux->segment.format == GST_FORMAT_BYTES) {
+    if (GST_BUFFER_OFFSET_IS_VALID (buf))
+      id3demux->segment.last_stop = GST_BUFFER_OFFSET (buf);
+    id3demux->segment.last_stop += GST_BUFFER_SIZE (buf);
+  } else if (id3demux->segment.format == GST_FORMAT_TIME) {
+    if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
+      id3demux->segment.last_stop = GST_BUFFER_TIMESTAMP (buf);
+    if (GST_BUFFER_DURATION_IS_VALID (buf))
+      id3demux->segment.last_stop += GST_BUFFER_DURATION (buf);
+  }
+
   if (id3demux->collect == NULL) {
     id3demux->collect = buf;
   } else {
@@ -549,6 +644,15 @@ gst_id3demux_chain (GstPad * pad, GstBuffer * buf)
         outbuf = gst_buffer_make_metadata_writable (outbuf);
         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (id3demux->srcpad));
 
+        /* Might need a new segment before the buffer */
+        if (id3demux->need_newseg) {
+          if (!gst_id3demux_send_new_segment (id3demux)) {
+            GST_DEBUG_OBJECT (id3demux, "Failed to send new segment event");
+            goto error;
+          }
+          id3demux->need_newseg = FALSE;
+        }
+
         return gst_pad_push (id3demux->srcpad, outbuf);
       }
     }
@@ -577,6 +681,22 @@ gst_id3demux_sink_event (GstPad * pad, GstEvent * event)
       }
       ret = gst_pad_event_default (pad, event);
       break;
+    case GST_EVENT_NEWSEGMENT:{
+      gboolean update;
+      gdouble rate, arate;
+      GstFormat format;
+      gint64 start, stop, position;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate,
+          &format, &start, &stop, &position);
+
+      gst_segment_set_newsegment_full (&demux->segment, update, rate,
+          arate, format, start, stop, position);
+      demux->newseg_update = update;
+      demux->need_newseg = TRUE;
+      ret = TRUE;
+      break;
+    }
     default:
       ret = gst_pad_event_default (pad, event);
       break;
index f8246ad..7f9e18f 100644 (file)
@@ -65,6 +65,10 @@ struct _GstID3Demux
   GstTagList *event_tags;
   GstTagList *parsed_tags;
   gboolean send_tag_event;
+
+  GstSegment segment;
+  gboolean need_newseg;
+  gboolean newseg_update;
 };
 
 struct _GstID3DemuxClass