gst/avi/gstavidemux.c: Improve allocation, cutting and sorting of the index. How takes a few seconds instead of minutes.

Original commit message from CVS:
* gst/avi/gstavidemux.c: (gst_avi_demux_stream_index),
(gst_avi_demux_stream_scan), (sort), (gst_avi_demux_massage_index),
(gst_avi_demux_stream_header), (gst_avi_demux_stream_data):
Improve allocation, cutting and sorting of the index. How takes a
few seconds instead of minutes.
This commit is contained in:
Ronald S. Bultje 2004-10-04 13:32:20 +00:00
parent 23fa458642
commit 3f20d7f4b8
2 changed files with 139 additions and 83 deletions

View file

@ -1,3 +1,11 @@
2004-10-04 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* gst/avi/gstavidemux.c: (gst_avi_demux_stream_index),
(gst_avi_demux_stream_scan), (sort), (gst_avi_demux_massage_index),
(gst_avi_demux_stream_header), (gst_avi_demux_stream_data):
Improve allocation, cutting and sorting of the index. How takes a
few seconds instead of minutes.
2004-10-03 Christophe Fergeau <teuf@gnome.org> 2004-10-03 Christophe Fergeau <teuf@gnome.org>
* gst/realmedia/rmdemux.c: (gst_rmdemux_parse_mdpr): * gst/realmedia/rmdemux.c: (gst_rmdemux_parse_mdpr):

View file

