diff --git a/ChangeLog b/ChangeLog index d47a909bc6..6d178aab52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-05-01 Tim-Philipp Müller + + * gst/asfdemux/asfpacket.c: (gst_asf_payload_queue_for_stream): + * gst/asfdemux/gstasfdemux.c: (gst_asf_demux_seek_index_lookup), + (gst_asf_demux_handle_seek_event), + (gst_asf_demux_push_complete_payloads): + Seeking improvements: honour the KEY_UNIT seek flag; after a seek, only + send data from the keyframe right before the new segment start to + make sure the decoder doesn't have to decode more than absolutely + necessary. + 2007-04-30 Tim-Philipp Müller * gst/asfdemux/asfheaders.c: diff --git a/gst/asfdemux/asfpacket.c b/gst/asfdemux/asfpacket.c index ad2fb02e6e..fe0455a2b6 100644 --- a/gst/asfdemux/asfpacket.c +++ b/gst/asfdemux/asfpacket.c @@ -142,6 +142,31 @@ gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload, GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); } + /* If we're about to queue a key frame that is before the segment start, we + * can ditch any previously queued payloads (which would also be before the + * segment start). This makes sure the decoder doesn't decode more than + * absolutely necessary after a seek (we don't push out payloads that are + * before the segment start until we have at least one that falls within the + * segment) */ + if (GST_CLOCK_TIME_IS_VALID (payload->ts) && + payload->ts < demux->segment.start && payload->keyframe) { + GST_DEBUG_OBJECT (demux, "Queueing keyframe before segment start, removing" + " %u previously-queued payloads, which would be out of segment too and" + " hence don't have to be decoded", stream->payloads->len); + while (stream->payloads->len > 0) { + AsfPayload *last; + guint idx_last; + + idx_last = stream->payloads->len - 1; + last = &g_array_index (stream->payloads, AsfPayload, idx_last); + gst_buffer_replace (&last->buf, NULL); + g_array_remove_index (stream->payloads, idx_last); + } + + /* Mark discontinuity (should be done via stream->discont anyway though) */ + GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); + } + g_array_append_vals (stream->payloads, payload, 1); } diff --git a/gst/asfdemux/gstasfdemux.c b/gst/asfdemux/gstasfdemux.c index 494d6a24d6..e0a9b6fe53 100644 --- a/gst/asfdemux/gstasfdemux.c +++ b/gst/asfdemux/gstasfdemux.c @@ -336,22 +336,40 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event) static gboolean gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet, - GstClockTime seek_time) + GstClockTime seek_time, GstClockTime * p_idx_time) { + GstClockTime idx_time; guint idx; if (demux->sidx_num_entries == 0 || demux->sidx_interval == 0) return FALSE; idx = (guint) (seek_time / demux->sidx_interval); + + /* FIXME: seek beyond end of file should result in immediate EOS from + * streaming thread instead of a failed seek */ if (idx >= demux->sidx_num_entries) return FALSE; *packet = demux->sidx_entries[idx]; + /* so we get closer to the actual time of the packet ... actually, let's not + * do this, since we throw away superfluous payloads before the seek position + * anyway; this way, our key unit seek 'snap resolution' is a bit better + * (ie. same as index resolution) */ + /* + while (idx > 0 && demux->sidx_entries[idx-1] == demux->sidx_entries[idx]) + --idx; + */ + + idx_time = demux->sidx_interval * idx; + GST_DEBUG_OBJECT (demux, "%" GST_TIME_FORMAT " => packet %u at %" GST_TIME_FORMAT, GST_TIME_ARGS (seek_time), *packet, - GST_TIME_ARGS (demux->sidx_interval * idx)); + GST_TIME_ARGS (idx_time)); + + if (p_idx_time) + *p_idx_time = idx_time; return TRUE; } @@ -389,6 +407,7 @@ gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * demux) static gboolean gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) { + GstClockTime idx_time; GstSegment segment; GstSeekFlags flags; GstSeekType cur_type, stop_type; @@ -467,7 +486,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) /* FIXME: should check the KEY_UNIT flag; need to adjust last_stop to * real start of data and segment_start to indexed time for key unit seek*/ - if (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time)) { + if (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time, &idx_time)) { /* Hackety hack, this sucks. We just seek to an earlier position * and let the sinks throw away the stuff before the segment start */ if (flush && (accurate || keyunit_sync)) { @@ -481,6 +500,15 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) if (packet > demux->num_packets) packet = demux->num_packets; + } else { + if (keyunit_sync) { + GST_DEBUG_OBJECT (demux, "key unit seek, adjust seek_time = %" + GST_TIME_FORMAT " to index_time = %" GST_TIME_FORMAT, + GST_TIME_ARGS (seek_time), GST_TIME_ARGS (idx_time)); + segment.start = idx_time; + segment.last_stop = idx_time; + segment.time = idx_time; + } } GST_DEBUG_OBJECT (demux, "seeking to packet %u", packet); @@ -945,6 +973,27 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux) AsfStream *stream; stream = &demux->stream[i]; + + /* Don't push any data until we have at least one payload that falls within + * the current segment. This way we can remove out-of-segment payloads that + * don't need to be decoded after a seek, sending only data from the + * keyframe directly before our segment start */ + if (stream->payloads->len > 0) { + AsfPayload *payload; + guint last_idx; + + last_idx = stream->payloads->len - 1; + payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + if (GST_CLOCK_TIME_IS_VALID (payload->ts) && + payload->ts < demux->segment.start) { + GST_DEBUG_OBJECT (stream->pad, "Last queued payload has timestamp %" + GST_TIME_FORMAT " which is before our segment start %" + GST_TIME_FORMAT ", not pushing yet", GST_TIME_ARGS (payload->ts), + GST_TIME_ARGS (demux->segment.start)); + continue; + } + } + while (stream->payloads->len > 0) { AsfPayload *payload;