gst/avi/gstavidemux.*: Save some memory (8%) by repacking the index entry structure (more to come). Add more FIXMEs t...

Original commit message from CVS:
* gst/avi/gstavidemux.c: (gst_avi_demux_class_init),
(gst_avi_demux_reset), (gst_avi_demux_index_entry_for_time),
(gst_avi_demux_handle_src_query), (gst_avi_demux_parse_superindex),
(gst_avi_demux_parse_subindex), (gst_avi_demux_parse_stream),
(gst_avi_demux_parse_index), (gst_avi_demux_stream_index),
(gst_avi_demux_sync), (gst_avi_demux_next_data_buffer),
(gst_avi_demux_stream_scan), (gst_avi_demux_massage_index),
(gst_avi_demux_calculate_durations_from_index),
(gst_avi_demux_push_event), (gst_avi_demux_stream_header_pull),
(gst_avi_demux_do_seek), (gst_avi_demux_process_next_entry),
(gst_avi_demux_stream_data), (gst_avi_demux_loop):
* gst/avi/gstavidemux.h:
Save some memory (8%) by repacking the index entry structure (more to
come). Add more FIXMEs to questionable parts.
This commit is contained in:
Stefan Kost 2007-02-12 15:29:44 +00:00
parent 77790aa2dc
commit 114afecd8d
3 changed files with 312 additions and 195 deletions

View file

@ -1,3 +1,20 @@
2007-02-12 Stefan Kost <ensonic@users.sf.net>
* gst/avi/gstavidemux.c: (gst_avi_demux_class_init),
(gst_avi_demux_reset), (gst_avi_demux_index_entry_for_time),
(gst_avi_demux_handle_src_query), (gst_avi_demux_parse_superindex),
(gst_avi_demux_parse_subindex), (gst_avi_demux_parse_stream),
(gst_avi_demux_parse_index), (gst_avi_demux_stream_index),
(gst_avi_demux_sync), (gst_avi_demux_next_data_buffer),
(gst_avi_demux_stream_scan), (gst_avi_demux_massage_index),
(gst_avi_demux_calculate_durations_from_index),
(gst_avi_demux_push_event), (gst_avi_demux_stream_header_pull),
(gst_avi_demux_do_seek), (gst_avi_demux_process_next_entry),
(gst_avi_demux_stream_data), (gst_avi_demux_loop):
* gst/avi/gstavidemux.h:
Save some memory (8%) by repacking the index entry structure (more to
come). Add more FIXMEs to questionable parts.
2007-02-12 Stefan Kost <ensonic@users.sf.net> 2007-02-12 Stefan Kost <ensonic@users.sf.net>
* sys/v4l2/gstv4l2src.c: (gst_v4l2src_v4l2fourcc_to_caps), * sys/v4l2/gstv4l2src.c: (gst_v4l2src_v4l2fourcc_to_caps),

View file

