Commit f7ea1932 authored by Ronald S. Bultje's avatar Ronald S. Bultje
Browse files

ext/gnomevfs/gstgnomevfssrc.c: Some debug.

Original commit message from CVS:
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnomevfssrc_get),
(gst_gnomevfssrc_srcpad_query), (gst_gnomevfssrc_srcpad_event):
Some debug.
* gst/avi/gstavidemux.c: (gst_avi_demux_reset),
(gst_avi_demux_handle_src_event), (gst_avi_demux_read_superindex),
(gst_avi_demux_read_subindexes), (gst_avi_demux_add_stream),
(gst_avi_demux_stream_index), (gst_avi_demux_skip),
(gst_avi_demux_sync), (gst_avi_demux_stream_scan),
(gst_avi_demux_massage_index), (gst_avi_demux_stream_header):
* gst/avi/gstavidemux.h:
Support for openDML-2.0 indx/ix## chunks. Support for broken index
recovery (where, if part of the index is broken, we will still read
the rest of the index and recover the broken part by stream
scanning). More broken media support. EOS workarounds. General AVI
braindamage headache recovery. Aspirin included.
parent a14944e6
2004-10-12 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnomevfssrc_get),
(gst_gnomevfssrc_srcpad_query), (gst_gnomevfssrc_srcpad_event):
Some debug.
* gst/avi/gstavidemux.c: (gst_avi_demux_reset),
(gst_avi_demux_handle_src_event), (gst_avi_demux_read_superindex),
(gst_avi_demux_read_subindexes), (gst_avi_demux_add_stream),
(gst_avi_demux_stream_index), (gst_avi_demux_skip),
(gst_avi_demux_sync), (gst_avi_demux_stream_scan),
(gst_avi_demux_massage_index), (gst_avi_demux_stream_header):
* gst/avi/gstavidemux.h:
Support for openDML-2.0 indx/ix## chunks. Support for broken index
recovery (where, if part of the index is broken, we will still read
the rest of the index and recover the broken part by stream
scanning). More broken media support. EOS workarounds. General AVI
braindamage headache recovery. Aspirin included.
2004-10-11 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* ext/cdparanoia/gstcdparanoia.c: (cdparanoia_open),
......
......@@ -177,6 +177,7 @@ gst_avi_demux_reset (GstAviDemux * avi)
avi->index_entries = NULL;
}
avi->index_size = 0;
avi->index_offset = 0;
avi->current_entry = 0;
avi->num_frames = 0;
......@@ -558,6 +559,7 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
avi->seek_flush =
(GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH);
avi->seek_entry = entry->index_nr;
GST_DEBUG ("Will seek to entry %d", avi->seek_entry);
} else {
GST_DEBUG ("no index entry found for format=%d value=%"
G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
......@@ -673,6 +675,197 @@ gst_avi_demux_stream_avih (GstAviDemux * avi,
return TRUE;
}
/*
* Read superindex/subindex (openDML-2).
*/
static gboolean
gst_avi_demux_read_superindex (GstAviDemux * avi,
gint stream_nr, guint64 ** locations)
{
GstRiffRead *riff = GST_RIFF_READ (avi);
guint32 tag;
GstBuffer *buf;
guint8 *data;
gint bpe = 16, num, i;
guint64 *indexes;
if (!gst_riff_read_data (riff, &tag, &buf))
return FALSE;
data = GST_BUFFER_DATA (buf);
if (tag != GST_MAKE_FOURCC ('i', 'n', 'd', 'x') &&
tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream_nr / 10,
'0' + stream_nr % 10)) {
g_warning ("Not an indx/ix## chunk");
gst_buffer_unref (buf);
return FALSE;
}
/* check type of index. The opendml2 specs state that
* there should be 4 dwords per array entry. Type can be
* either frame or field (and we don't care). */
if (GST_READ_UINT16_LE (data) != 4 ||
(data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
GST_WARNING ("Superindex for stream %d has unexpected "
"size_entry %d (bytes) or flags 0x%02x/0x%02x",
GST_READ_UINT16_LE (data), data[2], data[3]);
bpe = GST_READ_UINT16_LE (data) * 4;
}
num = GST_READ_UINT32_LE (&data[4]);
indexes = g_new (guint64, num + 1);
for (i = 0; i < num; i++) {
if (GST_BUFFER_SIZE (buf) < 24 + bpe * (i + 1))
break;
indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
}
indexes[i] = 0;
*locations = indexes;
gst_buffer_unref (buf);
return TRUE;
}
static gboolean
gst_avi_demux_read_subindexes (GstAviDemux * avi,
GList ** index, GList ** alloc_list)
{
GstRiffRead *riff = GST_RIFF_READ (avi);
guint64 pos = gst_bytestream_tell (riff->bs),
length = gst_bytestream_length (riff->bs), baseoff;
GstEvent *event;
GList *list = NULL;
gst_avi_index_entry *entries, *entry;
guint32 tag;
GstBuffer *buf;
guint8 *data;
GstFormat format = GST_FORMAT_TIME;
gint bpe, num, x, i, n;
for (n = 0; n < avi->num_streams; n++) {
avi_stream_context *stream = &avi->stream[n];
for (i = 0; stream->indexes[i] != 0; i++) {
/* eos check again */
if (stream->indexes[i] + 8 >= length) {
GST_WARNING ("Subindex %d for stream %d doesn't exist", i, n);
continue;
}
/* seek to that point */
if (!(event = gst_riff_read_seek (riff, stream->indexes[i]))) {
g_list_free (list);
return FALSE;
}
gst_event_unref (event);
if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) {
g_list_free (list);
return FALSE;
}
/* eos check again */
if (GST_READ_UINT32_LE (&data[4]) + gst_bytestream_tell (riff->bs) >
length) {
GST_WARNING ("Subindex %d for stream %d lies outside file", i, n);
continue;
}
/* now read */
if (!gst_riff_read_data (riff, &tag, &buf))
return FALSE;
data = GST_BUFFER_DATA (buf);
if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
'0' + stream->num % 10)) {
GST_WARNING ("Not an ix## chunk (" GST_FOURCC_FORMAT ")",
GST_FOURCC_ARGS (tag));
gst_buffer_unref (buf);
continue;
}
/* We don't support index-data yet */
if (data[3] & 0x80) {
GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
("Subindex-is-data is not implemented"));
return FALSE;
}
/* check type of index. The opendml2 specs state that
* there should be 4 dwords per array entry. Type can be
* either frame or field (and we don't care). */
bpe = (data[2] & 0x01) ? 12 : 8;
if (GST_READ_UINT16_LE (data) != bpe / 4 ||
(data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
GST_WARNING ("Superindex for stream %d has unexpected "
"size_entry %d (bytes) or flags 0x%02x/0x%02x",
GST_READ_UINT16_LE (data), data[2], data[3]);
bpe = GST_READ_UINT16_LE (data) * 4;
}
num = GST_READ_UINT32_LE (&data[4]);
baseoff = GST_READ_UINT64_LE (&data[12]);
entries = g_new (gst_avi_index_entry, num);
*alloc_list = g_list_append (*alloc_list, entries);
for (x = 0; x < num; x++) {
entry = &entries[x];
if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
break;
/* fill in */
entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]);
entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]);
entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME;
entry->size &= ~0x80000000;
entry->index_nr = x;
entry->stream_nr = stream->num;
/* timestamps */
if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
/* constant rate stream */
gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
stream->total_bytes, &format, &entry->ts);
gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
stream->total_bytes + entry->size, &format, &entry->dur);
} else {
/* VBR stream */
gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
stream->total_frames, &format, &entry->ts);
gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
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++;
list = g_list_prepend (list, entry);
}
GST_LOG ("Read %d index entries in subindex %d for stream %d "
"at location %" G_GUINT64_FORMAT, num, i, n, stream->indexes[i]);
gst_buffer_unref (buf);
}
g_free (stream->indexes);
stream->indexes = NULL;
}
/* seek back */
if (!(event = gst_riff_read_seek (riff, pos))) {
g_list_free (list);
return FALSE;
}
gst_event_unref (event);
*index = g_list_reverse (list);
return TRUE;
}
/*
* Add a stream.
*/
......@@ -691,6 +884,7 @@ gst_avi_demux_add_stream (GstAviDemux * avi)
GstPad *pad;
avi_stream_context *stream;
gint blockalign = 0, bitrate = 0;
guint64 *locations = NULL;
union
{
gst_riff_strf_vids *vids;
......@@ -754,13 +948,21 @@ gst_avi_demux_add_stream (GstAviDemux * avi)
break;
case GST_RIFF_TAG_strn:
if (name)
g_free (name);
g_free (name);
if (!gst_riff_read_ascii (riff, &tag, &name))
return FALSE;
break;
default:
if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
tag == GST_MAKE_FOURCC ('i', 'x', avi->num_streams / 10,
avi->num_streams % 10)) {
g_free (locations);
if (!gst_avi_demux_read_superindex (avi,
avi->num_streams, &locations))
return FALSE;
break;
}
GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
GST_FOURCC_ARGS (tag));
/* fall-through */
......@@ -874,6 +1076,7 @@ gst_avi_demux_add_stream (GstAviDemux * avi)
stream->skip = 0;
stream->blockalign = blockalign;
stream->bitrate = bitrate;
stream->indexes = locations;
gst_pad_set_element_private (pad, stream);
avi->num_streams++;
......@@ -1041,7 +1244,7 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
entry.size = GUINT32_FROM_LE (_entry->size);
target = &index_entries[i];
if (entry.id == GST_RIFF_rec)
if (entry.id == GST_RIFF_rec || entry.id == 0 || entry.size == 0)
continue;
stream_nr = CHUNKID_TO_STREAMNR (entry.id);
......@@ -1093,6 +1296,8 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
stream->total_bytes += target->size;
stream->total_frames++;
GST_DEBUG ("Adding index entry of size %u at offset %"
G_GUINT64_FORMAT, target->size, target->offset);
list = g_list_prepend (list, target);
}
......@@ -1129,6 +1334,38 @@ end:
* Sync to next data chunk.
*/
static gboolean
gst_avi_demux_skip (GstAviDemux * avi, gboolean prevent_eos)
{
GstRiffRead *riff = GST_RIFF_READ (avi);
if (prevent_eos) {
guint64 pos, length;
guint size;
guint8 *data;
pos = gst_bytestream_tell (riff->bs);
length = gst_bytestream_length (riff->bs);
if (pos + 8 > length)
return FALSE;
if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8)
return FALSE;
size = GST_READ_UINT32_LE (&data[4]);
if (size & 1)
size++;
/* Note, we're going to skip which might involve seeks. Therefore,
* we need 1 byte more! */
if (pos + 8 + size >= length)
return FALSE;
}
return gst_riff_read_skip (riff);
}
static gboolean
gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
{
......@@ -1136,7 +1373,7 @@ gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
guint32 tag;
guint64 length = gst_bytestream_length (riff->bs);
if (gst_bytestream_tell (riff->bs) + 12 >= length)
if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
return FALSE;
/* peek first (for the end of this 'list/movi' section) */
......@@ -1147,8 +1384,8 @@ gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
* 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)
while (1) {
if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
return FALSE;
if (!(tag = gst_riff_peek_tag (riff, NULL)))
......@@ -1161,40 +1398,49 @@ gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
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;
case GST_RIFF_LIST_movi:
if (!gst_riff_read_list (riff, &tag))
return FALSE;
/* fall-through */
case GST_RIFF_rec:
goto done;
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))
if (!gst_avi_demux_skip (avi, prevent_eos))
return FALSE;
break;
}
break;
default:
GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " before AVI data",
GST_FOURCC_ARGS (tag));
/* fall-through */
if ((tag & 0xff) >= '0' &&
(tag & 0xff) <= '9' &&
((tag >> 8) & 0xff) >= '0' && ((tag >> 8) & 0xff) <= '9') {
goto done;
}
/* pass-through */
case GST_RIFF_TAG_idx1:
case GST_RIFF_TAG_JUNK:
if (!gst_riff_read_skip (riff))
if (!gst_avi_demux_skip (avi, prevent_eos)) {
return FALSE;
}
break;
}
}
done:
/* And then, we get the data */
if (gst_bytestream_tell (riff->bs) + 12 >= length)
if (prevent_eos && gst_bytestream_tell (riff->bs) + 12 >= length)
return FALSE;
if (!(tag = gst_riff_peek_tag (riff, NULL)))
......@@ -1215,7 +1461,7 @@ gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
break;
case GST_RIFF_TAG_JUNK:
gst_riff_read_skip (riff);
gst_avi_demux_skip (avi, prevent_eos);
return FALSE;
}
......@@ -1238,7 +1484,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
GstRiffRead *riff = GST_RIFF_READ (avi);
gst_avi_index_entry *entry, *entries = NULL;
avi_stream_context *stream;
guint64 pos = gst_bytestream_tell (riff->bs);
guint64 pos = gst_bytestream_tell (riff->bs),
length = gst_bytestream_length (riff->bs);
guint32 tag;
GstEvent *event;
GList *list = NULL;
......@@ -1248,6 +1495,28 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
* - implement non-seekable source support.
*/
if (*index) {
GstEvent *event;
guint64 off;
entry = g_list_last (*index)->data;
off = entry->offset + avi->index_offset + entry->size;
if (entry->size & 1)
off++;
if (off < length) {
GST_LOG ("Incomplete index, seeking to last valid entry @ %"
G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT " (%"
G_GUINT64_FORMAT "+%u)", off, length, entry->offset, entry->size);
if (!(event = gst_riff_read_seek (riff, off)))
return FALSE;
gst_event_unref (event);
} else {
return TRUE;
}
}
GST_LOG_OBJECT (avi, "Creating index");
while (gst_avi_demux_sync (avi, &tag, TRUE)) {
......@@ -1273,8 +1542,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
/* fill in */
entry->index_nr = index_size++;
entry->stream_nr = stream_nr;
entry->flags = 0;
entry->offset = gst_bytestream_tell (riff->bs) + 8;
entry->flags = GST_RIFF_IF_KEYFRAME;
entry->offset = gst_bytestream_tell (riff->bs) + 8 - avi->index_offset;
entry->size = GST_READ_UINT32_LE (&data[4]);
/* timestamps */
......@@ -1302,12 +1571,10 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
list = g_list_prepend (list, entry);
next:
if (!gst_riff_read_skip (riff))
if (!gst_avi_demux_skip (avi, TRUE))
break;
}
avi->index_offset = 0;
/* seek back */
if (!(event = gst_riff_read_seek (riff, pos))) {
g_list_free (list);
......@@ -1317,7 +1584,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
GST_LOG_OBJECT (avi, "index created, %d items", index_size);
*index = g_list_reverse (list);
*index = g_list_concat (*index, g_list_reverse (list));
return TRUE;
}
......@@ -1470,6 +1737,11 @@ gst_avi_demux_massage_index (GstAviDemux * avi,
g_list_free (alloc_list);
g_list_free (list);
for (i = 0; i < avi->num_streams; i++) {
GST_LOG ("Stream %d, %d frames, %" G_GUINT64_FORMAT " bytes", i,
avi->stream[i].total_frames, avi->stream[i].total_bytes);
}
GST_LOG ("Index massaging done");
}
......@@ -1575,10 +1847,6 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
g_warning ("Stream header mentioned %d streams, but %d available",
streams, avi->num_streams);
}
/* at this point we know all the streams and we can signal the no more
* pads signal */
GST_DEBUG ("signaling no more pads");
gst_element_no_more_pads (GST_ELEMENT (avi));
/* Now, find the data (i.e. skip all junk between header and data) */
while (1) {
......@@ -1604,28 +1872,40 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
}
/* create or read stream index (for seeking) */
if (flags & GST_RIFF_AVIH_HASINDEX) {
if (!gst_avi_demux_stream_index (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
if (avi->stream[0].indexes != NULL) {
if (!gst_avi_demux_read_subindexes (avi, &index, &alloc))
return FALSE;
}
}
if (!index) {
if (!gst_avi_demux_stream_scan (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
return FALSE;
if (flags & GST_RIFF_AVIH_HASINDEX) {
if (!gst_avi_demux_stream_index (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
return FALSE;
}
}
/* some indexes are incomplete, continue streaming from there */
if (!index || avi->stream[0].total_frames < avi->num_frames) {
if (!gst_avi_demux_stream_scan (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
return FALSE;
}
}
}
if (index)
if (index) {
gst_avi_demux_massage_index (avi, index, alloc);
else {
} else {
g_list_free (index);
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
}
/* at this point we know all the streams and we can signal the no more
* pads signal */
GST_DEBUG ("signaling no more pads");
gst_element_no_more_pads (GST_ELEMENT (avi));
return TRUE;
}
......
......@@ -78,6 +78,8 @@ typedef struct {
guint32 total_frames;
guint32 skip;
guint64 *indexes;
} avi_stream_context;
typedef enum {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment