asfdemux: implement pull mode seek snap flags when an index is present

Also ensure we get a proper EOS when seeking past the end of the
file, or (when the snap after flag is set) between the last keyframe
and the end of the file.
This commit is contained in:
Vincent Penquerc'h 2012-05-10 10:11:44 +01:00
parent 649bd92cd2
commit cd8a5fb939

View file

@ -439,20 +439,43 @@ gst_asf_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
static gboolean static gboolean
gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet, gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet,
GstClockTime seek_time, GstClockTime * p_idx_time, guint * speed) GstClockTime seek_time, GstClockTime * p_idx_time, guint * speed,
gboolean next, gboolean * eos)
{ {
GstClockTime idx_time; GstClockTime idx_time;
guint idx; guint idx;
if (eos)
*eos = FALSE;
if (G_UNLIKELY (demux->sidx_num_entries == 0 || demux->sidx_interval == 0)) if (G_UNLIKELY (demux->sidx_num_entries == 0 || demux->sidx_interval == 0))
return FALSE; return FALSE;
idx = (guint) ((seek_time + demux->preroll) / demux->sidx_interval); idx = (guint) ((seek_time + demux->preroll) / demux->sidx_interval);
/* FIXME: seek beyond end of file should result in immediate EOS from if (next) {
* streaming thread instead of a failed seek */ /* if we want the next keyframe, we have to go forward till we find
if (G_UNLIKELY (idx >= demux->sidx_num_entries)) a different packet number */
guint idx2 = idx;
if (idx >= demux->sidx_num_entries - 1) {
/* If we get here, we're asking for next keyframe after the last one. There isn't one. */
if (eos)
*eos = TRUE;
return FALSE;
}
for (idx2 = idx + 1; idx2 < demux->sidx_num_entries; ++idx2) {
if (demux->sidx_entries[idx].packet != demux->sidx_entries[idx2].packet) {
idx = idx2;
break;
}
}
}
if (G_UNLIKELY (idx >= demux->sidx_num_entries)) {
if (eos)
*eos = TRUE;
return FALSE; return FALSE;
}
*packet = demux->sidx_entries[idx].packet; *packet = demux->sidx_entries[idx].packet;
if (speed) if (speed)
@ -538,9 +561,11 @@ gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event)
GST_DEBUG_OBJECT (demux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (cur)); GST_DEBUG_OBJECT (demux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
/* determine packet, by index or by estimation */ /* determine packet, by index or by estimation */
if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL, NULL)) { if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL, NULL, FALSE,
packet = (guint) gst_util_uint64_scale (demux->num_packets, NULL)) {
cur, demux->play_time); packet =
(guint) gst_util_uint64_scale (demux->num_packets, cur,
demux->play_time);
} }
if (packet > demux->num_packets) { if (packet > demux->num_packets) {
@ -572,12 +597,13 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
GstSeekType cur_type, stop_type; GstSeekType cur_type, stop_type;
GstFormat format; GstFormat format;
gboolean only_need_update; gboolean only_need_update;
gboolean keyunit_sync; gboolean keyunit_sync, after, before, next;
gboolean flush; gboolean flush;
gdouble rate; gdouble rate;
gint64 cur, stop; gint64 cur, stop;
gint64 seek_time; gint64 seek_time;
guint packet, speed_count = 1; guint packet, speed_count = 1;
gboolean eos;
if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 || if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 ||
demux->num_packets == 0 || demux->play_time == 0)) { demux->num_packets == 0 || demux->play_time == 0)) {
@ -607,6 +633,9 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
demux->accurate = demux->accurate =
((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE); ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE);
keyunit_sync = ((flags & GST_SEEK_FLAG_KEY_UNIT) == GST_SEEK_FLAG_KEY_UNIT); keyunit_sync = ((flags & GST_SEEK_FLAG_KEY_UNIT) == GST_SEEK_FLAG_KEY_UNIT);
after = ((flags & GST_SEEK_FLAG_SNAP_AFTER) == GST_SEEK_FLAG_SNAP_AFTER);
before = ((flags & GST_SEEK_FLAG_SNAP_BEFORE) == GST_SEEK_FLAG_SNAP_BEFORE);
next = after && !before;
if (G_UNLIKELY (demux->streaming)) { if (G_UNLIKELY (demux->streaming)) {
/* support it safely needs more segment handling, e.g. closing etc */ /* support it safely needs more segment handling, e.g. closing etc */
@ -672,13 +701,18 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
/* FIXME: should check the KEY_UNIT flag; need to adjust position to /* FIXME: should check the KEY_UNIT flag; need to adjust position to
* real start of data and segment_start to indexed time for key unit seek*/ * real start of data and segment_start to indexed time for key unit seek*/
if (G_UNLIKELY (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time, if (G_UNLIKELY (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time,
&idx_time, &speed_count))) { &idx_time, &speed_count, next, &eos))) {
gint64 offset;
if (eos) {
demux->packet = demux->num_packets;
goto skip;
}
/* First try to query our source to see if it can convert for us. This is /* First try to query our source to see if it can convert for us. This is
the case when our source is an mms stream, notice that in this case the case when our source is an mms stream, notice that in this case
gstmms will do a time based seek to get the byte offset, this is not a gstmms will do a time based seek to get the byte offset, this is not a
problem as the seek to this offset needs to happen anway. */ problem as the seek to this offset needs to happen anway. */
gint64 offset;
if (gst_pad_peer_query_convert (demux->sinkpad, GST_FORMAT_TIME, seek_time, if (gst_pad_peer_query_convert (demux->sinkpad, GST_FORMAT_TIME, seek_time,
GST_FORMAT_BYTES, &offset)) { GST_FORMAT_BYTES, &offset)) {
packet = (offset - demux->data_offset) / demux->packet_size; packet = (offset - demux->data_offset) / demux->packet_size;
@ -692,7 +726,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
* the hope of hitting a keyframe and let the sinks throw away the stuff * the hope of hitting a keyframe and let the sinks throw away the stuff
* before the segment start. For audio-only this is unnecessary as every * before the segment start. For audio-only this is unnecessary as every
* frame is 'key'. */ * frame is 'key'. */
if (flush && (demux->accurate || keyunit_sync) if (flush && (demux->accurate || (keyunit_sync && !next))
&& demux->num_video_streams > 0) { && demux->num_video_streams > 0) {
seek_time -= 5 * GST_SECOND; seek_time -= 5 * GST_SECOND;
if (seek_time < 0) if (seek_time < 0)
@ -726,6 +760,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
gst_asf_demux_reset_stream_state_after_discont (demux); gst_asf_demux_reset_stream_state_after_discont (demux);
GST_OBJECT_UNLOCK (demux); GST_OBJECT_UNLOCK (demux);
skip:
/* restart our task since it might have been stopped when we did the flush */ /* restart our task since it might have been stopped when we did the flush */
gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_asf_demux_loop, gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_asf_demux_loop,
demux); demux);