From: Wim Taymans Date: Mon, 7 Dec 2009 17:49:43 +0000 (+0100) Subject: oggdemux: improve keyframe seeking X-Git-Tag: 1.19.3~511^2~8911 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1ad0e4342ef5e46831ec99722ac9b6ccc6e33e48;p=platform%2Fupstream%2Fgstreamer.git oggdemux: improve keyframe seeking Improve keyframe seeking. Fix reverse playback. --- diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index e4bb3b0..0686109 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -518,35 +518,43 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) packet->granulepos); GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT, pad->current_granule); - } else if (pad->current_granule != -1) { + } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) { pad->current_granule += duration; GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT, pad->current_granule); } - /* we only push buffers after we have a valid granule. This is done so that - * we nicely skip packets without a timestamp after a seek. This is ok - * because we base or seek on the packet after the page with the smaller - * timestamp. */ - if (pad->current_granule == -1) - goto no_timestamp; - - if (pad->map.is_ogm) { - out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule); - out_duration = gst_util_uint64_scale (duration, - GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n); + if (ogg->segment.rate < 0.0 && packet->granulepos == -1) { + /* negative rates, only set timestamp on the packets with a granulepos */ + out_timestamp = -1; + out_duration = -1; + out_offset = -1; + out_offset_end = -1; } else { - out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule - duration); - out_duration = - gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule) - out_timestamp; + /* we only push buffers after we have a valid granule. This is done so that + * we nicely skip packets without a timestamp after a seek. This is ok + * because we base or seek on the packet after the page with the smaller + * timestamp. */ + if (pad->current_granule == -1) + goto no_timestamp; + + if (pad->map.is_ogm) { + out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, + pad->current_granule); + out_duration = gst_util_uint64_scale (duration, + GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n); + } else { + out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, + pad->current_granule - duration); + out_duration = + gst_ogg_stream_granule_to_time (&pad->map, + pad->current_granule) - out_timestamp; + } + out_offset_end = + gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, + pad->keyframe_granule); + out_offset = + gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); } - out_offset_end = - gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, - pad->keyframe_granule); - out_offset = - gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); } /* check for invalid buffer sizes */ @@ -1705,11 +1713,6 @@ do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin, begin = ogg->offset; /* raw offset of next page */ begintime = granuletime; - /* - if (target - begintime > GST_SECOND) - break; - */ - bisect = begin; /* *not* begin + 1 */ } else { if (bisect <= begin + 1) { @@ -1757,7 +1760,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, GstOggChain *chain = NULL; gint64 begin, end; gint64 begintime, endtime; - gint64 target; + gint64 target, keytarget; gint64 best; gint64 total; gint64 result = 0; @@ -1795,10 +1798,14 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, GST_DEBUG_OBJECT (ogg, "find keyframes"); len = pending = chain->streams->len; + /* figure out where the keyframes are */ + keytarget = target; + while (TRUE) { ogg_page og; gint64 granulepos; GstOggPad *pad; + GstClockTime keyframe_time, granule_time; ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result); GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT, @@ -1816,53 +1823,75 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, if (pad->map.is_skeleton) goto next; - /* we've seen this pad */ - if (pad->keyframe_granule != -1) - continue; - granulepos = ogg_page_granulepos (&og); if (granulepos == -1) { GST_LOG_OBJECT (ogg, "granulepos of next page is -1"); continue; } - /* store granule of this pad */ + /* in reverse we want to go past the page with the lower timestamp */ + if (segment->rate < 0.0) { + /* get time for this pad */ + granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, + granulepos); + + GST_LOG_OBJECT (ogg, + "looking at page with ts %" GST_TIME_FORMAT ", target %" + GST_TIME_FORMAT, GST_TIME_ARGS (granule_time), + GST_TIME_ARGS (target)); + if (granule_time < target) + continue; + } + + /* we've seen this pad before */ + if (pad->keyframe_granule != -1) + continue; + + /* convert granule of this pad to the granule of the keyframe */ pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos); GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT, pad->keyframe_granule); + /* get time of the keyframe */ + keyframe_time = + gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule); + GST_LOG_OBJECT (ogg, "stream %08x granule time %" GST_TIME_FORMAT, + pad->map.serialno, GST_TIME_ARGS (keyframe_time)); + + /* collect smallest value */ + if (keyframe_time != -1 && keyframe_time < keytarget) + keytarget = keyframe_time; + next: pending--; if (pending == 0) break; } - /* figure out where the keyframes are */ - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - GstClockTime granuletime; + /* for negative rates we will get to the keyframe backwards */ + if (segment->rate < 0.0) + goto done; - granuletime = - gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule); - GST_LOG_OBJECT (ogg, "stream %08x granule time %" GST_TIME_FORMAT, - pad->map.serialno, GST_TIME_ARGS (granuletime)); + if (keytarget != target) { + GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT, + GST_TIME_ARGS (keytarget)); - if (granuletime != -1 && granuletime < target) - target = granuletime; + /* last step, seek to the location of the keyframe */ + if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, + keytarget, &best)) + goto seek_error; + } else { + /* seek back to previous position */ + GST_LOG_OBJECT (ogg, "keyframe on target"); + gst_ogg_demux_seek (ogg, best); } - GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT, - GST_TIME_ARGS (target)); - - /* last step, seek to the location of the keyframe */ - if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target, - &best)) - goto seek_error; - +done: if (keyframe) { - segment->time = target; - segment->last_stop = target - begintime; + if (segment->rate > 0.0) + segment->time = keytarget; + segment->last_stop = keytarget - begintime; } *rchain = chain;