@ -1025,14 +1025,18 @@ gst_avi_demux_stream_odml (GstAviDemux * avi)
*/ */
static gboolean static gboolean
gst_avi_demux_stream_index (GstAviDemux * avi) gst_avi_demux_stream_index (GstAviDemux * avi,
GList ** index, GList ** alloc_list)
{ {
GList *list = NULL;
GstBuffer *buf = NULL; GstBuffer *buf = NULL;
guint i; guint i;
GstEvent *event; GstEvent *event;
GstRiffRead *riff = GST_RIFF_READ (avi); GstRiffRead *riff = GST_RIFF_READ (avi);
guint64 pos_before, pos_after, length; guint64 pos_before, pos_after, length;
guint32 tag; guint32 tag;
guint index_size;
gst_avi_index_entry *index_entries = NULL;
/* first, we need to know the current position (to seek back /* first, we need to know the current position (to seek back
* when we're done) and the total length of the file. */ * when we're done) and the total length of the file. */
@ -1068,12 +1072,11 @@ gst_avi_demux_stream_index (GstAviDemux * avi)
return FALSE; return FALSE;
/* parse all entries */ /* parse all entries */
avi->index_size = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry); index_size = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
avi->index_entries = index_entries = g_malloc (index_size * sizeof (gst_avi_index_entry));
g_malloc (avi->index_size * sizeof (gst_avi_index_entry));
GST_INFO ("%u index entries", avi->index_size); GST_INFO ("%u index entries", avi->index_size);
for (i = 0; i < avi->index_size; i++) { for (i = 0; i < index_size; i++) {
gst_riff_index_entry entry, *_entry; gst_riff_index_entry entry, *_entry;
avi_stream_context *stream; avi_stream_context *stream;
gint stream_nr; gint stream_nr;
@ -1085,14 +1088,14 @@ gst_avi_demux_stream_index (GstAviDemux * avi)
entry.offset = GUINT32_FROM_LE (_entry->offset); entry.offset = GUINT32_FROM_LE (_entry->offset);
entry.flags = GUINT32_FROM_LE (_entry->flags); entry.flags = GUINT32_FROM_LE (_entry->flags);
entry.size = GUINT32_FROM_LE (_entry->size); entry.size = GUINT32_FROM_LE (_entry->size);
target = &avi->index_entries[i]; target = &index_entries[i];
if (entry.id == GST_RIFF_rec) if (entry.id == GST_RIFF_rec)
continue; continue;
stream_nr = CHUNKID_TO_STREAMNR (entry.id); stream_nr = CHUNKID_TO_STREAMNR (entry.id);
if (stream_nr >= avi->num_streams || stream_nr < 0) { if (stream_nr >= avi->num_streams || stream_nr < 0) {
g_warning ("Index entry %d has invalid stream nr %d", i, stream_nr); GST_WARNING ("Index entry %d has invalid stream nr %d", i, stream_nr);
target->stream_nr = -1; target->stream_nr = -1;
continue; continue;
} }
@ -1138,6 +1141,8 @@ gst_avi_demux_stream_index (GstAviDemux * avi)
stream->total_bytes += target->size; stream->total_bytes += target->size;
stream->total_frames++; stream->total_frames++;
list = g_list_prepend (list, target);
} }
/* debug our indexes */ /* debug our indexes */
@ -1154,10 +1159,18 @@ end:
gst_buffer_unref (buf); gst_buffer_unref (buf);
/* seek back to the data */ /* seek back to the data */
if (!(event = gst_riff_read_seek (riff, pos_before))) if (!(event = gst_riff_read_seek (riff, pos_before))) {
g_free (index_entries);
g_list_free (list);
return FALSE; return FALSE;
}
gst_event_unref (event); gst_event_unref (event);
if (list)
*index = g_list_reverse (list);
if (index_entries)
*alloc_list = g_list_prepend (*alloc_list, index_entries);
return TRUE; return TRUE;
} }
@ -1268,14 +1281,17 @@ gst_avi_demux_sync (GstAviDemux * avi, guint32 * ret_tag, gboolean prevent_eos)
*/ */
static gboolean static gboolean
gst_avi_demux_stream_scan (GstAviDemux * avi) gst_avi_demux_stream_scan (GstAviDemux * avi,
GList ** index, GList ** alloc_list)
{ {
GstRiffRead *riff = GST_RIFF_READ (avi); GstRiffRead *riff = GST_RIFF_READ (avi);
gst_avi_index_entry *entry; gst_avi_index_entry *entry, *entries = NULL;
avi_stream_context *stream; avi_stream_context *stream;
guint64 pos = gst_bytestream_tell (riff->bs); guint64 pos = gst_bytestream_tell (riff->bs);
guint32 tag; guint32 tag;
GstEvent *event; GstEvent *event;
GList *list = NULL;
guint index_size = 0;
/* FIXME: /* FIXME:
* - implement non-seekable source support. * - implement non-seekable source support.
@ -1296,15 +1312,15 @@ 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;
/* increase allocated size for index */ /* pre-allocate */
if (avi->index_size % 256 == 0) { if (index_size % 1024 == 0) {
avi->index_entries = g_renew (gst_avi_index_entry, entries = g_new (gst_avi_index_entry, 1024);
avi->index_entries, avi->index_size + 256); *alloc_list = g_list_prepend (*alloc_list, entries);
} }
entry = &avi->index_entries[avi->index_size]; entry = &entries[index_size % 1024];
/* fill in */ /* fill in */
entry->index_nr = avi->index_size++; entry->index_nr = index_size++;
entry->stream_nr = stream_nr; entry->stream_nr = stream_nr;
entry->flags = 0; entry->flags = 0;
entry->offset = gst_bytestream_tell (riff->bs) + 8; entry->offset = gst_bytestream_tell (riff->bs) + 8;
@ -1332,6 +1348,8 @@ gst_avi_demux_stream_scan (GstAviDemux * avi)
entry->frames_before = stream->total_frames; entry->frames_before = stream->total_frames;
stream->total_frames++; stream->total_frames++;
list = g_list_prepend (list, entry);
next: next:
if (!gst_riff_read_skip (riff)) if (!gst_riff_read_skip (riff))
break; break;
@ -1340,11 +1358,15 @@ gst_avi_demux_stream_scan (GstAviDemux * avi)
avi->index_offset = 0; avi->index_offset = 0;
/* seek back */ /* seek back */
if (!(event = gst_riff_read_seek (riff, pos))) if (!(event = gst_riff_read_seek (riff, pos))) {
g_list_free (list);
return FALSE; return FALSE;
}
gst_event_unref (event); gst_event_unref (event);
GST_LOG_OBJECT (avi, "index created, %d items", avi->index_size); GST_LOG_OBJECT (avi, "index created, %d items", index_size);
*index = g_list_reverse (list);
return TRUE; return TRUE;
} }
@ -1359,12 +1381,27 @@ gst_avi_demux_stream_scan (GstAviDemux * avi)
* order. The end result should be a smoother playing AVI. * order. The end result should be a smoother playing AVI.
*/ */
static gint
sort (gst_avi_index_entry * a, gst_avi_index_entry * b)
{
if (a->ts > b->ts)
return 1;
else if (a->ts < b->ts)
return -1;
else
return a->stream_nr - b->stream_nr;
}
static void static void
gst_avi_demux_massage_index (GstAviDemux * avi) gst_avi_demux_massage_index (GstAviDemux * avi,
GList * list, GList * alloc_list)
{ {
gst_avi_index_entry *entry; gst_avi_index_entry *entry;
avi_stream_context *stream; avi_stream_context *stream;
gint i; gint i;
GList *one;
GST_LOG ("Starting index massage");
/* init frames */ /* init frames */
for (i = 0; i < avi->num_streams; i++) { for (i = 0; i < avi->num_streams; i++) {
@ -1379,8 +1416,8 @@ gst_avi_demux_massage_index (GstAviDemux * avi)
stream->strh->length / (stream->total_frames * stream->bitrate); stream->strh->length / (stream->total_frames * stream->bitrate);
} }
} }
for (i = 0; i < avi->index_size; i++) { for (one = list; one != NULL; one = one->next) {
entry = &avi->index_entries[i]; entry = one->data;
if (entry->stream_nr >= avi->num_streams) if (entry->stream_nr >= avi->num_streams)
continue; continue;
@ -1389,14 +1426,16 @@ gst_avi_demux_massage_index (GstAviDemux * avi)
entry->ts += stream->delay; entry->ts += stream->delay;
} }
GST_LOG ("I'm now going to cut large chunks into smaller pieces");
/* cut chunks in small (seekable) pieces */ /* cut chunks in small (seekable) pieces */
for (i = 0; i < avi->index_size; i++) { for (one = list; one != NULL; one = one->next) {
entry = &avi->index_entries[i]; entry = one->data;
if (entry->stream_nr >= avi->num_streams) if (entry->stream_nr >= avi->num_streams)
continue; continue;
#define MAX_DURATION (GST_SECOND / 4) #define MAX_DURATION (GST_SECOND / 2)
/* check for max duration of a single buffer. I suppose that /* check for max duration of a single buffer. I suppose that
* the allocation of index entries could be improved. */ * the allocation of index entries could be improved. */
@ -1404,81 +1443,77 @@ gst_avi_demux_massage_index (GstAviDemux * avi)
if (entry->dur > MAX_DURATION && stream->strh->type == GST_RIFF_FCC_auds) { if (entry->dur > MAX_DURATION && stream->strh->type == GST_RIFF_FCC_auds) {
guint32 ideal_size = stream->bitrate / 10; guint32 ideal_size = stream->bitrate / 10;
gst_avi_index_entry *entries; gst_avi_index_entry *entries;
gint old_size, n, num_added; gint old_size, num_added;
GList *one2;
/* copy index */ /* copy index */
old_size = entry->size; old_size = entry->size;
num_added = (entry->size - 1) / ideal_size; num_added = (entry->size - 1) / ideal_size;
avi->index_size += num_added; avi->index_size += num_added;
entries = g_malloc (sizeof (gst_avi_index_entry) * avi->index_size); entries = g_malloc (sizeof (gst_avi_index_entry) * num_added);
memcpy (entries, avi->index_entries, alloc_list = g_list_prepend (alloc_list, entries);
sizeof (gst_avi_index_entry) * (entry->index_nr + 1)); for (one2 = one->next; one2 != NULL; one2 = one2->next) {
if (entry->index_nr < avi->index_size - num_added - 1) { gst_avi_index_entry *entry2 = one2->data;
memcpy (&entries[entry->index_nr + 1 + num_added],
&avi->index_entries[entry->index_nr + 1], entry2->index_nr += num_added;
(avi->index_size - num_added - entry->index_nr - 1) * if (entry2->stream_nr == entry->stream_nr)
sizeof (gst_avi_index_entry)); entry2->frames_before += num_added;
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 */ /* new sized index chunks */
for (n = entry->index_nr; n < entry->index_nr + num_added + 1; n++) { for (i = 0; i < num_added + 1; i++) {
gst_avi_index_entry *entry2;
if (i == 0)
entry2 = entry;
else {
entry2 = &entries[i - 1];
list = g_list_insert_before (list, one, entry2);
entry = one->data;
one = one->next;
}
if (old_size >= ideal_size) { if (old_size >= ideal_size) {
entries[n].size = ideal_size; entry2->size = ideal_size;
old_size -= ideal_size; old_size -= ideal_size;
} else } else
entries[n].size = old_size; entry2->size = old_size;
entries[n].dur = GST_SECOND * entries[n].size / stream->bitrate; entry2->dur = GST_SECOND * entry2->size / stream->bitrate;
if (n != entry->index_nr) { if (i != 0) {
memcpy (&entries[n], &entries[n - 1], sizeof (gst_avi_index_entry)); memcpy (entry2, entry, sizeof (gst_avi_index_entry));
entries[n].index_nr++; entry2->index_nr++;
entries[n].ts += entries[n - 1].dur; entry2->ts += entry->dur;
entries[n].offset += entries[n - 1].size; entry2->offset += entry->size;
entries[n].bytes_before += entries[n - 1].size; entry2->bytes_before += entry->size;
entries[n].frames_before++; entry2->frames_before++;
i++;
} }
} }
/* set new pointer */
g_free (avi->index_entries);
avi->index_entries = entries;
} }
} }
GST_LOG ("I'm now going to reorder the index entries for time");
/* re-order for time */ /* re-order for time */
for (i = 1; i < avi->index_size; i++) { list = g_list_sort (list, (GCompareFunc) sort);
entry = &avi->index_entries[i];
if (entry->stream_nr >= avi->num_streams) GST_LOG ("Filling in index array");
continue;
/* check whether to rearrange according to time */ avi->index_size = g_list_length (list);
while (i > 0 && avi->index_entries[i - 1].stream_nr < avi->num_streams && avi->index_entries = g_new (gst_avi_index_entry, avi->index_size);
(entry->ts < avi->index_entries[i - 1].ts || for (i = 0, one = list; one != NULL; one = one->next, i++) {
(entry->ts == avi->index_entries[i - 1].ts && entry = one->data;
entry->stream_nr < avi->index_entries[i - 1].stream_nr))) { memcpy (&avi->index_entries[i], entry, sizeof (gst_avi_index_entry));
gst_avi_index_entry prev_entry; /*g_assert (i == entry->index_nr); */
/* 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--;
}
} }
GST_LOG ("Freeing original index list");
g_list_foreach (alloc_list, (GFunc) g_free, NULL);
g_list_free (alloc_list);
g_list_free (list);
GST_LOG ("Index massaging done");
} }
/* /*
@ -1490,6 +1525,7 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
{ {
GstRiffRead *riff = GST_RIFF_READ (avi); GstRiffRead *riff = GST_RIFF_READ (avi);
guint32 tag, flags, streams; guint32 tag, flags, streams;
GList *index = NULL, *alloc = NULL;
/* the header consists of a 'hdrl' LIST tag */ /* the header consists of a 'hdrl' LIST tag */
if (!(tag = gst_riff_peek_tag (riff, NULL))) if (!(tag = gst_riff_peek_tag (riff, NULL)))
@ -1612,14 +1648,26 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
/* create or read stream index (for seeking) */ /* create or read stream index (for seeking) */
if (flags & GST_RIFF_AVIH_HASINDEX) { if (flags & GST_RIFF_AVIH_HASINDEX) {
if (!gst_avi_demux_stream_index (avi)) if (!gst_avi_demux_stream_index (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
return FALSE; return FALSE;
}
} }
if (!avi->index_size) { if (!index) {
if (!gst_avi_demux_stream_scan (avi)) if (!gst_avi_demux_stream_scan (avi, &index, &alloc)) {
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
return FALSE; return FALSE;
}
}
if (index)
gst_avi_demux_massage_index (avi, index, alloc);
else {
g_list_free (index);
g_list_foreach (alloc, (GFunc) g_free, NULL);
g_list_free (alloc);
} }
gst_avi_demux_massage_index (avi);
return TRUE; return TRUE;
} }
@ -1753,7 +1801,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
if (stream_nr < 0 || stream_nr >= avi->num_streams) { if (stream_nr < 0 || stream_nr >= avi->num_streams) {
/* recoverable */ /* recoverable */
g_warning ("Invalid stream ID %d (" GST_FOURCC_FORMAT ")", GST_WARNING ("Invalid stream ID %d (" GST_FOURCC_FORMAT ")",
stream_nr, GST_FOURCC_ARGS (tag)); stream_nr, GST_FOURCC_ARGS (tag));
if (!gst_riff_read_skip (riff)) if (!gst_riff_read_skip (riff))
return FALSE; return FALSE;