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
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.
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);
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
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 {
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);
}
}
}
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;
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);
+}
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;
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
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 {
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);
}
}
}
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;
GstTagList *event_tags;
GstTagList *parsed_tags;
gboolean send_tag_event;
+
+ GstSegment segment;
+ gboolean need_newseg;
+ gboolean newseg_update;
};
struct _GstID3DemuxClass