From 0141934eec77c91add033886a05ad26688456a3c Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 14 Oct 2009 13:09:03 +0200 Subject: avidemux: use GstIndex for (limited) seeking in push mode ... but disable this for now. Although it basically works fine, user experience might be shaky (depending on taste), since there is no keyframe info in push mode. --- gst/avi/gstavidemux.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++- gst/avi/gstavidemux.h | 1 + 2 files changed, 263 insertions(+), 5 deletions(-) diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 844d0caf..419c55dd 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -90,6 +90,8 @@ static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format, static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment); static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event); +static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, + GstEvent * event); static void gst_avi_demux_loop (GstPad * pad); static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad); static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad, @@ -555,7 +557,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) gboolean seekable = TRUE; if (avi->streaming) { - seekable = FALSE; + seekable = avi->seekable; } gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, @@ -598,6 +600,54 @@ gst_avi_demux_get_event_mask (GstPad * pad) } #endif +static guint64 +gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before) +{ + GstAviStream *stream; + GstIndexEntry *entry; + gint i; + gint64 val, min = offset; + + for (i = 0; i < avi->num_streams; i++) { + stream = &avi->stream[i]; + + entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id, + before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER, + GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset); + + if (before) { + if (entry) { + GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %" + G_GUINT64_FORMAT, i, val); + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val); + if (val < min) + min = val; + } + continue; + } + + if (!entry) { + GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start"); + stream->current_entry = 0; + stream->current_total = 0; + continue; + } + + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val); + GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, + i, val); + + gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val); + stream->current_total = val; + gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val); + stream->current_entry = val; + } + + return min; +} + +#define GST_AVI_SEEK_PUSH_DISPLACE (4 * GST_SECOND) + static gboolean gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) { @@ -609,9 +659,93 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: - /* Drop NEWSEGMENT events, new ones are generated later */ + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time, offset = 0; + gboolean update; + GstSegment segment; + GstIndexEntry *entry; + + /* some debug output */ + gst_segment_init (&segment, GST_FORMAT_UNDEFINED); + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + gst_segment_set_newsegment_full (&segment, update, rate, arate, format, + start, stop, time); + GST_DEBUG_OBJECT (avi, + "received format %d newsegment %" GST_SEGMENT_FORMAT, format, + &segment); + + /* chain will send initial newsegment after pads have been added */ + if (avi->state != GST_AVI_DEMUX_MOVI) { + GST_DEBUG_OBJECT (avi, "still starting, eating event"); + goto exit; + } + + /* we only expect a BYTE segment, e.g. following a seek */ + if (format != GST_FORMAT_BYTES) { + GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring"); + goto exit; + } + + if (!avi->element_index) { + GST_WARNING_OBJECT (avi, "no index data, forcing EOS"); + goto eos; + } + + /* Let's check if we have an index entry for this position */ + entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id, + GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE, + GST_FORMAT_BYTES, start); + + /* we can not go where we have not yet been before ... */ + if (!entry) { + GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS"); + goto eos; + } + + offset = start; + gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &start); + stop = GST_CLOCK_TIME_NONE; + + /* compensate for slack */ + if (time) + time += GST_AVI_SEEK_PUSH_DISPLACE; + + /* set up segment and send downstream */ + gst_segment_set_newsegment_full (&avi->segment, update, rate, arate, + GST_FORMAT_TIME, time, stop, time); + GST_DEBUG_OBJECT (avi, "Pushing newseg update %d, rate %g, " + "applied rate %g, format %d, start %" G_GINT64_FORMAT ", " + "stop %" G_GINT64_FORMAT, update, rate, arate, GST_FORMAT_TIME, + time, stop); + gst_avi_demux_push_event (avi, + gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, + time, stop, time)); + + GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT, start); + + /* adjust state for streaming thread accordingly */ + avi->offset = offset; + gst_avi_demux_seek_streams (avi, offset, FALSE); + + /* set up streaming thread */ + avi->offset = offset; + avi->todrop = start - offset; + + exit: + /* in any case, clear leftover in current segment, if any */ + gst_adapter_clear (avi->adapter); gst_event_unref (event); + res = TRUE; break; + eos: + /* set up for EOS */ + avi->have_eos = TRUE; + goto exit; + } case GST_EVENT_EOS: { if (avi->state != GST_AVI_DEMUX_MOVI) { @@ -648,10 +782,10 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) /* handle seeking only in pull mode */ if (!avi->streaming) { res = gst_avi_demux_handle_seek (avi, pad, event); - gst_event_unref (event); } else { - res = gst_pad_event_default (pad, event); + res = gst_avi_demux_handle_seek_push (avi, pad, event); } + gst_event_unref (event); break; case GST_EVENT_QOS: case GST_EVENT_NAVIGATION: @@ -3490,6 +3624,104 @@ no_format: } } +static gboolean +gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, + GstEvent * event) +{ + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gboolean res; + gint64 byte_cur; + gint64 time = 0; + GstIndexEntry *entry; + + GST_DEBUG_OBJECT (avi, "doing push-based seek"); + + gst_event_parse_seek (event, &rate, &format, &flags, + &cur_type, &cur, &stop_type, &stop); + + if (stop_type != GST_SEEK_TYPE_NONE) + goto unsupported_seek; + stop = -1; + + /* only forward streaming and seeking is possible */ + if (rate <= 0) + goto unsupported_seek; + + /* only TIME */ + if (format != GST_FORMAT_TIME) + goto unsupported_format; + + /* not really advisable otherwise */ + if ((flags & GST_SEEK_FLAG_FLUSH) == 0) + goto unsupported_seek; + + /* should have index, let's check anyway */ + if (!avi->element_index) + goto abort_seek; + + /* find reasonable corresponding BYTE position + * note that we have no keyframe info in push mode whatsoever, + * so we can not cater for related stuff in any way */ + + /* some slack aiming for a keyframe */ + if (cur < GST_AVI_SEEK_PUSH_DISPLACE) + cur = 0; + else + cur -= GST_AVI_SEEK_PUSH_DISPLACE; + + entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id, + GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_TIME, cur); + + if (!entry) + goto abort_seek; + + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &byte_cur); + gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); + + GST_DEBUG_OBJECT (avi, "found index entry for %" GST_TIME_FORMAT + " at %" GST_TIME_FORMAT ", located at offset %" G_GINT64_FORMAT, + GST_TIME_ARGS (cur), GST_TIME_ARGS (time), byte_cur); + + /* adjust offset to be in each stream's region */ + byte_cur = gst_avi_demux_seek_streams (avi, byte_cur, TRUE); + + /* let's not try push seeking if target and entry are too far apart */ + if (cur - time > 30 * GST_SECOND) + goto abort_seek; + + GST_DEBUG_OBJECT (avi, "Pushing BYTE seek rate %g, " + "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur, + stop); + /* BYTE seek event */ + event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, + stop_type, stop); + res = gst_pad_push_event (avi->sinkpad, event); + + return res; + + /* ERRORS */ +abort_seek: + { + GST_DEBUG_OBJECT (avi, "could not determine byte position to seek to, " + "seek aborted."); + return FALSE; + } +unsupported_seek: + { + GST_DEBUG_OBJECT (avi, "unsupported seek, seek aborted."); + return FALSE; + } +unsupported_format: + { + GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted."); + return FALSE; + } +} + /* * Helper for gst_avi_demux_invert() */ @@ -3907,7 +4139,20 @@ gst_avi_demux_stream_data (GstAviDemux * avi) if (G_UNLIKELY (avi->have_eos)) { /* Clean adapter, we're done */ gst_adapter_clear (avi->adapter); - return res; + return GST_FLOW_UNEXPECTED; + } + + if (G_UNLIKELY (avi->todrop)) { + guint drop; + + if ((drop = gst_adapter_available (avi->adapter))) { + if (drop > avi->todrop) + drop = avi->todrop; + GST_DEBUG_OBJECT (avi, "Dropping %d bytes", drop); + gst_adapter_flush (avi->adapter, drop); + avi->todrop -= drop; + avi->offset += drop; + } } /* Iterate until need more data, so adapter won't grow too much */ @@ -4298,6 +4543,18 @@ gst_avi_demux_activate_push (GstPad * pad, gboolean active) if (active) { GST_DEBUG ("avi: activating push/chain function"); avi->streaming = TRUE; +#if 0 + /* create index for some push based seeking if not provided */ + GST_OBJECT_LOCK (avi); + if (!avi->element_index) { + GST_DEBUG_OBJECT (avi, "creating index"); + avi->element_index = gst_index_factory_make ("memindex"); + } + GST_OBJECT_UNLOCK (avi); + /* object lock might be taken again */ + gst_index_get_writer_id (avi->element_index, GST_OBJECT (avi), + &avi->index_id); +#endif } else { GST_DEBUG ("avi: deactivating push/chain function"); } diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h index 56a55bb2..7fcb7397 100644 --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -167,6 +167,7 @@ typedef struct _GstAviDemux { gboolean streaming; gboolean have_eos; GstAdapter *adapter; + guint todrop; /* some stream info for length */ gst_riff_avih *avih; -- cgit