@ -175,7 +175,8 @@ gst_avi_demux_class_init (GstAviDemuxClass * klass)
parent_class = g_type_class_peek_parent (klass); parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_avi_demux_finalize; gobject_class->finalize = gst_avi_demux_finalize;
gstelement_class->change_state = gst_avi_demux_change_state; gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
} }
static void static void
@ -220,8 +221,10 @@ gst_avi_demux_reset (GstAviDemux * avi)
gst_buffer_unref (avi->stream[i].initdata); gst_buffer_unref (avi->stream[i].initdata);
if (avi->stream[i].extradata) if (avi->stream[i].extradata)
gst_buffer_unref (avi->stream[i].extradata); gst_buffer_unref (avi->stream[i].extradata);
if (avi->stream[i].pad) if (avi->stream[i].pad) {
gst_pad_set_active (avi->stream[i].pad, FALSE);
gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad); gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
}
if (avi->stream[i].taglist) { if (avi->stream[i].taglist) {
gst_tag_list_free (avi->stream[i].taglist); gst_tag_list_free (avi->stream[i].taglist);
avi->stream[i].taglist = NULL; avi->stream[i].taglist = NULL;
@ -304,7 +307,7 @@ gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
*/ */
static gst_avi_index_entry * static gst_avi_index_entry *
gst_avi_demux_index_entry_for_time (GstAviDemux * avi, gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
gint stream_nr, guint64 time, guint32 flags) gint stream_nr, guint64 time, guchar flags)
{ {
gst_avi_index_entry *entry = NULL, *last_entry = NULL; gst_avi_index_entry *entry = NULL, *last_entry = NULL;
gint i; gint i;
@ -325,7 +328,7 @@ gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
GST_LOG_OBJECT (avi, GST_LOG_OBJECT (avi,
"looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
" flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur), " flags:%02x", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
entry->flags); entry->flags);
} while (entry->ts < time); } while (entry->ts < time);
@ -833,7 +836,8 @@ gst_avi_demux_parse_superindex (GstAviDemux * avi,
GstBuffer * buf, guint64 ** _indexes) GstBuffer * buf, guint64 ** _indexes)
{ {
guint8 *data; guint8 *data;
gint bpe = 16, num, i; guint16 bpe = 16;
guint32 num, i;
guint64 *indexes; guint64 *indexes;
guint size; guint size;
@ -904,7 +908,8 @@ gst_avi_demux_parse_subindex (GstAviDemux * avi,
GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list) GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
{ {
guint8 *data = GST_BUFFER_DATA (buf); guint8 *data = GST_BUFFER_DATA (buf);
gint bpe, num, x; guint16 bpe;
guint32 num, i;
guint64 baseoff; guint64 baseoff;
gst_avi_index_entry *entries, *entry; gst_avi_index_entry *entries, *entry;
GList *entries_list = NULL; GList *entries_list = NULL;
@ -938,22 +943,24 @@ gst_avi_demux_parse_subindex (GstAviDemux * avi,
num = GST_READ_UINT32_LE (&data[4]); num = GST_READ_UINT32_LE (&data[4]);
baseoff = GST_READ_UINT64_LE (&data[12]); baseoff = GST_READ_UINT64_LE (&data[12]);
entries = g_new (gst_avi_index_entry, num); if (!(entries = g_try_new (gst_avi_index_entry, num)))
goto out_of_mem;
for (x = 0; x < num; x++) { for (i = 0; i < num; i++) {
gint64 next_ts; gint64 next_ts;
entry = &entries[x]; entry = &entries[i];
if (size < 24 + bpe * (x + 1)) if (size < 24 + bpe * (i + 1))
break; break;
/* fill in */ /* fill in */
entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]); entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * i]);
entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]); entry->size = GST_READ_UINT32_LE (&data[24 + bpe * i + 4]);
entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME; entry->flags =
(entry->size & 0x80000000) ? 0 : GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME;
entry->size &= ~0x80000000; entry->size &= ~0x80000000;
entry->index_nr = x; entry->index_nr = i;
entry->stream_nr = stream->num; entry->stream_nr = stream->num;
/* stream duration unknown, now we can calculate it */ /* stream duration unknown, now we can calculate it */
@ -985,11 +992,11 @@ gst_avi_demux_parse_subindex (GstAviDemux * avi,
entries_list = g_list_prepend (entries_list, entry); entries_list = g_list_prepend (entries_list, entry);
} }
GST_LOG_OBJECT (avi, "Read %d index entries", x); GST_LOG_OBJECT (avi, "Read %d index entries", i);
gst_buffer_unref (buf); gst_buffer_unref (buf);
if (x > 0) { if (i > 0) {
*_entries_list = g_list_reverse (entries_list); *_entries_list = g_list_reverse (entries_list);
} else { } else {
g_free (entries); g_free (entries);
@ -1013,6 +1020,15 @@ not_implemented:
gst_buffer_unref (buf); gst_buffer_unref (buf);
return FALSE; return FALSE;
} }
out_of_mem:
{
GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
("Cannot allocate memory for %lu*%lu=%lu bytes",
sizeof (gst_avi_index_entry), num,
sizeof (gst_avi_index_entry) * num));
gst_buffer_unref (buf);
return FALSE;
}
} }
#if 0 #if 0
@ -1161,7 +1177,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) || if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
tag != GST_RIFF_TAG_strf) { tag != GST_RIFF_TAG_strf) {
GST_ERROR_OBJECT (avi, GST_ERROR_OBJECT (avi,
"Failed to find strh chunk (size: %d, tag: %" "Failed to find strf chunk (size: %d, tag: %"
GST_FOURCC_FORMAT ")", GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag)); GST_FOURCC_FORMAT ")", GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
goto fail; goto fail;
} else { } else {
@ -1347,6 +1363,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
GST_PAD_NAME (pad), caps); GST_PAD_NAME (pad), caps);
gst_caps_unref (caps); gst_caps_unref (caps);
/* make tags */
if (codec_name) { if (codec_name) {
if (!stream->taglist) if (!stream->taglist)
stream->taglist = gst_tag_list_new (); stream->taglist = gst_tag_list_new ();
@ -1479,16 +1496,25 @@ gst_avi_demux_parse_index (GstAviDemux * avi,
GList *entries_list = NULL; GList *entries_list = NULL;
guint i, num, n; guint i, num, n;
if (!buf) { /* DEBUG */
gulong _nr_keyframes = 0;
/* DEBUG */
if (!buf || !GST_BUFFER_SIZE (buf)) {
*_entries_list = NULL; *_entries_list = NULL;
GST_DEBUG ("empty index");
if (buf)
gst_buffer_unref (buf);
return; return;
} }
data = GST_BUFFER_DATA (buf); data = GST_BUFFER_DATA (buf);
num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry); num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
entries = g_new (gst_avi_index_entry, num); if (!(entries = g_try_new (gst_avi_index_entry, num)))
goto out_of_mem;
GST_DEBUG ("Parsing index, %d entries", num); GST_INFO ("Parsing index, nr_entries = %6d", num);
for (i = 0, n = 0; i < num; i++) { for (i = 0, n = 0; i < num; i++) {
gint64 next_ts; gint64 next_ts;
@ -1519,7 +1545,9 @@ gst_avi_demux_parse_index (GstAviDemux * avi,
stream = &avi->stream[stream_nr]; stream = &avi->stream[stream_nr];
target->index_nr = i; target->index_nr = i;
target->flags = entry.flags; target->flags =
(entry.
flags & GST_RIFF_IF_KEYFRAME) ? GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME : 0;
target->size = entry.size; target->size = entry.size;
target->offset = entry.offset + 8; target->offset = entry.offset + 8;
@ -1535,8 +1563,12 @@ gst_avi_demux_parse_index (GstAviDemux * avi,
format = GST_FORMAT_TIME; format = GST_FORMAT_TIME;
if (stream->strh->type == GST_RIFF_FCC_auds) { if (stream->strh->type == GST_RIFF_FCC_auds) {
/* all audio frames are keyframes */ /* all audio frames are keyframes */
target->flags |= GST_RIFF_IF_KEYFRAME; target->flags |= GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME;
} }
/* DEBUG */
if (target->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)
_nr_keyframes++;
/* DEBUG */
/* stream duration unknown, now we can calculate it */ /* stream duration unknown, now we can calculate it */
if (stream->idx_duration == -1) if (stream->idx_duration == -1)
@ -1565,7 +1597,7 @@ gst_avi_demux_parse_index (GstAviDemux * avi,
stream->idx_duration = next_ts; stream->idx_duration = next_ts;
GST_DEBUG_OBJECT (avi, GST_DEBUG_OBJECT (avi,
"Adding index entry %d (%6u), flags %08x, stream %d, size %u " "Adding index entry %d (%6u), flags %02x, stream %d, size %u "
", offset %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT ", dur %" ", offset %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT ", dur %"
GST_TIME_FORMAT, GST_TIME_FORMAT,
target->index_nr, stream->total_frames - 1, target->flags, target->index_nr, stream->total_frames - 1, target->flags,
@ -1576,8 +1608,29 @@ gst_avi_demux_parse_index (GstAviDemux * avi,
n++; n++;
} }
*_entries_list = g_list_reverse (entries_list); GST_INFO
("Parsed index, %6d entries, %5d keyframes, entry size = %2d, total size = %10ld",
num, _nr_keyframes, sizeof (gst_avi_index_entry),
num * sizeof (gst_avi_index_entry));
gst_buffer_unref (buf); gst_buffer_unref (buf);
if (n > 0) {
*_entries_list = g_list_reverse (entries_list);
} else {
g_free (entries);
}
return;
/* ERRORS */
out_of_mem:
{
GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
("Cannot allocate memory for %lu*%lu=%lu bytes",
sizeof (gst_avi_index_entry), num,
sizeof (gst_avi_index_entry) * num));
gst_buffer_unref (buf);
}
} }
/* /*
@ -1615,7 +1668,8 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
if (tag == GST_RIFF_TAG_LIST) { if (tag == GST_RIFF_TAG_LIST) {
GST_WARNING_OBJECT (avi, "skip LIST chunk"); GST_WARNING_OBJECT (avi, "skip LIST chunk, size %d",
(8 + ((size + 1) & ~1)));
offset += 8 + ((size + 1) & ~1); offset += 8 + ((size + 1) & ~1);
res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf); res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
if (res != GST_FLOW_OK) if (res != GST_FLOW_OK)
@ -1628,6 +1682,8 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
if (tag != GST_RIFF_TAG_idx1) if (tag != GST_RIFF_TAG_idx1)
goto no_index; goto no_index;
if (!size)
goto zero_index;
gst_buffer_unref (buf); gst_buffer_unref (buf);
@ -1638,6 +1694,9 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK) avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK)
return; return;
GST_INFO ("will parse index chunk size %d for tag %" GST_FOURCC_FORMAT,
GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
gst_avi_demux_parse_index (avi, buf, index); gst_avi_demux_parse_index (avi, buf, index);
if (*index) if (*index)
*alloc_list = g_list_append (*alloc_list, (*index)->data); *alloc_list = g_list_append (*alloc_list, (*index)->data);
@ -1673,6 +1732,12 @@ no_index:
gst_buffer_unref (buf); gst_buffer_unref (buf);
return; return;
} }
zero_index:
{
GST_WARNING_OBJECT (avi, "Empty index data (idx1) after movi chunk");
gst_buffer_unref (buf);
return;
}
} }
#if 0 #if 0
@ -1893,6 +1958,11 @@ gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
} }
/* /*
* gst_avi_demux_stream_scan:
* @avi: calling element (used for debugging/errors).
* @index: list of index entries, returned by this function.
* @alloc_list: list of allocated data, returned by this function.
*
* Scan the file for all chunks to "create" a new index. * Scan the file for all chunks to "create" a new index.
* Return value indicates if we can continue reading the stream. It * Return value indicates if we can continue reading the stream. It
* does not say anything about whether we created an index. * does not say anything about whether we created an index.
@ -1917,8 +1987,9 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
/* FIXME: /* FIXME:
* - implement non-seekable source support. * - implement non-seekable source support.
*/ */
GST_LOG_OBJECT (avi, "Creating index %s existing index", GST_DEBUG_OBJECT (avi,
(*index) ? "with" : "without"); "Creating index %s existing index, starting at offset %" G_GUINT64_FORMAT,
((*index) ? "with" : "without"), pos);
format = GST_FORMAT_BYTES; format = GST_FORMAT_BYTES;
if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength)) if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength))
@ -1967,7 +2038,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
entry->index_nr = index_size++; entry->index_nr = index_size++;
entry->stream_nr = stream_nr; entry->stream_nr = stream_nr;
entry->flags = GST_RIFF_IF_KEYFRAME; entry->flags = GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME;
entry->offset = pos - avi->index_offset; entry->offset = pos - avi->index_offset;
entry->size = size; entry->size = size;
@ -2013,6 +2084,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
} }
} }
/* FIXME: why is this disabled */
#if 0 #if 0
while (gst_avi_demux_sync (avi, &tag, TRUE)) { while (gst_avi_demux_sync (avi, &tag, TRUE)) {
gint stream_nr = CHUNKID_TO_STREAMNR (tag); gint stream_nr = CHUNKID_TO_STREAMNR (tag);
@ -2027,11 +2099,10 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8)
goto next; goto next;
/* fill in */ /* fill in */
entry->index_nr = index_size++; entry->index_nr = index_size++;
entry->stream_nr = stream_nr; entry->stream_nr = stream_nr;
entry->flags = GST_RIFF_IF_KEYFRAME; entry->flags = GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME;
entry->offset = gst_bytestream_tell (riff->bs) + 8 - avi->index_offset; entry->offset = gst_bytestream_tell (riff->bs) + 8 - avi->index_offset;
entry->size = GST_READ_UINT32_LE (&data[4]); entry->size = GST_READ_UINT32_LE (&data[4]);
@ -2075,7 +2146,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
gst_event_unref (event); gst_event_unref (event);
#endif #endif
GST_LOG_OBJECT (avi, "index created, %d items", index_size);
GST_DEBUG_OBJECT (avi, "index created, %d items", index_size);
*index = g_list_concat (*index, g_list_reverse (list)); *index = g_list_concat (*index, g_list_reverse (list));
@ -2084,6 +2156,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
/* /*
* gst_avi_demux_massage_index: * gst_avi_demux_massage_index:
* @avi: calling element (used for debugging/errors).
* *
* We're going to go over each entry in the index and finetune * 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 * some things we don't like about AVI. For example, a single
@ -2099,171 +2172,146 @@ gst_avi_demux_massage_index (GstAviDemux * avi,
gst_avi_index_entry *entry; gst_avi_index_entry *entry;
avi_stream_context *stream; avi_stream_context *stream;
gint i; gint i;
GList *one; GList *node;
#if 0
guint32 avih_init_frames;
guint32 init_frames;
GstFormat fmt = GST_FORMAT_TIME;
#endif
gint64 delay = G_GINT64_CONSTANT (0); gint64 delay = G_GINT64_CONSTANT (0);
GST_LOG_OBJECT (avi, "Starting index massage"); GST_LOG_OBJECT (avi, "Starting index massage, nr_entries = %d",
g_list_length (list));
/* the init_frames have no real meaning except to indicate how much if (list) {
* audio preroll there is to fill up the audio device */
#if 0
avih_init_frames = avi->avih->init_frames;
/* init frames, add constant delay for each index entry */ GST_LOG_OBJECT (avi,
for (i = 0; i < avi->num_streams; i++) { "I'm now going to cut large chunks into smaller pieces");
stream = &avi->stream[i];
init_frames = stream->strh->init_frames; /* cut chunks in small (seekable) pieces
if (init_frames >= avih_init_frames) * FIXME: this should be a property where a value of
init_frames -= avih_init_frames; * GST_CLOCK_TIME_NONE would disable the chunking
*/
if (!gst_avi_demux_src_convert (stream->pad,
GST_FORMAT_DEFAULT, init_frames, &fmt, &delay)) {
delay = 0;
continue;
}
GST_DEBUG_OBJECT (avi, "Adding init_time=%" GST_TIME_FORMAT " to stream %d",
GST_TIME_ARGS (delay), i);
for (one = list; one != NULL; one = one->next) {
entry = one->data;
if (entry->stream_nr == i)
entry->ts += delay;
}
}
#endif
GST_LOG_OBJECT (avi, "I'm now going to cut large chunks into smaller pieces");
/* cut chunks in small (seekable) pieces */
#define MAX_DURATION (GST_SECOND / 2) #define MAX_DURATION (GST_SECOND / 2)
for (i = 0; i < avi->num_streams; i++) { for (i = 0; i < avi->num_streams; i++) {
if (avi->stream[i].total_frames != 1) /* only chop streams that have exactly *one* chunk */
continue; if (avi->stream[i].total_frames != 1)
for (one = list; one != NULL; one = one->next) {
entry = one->data;
if (entry->stream_nr != i)
continue; continue;
/* check for max duration of a single buffer. I suppose that for (node = list; node != NULL; node = node->next) {
* the allocation of index entries could be improved. */ entry = node->data;
stream = &avi->stream[entry->stream_nr];
if (entry->dur > MAX_DURATION && stream->strh->type == GST_RIFF_FCC_auds) {
guint32 ideal_size;
gst_avi_index_entry *entries;
gint old_size, num_added;
GList *one2;
/* cut in 1/10th of a second */ if (entry->stream_nr != i)
ideal_size = stream->strf.auds->av_bps / 10; continue;
/* ensure chunk size is multiple of blockalign */ /* check for max duration of a single buffer. I suppose that
if (stream->strf.auds->blockalign > 1) * the allocation of index entries could be improved. */
ideal_size -= ideal_size % stream->strf.auds->blockalign; stream = &avi->stream[entry->stream_nr];
if (entry->dur > MAX_DURATION
&& stream->strh->type == GST_RIFF_FCC_auds) {
guint32 ideal_size;
gst_avi_index_entry *entries;
gint old_size, num_added;
GList *node2;
/* copy index */ /* cut in 1/10th of a second */
old_size = entry->size; ideal_size = stream->strf.auds->av_bps / 10;
num_added = (entry->size - 1) / ideal_size;
avi->index_size += num_added;
entries = g_malloc (sizeof (gst_avi_index_entry) * num_added);
alloc_list = g_list_prepend (alloc_list, entries);
for (one2 = one->next; one2 != NULL; one2 = one2->next) {
gst_avi_index_entry *entry2 = one2->data;
entry2->index_nr += num_added; /* ensure chunk size is multiple of blockalign */
if (entry2->stream_nr == entry->stream_nr) if (stream->strf.auds->blockalign > 1)
entry2->frames_before += num_added; ideal_size -= ideal_size % stream->strf.auds->blockalign;
}
/* new sized index chunks */ /* copy index */
for (i = 0; i < num_added + 1; i++) { old_size = entry->size;
gst_avi_index_entry *entry2; num_added = (entry->size - 1) / ideal_size;
avi->index_size += num_added;
entries = g_malloc (sizeof (gst_avi_index_entry) * num_added);
alloc_list = g_list_prepend (alloc_list, entries);
for (node2 = node->next; node2 != NULL; node2 = node2->next) {
gst_avi_index_entry *entry2 = node2->data;
if (i == 0) { entry2->index_nr += num_added;
entry2 = entry; if (entry2->stream_nr == entry->stream_nr)
} else { entry2->frames_before += num_added;
entry2 = &entries[i - 1];
list = g_list_insert_before (list, one->next, entry2);
entry = one->data;
one = one->next;
memcpy (entry2, entry, sizeof (gst_avi_index_entry));
} }
if (old_size >= ideal_size) { /* new sized index chunks */
entry2->size = ideal_size; for (i = 0; i < num_added + 1; i++) {
old_size -= ideal_size; gst_avi_index_entry *entry2;
} else {
entry2->size = old_size;
}
entry2->dur = GST_SECOND * entry2->size / stream->strf.auds->av_bps; if (i == 0) {
if (i != 0) { entry2 = entry;
entry2->index_nr++; } else {
entry2->ts += entry->dur; entry2 = &entries[i - 1];
entry2->offset += entry->size; list = g_list_insert_before (list, node->next, entry2);
entry2->bytes_before += entry->size; entry = node->data;
entry2->frames_before++; node = node->next;
memcpy (entry2, entry, sizeof (gst_avi_index_entry));
}
if (old_size >= ideal_size) {
entry2->size = ideal_size;
old_size -= ideal_size;
} else {
entry2->size = old_size;
}
entry2->dur = GST_SECOND * entry2->size / stream->strf.auds->av_bps;
if (i != 0) {
entry2->index_nr++;
entry2->ts += entry->dur;
entry2->offset += entry->size;
entry2->bytes_before += entry->size;
entry2->frames_before++;
}
} }
} }
} }
} }
}
GST_LOG_OBJECT (avi, "I'm now going to reorder the index entries for time"); GST_LOG_OBJECT (avi, "I'm now going to reorder the index entries for time");
/* re-order for time */ /* re-order for time */
list = g_list_sort (list, (GCompareFunc) sort); list = g_list_sort (list, (GCompareFunc) sort);
avi->index_size = g_list_length (list); /* make a continous array out of the list */
GST_LOG_OBJECT (avi, "Filling in index array, nr_entries = %d", avi->index_size = g_list_length (list);
avi->index_size); avi->index_entries = g_new (gst_avi_index_entry, avi->index_size);
avi->index_entries = g_new (gst_avi_index_entry, avi->index_size);
if (list) {
entry = (gst_avi_index_entry *) (list->data); entry = (gst_avi_index_entry *) (list->data);
delay = entry->ts; delay = entry->ts;
}
GST_DEBUG ("Fixing time offset : %" GST_TIME_FORMAT, GST_TIME_ARGS (delay)); GST_LOG_OBJECT (avi,
for (i = 0, one = list; one != NULL; one = one->next, i++) { "Building index array, nr_entries = %d (time offset = %"
entry = one->data; GST_TIME_FORMAT, avi->index_size, GST_TIME_ARGS (delay));
entry->ts -= delay;
memcpy (&avi->index_entries[i], entry, sizeof (gst_avi_index_entry));
avi->index_entries[i].index_nr = i;
GST_DEBUG ("Sorted index entry %3d for stream %d of size %6u" for (i = 0, node = list; node != NULL; node = node->next, i++) {
" at offset %7" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT entry = node->data;
" dur %" GST_TIME_FORMAT, entry->index_nr = i;
avi->index_entries[i].index_nr, entry->stream_nr, entry->size, entry->ts -= delay;
entry->offset, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur)); memcpy (&avi->index_entries[i], entry, sizeof (gst_avi_index_entry));
}
if (delay) { GST_DEBUG ("Sorted index entry %3d for stream %d of size %6u"
for (i = 0; i < avi->num_streams; i++) { " at offset %7" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT
stream = &avi->stream[i]; " dur %" GST_TIME_FORMAT,
stream->idx_duration -= delay; avi->index_entries[i].index_nr, entry->stream_nr, entry->size,
entry->offset, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur));
} }
if (delay) {
for (i = 0; i < avi->num_streams; i++) {
stream = &avi->stream[i];
stream->idx_duration -= delay;
}
}
GST_LOG_OBJECT (avi, "Freeing original index list");
/* all the node->data in list point to alloc_list chunks */
g_list_free (list);
} }
if (alloc_list) {
GST_LOG_OBJECT (avi, "Freeing original index list"); g_list_foreach (alloc_list, (GFunc) g_free, NULL);
g_list_free (alloc_list);
g_list_foreach (alloc_list, (GFunc) g_free, NULL); }
g_list_free (alloc_list); #ifndef GST_DISABLE_GST_DEBUG
g_list_free (list);
for (i = 0; i < avi->num_streams; i++) { for (i = 0; i < avi->num_streams; i++) {
GST_LOG_OBJECT (avi, "Stream %d, %d frames, %8" G_GUINT64_FORMAT " bytes", 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].total_frames, avi->stream[i].total_bytes);
} }
#endif
GST_LOG_OBJECT (avi, "Index massaging done"); GST_LOG_OBJECT (avi, "Index massaging done");
} }
@ -2349,14 +2397,19 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
gint i; gint i;
gboolean result = TRUE; gboolean result = TRUE;
for (i = 0; i < avi->num_streams; i++) { GST_DEBUG_OBJECT (avi, "sending event to %d streams", avi->num_streams);
avi_stream_context *stream = &avi->stream[i]; if (avi->num_streams) {
for (i = 0; i < avi->num_streams; i++) {
avi_stream_context *stream = &avi->stream[i];
gst_event_ref (event); gst_event_ref (event);
result &= gst_pad_push_event (stream->pad, event); result &= gst_pad_push_event (stream->pad, event);
}
} else {
/* return error, as the event was not send */
result = FALSE;
} }
gst_event_unref (event); gst_event_unref (event);
return result; return result;
} }
@ -2665,6 +2718,8 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
goto no_header; goto no_header;
} }
GST_DEBUG_OBJECT (avi, "hdrl LIST tag found");
/* the hdrl starts with a 'avih' header */ /* the hdrl starts with a 'avih' header */
if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub))
goto no_avih; goto no_avih;
@ -2887,7 +2942,13 @@ no_index:
g_list_foreach (alloc, (GFunc) g_free, NULL); g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc); g_list_free (alloc);
GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL), /* FIMXE: this happens e.g. if the file is empty (no index and not data
* chunks), wouldn't ERROR_DEMUX be better?
*
GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
("Could not get/create index"));
*/
GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
("Could not get/create index")); ("Could not get/create index"));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
@ -2924,7 +2985,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
* well. FIXME, this code assumes the main stream with keyframes is stream 0, * well. FIXME, this code assumes the main stream with keyframes is stream 0,
* which is mostly correct... */ * which is mostly correct... */
entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time, entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time,
(guint32) GST_RIFF_IF_KEYFRAME); GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME);
if (entry) { if (entry) {
GST_DEBUG_OBJECT (avi, GST_DEBUG_OBJECT (avi,
"Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT "Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT
@ -3122,6 +3183,7 @@ swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
* Invert DIB buffers... Takes existing buffer and * Invert DIB buffers... Takes existing buffer and
* returns either the buffer or a new one (with old * returns either the buffer or a new one (with old
* one dereferenced). * one dereferenced).
* FIXME: can't we preallocate tmp? and remember stride, bpp?
*/ */
static GstBuffer * static GstBuffer *
gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf)
@ -3206,7 +3268,9 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
/* get next entry, this will work as we checked for the size above */ /* get next entry, this will work as we checked for the size above */
entry = &avi->index_entries[avi->current_entry++]; entry = &avi->index_entries[avi->current_entry++];
/* see if we have a valid stream, ignore if not */ /* see if we have a valid stream, ignore if not
* FIXME: can't we check this when building the index?
*/
if (entry->stream_nr >= avi->num_streams) { if (entry->stream_nr >= avi->num_streams) {
GST_DEBUG_OBJECT (avi, GST_DEBUG_OBJECT (avi,
"Entry %d has non-existing stream nr %d", "Entry %d has non-existing stream nr %d",
@ -3217,7 +3281,7 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
/* get stream now */ /* get stream now */
stream = &avi->stream[entry->stream_nr]; stream = &avi->stream[entry->stream_nr];
if ((entry->flags & GST_RIFF_IF_KEYFRAME) if ((entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME)
&& GST_CLOCK_TIME_IS_VALID (entry->ts) && GST_CLOCK_TIME_IS_VALID (entry->ts)
&& GST_CLOCK_TIME_IS_VALID (avi->segment.stop) && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
&& (entry->ts > avi->segment.stop)) { && (entry->ts > avi->segment.stop)) {
@ -3250,7 +3314,7 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
buf = gst_avi_demux_invert (stream, buf); buf = gst_avi_demux_invert (stream, buf);
/* mark non-keyframes */ /* mark non-keyframes */
if (!(entry->flags & GST_RIFF_IF_KEYFRAME)) if (!(entry->flags & GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME))
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
GST_BUFFER_TIMESTAMP (buf) = entry->ts; GST_BUFFER_TIMESTAMP (buf) = entry->ts;
@ -3275,7 +3339,8 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
/* mark as processed, we increment the frame and byte counters then /* mark as processed, we increment the frame and byte counters then
* leave the while loop and return the GstFlowReturn */ * leave the while loop and return the GstFlowReturn */
processed = TRUE; processed = TRUE;
GST_DEBUG_OBJECT (avi, "Processed buffer %s", gst_flow_get_name (res)); GST_DEBUG_OBJECT (avi, "Processed buffer %d: %s", entry->index_nr,
gst_flow_get_name (res));
next: next:
stream->current_frame = entry->frames_before + 1; stream->current_frame = entry->frames_before + 1;
@ -3539,41 +3604,66 @@ gst_avi_demux_loop (GstPad * pad)
} }
/* process each index entry in turn */ /* process each index entry in turn */
res = gst_avi_demux_stream_data (avi); res = gst_avi_demux_stream_data (avi);
#if 0
/* if a pad is in e.g. WRONG_STATE, we want to pause to unlock the STREAM_LOCK */
if ((res == GST_FLOW_WRONG_STATE) || ((res != GST_FLOW_OK)
&& ((agg_res =
gst_avi_demux_aggregated_flow (avi)) != GST_FLOW_OK))) {
GST_WARNING ("stream_movi flow: %s / %s", gst_flow_get_name (res),
gst_flow_get_name (agg_res));
res = agg_res;
goto pause;
}
#endif
/* if a pad is in WRONG_STATE like after a flushing seek, we want to pause to
* unlock the STREAM_LOCK
*/
if (res != GST_FLOW_OK) {
GST_INFO ("stream_movi flow: %s", gst_flow_get_name (res));
/* if we do that then we can't play streams, that are partialy linked */
if (res == GST_FLOW_WRONG_STATE)
goto pause;
if ((agg_res = gst_avi_demux_aggregated_flow (avi)) != GST_FLOW_OK) {
GST_INFO ("stream_movi aggregated flow: %s",
gst_flow_get_name (agg_res));
res = agg_res;
goto pause;
}
}
break; break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();
} }
GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state, GST_LOG_OBJECT (avi, "state: %d res:%s", avi->state, gst_flow_get_name (res));
gst_flow_get_name (res));
/* if a pad is in e.g. WRONG_STATE, we want to pause to unlock the STREAM_LOCK */
if ((res == GST_FLOW_WRONG_STATE) || ((res != GST_FLOW_OK)
&& ((agg_res = gst_avi_demux_aggregated_flow (avi)) != GST_FLOW_OK))) {
GST_WARNING ("stream_movi flow: %s / %s", gst_flow_get_name (res),
gst_flow_get_name (agg_res));
res = agg_res;
goto pause;
}
return; return;
/* ERRORS */ /* ERRORS */
pause: pause:
GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res)); GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
avi->segment_running = FALSE;
gst_pad_pause_task (avi->sinkpad); gst_pad_pause_task (avi->sinkpad);
if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) { if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) {
gboolean push_eos = TRUE; gboolean push_eos = TRUE;
if (res == GST_FLOW_UNEXPECTED) { if (res == GST_FLOW_UNEXPECTED) {
/* we completed the segment on EOS. */
avi->segment_running = FALSE;
/* handle end-of-stream/segment */ /* handle end-of-stream/segment */
if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) { if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gint64 stop;
if ((stop = avi->segment.stop) == -1)
stop = avi->segment.duration;
GST_INFO_OBJECT (avi, "sending segment_done");
gst_element_post_message gst_element_post_message
(GST_ELEMENT (avi), (GST_ELEMENT (avi),
gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME, gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME,
avi->segment.stop)); stop));
push_eos = FALSE; push_eos = FALSE;
} }
} else { } else {
@ -3583,7 +3673,12 @@ pause:
("streaming stopped, reason %s", gst_flow_get_name (res))); ("streaming stopped, reason %s", gst_flow_get_name (res)));
} }
if (push_eos) { if (push_eos) {
gst_avi_demux_push_event (avi, gst_event_new_eos ()); GST_INFO_OBJECT (avi, "sending eos");
if (!(gst_avi_demux_push_event (avi, gst_event_new_eos ()))) {
/* if we don't error out here it will hand */
GST_ELEMENT_ERROR (avi, STREAM, FAILED,
(_("Internal data stream error.")), ("corrupted file"));
}
} }
} }
} }

