mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-27 09:38:17 +00:00
oggdemux: improve keyframe seeking
Improve keyframe seeking. Fix reverse playback.
This commit is contained in:
parent
c53cd385e3
commit
1ad0e4342e
1 changed files with 83 additions and 54 deletions
|
@ -518,35 +518,43 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
|
||||||
packet->granulepos);
|
packet->granulepos);
|
||||||
GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
|
GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
|
||||||
pad->current_granule);
|
pad->current_granule);
|
||||||
} else if (pad->current_granule != -1) {
|
} else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
|
||||||
pad->current_granule += duration;
|
pad->current_granule += duration;
|
||||||
GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
|
GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
|
||||||
pad->current_granule);
|
pad->current_granule);
|
||||||
}
|
}
|
||||||
/* we only push buffers after we have a valid granule. This is done so that
|
if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
|
||||||
* we nicely skip packets without a timestamp after a seek. This is ok
|
/* negative rates, only set timestamp on the packets with a granulepos */
|
||||||
* because we base or seek on the packet after the page with the smaller
|
out_timestamp = -1;
|
||||||
* timestamp. */
|
out_duration = -1;
|
||||||
if (pad->current_granule == -1)
|
out_offset = -1;
|
||||||
goto no_timestamp;
|
out_offset_end = -1;
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
|
/* we only push buffers after we have a valid granule. This is done so that
|
||||||
pad->current_granule - duration);
|
* we nicely skip packets without a timestamp after a seek. This is ok
|
||||||
out_duration =
|
* because we base or seek on the packet after the page with the smaller
|
||||||
gst_ogg_stream_granule_to_time (&pad->map,
|
* timestamp. */
|
||||||
pad->current_granule) - out_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 */
|
/* 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 */
|
begin = ogg->offset; /* raw offset of next page */
|
||||||
begintime = granuletime;
|
begintime = granuletime;
|
||||||
|
|
||||||
/*
|
|
||||||
if (target - begintime > GST_SECOND)
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
|
|
||||||
bisect = begin; /* *not* begin + 1 */
|
bisect = begin; /* *not* begin + 1 */
|
||||||
} else {
|
} else {
|
||||||
if (bisect <= begin + 1) {
|
if (bisect <= begin + 1) {
|
||||||
|
@ -1757,7 +1760,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
|
||||||
GstOggChain *chain = NULL;
|
GstOggChain *chain = NULL;
|
||||||
gint64 begin, end;
|
gint64 begin, end;
|
||||||
gint64 begintime, endtime;
|
gint64 begintime, endtime;
|
||||||
gint64 target;
|
gint64 target, keytarget;
|
||||||
gint64 best;
|
gint64 best;
|
||||||
gint64 total;
|
gint64 total;
|
||||||
gint64 result = 0;
|
gint64 result = 0;
|
||||||
|
@ -1795,10 +1798,14 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
|
||||||
GST_DEBUG_OBJECT (ogg, "find keyframes");
|
GST_DEBUG_OBJECT (ogg, "find keyframes");
|
||||||
len = pending = chain->streams->len;
|
len = pending = chain->streams->len;
|
||||||
|
|
||||||
|
/* figure out where the keyframes are */
|
||||||
|
keytarget = target;
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
ogg_page og;
|
ogg_page og;
|
||||||
gint64 granulepos;
|
gint64 granulepos;
|
||||||
GstOggPad *pad;
|
GstOggPad *pad;
|
||||||
|
GstClockTime keyframe_time, granule_time;
|
||||||
|
|
||||||
ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
|
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,
|
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)
|
if (pad->map.is_skeleton)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/* we've seen this pad */
|
|
||||||
if (pad->keyframe_granule != -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
granulepos = ogg_page_granulepos (&og);
|
granulepos = ogg_page_granulepos (&og);
|
||||||
if (granulepos == -1) {
|
if (granulepos == -1) {
|
||||||
GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
|
GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
|
||||||
continue;
|
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,
|
pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map,
|
||||||
granulepos);
|
granulepos);
|
||||||
GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
|
GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
|
||||||
pad->keyframe_granule);
|
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:
|
next:
|
||||||
pending--;
|
pending--;
|
||||||
if (pending == 0)
|
if (pending == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* figure out where the keyframes are */
|
/* for negative rates we will get to the keyframe backwards */
|
||||||
for (i = 0; i < chain->streams->len; i++) {
|
if (segment->rate < 0.0)
|
||||||
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
|
goto done;
|
||||||
GstClockTime granuletime;
|
|
||||||
|
|
||||||
granuletime =
|
if (keytarget != target) {
|
||||||
gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
|
GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
|
||||||
GST_LOG_OBJECT (ogg, "stream %08x granule time %" GST_TIME_FORMAT,
|
GST_TIME_ARGS (keytarget));
|
||||||
pad->map.serialno, GST_TIME_ARGS (granuletime));
|
|
||||||
|
|
||||||
if (granuletime != -1 && granuletime < target)
|
/* last step, seek to the location of the keyframe */
|
||||||
target = granuletime;
|
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,
|
done:
|
||||||
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;
|
|
||||||
|
|
||||||
if (keyframe) {
|
if (keyframe) {
|
||||||
segment->time = target;
|
if (segment->rate > 0.0)
|
||||||
segment->last_stop = target - begintime;
|
segment->time = keytarget;
|
||||||
|
segment->last_stop = keytarget - begintime;
|
||||||
}
|
}
|
||||||
|
|
||||||
*rchain = chain;
|
*rchain = chain;
|
||||||
|
|
Loading…
Reference in a new issue