baseaudiodecoder: add limited legacy seeking support

This commit is contained in:
Mark Nauwelaerts 2011-03-17 12:09:47 +01:00 committed by Tim-Philipp Müller
parent 0c33df6540
commit 79b41f59f6
2 changed files with 159 additions and 17 deletions

View file

@ -119,9 +119,11 @@
* output buffer aggregation which may help to redue large(r) numbers of
* small(er) buffers being pushed and processed downstream.
*
* On the other hand, it should be noted that baseclass does not handle seeking,
* nor are subclasses expected to do so, as this should rather be left to
* upstream demuxer, parser or alike.
* On the other hand, it should be noted that baseclass only provides limited
* seeking support (upon explicit subclass request), as full-fledged support
* should rather be left to upstream demuxer, parser or alike. This simple
* approach caters for seeking and duration reporting using estimated input
* bitrates.
*
* Things that subclass need to take care of:
* <itemizedlist>
@ -1209,9 +1211,24 @@ gst_base_audio_decoder_chain (GstPad * pad, GstBuffer * buffer)
GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
gint64 samples, ts;
/* track present position */
ts = dec->priv->base_ts;
samples = dec->priv->samples;
GST_DEBUG_OBJECT (dec, "handling discont");
gst_base_audio_decoder_flush (dec, FALSE);
dec->priv->discont = TRUE;
/* buffer may claim DISCONT loudly, if it can't tell us where we are now,
* we'll stick to where we were ...
* Particularly useful/needed for upstream BYTE based */
if (dec->segment.rate > 0.0 && !GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
GST_DEBUG_OBJECT (dec, "... but restoring previous ts tracking");
dec->priv->base_ts = ts;
dec->priv->samples = samples;
}
}
if (dec->segment.rate > 0.0)
@ -1222,6 +1239,15 @@ gst_base_audio_decoder_chain (GstPad * pad, GstBuffer * buffer)
return ret;
}
/* perform upstream byte <-> time conversion (duration, seeking)
* if subclass allows and if enough data for moderately decent conversion */
static inline gboolean
gst_base_audio_decoder_do_byte (GstBaseAudioDecoder * dec)
{
return dec->ctx->do_byte_time && dec->ctx->state.bpf &&
dec->ctx->state.rate <= dec->priv->samples_out;
}
static gboolean
gst_base_audio_decoder_sink_eventfunc (GstBaseAudioDecoder * dec,
GstEvent * event)
@ -1242,19 +1268,37 @@ gst_base_audio_decoder_sink_eventfunc (GstBaseAudioDecoder * dec,
if (format == GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (dec, "received TIME NEW_SEGMENT %" GST_TIME_FORMAT
" -- %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT
", accum %" GST_TIME_FORMAT,
GST_TIME_ARGS (dec->segment.start),
GST_TIME_ARGS (dec->segment.stop),
GST_TIME_ARGS (dec->segment.time),
GST_TIME_ARGS (dec->segment.accum));
", rate %g, applied_rate %g",
GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time),
rate, arate);
} else {
GstFormat dformat = GST_FORMAT_TIME;
GST_DEBUG_OBJECT (dec, "received NEW_SEGMENT %" G_GINT64_FORMAT
" -- %" G_GINT64_FORMAT ", time %" G_GINT64_FORMAT
", accum %" G_GINT64_FORMAT,
dec->segment.start, dec->segment.stop,
dec->segment.time, dec->segment.accum);
GST_DEBUG_OBJECT (dec, "unsupported format; ignoring");
break;
", rate %g, applied_rate %g", start, stop, time, rate, arate);
/* handle newsegment resulting from legacy simple seeking */
/* note that we need to convert this whether or not enough data
* to handle initial newsegment */
if (dec->ctx->do_byte_time &&
gst_pad_query_convert (dec->sinkpad, GST_FORMAT_BYTES, start,
&dformat, &start)) {
/* best attempt convert */
/* as these are only estimates, stop is kept open-ended to avoid
* premature cutting */
GST_DEBUG_OBJECT (dec, "converted to TIME start %" GST_TIME_FORMAT,
GST_TIME_ARGS (start));
format = GST_FORMAT_TIME;
time = start;
stop = GST_CLOCK_TIME_NONE;
/* replace event */
gst_event_unref (event);
event = gst_event_new_new_segment_full (update, rate, arate,
GST_FORMAT_TIME, start, stop, time);
} else {
GST_DEBUG_OBJECT (dec, "unsupported format; ignoring");
break;
}
}
/* finish current segment */
@ -1281,11 +1325,21 @@ gst_base_audio_decoder_sink_eventfunc (GstBaseAudioDecoder * dec,
} else {
/* prepare for next one */
gst_base_audio_decoder_flush (dec, FALSE);
/* and that's where we time from,
* in case upstream does not come up with anything better
* (e.g. upstream BYTE) */
if (format != GST_FORMAT_TIME) {
dec->priv->base_ts = start;
dec->priv->samples = 0;
}
}
/* and follow along with segment */
gst_segment_set_newsegment_full (&dec->segment, update, rate, arate,
format, start, stop, time);
gst_pad_push_event (dec->srcpad, event);
handled = TRUE;
break;
}
@ -1337,6 +1391,62 @@ gst_base_audio_decoder_sink_event (GstPad * pad, GstEvent * event)
return ret;
}
static gboolean
gst_base_audio_decoder_do_seek (GstBaseAudioDecoder * dec, GstEvent * event)
{
GstSeekFlags flags;
GstSeekType start_type, end_type;
GstFormat format;
gdouble rate;
gint64 start, start_time, end_time;
GstSegment seek_segment;
gst_event_parse_seek (event, &rate, &format, &flags, &start_type,
&start_time, &end_type, &end_time);
/* we'll handle plain open-ended flushing seeks with the simple approach */
if (rate != 1.0) {
GST_DEBUG_OBJECT (dec, "unsupported seek: rate");
return FALSE;
}
if (start_type != GST_SEEK_TYPE_SET) {
GST_DEBUG_OBJECT (dec, "unsupported seek: start time");
return FALSE;
}
if (end_type != GST_SEEK_TYPE_NONE ||
(end_type == GST_SEEK_TYPE_SET && end_time != GST_CLOCK_TIME_NONE)) {
GST_DEBUG_OBJECT (dec, "unsupported seek: end time");
return FALSE;
}
if (!(flags & GST_SEEK_FLAG_FLUSH)) {
GST_DEBUG_OBJECT (dec, "unsupported seek: not flushing");
return FALSE;
}
memcpy (&seek_segment, &dec->segment, sizeof (seek_segment));
gst_segment_set_seek (&seek_segment, rate, format, flags, start_type,
start_time, end_type, end_time, NULL);
start_time = seek_segment.last_stop;
format = GST_FORMAT_BYTES;
if (!gst_pad_query_convert (dec->sinkpad, GST_FORMAT_TIME, start_time,
&format, &start)) {
GST_DEBUG_OBJECT (dec, "conversion failed");
return FALSE;
}
event = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags,
GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE, -1);
GST_DEBUG_OBJECT (dec, "seeking to %" GST_TIME_FORMAT " at byte offset %"
G_GINT64_FORMAT, GST_TIME_ARGS (start_time), start);
return gst_pad_push_event (dec->sinkpad, event);
}
static gboolean
gst_base_audio_decoder_src_event (GstPad * pad, GstEvent * event)
{
@ -1366,12 +1476,17 @@ gst_base_audio_decoder_src_event (GstPad * pad, GstEvent * event)
seqnum = gst_event_get_seqnum (event);
/* upstream gets a chance first */
res = gst_pad_push_event (dec->sinkpad, event);
/* if upstream fails for a non-time seek, maybe we can help */
if (G_LIKELY (format == GST_FORMAT_TIME || res))
if ((res = gst_pad_push_event (dec->sinkpad, event)))
break;
/* if upstream fails for a time seek, maybe we can help if allowed */
if (format == GST_FORMAT_TIME) {
if (gst_base_audio_decoder_do_byte (dec))
res = gst_base_audio_decoder_do_seek (dec, event);
break;
}
/* ... though a non-time seek can be aided as well */
/* First bring the requested format to time */
tformat = GST_FORMAT_TIME;
if (!(res = gst_pad_query_convert (pad, format, cur, &tformat, &tcur)))
@ -1473,6 +1588,32 @@ gst_base_audio_decoder_src_query (GstPad * pad, GstQuery * query)
GST_LOG_OBJECT (dec, "handling query: %" GST_PTR_FORMAT, query);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_DURATION:
{
GstFormat format;
/* upstream in any case */
if ((res = gst_pad_query_default (pad, query)))
break;
gst_query_parse_duration (query, &format, NULL);
/* try answering TIME by converting from BYTE if subclass allows */
if (format == GST_FORMAT_TIME && gst_base_audio_decoder_do_byte (dec)) {
gint64 value;
format = GST_FORMAT_BYTES;
if (gst_pad_query_peer_duration (dec->sinkpad, &format, &value)) {
GST_LOG_OBJECT (dec, "upstream size %" G_GINT64_FORMAT, value);
format = GST_FORMAT_TIME;
if (gst_pad_query_convert (dec->sinkpad, GST_FORMAT_BYTES, value,
&format, &value)) {
gst_query_set_duration (query, GST_FORMAT_TIME, value);
res = TRUE;
}
}
}
break;
}
case GST_QUERY_POSITION:
{
GstFormat format;

View file

@ -110,6 +110,7 @@ struct _GstBaseAudioDecoderContext {
/* output */
gboolean do_plc;
gboolean do_byte_time;
/* MT-protected (with LOCK) */
GstClockTime min_latency;
GstClockTime max_latency;