From 1619c09e3e8d3570a049a9d6fdf419837acf6a3f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 May 2009 19:35:11 +0200 Subject: [PATCH] qtdemux: add some more micro optimisations --- gst/qtdemux/qtdemux.c | 147 ++++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 55 deletions(-) diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index 4be965e47c..64c32f4662 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -83,7 +83,6 @@ struct _QtNode struct _QtDemuxSample { - guint32 chunk; guint32 size; guint64 offset; GstClockTimeDiff pts_offset; /* Add this value to timestamp to get the pts */ @@ -2429,10 +2428,9 @@ next_entry_size (GstQTDemux * demux) } GST_LOG_OBJECT (demux, - "Checking Stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)", + "Checking Stream %d (sample_index:%d / offset:%lld / size:%d)", i, stream->sample_index, stream->samples[stream->sample_index].offset, - stream->samples[stream->sample_index].size, - stream->samples[stream->sample_index].chunk); + stream->samples[stream->sample_index].size); if (((smalloffs == -1) || (stream->samples[stream->sample_index].offset < smalloffs)) @@ -2620,11 +2618,10 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) if (stream->sample_index >= stream->n_samples) continue; GST_LOG_OBJECT (demux, - "Checking stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)", + "Checking stream %d (sample_index:%d / offset:%lld / size:%d)", i, stream->sample_index, stream->samples[stream->sample_index].offset, - stream->samples[stream->sample_index].size, - stream->samples[stream->sample_index].chunk); + stream->samples[stream->sample_index].size); if (stream->samples[stream->sample_index].offset == demux->offset) break; @@ -3337,7 +3334,6 @@ static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) { - int offset; GNode *stsc; GNode *stsz; GNode *stco; @@ -3346,7 +3342,7 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode *stss; GNode *stps; GNode *ctts; - const guint8 *stsc_data, *stsz_data, *stco_data; + const guint8 *stsc_data, *stsz_data, *stco_data, *co64_data, *stts_data; int sample_size; int sample_index; int n_samples; @@ -3361,43 +3357,59 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, if (!(stsc = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsc))) goto corrupt_file; stsc_data = (const guint8 *) stsc->data; + /* sample size */ if (!(stsz = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsz))) goto corrupt_file; stsz_data = (const guint8 *) stsz->data; + /* chunk offsets */ stco = qtdemux_tree_get_child_by_type (stbl, FOURCC_stco); co64 = qtdemux_tree_get_child_by_type (stbl, FOURCC_co64); if (stco) { stco_data = (const guint8 *) stco->data; + co64_data = NULL; } else { stco_data = NULL; if (co64 == NULL) goto corrupt_file; + co64_data = (const guint8 *) co64->data; } /* sample time */ if (!(stts = qtdemux_tree_get_child_by_type (stbl, FOURCC_stts))) goto corrupt_file; + stts_data = (const guint8 *) stts->data; sample_size = QT_UINT32 (stsz_data + 12); if (sample_size == 0 || stream->sampled) { n_samples = QT_UINT32 (stsz_data + 16); + + if (n_samples == 0) + goto no_samples; + GST_DEBUG_OBJECT (qtdemux, "stsz sample_size 0, allocating n_samples %d", n_samples); stream->n_samples = n_samples; samples = g_new0 (QtDemuxSample, n_samples); stream->samples = samples; - for (i = 0; i < n_samples; i++) { - if (sample_size == 0) - samples[i].size = QT_UINT32 (stsz_data + i * 4 + 20); - else + /* set the sample sizes */ + if (sample_size == 0) { + const guint8 *stsz_p = stsz_data + 20; + /* different sizes for each sample */ + for (i = 0; i < n_samples; i++) { + samples[i].size = QT_UINT32 (stsz_p); + GST_LOG_OBJECT (qtdemux, "sample %d has size %d", i, samples[i].size); + stsz_p += 4; + } + } else { + /* samples have the same size */ + GST_LOG_OBJECT (qtdemux, "all samples have size %d", sample_size); + for (i = 0; i < n_samples; i++) samples[i].size = sample_size; - - GST_LOG_OBJECT (qtdemux, "sample %d has size %d", i, samples[i].size); - /* init other fields to defaults for this sample */ - samples[i].keyframe = FALSE; } + + /* set the sample offsets in the file */ n_samples_per_chunk = QT_UINT32 (stsc_data + 12); index = 0; for (i = 0; i < n_samples_per_chunk; i++) { @@ -3405,7 +3417,7 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 samples_per_chunk; first_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 0) - 1; - if (i == n_samples_per_chunk - 1) { + if (G_UNLIKELY (i == n_samples_per_chunk - 1)) { last_chunk = G_MAXUINT32; } else { last_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 12) - 1; @@ -3418,49 +3430,56 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, if (stco) { chunk_offset = QT_UINT32 (stco_data + 16 + j * 4); } else { - chunk_offset = QT_UINT64 ((guint8 *) co64->data + 16 + j * 8); + chunk_offset = QT_UINT64 (co64_data + 16 + j * 8); } for (k = 0; k < samples_per_chunk; k++) { GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %lld", index, chunk_offset); - samples[index].chunk = j; samples[index].offset = chunk_offset; chunk_offset += samples[index].size; index++; - if (index >= n_samples) + if (G_UNLIKELY (index >= n_samples)) goto done2; } } } done2: - n_sample_times = QT_UINT32 ((guint8 *) stts->data + 12); + n_sample_times = QT_UINT32 (stts_data + 12); timestamp = 0; stream->min_duration = 0; time = 0; index = 0; - for (i = 0; (i < n_sample_times) && (index < stream->n_samples); i++) { + stts_data += 16; + for (i = 0; i < n_sample_times; i++) { guint32 n; guint32 duration; - n = QT_UINT32 ((guint8 *) stts->data + 16 + 8 * i); - duration = QT_UINT32 ((guint8 *) stts->data + 16 + 8 * i + 4); - for (j = 0; (j < n) && (index < stream->n_samples); j++) { + n = QT_UINT32 (stts_data); + stts_data += 4; + duration = QT_UINT32 (stts_data); + stts_data += 4; + + /* take first duration for fps */ + if (G_UNLIKELY (stream->min_duration == 0)) + stream->min_duration = duration; + + for (j = 0; j < n; j++) { GST_DEBUG_OBJECT (qtdemux, "sample %d: timestamp %" GST_TIME_FORMAT, index, GST_TIME_ARGS (timestamp)); samples[index].timestamp = timestamp; - /* take first duration for fps */ - if (stream->min_duration == 0) - stream->min_duration = duration; /* add non-scaled values to avoid rounding errors */ time += duration; timestamp = gst_util_uint64_scale (time, GST_SECOND, stream->timescale); samples[index].duration = timestamp - samples[index].timestamp; index++; + if (G_UNLIKELY (index >= n_samples)) + goto done3; } } + done3: /* sample sync, can be NULL */ stss = qtdemux_tree_get_child_by_type (stbl, FOURCC_stss); @@ -3468,38 +3487,39 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, if (stss) { /* mark keyframes */ guint32 n_sample_syncs; + const guint8 *stss_p = (guint8 *) stss->data; - n_sample_syncs = QT_UINT32 ((guint8 *) stss->data + 12); + stss_p += 12; + n_sample_syncs = QT_UINT32 (stss_p); if (n_sample_syncs == 0) { stream->all_keyframe = TRUE; } else { - offset = 16; for (i = 0; i < n_sample_syncs; i++) { + stss_p += 4; /* note that the first sample is index 1, not 0 */ - index = QT_UINT32 ((guint8 *) stss->data + offset); - if (index > 0 && index <= stream->n_samples) { + index = QT_UINT32 (stss_p); + if (G_LIKELY (index > 0 && index <= n_samples)) samples[index - 1].keyframe = TRUE; - offset += 4; - } } } stps = qtdemux_tree_get_child_by_type (stbl, FOURCC_stps); if (stps) { - /* mark keyframes */ + /* stps marks partial sync frames like open GOP I-Frames */ guint32 n_sample_syncs; + const guint8 *stps_p = (guint8 *) stps->data; - n_sample_syncs = QT_UINT32 ((guint8 *) stps->data + 12); - if (n_sample_syncs == 0) { - //stream->all_keyframe = TRUE; + stps_p += 12; + n_sample_syncs = QT_UINT32 (stps_p); + if (n_sample_syncs != 0) { + /* no entries, the stss table contains the real sync + * samples */ } else { - offset = 16; for (i = 0; i < n_sample_syncs; i++) { + stps_p += 4; /* note that the first sample is index 1, not 0 */ - index = QT_UINT32 ((guint8 *) stps->data + offset); - if (index > 0 && index <= stream->n_samples) { + index = QT_UINT32 (stps_p); + if (G_LIKELY (index > 0 && index <= n_samples)) samples[index - 1].keyframe = TRUE; - offset += 4; - } } } } @@ -3510,13 +3530,16 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, } else { GST_DEBUG_OBJECT (qtdemux, "stsz sample_size %d != 0, treating chunks as samples", sample_size); - /* treat chunks as samples */ if (stco) { n_samples = QT_UINT32 (stco_data + 12); } else { - n_samples = QT_UINT32 ((guint8 *) co64->data + 12); + n_samples = QT_UINT32 (co64_data + 12); } + + if (n_samples == 0) + goto no_samples; + stream->n_samples = n_samples; GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %d", n_samples); samples = g_new0 (QtDemuxSample, n_samples); @@ -3554,13 +3577,12 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, if (stco) { chunk_offset = QT_UINT32 (stco_data + 16 + j * 4); } else { - chunk_offset = QT_UINT64 ((guint8 *) co64->data + 16 + j * 8); + chunk_offset = QT_UINT64 (co64_data + 16 + j * 8); } GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %" G_GUINT64_FORMAT, j, chunk_offset); - samples[j].chunk = j; samples[j].offset = chunk_offset; if (stream->samples_per_frame * stream->bytes_per_frame) { @@ -3587,18 +3609,28 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, /* composition time to sample */ if ((ctts = qtdemux_tree_get_child_by_type (stbl, FOURCC_ctts))) { - const guint8 *ctts_data = (const guint8 *) ctts->data; - guint32 n_entries = QT_UINT32 (ctts_data + 12); + const guint8 *ctts_data, *ctts_p; + guint32 n_entries; guint32 count; gint32 soffset; + ctts_data = (const guint8 *) ctts->data; + n_entries = QT_UINT32 (ctts_data + 12); + /* Fill in the pts_offsets */ - for (i = 0, j = 0; (j < stream->n_samples) && (i < n_entries); i++) { - count = QT_UINT32 (ctts_data + 16 + i * 8); - soffset = QT_UINT32 (ctts_data + 20 + i * 8); - for (k = 0; (k < count) && (j < stream->n_samples); k++, j++) { + index = 0; + ctts_p = ctts_data + 16; + for (i = 0; i < n_entries; i++) { + count = QT_UINT32 (ctts_p); + ctts_p += 4; + soffset = QT_UINT32 (ctts_p); + ctts_p += 4; + for (j = 0; j < count; j++) { /* we operate with very small soffset values here, it shouldn't overflow */ - samples[j].pts_offset = soffset * GST_SECOND / stream->timescale; + samples[index].pts_offset = soffset * GST_SECOND / stream->timescale; + index++; + if (G_UNLIKELY (index >= n_samples)) + goto done; } } } @@ -3612,6 +3644,11 @@ corrupt_file: (_("This file is corrupt and cannot be played.")), (NULL)); return FALSE; } +no_samples: + { + GST_WARNING_OBJECT (qtdemux, "stream has no samples"); + return FALSE; + } } /* collect all segment info for @stream.