View file

@ -47,15 +47,19 @@ G_BEGIN_DECLS
((((chunkid) & 0xff) - '0') * 10 + \ ((((chunkid) & 0xff) - '0') * 10 + \
(((chunkid) >> 8) & 0xff) - '0') (((chunkid) >> 8) & 0xff) - '0')
#define GST_AVI_INDEX_ENTRY_FLAG_KEYFRAME 1
/* 48 bytes */
typedef struct { typedef struct {
gint index_nr; guint index_nr; /* = (entry-index_entries)/sizeof(gst_avi_index_entry); */
gint stream_nr; guchar stream_nr;
guint64 ts, dur; guchar flags;
guint32 flags; guint64 ts;
guint64 dur; /* =entry[1].ts-entry->ts */
guint64 offset; guint64 offset;
gint size; guint64 bytes_before; /* calculated */
guint64 bytes_before; guint32 frames_before; /* calculated */
guint32 frames_before; guint32 size; /* could be read from the chunk (if we don't split) */
} gst_avi_index_entry; } gst_avi_index_entry;
typedef struct { typedef struct {
@ -95,6 +99,7 @@ typedef struct {
/* VBR indicator */ /* VBR indicator */
gboolean is_vbr; gboolean is_vbr;
/* openDML support (for files >4GB) */
gboolean superindex; gboolean superindex;
guint64 *indexes; guint64 *indexes;