gst/avi/gstavidemux.*: Implement reverse playback. Fixes #535300.
Original commit message from CVS: Patch by: Thijs Vermeir <thijsvermeir at gmail dot com> * gst/avi/gstavidemux.c: (gst_avi_demux_index_next), (gst_avi_demux_index_prev), (gst_avi_demux_index_entry_for_time), (gst_avi_demux_do_seek), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry): * gst/avi/gstavidemux.h: Implement reverse playback. Fixes #535300. Small cleanups.
This commit is contained in:
parent
35a5e9d33f
commit
2c6e50598e
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
|||||||
|
2008-06-02 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||||
|
|
||||||
|
Patch by: Thijs Vermeir <thijsvermeir at gmail dot com>
|
||||||
|
|
||||||
|
* gst/avi/gstavidemux.c: (gst_avi_demux_index_next),
|
||||||
|
(gst_avi_demux_index_prev), (gst_avi_demux_index_entry_for_time),
|
||||||
|
(gst_avi_demux_do_seek), (gst_avi_demux_handle_seek),
|
||||||
|
(gst_avi_demux_process_next_entry):
|
||||||
|
* gst/avi/gstavidemux.h:
|
||||||
|
Implement reverse playback. Fixes #535300.
|
||||||
|
Small cleanups.
|
||||||
|
|
||||||
2008-06-02 Sebastian Dröge <slomo@circular-chaos.org>
|
2008-06-02 Sebastian Dröge <slomo@circular-chaos.org>
|
||||||
|
|
||||||
* gst/videomixer/videomixer.c: (gst_videomixer_query_duration),
|
* gst/videomixer/videomixer.c: (gst_videomixer_query_duration),
|
||||||
|
@ -287,15 +287,44 @@ gst_avi_demux_index_last (GstAviDemux * avi, gint stream_nr)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static gst_avi_index_entry *
|
static gst_avi_index_entry *
|
||||||
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
|
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint last,
|
||||||
|
guchar flags)
|
||||||
{
|
{
|
||||||
gint i;
|
gint i;
|
||||||
gst_avi_index_entry *result = NULL;
|
gst_avi_index_entry *result = NULL, *entry;
|
||||||
|
|
||||||
for (i = start; i < avi->index_size; i++) {
|
for (i = last + 1; i < avi->index_size; i++) {
|
||||||
if (avi->index_entries[i].stream_nr == stream_nr) {
|
entry = &avi->index_entries[i];
|
||||||
result = &avi->index_entries[i];
|
|
||||||
|
if (entry->stream_nr != stream_nr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((entry->flags & flags) == flags) {
|
||||||
|
result = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static gst_avi_index_entry *
|
||||||
|
gst_avi_demux_index_prev (GstAviDemux * avi, gint stream_nr, gint last,
|
||||||
|
guchar flags)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
gst_avi_index_entry *result = NULL, *entry;
|
||||||
|
|
||||||
|
for (i = last - 1; i >= 0; i--) {
|
||||||
|
entry = &avi->index_entries[i];
|
||||||
|
|
||||||
|
if (entry->stream_nr != stream_nr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((entry->flags & flags) == flags) {
|
||||||
|
result = entry;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +336,6 @@ gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
|
|||||||
* @avi: Avi object
|
* @avi: Avi object
|
||||||
* @stream_nr: stream number
|
* @stream_nr: stream number
|
||||||
* @time: seek time position
|
* @time: seek time position
|
||||||
* @flags: index entry flags to match
|
|
||||||
*
|
*
|
||||||
* Finds the index entry which time is less or equal than the requested time.
|
* Finds the index entry which time is less or equal than the requested time.
|
||||||
*
|
*
|
||||||
@ -315,30 +343,30 @@ gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
|
|||||||
*/
|
*/
|
||||||
static gst_avi_index_entry *
|
static gst_avi_index_entry *
|
||||||
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
|
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
|
||||||
gint stream_nr, guint64 time, guchar flags)
|
gint stream_nr, guint64 time)
|
||||||
{
|
{
|
||||||
gst_avi_index_entry *entry = NULL, *last_entry = NULL;
|
gst_avi_index_entry *entry = NULL, *last_entry = NULL;
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%x",
|
GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT,
|
||||||
stream_nr, GST_TIME_ARGS (time), flags);
|
stream_nr, GST_TIME_ARGS (time));
|
||||||
|
|
||||||
i = -1;
|
for (i = 0; i < avi->index_size; i++) {
|
||||||
do {
|
entry = &avi->index_entries[i];
|
||||||
/* get next entry for given stream */
|
|
||||||
entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
|
if (entry->stream_nr != stream_nr)
|
||||||
if (!entry)
|
continue;
|
||||||
|
|
||||||
|
if (entry->ts > time)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
i = entry->index_nr;
|
last_entry = entry;
|
||||||
if (entry->ts <= time && (entry->flags & flags) == flags)
|
|
||||||
last_entry = entry;
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (avi,
|
GST_LOG_OBJECT (avi,
|
||||||
"looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
|
"best at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
|
||||||
" flags:%02x", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
|
" flags:%02x", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
|
||||||
entry->flags);
|
entry->flags);
|
||||||
} while (entry->ts < time);
|
}
|
||||||
|
|
||||||
return last_entry;
|
return last_entry;
|
||||||
}
|
}
|
||||||
@ -3078,7 +3106,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
|
|||||||
{
|
{
|
||||||
GstClockTime seek_time;
|
GstClockTime seek_time;
|
||||||
gboolean keyframe;
|
gboolean keyframe;
|
||||||
gst_avi_index_entry *entry;
|
gst_avi_index_entry *entry, *kentry;
|
||||||
gint old_entry;
|
gint old_entry;
|
||||||
|
|
||||||
seek_time = segment->last_stop;
|
seek_time = segment->last_stop;
|
||||||
@ -3095,24 +3123,52 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
|
|||||||
* time and stream we automagically are positioned for the other streams as
|
* time and stream we automagically are positioned for the other streams as
|
||||||
* well. FIXME, this code assumes the main stream with keyframes is stream 0,
|
* well. FIXME, this code assumes the main stream with keyframes is stream 0,
|
||||||
* which is mostly correct... */
|
* which is mostly correct... */
|
||||||
entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time,
|
if (!(entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time)))
|
||||||
GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME);
|
goto no_entry;
|
||||||
if (entry) {
|
|
||||||
GST_DEBUG_OBJECT (avi,
|
|
||||||
"Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT
|
|
||||||
" / duration:%" GST_TIME_FORMAT "]", entry->index_nr,
|
|
||||||
entry->stream_nr, GST_TIME_ARGS (entry->ts),
|
|
||||||
GST_TIME_ARGS (entry->dur));
|
|
||||||
|
|
||||||
avi->current_entry = entry->index_nr;
|
GST_DEBUG_OBJECT (avi,
|
||||||
|
"Got requested entry %d [stream:%d / ts:%" GST_TIME_FORMAT
|
||||||
|
" / duration:%" GST_TIME_FORMAT "]", entry->index_nr,
|
||||||
|
entry->stream_nr, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur));
|
||||||
|
|
||||||
|
/* check if we are already on a keyframe */
|
||||||
|
if (!(entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)) {
|
||||||
|
/* now go to the previous keyframe, this is where we should start
|
||||||
|
* decoding from. */
|
||||||
|
if (!(kentry = gst_avi_demux_index_prev (avi, 0, entry->index_nr,
|
||||||
|
GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME))) {
|
||||||
|
goto no_entry;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (avi,
|
/* we were on a keyframe */
|
||||||
"Couldn't find AviIndexEntry for time:%" GST_TIME_FORMAT,
|
kentry = entry;
|
||||||
GST_TIME_ARGS (seek_time));
|
|
||||||
if (avi->current_entry >= avi->index_size && avi->index_size > 0)
|
|
||||||
avi->current_entry = avi->index_size - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (avi,
|
||||||
|
"Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT
|
||||||
|
" / duration:%" GST_TIME_FORMAT "]", kentry->index_nr,
|
||||||
|
entry->stream_nr, GST_TIME_ARGS (kentry->ts),
|
||||||
|
GST_TIME_ARGS (kentry->dur));
|
||||||
|
|
||||||
|
/* we must start decoding at the keyframe */
|
||||||
|
avi->current_entry = kentry->index_nr;
|
||||||
|
|
||||||
|
if (segment->rate < 0.0) {
|
||||||
|
/* play between the keyframe and the destination entry */
|
||||||
|
avi->reverse_start_index = kentry->index_nr;
|
||||||
|
avi->reverse_stop_index = entry->index_nr;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (avi, "reverse seek: start idx (%d) and stop idx (%d)",
|
||||||
|
avi->reverse_start_index, avi->reverse_stop_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyframe) {
|
||||||
|
/* when seeking to a keyframe, we update the result seek time
|
||||||
|
* to the time of the keyframe. */
|
||||||
|
seek_time = avi->index_entries[avi->current_entry].ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
/* if we changed position, mark a DISCONT on all streams */
|
/* if we changed position, mark a DISCONT on all streams */
|
||||||
if (avi->current_entry != old_entry) {
|
if (avi->current_entry != old_entry) {
|
||||||
gint i;
|
gint i;
|
||||||
@ -3125,16 +3181,23 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
|
|||||||
GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT
|
GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT
|
||||||
" keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
|
" keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
|
||||||
|
|
||||||
if (keyframe) {
|
|
||||||
/* when seeking to a keyframe, we update the result seek time
|
|
||||||
* to the time of the keyframe. */
|
|
||||||
seek_time = avi->index_entries[avi->current_entry].ts;
|
|
||||||
}
|
|
||||||
/* the seek time is also the last_stop and stream time */
|
/* the seek time is also the last_stop and stream time */
|
||||||
segment->last_stop = seek_time;
|
segment->last_stop = seek_time;
|
||||||
segment->time = seek_time;
|
segment->time = seek_time;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
no_entry:
|
||||||
|
{
|
||||||
|
/* we could not find an entry for the given time */
|
||||||
|
GST_WARNING_OBJECT (avi,
|
||||||
|
"Couldn't find AviIndexEntry for time:%" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (seek_time));
|
||||||
|
if (avi->current_entry >= avi->index_size && avi->index_size > 0)
|
||||||
|
avi->current_entry = avi->index_size - 1;
|
||||||
|
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3175,6 +3238,9 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
|
|||||||
|
|
||||||
format = fmt;
|
format = fmt;
|
||||||
}
|
}
|
||||||
|
GST_DEBUG_OBJECT (avi,
|
||||||
|
"seek requested: rate %g cur %" GST_TIME_FORMAT " stop %"
|
||||||
|
GST_TIME_FORMAT, rate, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
|
||||||
/* FIXME: can we do anything with rate!=1.0 */
|
/* FIXME: can we do anything with rate!=1.0 */
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (avi, "doing seek without event");
|
GST_DEBUG_OBJECT (avi, "doing seek without event");
|
||||||
@ -3260,9 +3326,15 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
|
|||||||
/* queue the segment event for the streaming thread. */
|
/* queue the segment event for the streaming thread. */
|
||||||
if (avi->seek_event)
|
if (avi->seek_event)
|
||||||
gst_event_unref (avi->seek_event);
|
gst_event_unref (avi->seek_event);
|
||||||
avi->seek_event = gst_event_new_new_segment (FALSE,
|
if (avi->segment.rate > 0.0) {
|
||||||
avi->segment.rate, avi->segment.format,
|
avi->seek_event = gst_event_new_new_segment (FALSE,
|
||||||
avi->segment.last_stop, stop, avi->segment.time);
|
avi->segment.rate, avi->segment.format,
|
||||||
|
avi->segment.last_stop, stop, avi->segment.time);
|
||||||
|
} else {
|
||||||
|
avi->seek_event = gst_event_new_new_segment (FALSE,
|
||||||
|
avi->segment.rate, avi->segment.format,
|
||||||
|
avi->segment.start, avi->segment.last_stop, avi->segment.start);
|
||||||
|
}
|
||||||
|
|
||||||
if (!avi->streaming) {
|
if (!avi->streaming) {
|
||||||
avi->segment_running = TRUE;
|
avi->segment_running = TRUE;
|
||||||
@ -3376,15 +3448,44 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
|
|||||||
avi_stream_context *stream;
|
avi_stream_context *stream;
|
||||||
gst_avi_index_entry *entry;
|
gst_avi_index_entry *entry;
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
|
gint i;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* see if we are at the end */
|
/* see if we are at the end */
|
||||||
if (avi->current_entry >= avi->index_size)
|
if ((avi->segment.rate > 0 && avi->current_entry >= avi->index_size))
|
||||||
goto eos;
|
goto eos;
|
||||||
|
|
||||||
/* get next entry, this will work as we checked for the index size above */
|
/* get next entry, this will work as we checked for the index size above */
|
||||||
entry = &avi->index_entries[avi->current_entry++];
|
entry = &avi->index_entries[avi->current_entry++];
|
||||||
|
|
||||||
|
/* check for reverse playback */
|
||||||
|
if (avi->segment.rate < 0 && avi->current_entry > avi->reverse_stop_index) {
|
||||||
|
GST_LOG_OBJECT (avi, "stop_index %d reached", avi->reverse_stop_index);
|
||||||
|
avi->reverse_stop_index = avi->reverse_start_index;
|
||||||
|
if (avi->reverse_start_index == 0) {
|
||||||
|
GST_DEBUG_OBJECT (avi, "start_index was 0, sending eos");
|
||||||
|
goto eos;
|
||||||
|
}
|
||||||
|
entry =
|
||||||
|
gst_avi_demux_index_prev (avi, 0, avi->reverse_stop_index,
|
||||||
|
GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME);
|
||||||
|
if (!entry) {
|
||||||
|
GST_DEBUG_OBJECT (avi, "no valid index entry found index %d",
|
||||||
|
avi->reverse_stop_index);
|
||||||
|
goto eos;
|
||||||
|
}
|
||||||
|
avi->current_entry = avi->reverse_start_index = entry->index_nr;
|
||||||
|
GST_DEBUG_OBJECT (avi,
|
||||||
|
"reverse playback jump: start idx (%d) and stop idx (%d)",
|
||||||
|
avi->reverse_start_index, avi->reverse_stop_index);
|
||||||
|
gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, entry->ts);
|
||||||
|
for (i = 0; i < avi->num_streams; i++) {
|
||||||
|
avi->stream[i].last_flow = GST_FLOW_OK;
|
||||||
|
avi->stream[i].discont = TRUE;
|
||||||
|
}
|
||||||
|
avi->current_entry++;
|
||||||
|
}
|
||||||
|
|
||||||
/* see if we have a valid stream, ignore if not
|
/* see if we have a valid stream, ignore if not
|
||||||
* FIXME: can't we check this when building the index?
|
* FIXME: can't we check this when building the index?
|
||||||
* we check it in _parse_index(), _stream_scan()
|
* we check it in _parse_index(), _stream_scan()
|
||||||
@ -3399,11 +3500,14 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
|
|||||||
/* get stream now */
|
/* get stream now */
|
||||||
stream = &avi->stream[entry->stream_nr];
|
stream = &avi->stream[entry->stream_nr];
|
||||||
|
|
||||||
if ((entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)
|
if (avi->segment.rate > 0.0) {
|
||||||
&& GST_CLOCK_TIME_IS_VALID (entry->ts)
|
/* only check this for fowards playback for now */
|
||||||
&& GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
|
if ((entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)
|
||||||
&& (entry->ts > avi->segment.stop)) {
|
&& GST_CLOCK_TIME_IS_VALID (entry->ts)
|
||||||
goto eos_stop;
|
&& GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
|
||||||
|
&& (entry->ts > avi->segment.stop)) {
|
||||||
|
goto eos_stop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* skip empty entries */
|
/* skip empty entries */
|
||||||
|
@ -137,6 +137,8 @@ typedef struct _GstAviDemux {
|
|||||||
guint index_size;
|
guint index_size;
|
||||||
guint64 index_offset;
|
guint64 index_offset;
|
||||||
guint current_entry;
|
guint current_entry;
|
||||||
|
guint reverse_start_index;
|
||||||
|
guint reverse_stop_index;
|
||||||
|
|
||||||
/* streams */
|
/* streams */
|
||||||
guint num_streams;
|
guint num_streams;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user