From 9c37611dfab96d055a426592f8889138e2390469 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 21 Sep 2009 18:04:25 +0200 Subject: avi: rewrite index playback disable code, start on reimplementing loop based operation. Rewrite the index handling so that all streams use their own index for decoding media. --- gst/avi/gstavidemux.c | 976 +++++++++++++++++++++++++++++++------------------- gst/avi/gstavidemux.h | 40 +-- 2 files changed, 628 insertions(+), 388 deletions(-) (limited to 'gst/avi') diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index a1cc0a05..68050f7f 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -252,11 +252,10 @@ gst_avi_demux_reset (GstAviDemux * avi) avi->state = GST_AVI_DEMUX_START; avi->offset = 0; - g_free (avi->index_entries); - avi->index_entries = NULL; - avi->index_size = 0; + //g_free (avi->index_entries); + //avi->index_entries = NULL; + //avi->index_size = 0; avi->index_offset = 0; - avi->current_entry = 0; g_free (avi->avih); avi->avih = NULL; @@ -277,122 +276,6 @@ gst_avi_demux_reset (GstAviDemux * avi) gst_segment_init (&avi->segment, GST_FORMAT_TIME); } -/* Index helper */ -static gst_avi_index_entry * -gst_avi_demux_index_last (GstAviDemux * avi, gint stream_nr) -{ - gint i; - gst_avi_index_entry *result = NULL; - - for (i = avi->index_size - 1; i >= 0; i--) { - if (avi->index_entries[i].stream_nr == stream_nr) { - result = &avi->index_entries[i]; - break; - } - } - return result; -} - -static gst_avi_index_entry * -gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint last, - guchar flags) -{ - gint i; - gst_avi_index_entry *result = NULL, *entry; - - for (i = last + 1; i < avi->index_size; i++) { - entry = &avi->index_entries[i]; - - if (entry->stream_nr != stream_nr) - continue; - - if ((entry->flags & flags) == flags) { - result = entry; - break; - } - } - return result; -} - -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; - } - } - return result; -} - -static gint -gst_avi_demux_index_entry_search (gst_avi_index_entry * entry, guint64 * time) -{ - if (entry->ts < *time) - return -1; - else if (entry->ts > *time) - return 1; - return 0; -} - -/* - * gst_avi_index_entry: - * @avi: Avi object - * @stream_nr: stream number - * @time: seek time position - * - * Finds the index entry which time is less or equal than the requested time. - * - * Returns: the found index entry or %NULL - */ -static gst_avi_index_entry * -gst_avi_demux_index_entry_for_time (GstAviDemux * avi, - gint stream_nr, guint64 time) -{ - gst_avi_index_entry *entry = NULL; - guint n; - - GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT, - stream_nr, GST_TIME_ARGS (time)); - - entry = gst_util_array_binary_search (avi->index_entries, - avi->index_size, - sizeof (gst_avi_index_entry), - (GCompareDataFunc) gst_avi_demux_index_entry_search, - GST_SEARCH_MODE_BEFORE, &time, NULL); - - n = entry - avi->index_entries; - if (entry == NULL) { - entry = &avi->index_entries[0]; - n = 0; - while (entry->stream_nr != stream_nr && n < avi->index_size - 1) { - n++; - entry = &avi->index_entries[n]; - } - } else if (entry->stream_nr != stream_nr) { - while (entry->stream_nr != stream_nr && n > 0) { - n--; - entry = &avi->index_entries[n]; - } - } - - GST_LOG_OBJECT (avi, - "best at entry %u / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT - " flags:%02x", n, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur), - entry->flags); - - return entry; -} /* GstElement methods */ @@ -400,7 +283,7 @@ gst_avi_demux_index_entry_for_time (GstAviDemux * avi, static const GstFormat * gst_avi_demux_get_src_formats (GstPad * pad) { - avi_stream_context *stream = gst_pad_get_element_private (pad); + GstAviStream *stream = gst_pad_get_element_private (pad); static const GstFormat src_a_formats[] = { GST_FORMAT_TIME, @@ -421,27 +304,42 @@ gst_avi_demux_get_src_formats (GstPad * pad) /* assumes stream->strf.auds->av_bps != 0 */ static inline GstClockTime -avi_stream_convert_bytes_to_time_unchecked (avi_stream_context * stream, +avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream, guint64 bytes) { return gst_util_uint64_scale (bytes, GST_SECOND, stream->strf.auds->av_bps); } +static inline guint64 +avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream, + GstClockTime time) +{ + return gst_util_uint64_scale (time, stream->strf.auds->av_bps, GST_SECOND); +} + /* assumes stream->strh->rate != 0 */ static inline GstClockTime -avi_stream_convert_frames_to_time_unchecked (avi_stream_context * stream, +avi_stream_convert_frames_to_time_unchecked (GstAviStream * stream, guint64 frames) { return gst_util_uint64_scale (frames, stream->strh->scale * GST_SECOND, stream->strh->rate); } +static inline guint64 +avi_stream_convert_time_to_frames_unchecked (GstAviStream * stream, + GstClockTime time) +{ + return gst_util_uint64_scale (time, stream->strh->rate, + stream->strh->scale * GST_SECOND); +} + static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { - avi_stream_context *stream = gst_pad_get_element_private (pad); + GstAviStream *stream = gst_pad_get_element_private (pad); gboolean res = TRUE; GST_LOG_OBJECT (pad, @@ -472,22 +370,10 @@ gst_avi_demux_src_convert (GstPad * pad, (guint64) stream->strf.auds->av_bps, GST_SECOND); break; case GST_FORMAT_DEFAULT: - { - gdouble error; - - *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate, + *dest_value = + gst_util_uint64_scale_round (src_value, stream->strh->rate, stream->strh->scale * GST_SECOND); - - /* Attempt to round to nearest integer: if the difference is more - * than 0.5 (less than -0.5), it means that gst_util_uint64_scale() - * just truncated an integer, while it had to be rounded - */ - error = *dest_value * GST_SECOND - - src_value * stream->strh->rate / stream->strh->scale; - if (error <= -0.5) - *dest_value += 1; break; - } default: res = FALSE; break; @@ -549,7 +435,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) gboolean res = TRUE; GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); - avi_stream_context *stream = gst_pad_get_element_private (pad); + GstAviStream *stream = gst_pad_get_element_private (pad); if (!stream->strh || !stream->strf.data) return gst_pad_query_default (pad, query); @@ -575,14 +461,14 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) GST_DEBUG_OBJECT (avi, "CBR convert bytes %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT, stream->current_byte, GST_TIME_ARGS (pos)); - } else if (stream->total_frames != 0 && stream->total_bytes != 0) { + } else if (stream->idx_n != 0 && stream->total_bytes != 0) { /* calculate timestamps based on percentage of length */ guint64 xlen = avi->avih->us_frame * avi->avih->tot_frames * GST_USECOND; if (stream->is_vbr) { pos = gst_util_uint64_scale (xlen, stream->current_frame, - stream->total_frames); + stream->idx_n); GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %" GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos)); } else { @@ -631,10 +517,10 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) { gint64 dur; GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT, - stream->total_frames); + stream->idx_n); - if (stream->total_frames >= 0) - gst_query_set_duration (query, fmt, stream->total_frames); + if (stream->idx_n >= 0) + gst_query_set_duration (query, fmt, stream->idx_n); else if (gst_pad_query_convert (pad, GST_FORMAT_TIME, stream->duration, &fmt, &dur)) gst_query_set_duration (query, fmt, dur); @@ -655,14 +541,6 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) if (avi->streaming) { seekable = FALSE; - } else { - if (avi->index_entries == NULL) { - seekable = FALSE; - /* FIXME: when building index_entried, count keyframes - if (!(avi->key_frame_ct > 1)) - seekable = FALSE; - */ - } } gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, @@ -792,9 +670,8 @@ gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size) { const guint8 *data = NULL; - if (gst_adapter_available (avi->adapter) < 8) { + if (gst_adapter_available (avi->adapter) < 8) return FALSE; - } data = gst_adapter_peek (avi->adapter, 8); *tag = GST_READ_UINT32_LE (data); @@ -819,30 +696,47 @@ gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size) guint32 peek_size = 0; gint available; - if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) { - return FALSE; - } + if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) + goto peek_failed; /* size 0 -> empty data buffer would surprise most callers, * large size -> do not bother trying to squeeze that into adapter, * so we throw poor man's exception, which can be caught if caller really * wants to handle 0 size chunk */ - if (!(*size) || (*size) >= (1 << 30)) { - GST_INFO ("Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, - *size, GST_FOURCC_ARGS (*tag)); - /* chain should give up */ - avi->abort_buffering = TRUE; - return FALSE; - } + if (!(*size) || (*size) >= (1 << 30)) + goto strange_size; + peek_size = (*size + 1) & ~1; available = gst_adapter_available (avi->adapter); - GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT + GST_DEBUG_OBJECT (avi, + "Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available); - if (available >= (8 + peek_size)) { - return TRUE; - } else { + if (available < (8 + peek_size)) + goto need_more; + + return TRUE; + + /* ERRORS */ +peek_failed: + { + GST_INFO_OBJECT (avi, "Failed to peek"); + return FALSE; + } +strange_size: + { + GST_INFO_OBJECT (avi, + "Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, *size, + GST_FOURCC_ARGS (*tag)); + /* chain should give up */ + avi->abort_buffering = TRUE; + return FALSE; + } +need_more: + { + GST_INFO_OBJECT (avi, "need more %d < %" G_GUINT32_FORMAT, + available, 8 + peek_size); return FALSE; } } @@ -1093,6 +987,7 @@ too_small: } } +#if 0 /* * gst_avi_demux_parse_subindex: * @avi: Avi object @@ -1113,7 +1008,7 @@ too_small: */ static gboolean gst_avi_demux_parse_subindex (GstAviDemux * avi, - GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list) + GstBuffer * buf, GstAviStream * stream, GList ** _entries_list) { guint8 *data = GST_BUFFER_DATA (buf); guint16 bpe; @@ -1202,7 +1097,7 @@ gst_avi_demux_parse_subindex (GstAviDemux * avi, stream->total_blocks + 1); } else { next_ts = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames + 1); + stream->idx_n + 1); } } else { /* CBR get next timestamp */ @@ -1214,10 +1109,10 @@ gst_avi_demux_parse_subindex (GstAviDemux * avi, /* stream position */ entry->bytes_before = stream->total_bytes; - entry->frames_before = stream->total_frames; + entry->frames_before = stream->idx_n; stream->total_bytes += entry->size; - stream->total_frames++; + stream->idx_n++; if (stream->strh->type == GST_RIFF_FCC_auds) { if (stream->strf.auds->blockalign > 0) stream->total_blocks += @@ -1272,6 +1167,7 @@ out_of_mem: return FALSE; } } +#endif #if 0 /* @@ -1290,7 +1186,7 @@ gst_avi_demux_read_subindexes_push (GstAviDemux * avi, avi->num_streams); for (n = 0; n < avi->num_streams; n++) { - avi_stream_context *stream = &avi->stream[n]; + GstAviStream *stream = &avi->stream[n]; for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) { if (!gst_avi_demux_peek_chunk (avi, &tag, &size)) @@ -1326,6 +1222,7 @@ gst_avi_demux_read_subindexes_push (GstAviDemux * avi, } #endif +#if 0 /* * Read AVI index */ @@ -1342,7 +1239,7 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi, avi->num_streams); for (n = 0; n < avi->num_streams; n++) { - avi_stream_context *stream = &avi->stream[n]; + GstAviStream *stream = &avi->stream[n]; for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) { if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad, @@ -1376,6 +1273,7 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi, } GST_DEBUG_OBJECT (avi, "index %s", ((*index) ? "!= 0" : "== 0")); } +#endif /* * gst_avi_demux_riff_parse_vprp: @@ -1514,7 +1412,7 @@ too_small: static gboolean gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) { - avi_stream_context *stream; + GstAviStream *stream; GstElementClass *klass; GstPadTemplate *templ; GstBuffer *sub = NULL; @@ -1852,7 +1750,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) stream->num = avi->num_streams; stream->total_bytes = 0; - stream->total_frames = 0; + stream->idx_n = 0; stream->total_blocks = 0; stream->current_frame = 0; stream->current_byte = 0; @@ -1899,7 +1797,7 @@ fail: gst_buffer_unref (stream->initdata); if (stream->extradata) gst_buffer_unref (stream->extradata); - memset (stream, 0, sizeof (avi_stream_context)); + memset (stream, 0, sizeof (GstAviStream)); avi->num_streams++; return FALSE; } @@ -1966,23 +1864,168 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf) gst_buffer_unref (buf); } +/* Index helper */ +static guint +gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream) +{ + return stream->idx_n - 1; +} + +/* find a previous entry in the index with the given flags */ +static guint +gst_avi_demux_index_prev (GstAviDemux * avi, GstAviStream * stream, + guint last, guint32 flags) +{ + GstAviIndexEntry *entry; + guint i; + + for (i = last; i > 0; i--) { + entry = &stream->index[i - 1]; + if ((entry->flags & flags) == flags) { + return i; + } + } + return 0; +} + +static guint +gst_avi_demux_index_next (GstAviDemux * avi, GstAviStream * stream, + guint last, guint32 flags) +{ + GstAviIndexEntry *entry; + gint i; + + for (i = last + 1; i < stream->idx_n; i++) { + entry = &stream->index[i]; + if ((entry->flags & flags) == flags) { + return i; + } + } + return stream->idx_n - 1; +} + +static guint +gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total) +{ + if (entry->total < *total) + return -1; + else if (entry->total > *total) + return 1; + return 0; +} + /* - * Sort helper for index entries that sorts by index time. - * If times are equal we sort by stream number. + * gst_avi_index_entry: + * @avi: Avi object + * @stream: the stream + * @time: seek time position + * + * Finds the index entry which time is less or equal than the requested time. + * + * Returns: the found position in the index. */ -static gint -sort (gst_avi_index_entry * a, gst_avi_index_entry * b) +static guint +gst_avi_demux_index_for_time (GstAviDemux * avi, + GstAviStream * stream, guint64 time) { - if (a->ts > b->ts) - return 1; - else if (a->ts < b->ts) - return -1; - else - return a->stream_nr - b->stream_nr; + guint index = -1; + guint64 total; + + GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time)); + + /* easy (and common) cases */ + if (time == 0 || stream->idx_n == 0) + return 0; + if (time >= stream->idx_duration) + return stream->idx_n - 1; + + /* figure out where we need to go. For that we convert the time to an + * index entry or we convert it to a total and then do a binary search. */ + if (stream->is_vbr) { + /* VBR stream next timestamp */ + if (stream->strh->type == GST_RIFF_FCC_auds) { + total = avi_stream_convert_time_to_frames_unchecked (stream, time); + } else { + index = avi_stream_convert_time_to_frames_unchecked (stream, time); + } + } else { + /* constant rate stream */ + total = avi_stream_convert_time_to_bytes_unchecked (stream, time); + } + + if (index == -1) { + GstAviIndexEntry *entry; + + /* no index, find index with binary search on total */ + GST_LOG_OBJECT (avi, "binary search for entry with total %" + G_GUINT64_FORMAT, total); + + entry = gst_util_array_binary_search (stream->index, + stream->idx_n, sizeof (GstAviIndexEntry), + (GCompareDataFunc) gst_avi_demux_index_entry_search, + GST_SEARCH_MODE_BEFORE, &total, NULL); + + if (entry == NULL) { + GST_LOG_OBJECT (avi, "not found, assume index 0"); + index = 0; + } else { + index = entry - stream->index; + GST_LOG_OBJECT (avi, "found at %u", index); + } + } + + return index; +} + +static void +gst_avi_demux_get_entry_info (GstAviDemux * avi, GstAviStream * stream, + guint entry_n, GstClockTime * timestamp, GstClockTime * duration, + guint64 * offset, guint64 * size, gboolean * keyframe) +{ + GstAviIndexEntry *entry; + GstClockTime next_ts = 0, ts = 0; + + entry = &stream->index[entry_n]; + + if (stream->is_vbr) { + /* VBR stream next timestamp */ + if (stream->strh->type == GST_RIFF_FCC_auds) { + if (timestamp || duration) + ts = avi_stream_convert_frames_to_time_unchecked (stream, entry->total); + if (duration) + next_ts = avi_stream_convert_frames_to_time_unchecked (stream, + entry->total + entry->size); + } else { + if (timestamp || duration) + ts = avi_stream_convert_frames_to_time_unchecked (stream, entry_n); + if (duration) + next_ts = avi_stream_convert_frames_to_time_unchecked (stream, + entry_n + 1); + } + } else { + /* constant rate stream */ + if (timestamp || duration) + ts = avi_stream_convert_bytes_to_time_unchecked (stream, entry->total); + if (duration) + next_ts = avi_stream_convert_bytes_to_time_unchecked (stream, + entry->total + entry->size); + } + if (timestamp) + *timestamp = ts; + if (duration) + *duration = next_ts - ts; + + if (offset) + *offset = entry->offset; + if (size) + *size = entry->size; + if (keyframe) + *keyframe = (entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME) != 0; } + /* - * gst_avi_demux_parse_index2: + * gst_avi_demux_parse_index: * @avi: calling element (used for debugging/errors). * @buf: buffer containing the full index. * @@ -1990,7 +2033,7 @@ sort (gst_avi_index_entry * a, gst_avi_index_entry * b) * The buffer should contain a GST_RIFF_TAG_idx1 chunk. */ static void -gst_avi_demux_parse_index2 (GstAviDemux * avi, GstBuffer * buf) +gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf) { guint64 pos_before; guint8 *data; @@ -1998,7 +2041,7 @@ gst_avi_demux_parse_index2 (GstAviDemux * avi, GstBuffer * buf) guint i, num, n; gst_riff_index_entry *index; GstClockTime stamp; - avi_stream_context *stream; + GstAviStream *stream; #ifndef GST_DISABLE_GST_DEBUG guint total_idx = 0, total_max = 0; #endif @@ -2064,8 +2107,20 @@ gst_avi_demux_parse_index2 (GstAviDemux * avi, GstBuffer * buf) /* handle size */ entry.size = GST_READ_UINT32_LE (&index[i].size); + /* update stats */ + entry.total = stream->total_bytes; + stream->total_bytes += entry.size; + if (stream->strh->type == GST_RIFF_FCC_auds) { + if (stream->strf.auds->blockalign > 0) + stream->total_blocks += + (entry.size + stream->strf.auds->blockalign - + 1) / stream->strf.auds->blockalign; + else + stream->total_blocks++; + } + /* add to the index */ - if (stream->idx_n >= stream->idx_max) { + if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) { /* we need to make some more room */ if (stream->idx_max == 0) { /* initial size guess, assume each stream has an equal amount of entries, @@ -2082,14 +2137,7 @@ gst_avi_demux_parse_index2 (GstAviDemux * avi, GstBuffer * buf) if (G_UNLIKELY (!stream->index)) goto out_of_mem; } - if (stream->idx_n == 0) { - /* first entry, set total bytes to 0 */ - entry.total = 0; - } else { - /* calculate bytes based on previous entry */ - entry.total = stream->index[stream->idx_n - 1].total + - stream->index[stream->idx_n - 1].size; - } + /* and copy */ GST_LOG_OBJECT (avi, "Adding stream %d, index entry %d, flags %02x, size %u " @@ -2112,11 +2160,11 @@ gst_avi_demux_parse_index2 (GstAviDemux * avi, GstBuffer * buf) GstAviIndexEntry *entry; guint64 total; - if (!(stream = &avi->stream[i])) + if (G_UNLIKELY (!(stream = &avi->stream[i]))) continue; - if (!stream->strh) + if (G_UNLIKELY (!stream->strh)) continue; - if (!stream->index || stream->idx_n == 0) + if (G_UNLIKELY (!stream->index || stream->idx_n == 0)) continue; entry = &stream->index[stream->idx_n - 1]; @@ -2177,6 +2225,7 @@ out_of_mem: } } +#if 0 /* * gst_avi_demux_parse_index: * @avi: calling element (used for debugging/errors). @@ -2223,7 +2272,7 @@ gst_avi_demux_parse_index (GstAviDemux * avi, for (i = 0, n = 0; i < num; i++) { gint64 next_ts; gst_riff_index_entry entry, *_entry; - avi_stream_context *stream; + GstAviStream *stream; guint stream_nr; gst_avi_index_entry *target; @@ -2290,7 +2339,7 @@ gst_avi_demux_parse_index (GstAviDemux * avi, stream->total_blocks + 1); } else { next_ts = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames + 1); + stream->idx_n + 1); } } else { /* constant rate stream */ @@ -2302,10 +2351,10 @@ gst_avi_demux_parse_index (GstAviDemux * avi, /* stream position */ target->bytes_before = stream->total_bytes; - target->frames_before = stream->total_frames; + target->frames_before = stream->idx_n; stream->total_bytes += target->size; - stream->total_frames++; + stream->idx_n++; if (stream->strh->type == GST_RIFF_FCC_auds) { if (stream->strf.auds->blockalign > 0) stream->total_blocks += @@ -2320,7 +2369,7 @@ gst_avi_demux_parse_index (GstAviDemux * avi, "Adding index entry %d (%6u), flags %02x, stream %d, size %u " ", offset %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, - target->index_nr, stream->total_frames - 1, target->flags, + target->index_nr, stream->idx_n - 1, target->flags, target->stream_nr, target->size, target->offset, GST_TIME_ARGS (target->ts), GST_TIME_ARGS (target->dur)); entries_list = g_list_prepend (entries_list, target); @@ -2357,18 +2406,16 @@ out_of_mem: gst_buffer_unref (buf); } } +#endif /* * gst_avi_demux_stream_index: * @avi: avi demuxer object. - * @index: list of index entries, returned by this function. - * @alloc_list: list of allocated data, returned by this function. * * Seeks to index and reads it. */ static void -gst_avi_demux_stream_index (GstAviDemux * avi, - GList ** index, GList ** alloc_list) +gst_avi_demux_stream_index (GstAviDemux * avi) { GstFlowReturn res; guint64 offset = avi->offset; @@ -2378,9 +2425,6 @@ gst_avi_demux_stream_index (GstAviDemux * avi, GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset); - *alloc_list = NULL; - *index = NULL; - /* get chunk information */ res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf); if (res != GST_FLOW_OK) @@ -2423,21 +2467,19 @@ gst_avi_demux_stream_index (GstAviDemux * avi, GST_DEBUG ("will parse index chunk size %u for tag %" GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag)); - gst_avi_demux_parse_index2 (avi, buf); - gst_avi_demux_parse_index (avi, buf, index); - if (*index) - *alloc_list = g_list_append (*alloc_list, (*index)->data); + gst_avi_demux_parse_index (avi, buf); + gst_buffer_unref (buf); #ifndef GST_DISABLE_GST_DEBUG /* debug our indexes */ { gint i; - avi_stream_context *stream; + GstAviStream *stream; for (i = 0; i < avi->num_streams; i++) { stream = &avi->stream[i]; GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes", - i, stream->total_frames, stream->total_bytes); + i, stream->idx_n, stream->total_bytes); } } #endif @@ -2613,6 +2655,7 @@ done: } #endif +#if 0 /* * gst_avi_demux_peek_tag: * @@ -2658,7 +2701,9 @@ wrong_size: goto done; } } +#endif +#if 0 /* * gst_avi_demux_next_data_buffer: * @@ -2688,7 +2733,9 @@ gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset, return res; } +#endif +#if 0 /* * gst_avi_demux_stream_scan: * @avi: calling element (used for debugging/errors). @@ -2707,7 +2754,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, { GstFlowReturn res; gst_avi_index_entry *entry, *entries = NULL; - avi_stream_context *stream; + GstAviStream *stream; GstFormat format; guint64 pos = avi->offset; guint64 length; @@ -2788,9 +2835,9 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, if (stream->is_vbr) { /* VBR stream */ entry->ts = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames); + stream->idx_n); entry->dur = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames + 1); + stream->idx_n + 1); } else { /* constant rate stream */ entry->ts = avi_stream_convert_bytes_to_time_unchecked (stream, @@ -2803,8 +2850,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, /* stream position */ entry->bytes_before = stream->total_bytes; stream->total_bytes += entry->size; - entry->frames_before = stream->total_frames; - stream->total_frames++; + entry->frames_before = stream->idx_n; + stream->idx_n++; stream->idx_duration = entry->ts + entry->dur; list = g_list_prepend (list, entry); @@ -2849,9 +2896,9 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, if (stream->is_vbr) { /* VBR stream */ entry->ts = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames); + stream->idx_n); entry->dur = avi_stream_convert_frames_to_time_unchecked (stream, - stream->total_frames + 1); + stream->idx_n + 1); } else { /* constant rate stream */ entry->ts = avi_stream_convert_bytes_to_time_unchecked (stream, @@ -2864,8 +2911,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, /* stream position */ entry->bytes_before = stream->total_bytes; stream->total_bytes += entry->size; - entry->frames_before = stream->total_frames; - stream->total_frames++; + entry->frames_before = stream->idx_n; + stream->idx_n++; list = g_list_prepend (list, entry); GST_DEBUG_OBJECT (avi, "Added index entry %d (in stream: %d), offset %" @@ -2892,7 +2939,9 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, return TRUE; } +#endif +#if 0 /* * gst_avi_demux_massage_index: * @avi: calling element (used for debugging/errors). @@ -2909,7 +2958,7 @@ gst_avi_demux_massage_index (GstAviDemux * avi, GList * list, GList * alloc_list) { gst_avi_index_entry *entry; - avi_stream_context *stream; + GstAviStream *stream; guint i; GList *node; gint64 delay = G_GINT64_CONSTANT (0); @@ -2935,7 +2984,7 @@ gst_avi_demux_massage_index (GstAviDemux * avi, #define MAX_DURATION (GST_SECOND / 2) for (i = 0; i < avi->num_streams; i++) { /* only chop streams that have exactly *one* chunk */ - if (avi->stream[i].total_frames != 1) + if (avi->stream[i].idx_n != 1) continue; for (node = list; node != NULL; node = node->next) { @@ -3083,7 +3132,7 @@ gst_avi_demux_massage_index (GstAviDemux * avi, #ifndef GST_DISABLE_GST_DEBUG for (i = 0; i < avi->num_streams; i++) { GST_LOG_OBJECT (avi, "Stream %d, %d frames, %8" G_GUINT64_FORMAT " bytes", - i, avi->stream[i].total_frames, avi->stream[i].total_bytes); + i, avi->stream[i].idx_n, avi->stream[i].total_bytes); } #endif @@ -3104,6 +3153,7 @@ out_of_mem: return FALSE; } } +#endif static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi) @@ -3116,7 +3166,7 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi) /* all streams start at a timestamp 0 */ for (stream = 0; stream < avi->num_streams; stream++) { GstClockTime duration, hduration; - avi_stream_context *streamc = &avi->stream[stream]; + GstAviStream *streamc = &avi->stream[stream]; gst_riff_strh *strh = streamc->strh; if (!strh) @@ -3148,7 +3198,7 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi) if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) { /* now update the duration for those streams where we had none */ for (stream = 0; stream < avi->num_streams; stream++) { - avi_stream_context *streamc = &avi->stream[stream]; + GstAviStream *streamc = &avi->stream[stream]; if (!GST_CLOCK_TIME_IS_VALID (streamc->duration) || streamc->duration == 0) { @@ -3180,7 +3230,7 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event) if (avi->num_streams) { for (i = 0; i < avi->num_streams; i++) { - avi_stream_context *stream = &avi->stream[i]; + GstAviStream *stream = &avi->stream[i]; if (stream->pad) { result = TRUE; @@ -3481,7 +3531,9 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi) GstFlowReturn res; GstBuffer *buf, *sub = NULL; guint32 tag; +#if 0 GList *index = NULL, *alloc = NULL; +#endif guint offset = 4; gint64 stop; GstElement *element = GST_ELEMENT_CAST (avi); @@ -3684,15 +3736,18 @@ skipping_done: GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)", avi->num_streams, avi->stream[0].indexes); +#if 0 /* create or read stream index (for seeking) */ if (avi->stream[0].indexes != NULL) { /* we read a super index already (gst_avi_demux_parse_superindex() ) */ gst_avi_demux_read_subindexes_pull (avi, &index, &alloc); } if (!index) { +#endif if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX) { - gst_avi_demux_stream_index (avi, &index, &alloc); + gst_avi_demux_stream_index (avi); } +#if 0 /* some indexes are incomplete, continue streaming from there */ if (!index) gst_avi_demux_stream_scan (avi, &index, &alloc); @@ -3701,9 +3756,7 @@ skipping_done: /* this is a fatal error */ if (!index) goto no_index; - - if (!gst_avi_demux_massage_index (avi, index, alloc)) - goto no_index; +#endif gst_avi_demux_calculate_durations_from_index (avi); @@ -3713,6 +3766,9 @@ skipping_done: GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop); + /* do initial seek to the configured segment values */ + gst_avi_demux_do_seek (avi, &avi->segment); + if (avi->seek_event) gst_event_unref (avi->seek_event); avi->seek_event = gst_event_new_new_segment @@ -3769,6 +3825,7 @@ no_streams: GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found")); return GST_FLOW_ERROR; } +#if 0 no_index: { GST_WARNING ("file without or too big index"); @@ -3780,6 +3837,7 @@ no_index: ("Could not get/create index")); return GST_FLOW_ERROR; } +#endif pull_range_failed: { GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), @@ -3788,6 +3846,40 @@ pull_range_failed: } } +/* move a stream to an offset */ +static void +gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream, + GstSegment * segment, guint index) +{ + GST_DEBUG_OBJECT (avi, "Move stream %d to %u", stream->num, index); + + if (segment->rate < 0.0) { + guint next_key; + /* Because we don't know the frame order we need to push from the prev keyframe + * to the next keyframe. If there is a smart decoder downstream he will notice + * that there are too many encoded frames send and return UNEXPECTED when there + * are enough decoded frames to fill the segment. */ + next_key = gst_avi_demux_index_next (avi, stream, index, + GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME); + + stream->start_entry = 0; + stream->step_entry = index; + stream->current_entry = index; + stream->stop_entry = next_key; + + GST_DEBUG_OBJECT (avi, "reverse seek: start %u, step %u, stop %u", + stream->start_entry, stream->step_entry, stream->stop_entry); + } else { + stream->start_entry = index; + stream->step_entry = index; + stream->stop_entry = gst_avi_demux_index_last (avi, stream); + } + if (stream->current_entry != index) { + stream->current_entry = index; + stream->discont = TRUE; + } +} + /* * Do the actual seeking. */ @@ -3796,88 +3888,73 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment) { GstClockTime seek_time; gboolean keyframe; - gst_avi_index_entry *entry, *kentry; - gint old_entry; + guint i, index; + GstAviStream *stream; + GstClockTime timestamp, duration; + gboolean kentry; seek_time = segment->last_stop; keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT); - /* FIXME: if we seek in an openDML file, we will have multiple - * primary levels. Seeking in between those will cause havoc. */ + /* FIXME, this code assumes the main stream with keyframes is stream 0, + * which is mostly correct... */ + stream = &avi->stream[0]; - /* save old position so we can see if we must mark a discont. */ - old_entry = avi->current_entry; + /* get the entry index for the requested position */ + index = gst_avi_demux_index_for_time (avi, stream, seek_time); - /* get the entry for the requested position, which is always in last_stop. - * we search the index entry for stream 0, since all entries are sorted by - * 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, - * which is mostly correct... */ - if (!(entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time))) - goto no_entry; + /* take a look at the entry info */ + gst_avi_demux_get_entry_info (avi, stream, index, + NULL, NULL, NULL, NULL, &kentry); - 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)); + GST_DEBUG_OBJECT (avi, "Got entry %u", index); /* check if we are already on a keyframe */ - if (!(entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)) { + if (!kentry) { + GST_DEBUG_OBJECT (avi, "not keyframe, searching back"); /* 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 { - /* we were on a keyframe */ - kentry = entry; - } - - 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) { - gst_avi_index_entry *next_keyframe; - - /* Because we don't know the frame order we need to push from the prev keyframe - * to the next keyframe. If there is a smart decoder downstream he will notice - * that there are too many encoded frames send and return UNEXPECTED when there - * are enough decoded frames to fill the segment. - */ - next_keyframe = - gst_avi_demux_index_next (avi, 0, kentry->index_nr, + index = gst_avi_demux_index_prev (avi, stream, index, GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME); - if (!next_keyframe) - next_keyframe = gst_avi_demux_index_last (avi, 0); + GST_DEBUG_OBJECT (avi, "previous keyframe at %u", index); + } - avi->reverse_start_index = kentry->index_nr; - avi->reverse_stop_index = next_keyframe->index_nr; + /* take a look at the final entry */ + gst_avi_demux_get_entry_info (avi, stream, index, + ×tamp, &duration, NULL, NULL, NULL); - GST_DEBUG_OBJECT (avi, "reverse seek: start idx (%d) and stop idx (%d)", - avi->reverse_start_index, avi->reverse_stop_index); - } + GST_DEBUG_OBJECT (avi, + "Got keyframe entry %d [ts:%" GST_TIME_FORMAT + " / duration:%" GST_TIME_FORMAT "]", index, + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); 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; + seek_time = timestamp; } -next: - /* if we changed position, mark a DISCONT on all streams */ - if (avi->current_entry != old_entry) { - gint i; + /* move the main stream */ + gst_avi_demux_move_stream (avi, stream, segment, index); - for (i = 0; i < avi->num_streams; i++) { - avi->stream[i].discont = TRUE; + /* now set DISCONT and align the other streams */ + for (i = 0; i < avi->num_streams; i++) { + GstAviStream *ostream; + + ostream = &avi->stream[i]; + if (ostream == stream) + continue; + + /* get the entry index for the requested position */ + index = gst_avi_demux_index_for_time (avi, ostream, seek_time); + + gst_avi_demux_get_entry_info (avi, ostream, index, + NULL, NULL, NULL, NULL, &kentry); + if (!kentry) { + index = gst_avi_demux_index_prev (avi, ostream, index, + GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME); } + gst_avi_demux_move_stream (avi, ostream, segment, index); } GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT @@ -3888,18 +3965,6 @@ next: segment->time = seek_time; 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; - } } /* @@ -3980,7 +4045,6 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) gst_segment_set_seek (&seeksegment, rate, format, flags, cur_type, cur, stop_type, stop, &update); } - /* do the seek, seeksegment.last_stop contains the new position, this * actually never fails. */ gst_avi_demux_do_seek (avi, &seeksegment); @@ -4077,7 +4141,7 @@ swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes) * FIXME: can't we preallocate tmp? and remember stride, bpp? */ static GstBuffer * -gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) +gst_avi_demux_invert (GstAviStream * stream, GstBuffer * buf) { GstStructure *s; gint y, w, h; @@ -4128,7 +4192,7 @@ gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) * Returns the aggregated GstFlowReturn. */ static GstFlowReturn -gst_avi_demux_combine_flows (GstAviDemux * avi, avi_stream_context * stream, +gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream, GstFlowReturn ret) { guint i; @@ -4142,7 +4206,7 @@ gst_avi_demux_combine_flows (GstAviDemux * avi, avi_stream_context * stream, /* only return NOT_LINKED if all other pads returned NOT_LINKED */ for (i = 0; i < avi->num_streams; i++) { - avi_stream_context *ostream = &avi->stream[i]; + GstAviStream *ostream = &avi->stream[i]; ret = ostream->last_flow; /* some other return value (must be SUCCESS but we can return @@ -4157,38 +4221,7 @@ done: return ret; } -/* - * prepare the avi element for a reverse jump to a prev keyframe - * this function will return the start entry. if the function returns - * NULL there was no prev keyframe. - */ -static gst_avi_index_entry * -gst_avi_demux_step_reverse (GstAviDemux * avi) -{ - gst_avi_index_entry *entry; - gint i; - - avi->reverse_stop_index = avi->reverse_start_index; - 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); - return NULL; - } - 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; - } - return entry; -} - +#if 0 /* * Read data from one index entry */ @@ -4197,7 +4230,7 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi) { GstFlowReturn res = GST_FLOW_OK; gboolean processed = FALSE; - avi_stream_context *stream; + GstAviStream *stream; gst_avi_index_entry *entry; GstBuffer *buf; @@ -4398,6 +4431,238 @@ short_buffer: goto beach; } } +#endif + +/* move @stream to the next position in its index */ +static GstFlowReturn +gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream, + GstFlowReturn ret) +{ + guint i; + + /* move to the next entry */ + stream->current_entry++; + stream->current_frame++; + + /* see if we reached the end */ + if (stream->current_entry >= stream->stop_entry) { + if (avi->segment.rate < 0.0) { + if (stream->step_entry == stream->start_entry) { + /* we stepped all the way to the start, eos */ + GST_DEBUG_OBJECT (avi, "reverse reached start %u", stream->start_entry); + goto eos; + } + /* backwards, stop becomes step, find a new step */ + stream->stop_entry = stream->step_entry; + stream->step_entry = gst_avi_demux_index_prev (avi, stream, + stream->stop_entry, GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME); + stream->current_entry = stream->step_entry; + + GST_DEBUG_OBJECT (avi, + "reverse playback jump: start %u, step %u, stop %u", + stream->start_entry, stream->step_entry, stream->stop_entry); + + /* mark DISCONT */ + for (i = 0; i < avi->num_streams; i++) { + avi->stream[i].last_flow = GST_FLOW_OK; + avi->stream[i].discont = TRUE; + } + } else { + /* EOS */ + GST_DEBUG_OBJECT (avi, "forward reached stop %u", stream->stop_entry); + goto eos; + } + } + return ret; + + /* ERROR */ +eos: + { + GST_DEBUG_OBJECT (avi, "we are EOS"); + /* setting current_time to -1 marks EOS */ + stream->current_time = -1; + return GST_FLOW_UNEXPECTED; + } +} + +static GstFlowReturn +gst_avi_demux_loop_data (GstAviDemux * avi) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint64 min_time; + guint stream_num, i; + GstAviStream *stream; + gboolean processed = FALSE; + GstBuffer *buf; + guint64 offset, size; + GstClockTime timestamp, duration; + gboolean keyframe; + + do { + /* first find the stream with the lowest current position, this is the one + * we should push from next */ + min_time = G_MAXUINT64; + stream_num = -1; + for (i = 0; i < avi->num_streams; i++) { + guint64 position; + + stream = &avi->stream[i]; + position = stream->current_time; + + /* position of -1 is EOS */ + if (position != -1 && position < min_time) { + min_time = position; + stream_num = i; + } + } + + /* all are EOS */ + if (G_UNLIKELY (stream_num == -1)) { + GST_DEBUG_OBJECT (avi, "all streams are EOS"); + goto eos; + } + + /* we have the stream now */ + stream = &avi->stream[stream_num]; + + /* skip streams without pads */ + if (!stream->pad) { + GST_DEBUG_OBJECT (avi, "skipping entry from stream %d without pad", + stream_num); + goto next; + } + + /* get the timing info for the entry */ + gst_avi_demux_get_entry_info (avi, stream, stream->current_entry, + ×tamp, &duration, &offset, &size, &keyframe); + + /* skip empty entries */ + if (size == 0) { + GST_DEBUG_OBJECT (avi, "Skipping entry %d (%d, %p)", + stream->current_entry, size, stream->pad); + goto next; + } + + if (avi->segment.rate > 0.0) { + /* only check this for fowards playback for now */ + if (keyframe && GST_CLOCK_TIME_IS_VALID (timestamp) + && GST_CLOCK_TIME_IS_VALID (avi->segment.stop) + && (timestamp > avi->segment.stop)) { + goto eos_stop; + } + } + + /* correct for index offset */ + offset += avi->index_offset; + + GST_LOG ("reading buffer (size=%d) from stream %d at current pos %" + G_GUINT64_FORMAT " (%llx)", size, stream_num, offset, offset); + + /* pull in the data */ + ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf); + if (ret != GST_FLOW_OK) + goto pull_failed; + + /* check for short buffers, this is EOS as well */ + if (GST_BUFFER_SIZE (buf) < size) + goto short_buffer; + + /* invert the picture if needed */ + buf = gst_avi_demux_invert (stream, buf); + + /* mark non-keyframes */ + if (!keyframe) + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + + GST_BUFFER_TIMESTAMP (buf) = timestamp; + GST_BUFFER_DURATION (buf) = duration; + GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; + + gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad)); + + GST_DEBUG_OBJECT (avi, "Pushing buffer of size %d, offset %" + G_GUINT64_FORMAT " and time %" + GST_TIME_FORMAT " on pad %s", + GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf), + GST_TIME_ARGS (timestamp), GST_PAD_NAME (stream->pad)); + + /* update current position in the segment */ + gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, timestamp); + + /* mark discont when pending */ + if (stream->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + + ret = gst_pad_push (stream->pad, buf); + + /* mark as processed, we increment the frame and byte counters then + * leave the while loop and return the GstFlowReturn */ + processed = TRUE; + GST_DEBUG_OBJECT (avi, "Processed buffer %d: %s", stream->current_entry, + gst_flow_get_name (ret)); + + if (avi->segment.rate < 0) { + if (timestamp > avi->segment.stop && ret == GST_FLOW_UNEXPECTED) { + /* In reverse playback we can get a GST_FLOW_UNEXPECTED when + * we are at the end of the segment, so we just need to jump + * back to the previous section. */ + GST_DEBUG_OBJECT (avi, "downstream has reached end of segment"); + ret = GST_FLOW_OK; + } + } + next: + ret = gst_avi_demux_advance (avi, stream, ret); + + /* combine flows */ + ret = gst_avi_demux_combine_flows (avi, stream, ret); + } while (!processed); + +beach: + return ret; + + /* special cases */ +eos: + { + GST_DEBUG_OBJECT (avi, "No samples left for any streams - EOS"); + ret = GST_FLOW_UNEXPECTED; + goto beach; + } +eos_stop: + { + GST_LOG_OBJECT (avi, "Found keyframe after segment," + " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")", + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (avi->segment.stop)); + ret = GST_FLOW_UNEXPECTED; + goto beach; + } +pull_failed: + { + GST_DEBUG_OBJECT (avi, "pull range failed: pos=%" G_GUINT64_FORMAT + " size=%d", offset, size); + goto beach; + } +short_buffer: + { + GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT + ", only got %d/%d bytes (truncated file?)", offset, + GST_BUFFER_SIZE (buf), size); + gst_buffer_unref (buf); + ret = GST_FLOW_UNEXPECTED; + goto beach; + } +#if 0 +eos_stream: + { + GST_DEBUG_OBJECT (avi, "No samples left for stream"); + /* EOS will be raised if all are EOS */ + ret = GST_FLOW_OK; + goto beach; + } +#endif +} /* * Read data. If we have an index it delegates to @@ -4412,22 +4677,12 @@ gst_avi_demux_stream_data (GstAviDemux * avi) GstFlowReturn res = GST_FLOW_OK; GstFormat format = GST_FORMAT_TIME; - /* if we have a avi->index_entries[], we don't want to read - * the stream linearly, but seek to the next ts/index_entry. */ - if (G_LIKELY (avi->index_entries != NULL)) - return gst_avi_demux_process_next_entry (avi); - if (G_UNLIKELY (avi->have_eos)) { /* Clean adapter, we're done */ gst_adapter_clear (avi->adapter); return res; } - /* - if (!gst_avi_demux_sync (avi, &tag, FALSE)) - return FALSE; - */ - /* Iterate until need more data, so adapter won't grow too much */ while (1) { if (G_UNLIKELY (!gst_avi_demux_peek_chunk_info (avi, &tag, &size))) { @@ -4499,7 +4754,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi) avi->offset += 8 + ((size + 1) & ~1); gst_adapter_flush (avi->adapter, 8 + ((size + 1) & ~1)); } else { - avi_stream_context *stream; + GstAviStream *stream; GstClockTime next_ts = 0; GstBuffer *buf; @@ -4519,7 +4774,6 @@ gst_avi_demux_stream_data (GstAviDemux * avi) stream->delay = next_ts; */ - /* parsing of corresponding header may have failed */ if (G_UNLIKELY (!stream->pad)) { GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT, @@ -4657,7 +4911,7 @@ gst_avi_demux_loop (GstPad * pad) push_tag_lists (avi); } /* process each index entry in turn */ - res = gst_avi_demux_stream_data (avi); + res = gst_avi_demux_loop_data (avi); /* pause when error */ if (G_UNLIKELY (res != GST_FLOW_OK)) { diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h index d713fa09..91893929 100644 --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -49,19 +49,6 @@ G_BEGIN_DECLS #define GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME 1 -/* 48 bytes */ -typedef struct { - guint index_nr; /* = (entry-index_entries)/sizeof(gst_avi_index_entry); */ - guchar stream_nr; - guchar flags; - guint64 ts; - guint64 dur; /* =entry[1].ts-entry->ts */ - guint64 offset; - guint64 bytes_before; /* calculated */ - guint32 frames_before; /* calculated */ - guint32 size; /* could be read from the chunk (if we don't split) */ -} gst_avi_index_entry; - /* new index entries 24 bytes */ typedef struct { guint32 flags; @@ -88,15 +75,21 @@ typedef struct { GstBuffer *extradata, *initdata; gchar *name; + /* the start/step/stop entries */ + guint start_entry; + guint step_entry; + guint stop_entry; /* current position (byte, frame, time) and other status vars */ + guint current_entry; guint current_frame; guint64 current_byte; + guint64 current_time; + GstFlowReturn last_flow; gboolean discont; /* stream length */ guint64 total_bytes; - guint32 total_frames; guint32 total_blocks; guint n_keyframes; /* stream length according to index */ @@ -119,7 +112,7 @@ typedef struct { guint idx_max; /* max allocated size of entries */ GstTagList *taglist; -} avi_stream_context; +} GstAviStream; typedef enum { GST_AVI_DEMUX_START, @@ -148,25 +141,19 @@ typedef struct _GstAviDemux { guint64 offset; gboolean abort_buffering; - /* index */ - gst_avi_index_entry *index_entries; - guint index_size; + /* index offset in the file */ guint64 index_offset; - guint current_entry; - guint reverse_start_index; - guint reverse_stop_index; /* streams */ + GstAviStream stream[GST_AVI_DEMUX_MAX_STREAMS]; guint num_streams; guint num_v_streams; guint num_a_streams; guint num_t_streams; /* subtitle text streams */ - avi_stream_context stream[GST_AVI_DEMUX_MAX_STREAMS]; - /* for streaming mode */ - gboolean streaming; - gboolean have_eos; + gboolean streaming; + gboolean have_eos; GstAdapter *adapter; /* some stream info for length */ @@ -179,8 +166,7 @@ typedef struct _GstAviDemux { /* pending tags/events */ GstEvent *seek_event; GstTagList *globaltags; - gboolean got_tags; - + gboolean got_tags; } GstAviDemux; typedef struct _GstAviDemuxClass { -- cgit