From 4a7ecfb81482c873dc7b97c65dc601abdfbdc9fa Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 25 May 2007 10:44:12 +0000 Subject: [PATCH] gst/: Handle and adjust new-segment events so that downstream really sees a stream with the tag pieces stripped off t... 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 | 15 +++++ gst/apetag/gsttagdemux.c | 126 +++++++++++++++++++++++++++++++++++++ gst/id3demux/gstid3demux.c | 120 +++++++++++++++++++++++++++++++++++ gst/id3demux/gstid3demux.h | 4 ++ 4 files changed, 265 insertions(+) diff --git a/ChangeLog b/ChangeLog index f883165de..03e761f54 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2007-05-25 Jan Schmidt + + * 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 * gst/autodetect/gstautoaudiosink.c: (gst_auto_audio_sink_detect): diff --git a/gst/apetag/gsttagdemux.c b/gst/apetag/gsttagdemux.c index fde7e52a7..8587159ba 100644 --- a/gst/apetag/gsttagdemux.c +++ b/gst/apetag/gsttagdemux.c @@ -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); +} diff --git a/gst/id3demux/gstid3demux.c b/gst/id3demux/gstid3demux.c index 6fec9dbdb..77bf0e46d 100644 --- a/gst/id3demux/gstid3demux.c +++ b/gst/id3demux/gstid3demux.c @@ -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; diff --git a/gst/id3demux/gstid3demux.h b/gst/id3demux/gstid3demux.h index f8246ad41..7f9e18fdc 100644 --- a/gst/id3demux/gstid3demux.h +++ b/gst/id3demux/gstid3demux.h @@ -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 -- 2.34.1