CVS Root: /home/cvs/gstreamer Module: gst-plugins Changes by: rbultje Date: Wed Sep 29 2004 02:45:53 PDT Log message: * ext/flac/gstflacdec.c: (gst_flacdec_src_query): Only return true if we actually filled something in. Prevents player applications from showing a random length for flac files. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), (gst_riff_read_use_event), (gst_riff_read_handle_event), (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): OK, ok, so I implemented event handling. Apparently it's normal that we receive random events at random points without asking for it. * gst/avi/gstavidemux.c: (gst_avi_demux_reset), (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), (gst_avi_demux_sync), (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), (gst_avi_demux_loop): * gst/avi/gstavidemux.h: Implement non-lineair chunk handling and subchunk processing. The first solves playback of AVI files where the audio and video data of individual buffers that we read are not synchronized. This should not happen according to the wonderful AVI specs, but of course it does happen in reality. It is also a prerequisite for the second. Subchunk processing allows us to cut chunks in small pieces and process each of these pieces separately. This is required because I've seen several AVI files with incredibly large audio chunks, even some files with only one audio chunk for the whole file. This allows for proper playback including seeking. This patch is supposed to fix all AVI A/V sync issues. * gst/flx/gstflxdec.c: (gst_flxdec_class_init), (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): Work. * gst/modplug/gstmodplug.cc: Proper return value setting for the query() function. * gst/playback/gstplaybasebin.c: (setup_source): Being in non-playing state (after, e.g., EOS) is not necessarily a bad thing. Allow for that. This fixes playback of short files. They don't actually playback fully now, because the clock already runs. This means that small files (<500kB) with a small length (<2sec) will still not or barely play. Other files, such as mod or flx, will work correctly, however. Modified files: . : ChangeLog ext/flac : gstflacdec.c gst-libs/gst/riff: riff-read.c gst/avi : gstavidemux.c gstavidemux.h gst/flx : gstflxdec.c gst/modplug : gstmodplug.cc gst/playback : gstplaybasebin.c Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/ChangeLog.diff?r1=1.1096&r2=1.1097 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/ext/flac/gstflacdec.c.diff?r1=1.48&r2=1.49 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst-libs/gst/riff/riff-read.c.diff?r1=1.29&r2=1.30 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst/avi/gstavidemux.c.diff?r1=1.113&r2=1.114 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst/avi/gstavidemux.h.diff?r1=1.24&r2=1.25 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst/flx/gstflxdec.c.diff?r1=1.39&r2=1.40 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst/modplug/gstmodplug.cc.diff?r1=1.50&r2=1.51 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/gst/playback/gstplaybasebin.c.diff?r1=1.16&r2=1.17 ====Begin Diffs==== Index: ChangeLog =================================================================== RCS file: /home/cvs/gstreamer/gst-plugins/ChangeLog,v retrieving revision 1.1096 retrieving revision 1.1097 diff -u -d -r1.1096 -r1.1097 --- ChangeLog 28 Sep 2004 16:44:12 -0000 1.1096 +++ ChangeLog 29 Sep 2004 09:45:39 -0000 1.1097 @@ -1,3 +1,48 @@ +2004-09-29 Ronald S. Bultje <rb...@ro...> + + * ext/flac/gstflacdec.c: (gst_flacdec_src_query): + Only return true if we actually filled something in. Prevents + player applications from showing a random length for flac files. + * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), + (gst_riff_read_use_event), (gst_riff_read_handle_event), + (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), + (gst_riff_read_strf_vids_with_data), + (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): + OK, ok, so I implemented event handling. Apparently it's normal + that we receive random events at random points without asking + for it. + * gst/avi/gstavidemux.c: (gst_avi_demux_reset), + (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), + (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), + (gst_avi_demux_sync), (gst_avi_demux_stream_scan), + (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), + (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), + (gst_avi_demux_stream_data), (gst_avi_demux_loop): + * gst/avi/gstavidemux.h: + Implement non-lineair chunk handling and subchunk processing. + The first solves playback of AVI files where the audio and video + data of individual buffers that we read are not synchronized. + This should not happen according to the wonderful AVI specs, but + of course it does happen in reality. It is also a prerequisite for + the second. Subchunk processing allows us to cut chunks in small + pieces and process each of these pieces separately. This is + required because I've seen several AVI files with incredibly large + audio chunks, even some files with only one audio chunk for the + whole file. This allows for proper playback including seeking. + This patch is supposed to fix all AVI A/V sync issues. + * gst/flx/gstflxdec.c: (gst_flxdec_class_init), + (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): + Work. + * gst/modplug/gstmodplug.cc: + Proper return value setting for the query() function. + * gst/playback/gstplaybasebin.c: (setup_source): + Being in non-playing state (after, e.g., EOS) is not necessarily + a bad thing. Allow for that. This fixes playback of short files. + They don't actually playback fully now, because the clock already + runs. This means that small files (<500kB) with a small length + (<2sec) will still not or barely play. Other files, such as mod + or flx, will work correctly, however. 2004-09-28 Wim Taymans <wi...@fl...> * ext/speex/gstspeex.c: (plugin_init): Index: gstflacdec.c RCS file: /home/cvs/gstreamer/gst-plugins/ext/flac/gstflacdec.c,v retrieving revision 1.48 retrieving revision 1.49 diff -u -d -r1.48 -r1.49 --- gstflacdec.c 3 Jul 2004 04:27:18 -0000 1.48 +++ gstflacdec.c 29 Sep 2004 09:45:40 -0000 1.49 @@ -700,12 +700,12 @@ else samples = flacdec->stream_samples; - gst_pad_convert (flacdec->srcpad, + res = gst_pad_convert (flacdec->srcpad, GST_FORMAT_DEFAULT, samples, format, value); break; } case GST_QUERY_POSITION: GST_FORMAT_DEFAULT, flacdec->total_samples, format, value); default: Index: riff-read.c RCS file: /home/cvs/gstreamer/gst-plugins/gst-libs/gst/riff/riff-read.c,v retrieving revision 1.29 retrieving revision 1.30 diff -u -d -r1.29 -r1.30 --- riff-read.c 23 Sep 2004 14:59:22 -0000 1.29 +++ riff-read.c 29 Sep 2004 09:45:40 -0000 1.30 @@ -29,6 +29,9 @@ #include "riff-ids.h" #include "riff-read.h" +GST_DEBUG_CATEGORY_STATIC (riffread_debug); +#define GST_CAT_DEFAULT riffread_debug enum { ARG_0, @@ -76,6 +79,9 @@ parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + GST_DEBUG_CATEGORY_INIT (riffread_debug, "riffread", + 0, "RIFF stream helper class"); gstelement_class->change_state = gst_riff_read_change_state; } @@ -146,6 +152,64 @@ /* + * Event handler. Basic: + * - EOS: end-of-file, stop processing, forward EOS. + * - Interrupt: stop processing. + * - Discont: shouldn't be handled here but in the seek handler. Error. + * - Flush: ignore, since we check for flush flags manually. Don't forward. + * - Others: warn, ignore. + * Return value indicates whether to continue processing. + */ +static gboolean +gst_riff_read_use_event (GstRiffRead * riff, GstEvent * event) +{ + if (!event) { + GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); + return FALSE; + } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_pad_event_default (riff->sinkpad, event); + return FALSE; + case GST_EVENT_INTERRUPT: + gst_event_unref (event); + case GST_EVENT_DISCONTINUOUS: + GST_WARNING_OBJECT (riff, "Unexected discont - might lose sync"); + return TRUE; + case GST_EVENT_FLUSH: + default: + GST_WARNING ("don't know how to handle event %d", GST_EVENT_TYPE (event)); + /* happy */ + g_assert_not_reached (); + return FALSE; +} +gst_riff_read_handle_event (GstRiffRead * riff) + GstEvent *event = NULL; + guint32 remaining; + gst_bytestream_get_status (riff->bs, &remaining, &event); + return gst_riff_read_use_event (riff, event); +/* * Read the next tag plus length (may be NULL). Return * TRUE on success or FALSE on failure. */ @@ -174,21 +238,8 @@ /* read */ while (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) { - GstEvent *event = NULL; - guint32 remaining; - - /* Here, we might encounter EOS */ - gst_bytestream_get_status (riff->bs, &remaining, &event); - if (event && GST_IS_EVENT (event)) { - gboolean eos = (GST_EVENT_TYPE (event) == GST_EVENT_EOS); - gst_pad_event_default (riff->sinkpad, event); - if (eos) - return FALSE; - } else { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); + if (!gst_riff_read_handle_event (riff)) return FALSE; - } } /* parse tag + length (if wanted) */ @@ -215,33 +266,10 @@ guint32 got; while ((got = gst_bytestream_peek (riff->bs, &buf, length)) != length) { - /*GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); */ - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - if (buf) - gst_buffer_unref (buf); - if (got_bytes) - *got_bytes = got; - return NULL; - } - if (buf) - gst_buffer_unref (buf); - if (got_bytes) - *got_bytes = got; + if (buf) + gst_buffer_unref (buf); return NULL; /* we need 16-bit alignment */ @@ -275,7 +303,9 @@ /* first, flush remaining buffers */ gst_bytestream_get_status (riff->bs, &remaining, &event); if (event) { - g_warning ("Unexpected event before seek"); + GST_WARNING ("Unexpected event before seek"); + if (!gst_riff_read_use_event (riff, event)) + return NULL; if (remaining) @@ -301,10 +331,7 @@ GST_WARNING ("No discontinuity event after seek - seek failed"); } else if (GST_EVENT_TYPE (event) != GST_EVENT_DISCONTINUOUS) { - GstEventType type = GST_EVENT_TYPE (event); - if (type == GST_EVENT_EOS || type == GST_EVENT_INTERRUPT) + if (!gst_riff_read_use_event (riff, event)) return NULL; event = NULL; @@ -361,7 +388,7 @@ gst_riff_read_skip (GstRiffRead * riff) guint32 tag, length; - GstEvent *event; guint32 remaining; if (!gst_riff_peek_head (riff, &tag, &length, NULL)) @@ -376,12 +403,9 @@ /* see if we have that much data available */ - if (event && GST_IS_EVENT (event)) { - gboolean eos = (GST_EVENT_TYPE (event) == GST_EVENT_EOS); - g_warning ("Unexpected event in skip"); - gst_pad_event_default (riff->sinkpad, event); - if (eos) + if (event) { + GST_WARNING ("Unexpected event in skip"); @@ -458,7 +482,7 @@ return FALSE; if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strh)) { - g_warning ("Too small strh (%d available, %d needed)", + GST_WARNING ("Too small strh (%d available, %d needed)", GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strh)); gst_buffer_unref (buf); @@ -526,7 +550,7 @@ if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_vids)) { - g_warning ("Too small strf_vids (%d available, %d needed)", + GST_WARNING ("Too small strf_vids (%d available, %d needed)", GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_vids)); @@ -620,7 +644,7 @@ if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_auds)) { - g_warning ("Too small strf_auds (%d available, %d needed)", + GST_WARNING ("Too small strf_auds (%d available, %d needed)", GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_auds)); @@ -702,7 +726,7 @@ if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_iavs)) { - g_warning ("Too small strf_iavs (%d available, %d needed)", + GST_WARNING ("Too small strf_iavs (%d available, %d needed)", GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_iavs)); Index: gstavidemux.c RCS file: /home/cvs/gstreamer/gst-plugins/gst/avi/gstavidemux.c,v retrieving revision 1.113 retrieving revision 1.114 diff -u -d -r1.113 -r1.114 --- gstavidemux.c 23 Sep 2004 14:59:22 -0000 1.113 +++ gstavidemux.c 29 Sep 2004 09:45:40 -0000 1.114 @@ -177,6 +177,7 @@ avi->index_entries = NULL; avi->index_size = 0; + avi->current_entry = 0; avi->num_frames = 0; avi->us_per_frame = 0; @@ -310,8 +311,7 @@ case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: - *dest_value = src_value * stream->strh->rate / - (stream->strh->scale * GST_SECOND); + *dest_value = src_value * stream->bitrate / GST_SECOND; break; case GST_FORMAT_DEFAULT: *dest_value = src_value * stream->strh->rate / @@ -325,7 +325,7 @@ case GST_FORMAT_BYTES: case GST_FORMAT_TIME: - *dest_value = ((gfloat) src_value) * GST_SECOND / stream->strh->rate; + *dest_value = ((gfloat) src_value) * GST_SECOND / stream->bitrate; default: res = FALSE; @@ -386,9 +386,12 @@ res = FALSE; - if (stream->strh->type == GST_RIFF_FCC_auds) - *value = stream->strh->length * stream->strh->samplesize; - else if (stream->strh->type == GST_RIFF_FCC_vids) + if (stream->strh->type == GST_RIFF_FCC_auds) { + if (!stream->strh->samplesize) + *value = stream->total_frames; + else + *value = stream->total_bytes / stream->strh->samplesize; + } else if (stream->strh->type == GST_RIFF_FCC_vids) *value = stream->strh->length; else @@ -402,20 +405,23 @@ switch (*format) { if (stream->strh->type == GST_RIFF_FCC_auds) { - if (stream->strh->samplesize == 1 && stream->blockalign != 0) { - *value = stream->current_byte * GST_SECOND / - (stream->blockalign * stream->strh->rate); + if (!stream->strh->samplesize) { + *value = GST_SECOND * stream->current_frame * + stream->strh->scale / stream->strh->rate; } else if (stream->bitrate != 0) { *value = ((gfloat) stream->current_byte) * GST_SECOND / stream->bitrate; - } else if (stream->total_frames != 0) { + } else if (stream->total_frames != 0 && stream->total_bytes != 0) { /* calculate timestamps based on video size */ guint64 len = demux->us_per_frame * demux->num_frames * GST_USECOND; - *value = len * stream->current_frame / stream->total_frames; + if (!stream->strh->samplesize) + *value = len * stream->current_frame / stream->total_frames; + else + *value = len * stream->current_byte / stream->total_bytes; } else { - *value = 0; + res = FALSE; } } else { if (stream->strh->rate != 0) { @@ -433,7 +439,7 @@ if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) - *value = stream->current_byte * stream->strh->samplesize; + *value = stream->current_byte / stream->strh->samplesize; *value = stream->current_frame; @@ -551,6 +557,9 @@ GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); avi_stream_context *stream; + if (!avi->index_entries) stream = gst_pad_get_element_private (pad); switch (GST_EVENT_TYPE (event)) { @@ -596,6 +605,9 @@ avi->seek_offset = seek_entry->offset + avi->index_offset; avi->last_seek = entry->ts; + avi->seek_flush = + (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH); + avi->seek_entry = entry->index_nr; GST_DEBUG ("no index entry found for format=%d value=%" G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset); @@ -623,7 +635,7 @@ * "Open" a RIFF file. -gboolean gst_avi_demux_stream_init (GstAviDemux * avi) GstRiffRead *riff = GST_RIFF_READ (avi); @@ -643,7 +655,7 @@ * Read 'avih' header. gst_avi_demux_stream_avih (GstAviDemux * avi, guint32 * flags, guint32 * streams) @@ -1003,9 +1015,11 @@ * Seek to index, read it, seek back. + * Return value indicates if we can continue processing. It + * does not indicate if index-reading succeeded. gst_avi_demux_stream_index (GstAviDemux * avi) GstBuffer *buf = NULL; @@ -1020,7 +1034,11 @@ length = gst_bytestream_length (riff->bs); pos_before = gst_bytestream_tell (riff->bs); - /* skip movi */ + /* skip movi + * + * FIXME: + * - we want to add error handling here so we can recover. + */ if (!gst_riff_read_skip (riff)) @@ -1079,7 +1097,7 @@ target->index_nr = i; target->flags = entry.flags; target->size = entry.size; - target->offset = entry.offset; + target->offset = entry.offset + 8; /* figure out if the index is 0 based or relative to the MOVI start */ if (i == 0) { @@ -1102,11 +1120,16 @@ /* constant rate stream */ gst_pad_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes, &format, &target->ts); + gst_pad_convert (stream->pad, GST_FORMAT_BYTES, + stream->total_bytes + target->size, &format, &target->dur); } else { /* VBR stream */ gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames, &format, &target->ts); + gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames + 1, &format, &target->dur); + target->dur -= target->ts; stream->total_bytes += target->size; stream->total_frames++; @@ -1134,20 +1157,323 @@ + * Sync to next data chunk. +gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos) + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 tag; + guint64 length = gst_bytestream_length (riff->bs); + if (gst_bytestream_tell (riff->bs) + 12 >= length) + /* peek first (for the end of this 'list/movi' section) */ + if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) + /* if we're at top-level, we didn't read the 'movi' + * list tag yet. This can also be 'AVIX' in case of + * openDML-2.0 AVI files. Lastly, it might be idx1, + * in which case we skip it so we come at EOS. */ + while (g_list_length (riff->level) < 2) { + if (gst_bytestream_tell (riff->bs) + 12 >= length) + if (!(tag = gst_riff_peek_tag (riff, NULL))) + switch (tag) { + case GST_RIFF_TAG_LIST: + if (!(tag = gst_riff_peek_list (riff))) + return FALSE; + switch (tag) { + case GST_RIFF_LIST_AVIX: + case GST_RIFF_LIST_movi: + if (!gst_riff_read_list (riff, &tag)) + return FALSE; + /* we're now going to read buffers! */ + break; + default: + GST_WARNING ("Unknown list " GST_FOURCC_FORMAT " before AVI data", + GST_FOURCC_ARGS (tag)); + /* fall-through */ + case GST_RIFF_TAG_JUNK: + if (!gst_riff_read_skip (riff)) + } + break; + default: + GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " before AVI data", + GST_FOURCC_ARGS (tag)); + /* fall-through */ + case GST_RIFF_TAG_idx1: + case GST_RIFF_TAG_JUNK: + if (!gst_riff_read_skip (riff)) + } + /* And then, we get the data */ + if (!(tag = gst_riff_peek_tag (riff, NULL))) + /* Support for rec-list files */ + switch (tag) { + case GST_RIFF_TAG_LIST: + if (!(tag = gst_riff_peek_list (riff))) + return FALSE; + if (tag == GST_RIFF_rec) { + /* Simply skip the list */ + if (!gst_riff_read_list (riff, &tag)) + if (!(tag = gst_riff_peek_tag (riff, NULL))) + } + break; + case GST_RIFF_TAG_JUNK: + gst_riff_read_skip (riff); + if (ret_tag) + *ret_tag = tag; + return TRUE; * Scan the file for all chunks to "create" a new index. + * Return value indicates if we can continue reading the stream. It + * does not say anything about whether we created an index. gst_avi_demux_stream_scan (GstAviDemux * avi) - //GstRiffRead *riff = GST_RIFF_READ (avi); + gst_avi_index_entry *entry; + avi_stream_context *stream; + guint64 pos = gst_bytestream_tell (riff->bs); + GstEvent *event; - /* FIXME */ + /* FIXME: + * - implement non-seekable source support. + GST_LOG_OBJECT (avi, "Creating index"); + while (gst_avi_demux_sync (avi, &tag, TRUE)) { + gint stream_nr = CHUNKID_TO_STREAMNR (tag); + guint8 *data; + GstFormat format = GST_FORMAT_TIME; + if (stream_nr < 0 || stream_nr >= avi->num_streams) + goto next; + stream = &avi->stream[stream_nr]; + /* get chunk size */ + if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) + /* increase allocated size for index */ + if (avi->index_size % 256 == 0) { + avi->index_entries = g_renew (gst_avi_index_entry, + avi->index_entries, avi->index_size + 256); + entry = &avi->index_entries[avi->index_size]; + /* fill in */ + entry->index_nr = avi->index_size++; + entry->stream_nr = stream_nr; + entry->flags = 0; + entry->offset = gst_bytestream_tell (riff->bs) + 8; + entry->size = GST_READ_UINT32_LE (&data[4]); + /* timestamps */ + if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { + /* constant rate stream */ + stream->total_bytes, &format, &entry->ts); + stream->total_bytes + entry->size, &format, &entry->dur); + } else { + /* VBR stream */ + stream->total_frames, &format, &entry->ts); + stream->total_frames + 1, &format, &entry->dur); + entry->dur -= entry->ts; + /* stream position */ + entry->bytes_before = stream->total_bytes; + stream->total_bytes += entry->size; + entry->frames_before = stream->total_frames; + stream->total_frames++; + next: + if (!gst_riff_read_skip (riff)) + avi->index_offset = 0; + /* seek back */ + if (!(event = gst_riff_read_seek (riff, pos))) + gst_event_unref (event); + GST_LOG_OBJECT (avi, "index created, %d items", avi->index_size); return TRUE; + * Massage index. + * We're going to go over each entry in the index and finetune + * some things we don't like about AVI. For example, a single + * chunk might be too long. Also, individual streams might be + * out-of-sync. In the first case, we cut the chunk in several + * smaller pieces. In the second case, we re-order chunk reading + * order. The end result should be a smoother playing AVI. +static void +gst_avi_demux_massage_index (GstAviDemux * avi) + gint i; + /* init frames */ + for (i = 0; i < avi->num_streams; i++) { + stream = &avi->stream[i]; + if (stream->strh->type == GST_RIFF_FCC_vids) + stream->delay = stream->strh->init_frames * GST_SECOND * + stream->strh->scale / stream->strh->rate; + else + stream->delay = GST_SECOND * stream->strh->init_frames * + stream->strh->length / (stream->total_frames * stream->bitrate); + for (i = 0; i < avi->index_size; i++) { + entry = &avi->index_entries[i]; + if (entry->stream_nr >= avi->num_streams) + continue; + stream = &avi->stream[entry->stream_nr]; + entry->ts += stream->delay; + /* cut chunks in small (seekable) pieces */ +#define MAX_DURATION (GST_SECOND / 4) + /* check for max duration of a single buffer. I suppose that + * the allocation of index entries could be improved. */ + if (entry->dur > MAX_DURATION && stream->strh->type == GST_RIFF_FCC_auds) { + guint32 ideal_size = stream->bitrate / 10; + gst_avi_index_entry *entries; + gint old_size, n, num_added; + /* copy index */ + old_size = entry->size; + num_added = (entry->size - 1) / ideal_size; + avi->index_size += num_added; + entries = g_malloc (sizeof (gst_avi_index_entry) * avi->index_size); + memcpy (entries, avi->index_entries, + sizeof (gst_avi_index_entry) * (entry->index_nr + 1)); + if (entry->index_nr < avi->index_size - num_added - 1) { + memcpy (&entries[entry->index_nr + 1 + num_added], + &avi->index_entries[entry->index_nr + 1], + (avi->index_size - num_added - entry->index_nr - 1) * + sizeof (gst_avi_index_entry)); + for (n = entry->index_nr + 1 + num_added; n < avi->index_size; n++) { + entries[n].index_nr += num_added; + if (entries[n].stream_nr == entry->stream_nr) + entries[n].frames_before += num_added; + /* new sized index chunks */ + for (n = entry->index_nr; n < entry->index_nr + num_added + 1; n++) { + if (old_size >= ideal_size) { + entries[n].size = ideal_size; + old_size -= ideal_size; + } else + entries[n].size = old_size; + entries[n].dur = GST_SECOND * entries[n].size / stream->bitrate; + if (n != entry->index_nr) { + memcpy (&entries[n], &entries[n - 1], sizeof (gst_avi_index_entry)); + entries[n].index_nr++; + entries[n].ts += entries[n - 1].dur; + entries[n].offset += entries[n - 1].size; + entries[n].bytes_before += entries[n - 1].size; + entries[n].frames_before++; + i++; + /* set new pointer */ + g_free (avi->index_entries); + avi->index_entries = entries; + /* re-order for time */ + for (i = 1; i < avi->index_size; i++) { + /* check whether to rearrange according to time */ + while (i > 0 && avi->index_entries[i - 1].stream_nr < avi->num_streams && + (entry->ts < avi->index_entries[i - 1].ts || + (entry->ts == avi->index_entries[i - 1].ts && + entry->stream_nr < avi->index_entries[i - 1].stream_nr))) { + gst_avi_index_entry prev_entry; + /* move around */ + memcpy (&prev_entry, &avi->index_entries[i - 1], + sizeof (gst_avi_index_entry)); + entry->index_nr--; + memcpy (&avi->index_entries[i - 1], entry, sizeof (gst_avi_index_entry)); + memcpy (entry, &prev_entry, sizeof (gst_avi_index_entry)); + entry->index_nr++; + /* update pointer */ + entry = &avi->index_entries[i - 1]; + i--; * Read full AVI headers. @@ -1280,10 +1606,12 @@ if (flags & GST_RIFF_AVIH_HASINDEX) { if (!gst_avi_demux_stream_index (avi)) - } else { + if (!avi->index_size) { if (!gst_avi_demux_stream_scan (avi)) + gst_avi_demux_massage_index (avi); @@ -1295,23 +1623,24 @@ static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi) - GstRiffRead *riff = GST_RIFF_READ (avi); guint i; GstEvent *event; /* FIXME: if we seek in an openDML file, we will have multiple * primary levels. Seeking in between those will cause havoc. */ - if (!(event = gst_riff_read_seek (riff, avi->seek_offset))) - return FALSE; - gst_event_unref (event); + avi->current_entry = avi->seek_entry; for (i = 0; i < avi->num_streams; i++) { avi_stream_context *stream = &avi->stream[i]; if (GST_PAD_IS_USABLE (stream->pad)) { + if (avi->seek_flush) { + event = gst_event_new (GST_EVENT_FLUSH); + gst_pad_push (stream->pad, GST_DATA (event)); event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - avi->last_seek + stream->delay, NULL); + avi->last_seek, NULL); gst_pad_push (stream->pad, GST_DATA (event)); @@ -1319,6 +1648,74 @@ +gst_avi_demux_process_next_entry (GstAviDemux * avi) + gboolean processed; + for (processed = FALSE; !processed;) { + if (avi->current_entry >= avi->index_size) { + gst_bytestream_seek (riff->bs, 0, GST_SEEK_METHOD_END); + /* get eos */ + gst_riff_peek_tag (GST_RIFF_READ (avi), &avi->level_up); + gst_pad_event_default (avi->sinkpad, gst_event_new (GST_EVENT_EOS)); + processed = TRUE; + GstBuffer *buf; + guint got; + gst_avi_index_entry *entry = &avi->index_entries[avi->current_entry++]; + avi_stream_context *stream; + if (entry->stream_nr >= avi->num_streams) { + continue; + stream = &avi->stream[entry->stream_nr]; + if (GST_PAD_IS_USABLE (stream->pad) && entry->size > 0) { + guint64 needed_off = entry->offset + avi->index_offset, pos; + guint32 remain; + pos = gst_bytestream_tell (riff->bs); + gst_bytestream_get_status (riff->bs, &remain, NULL); + if (pos <= needed_off && needed_off - pos <= remain) { + gst_bytestream_flush_fast (riff->bs, needed_off - pos); + } else { + GstEvent *event; + event = gst_riff_read_seek (riff, needed_off); + if (event) + gst_event_unref (event); + else { + GST_ELEMENT_ERROR (avi, RESOURCE, READ, (NULL), (NULL)); + return FALSE; + } + if (!(buf = gst_riff_read_element_data (riff, entry->size, &got))) { + if (entry->flags & GST_RIFF_IF_KEYFRAME) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_KEY_UNIT); + GST_BUFFER_TIMESTAMP (buf) = entry->ts; + GST_BUFFER_DURATION (buf) = entry->dur; + GST_DEBUG_OBJECT (avi, "Processing buffer of size %d and time %" + GST_TIME_FORMAT " on pad %s", + GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + gst_pad_get_name (stream->pad)); + gst_pad_push (stream->pad, GST_DATA (buf)); + processed = TRUE; + stream->current_frame++; + stream->current_byte += entry->size; * Read data. @@ -1329,7 +1726,6 @@ guint32 tag; guint stream_nr; - gst_avi_index_entry *entry; if (avi->seek_offset != (guint64) - 1) { if (!gst_avi_demux_handle_seek (avi)) @@ -1337,80 +1733,16 @@ avi->seek_offset = (guint64) - 1; - /* peek first (for the end of this 'list/movi' section) */ - if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) - /* if we're at top-level, we didn't read the 'movi' - * list tag yet. This can also be 'AVIX' in case of - * openDML-2.0 AVI files. Lastly, it might be idx1, - * in which case we skip it so we come at EOS. */ - while (g_list_length (riff->level) < 2) { - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - switch (tag) { - case GST_RIFF_TAG_LIST: - if (!(tag = gst_riff_peek_list (riff))) - return FALSE; - switch (tag) { - case GST_RIFF_LIST_AVIX: - case GST_RIFF_LIST_movi: - if (!gst_riff_read_list (riff, &tag)) - return FALSE; - /* we're now going to read buffers! */ - break; - default: - GST_WARNING ("Unknown list " GST_FOURCC_FORMAT " before AVI data", - GST_FOURCC_ARGS (tag)); - /* fall-through */ - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) - } - break; - default: - GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " before AVI data", - GST_FOURCC_ARGS (tag)); - /* fall-through */ - case GST_RIFF_TAG_idx1: - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) + /* 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 (avi->index_entries != NULL) { + return gst_avi_demux_process_next_entry (avi); - /* And then, we get the data */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) + if (!gst_avi_demux_sync (avi, &tag, FALSE)) - /* Support for rec-list files */ - switch (tag) { - case GST_RIFF_TAG_LIST: - if (!(tag = gst_riff_peek_list (riff))) - if (tag == GST_RIFF_rec) { - /* Simply skip the list */ - if (!gst_riff_read_list (riff, &tag)) - if (!(tag = gst_riff_peek_tag (riff, NULL))) - break; - case GST_RIFF_TAG_JUNK: - return gst_riff_read_skip (riff); - } stream_nr = CHUNKID_TO_STREAMNR (tag); if (stream_nr < 0 || stream_nr >= avi->num_streams) { /* recoverable */ g_warning ("Invalid stream ID %d (" GST_FOURCC_FORMAT ")", @@ -1429,14 +1761,6 @@ /* get time of this buffer */ stream = &avi->stream[stream_nr]; - entry = gst_avi_demux_index_next (avi, stream_nr, - stream->current_entry + 1, 0); - if (entry) { - stream->current_entry = entry->index_nr; - if (entry->flags & GST_RIFF_IF_KEYFRAME) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_KEY_UNIT); format = GST_FORMAT_TIME; gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &next_ts); @@ -1487,7 +1811,7 @@ if (!gst_avi_demux_stream_header (avi)) return; avi->state = GST_AVI_DEMUX_MOVI; - /* fall-through */ case GST_AVI_DEMUX_MOVI: if (!gst_avi_demux_stream_data (avi)) Index: gstavidemux.h RCS file: /home/cvs/gstreamer/gst-plugins/gst/avi/gstavidemux.h,v retrieving revision 1.24 retrieving revision 1.25 diff -u -d -r1.24 -r1.25 --- gstavidemux.h 23 Sep 2004 14:59:22 -0000 1.24 +++ gstavidemux.h 29 Sep 2004 09:45:40 -0000 1.25 @@ -47,9 +47,9 @@ typedef struct { gint index_nr; gint stream_nr; - guint64 ts; + guint64 ts, dur; guint32 flags; - guint32 offset; + guint64 offset; gint size; guint64 bytes_before; guint32 frames_before; @@ -100,6 +100,7 @@ gst_avi_index_entry *index_entries; guint index_size; guint64 index_offset; + guint current_entry; /* streams */ guint num_streams; @@ -114,6 +115,8 @@ /* seeking */ guint64 seek_offset; guint64 last_seek; + gint seek_entry; + gboolean seek_flush; } GstAviDemux; typedef struct _GstAviDemuxClass { Index: gstflxdec.c RCS file: /home/cvs/gstreamer/gst-plugins/gst/flx/gstflxdec.c,v retrieving revision 1.39 retrieving revision 1.40 diff -u -d -r1.39 -r1.40 --- gstflxdec.c 15 Mar 2004 19:32:05 -0000 1.39 +++ gstflxdec.c 29 Sep 2004 09:45:40 -0000 1.40 @@ -28,6 +28,9 @@ #define JIFFIE (GST_SECOND/70) +GST_DEBUG_CATEGORY_STATIC (flxdec_debug); +#define GST_CAT_DEFAULT flxdec_debug /* flx element information */ static GstElementDetails flxdec_details = { "FLX Decoder", @@ -133,6 +136,8 @@ + GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder"); gobject_class->set_property = gst_flxdec_set_property; gobject_class->get_property = gst_flxdec_get_property; @@ -206,9 +211,8 @@ break; default: - g_print ("GstFlxDec: Unimplented chunk type: 0x%02x size: %d\n", + GST_WARNING ("Unimplented chunk type: 0x%02x size: %d - skipping", hdr->id, hdr->size); - g_print ("GstFlxDec: Skipping...\n"); data += rndalign (hdr->size) - FlxFrameChunkSize; @@ -228,7 +232,7 @@ data += 2; indx = 0; - g_print ("GstFlxDec: cmap packs: %d\n", packs); + GST_LOG ("GstFlxDec: cmap packs: %d", packs); while (packs--) { /* color map index + skip count */ indx += *data++; @@ -238,7 +242,7 @@ if (count == 0) count = 256; - g_print ("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx); + GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx); flx_set_palette_vector (flxdec->converter, indx, count, data, scale); data += (count * 3); @@ -449,7 +453,7 @@ databuf = flx_get_data (flxdec, FlxHeaderSize); if (!databuf) { - g_print ("empty buffer\n"); + GST_LOG ("empty buffer"); return; @@ -470,12 +474,12 @@ - g_print ("GstFlxDec: size : %d\n", flxh->size); - g_print ("GstFlxDec: frames : %d\n", flxh->frames); - g_print ("GstFlxDec: width : %d\n", flxh->width); - g_print ("GstFlxDec: height : %d\n", flxh->height); - g_print ("GstFlxDec: depth : %d\n", flxh->depth); - g_print ("GstFlxDec: speed : %d\n", flxh->speed); + GST_LOG ("size : %d\n", flxh->size); + GST_LOG ("frames : %d\n", flxh->frames); + GST_LOG ("width : %d\n", flxh->width); + GST_LOG ("height : %d\n", flxh->height); + GST_LOG ("depth : %d\n", flxh->depth); + GST_LOG ("speed : %d\n", flxh->speed); flxdec->next_time = 0; @@ -496,10 +500,10 @@ flx_colorspace_converter_new (flxh->width, flxh->height); if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) { - g_print ("GstFlxDec: (FLC) aspect_dx : %d\n", flxh->aspect_dx); - g_print ("GstFlxDec: (FLC) aspect_dy : %d\n", flxh->aspect_dy); - g_print ("GstFlxDec: (FLC) oframe1 : 0x%08x\n", flxh->oframe1); - g_print ("GstFlxDec: (FLC) oframe2 : 0x%08x\n", flxh->oframe2); + GST_LOG ("(FLC) aspect_dx : %d\n", flxh->aspect_dx); + GST_LOG ("(FLC) aspect_dy : %d\n", flxh->aspect_dy); + GST_LOG ("(FLC) oframe1 : 0x%08x\n", flxh->oframe1); + GST_LOG ("(FLC) oframe2 : 0x%08x\n", flxh->oframe2); flxdec->size = (flxh->width * flxh->height); Index: gstmodplug.cc RCS file: /home/cvs/gstreamer/gst-plugins/gst/modplug/gstmodplug.cc,v retrieving revision 1.50 retrieving revision 1.51 diff -u -d -r1.50 -r1.51 --- gstmodplug.cc 21 May 2004 22:39:26 -0000 1.50 +++ gstmodplug.cc 29 Sep 2004 09:45:40 -0000 1.51 @@ -346,15 +346,19 @@ - default: + case GST_FORMAT_TIME: tmp = ((float) (modplug->mSoundFile->GetSongTime () * modplug->mSoundFile->GetCurrentPos ()) / (float) modplug->mSoundFile->GetMaxPosition ()); *value = (gint64) (tmp * GST_SECOND); + default: + res = FALSE; + break; } + res = FALSE; Index: gstplaybasebin.c RCS file: /home/cvs/gstreamer/gst-plugins/gst/playback/gstplaybasebin.c,v retrieving revision 1.16 retrieving revision 1.17 diff -u -d -r1.16 -r1.17 --- gstplaybasebin.c 24 Sep 2004 17:33:00 -0000 1.16 +++ gstplaybasebin.c 29 Sep 2004 09:45:40 -0000 1.17 @@ -453,7 +453,7 @@ play_base_bin->need_rebuild = FALSE; - return (GST_STATE (play_base_bin->thread) == GST_STATE_PLAYING); static void |