qtdemux: Separate off stbl sub-atom initialisation

This commit is contained in:
Robert Swain 2009-10-28 17:49:02 +00:00
parent 6a6d2c4970
commit 1f7b878d89

View file

@ -271,9 +271,14 @@ struct _QtDemuxStream
/* stts */ /* stts */
guint32 n_sample_times; guint32 n_sample_times;
/* stss */ /* stss */
gboolean stss_present;
guint32 n_sample_syncs; guint32 n_sample_syncs;
/* stps */ /* stps */
gboolean stps_present;
guint32 n_sample_partial_syncs; guint32 n_sample_partial_syncs;
/* ctts */
gboolean ctts_present;
guint32 n_composition_times;
}; };
enum QtDemuxState enum QtDemuxState
@ -3734,26 +3739,93 @@ too_many_streams:
} }
} }
/* collect all samples for @stream by reading the info from @stbl /* initialise bytereaders for stbl sub-atoms */
*/
static gboolean static gboolean
qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
GNode * stbl)
{ {
int sample_index = 0; /* time-to-sample atom */
gint i, j, k; if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
int index = 0; goto corrupt_file;
guint64 timestamp = 0;
/* sample to chunk */ /* skip version + flags */
if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
goto corrupt_file;
GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 2 * 4))
goto corrupt_file;
/* sync sample atom */
stream->stps_present = FALSE;
if ((stream->stss_present =
!!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
&stream->stss) ? TRUE : FALSE) == TRUE) {
/* skip version + flags */
if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
goto corrupt_file;
if (stream->n_sample_syncs) {
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
goto corrupt_file;
}
/* partial sync sample atom */
if ((stream->stps_present =
!!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
&stream->stps) ? TRUE : FALSE) == TRUE) {
/* skip version + flags */
if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stps,
&stream->n_sample_partial_syncs))
goto corrupt_file;
/* if there are no entries, the stss table contains the real
* sync samples */
if (stream->n_sample_partial_syncs) {
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stps,
stream->n_sample_partial_syncs, 4))
goto corrupt_file;
}
}
}
/* sample-to-chunk atom */
if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc)) if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
goto corrupt_file; goto corrupt_file;
/* skip version + flags */
if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stsc,
&stream->n_samples_per_chunk))
goto corrupt_file;
GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
stream->n_samples_per_chunk);
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
12))
goto corrupt_file;
/* sample size */ /* sample size */
if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz)) if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
goto corrupt_file; goto corrupt_file;
/* chunk offsets */ /* skip version + flags */
if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
goto corrupt_file;
/* chunk offset */
if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco)) if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
stream->co_size = sizeof (guint32); stream->co_size = sizeof (guint32);
else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64, else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
@ -3762,18 +3834,11 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream,
else else
goto corrupt_file; goto corrupt_file;
/* sample time */
if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
goto corrupt_file;
if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
goto corrupt_file;
/* skip version + flags */ /* skip version + flags */
if (!gst_byte_reader_skip (&stream->stco, 1 + 3)) if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
goto corrupt_file; goto corrupt_file;
/* chunks_are_chunks == 0 means treat chunks as samples */
stream->chunks_are_chunks = !stream->sample_size || stream->sampled; stream->chunks_are_chunks = !stream->sample_size || stream->sampled;
if (stream->chunks_are_chunks) { if (stream->chunks_are_chunks) {
/* skip number of entries */ /* skip number of entries */
@ -3782,37 +3847,85 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream,
if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples)) if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
goto corrupt_file; goto corrupt_file;
/* make sure there are enough data in the stsz atom */
if (!stream->sample_size) {
/* different sizes for each sample */
if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
goto corrupt_file;
}
} else { } else {
/* treat chunks as samples */ /* treat chunks as samples */
if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples)) if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
goto corrupt_file; goto corrupt_file;
} }
if (!stream->n_samples) if (!stream->n_samples) {
goto no_samples; GST_WARNING_OBJECT (qtdemux, "stream has no samples");
return FALSE;
if (stream->chunks_are_chunks) }
GST_DEBUG_OBJECT (qtdemux, "stsz sample_size 0");
GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u (%u MB)", GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u (%u MB)",
stream->n_samples, stream->n_samples,
(guint) (stream->n_samples * sizeof (QtDemuxSample)) >> 20); (guint) (stream->n_samples * sizeof (QtDemuxSample)) >> 20);
if (stream->n_samples >= if (stream->n_samples >=
QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
goto index_too_big; GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
"be larger than %uMB (broken file?)", stream->n_samples,
QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
return FALSE;
}
stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples); stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
if (!stream->samples) if (!stream->samples) {
goto out_of_memory; GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
stream->n_samples);
return FALSE;
}
/* composition time-to-sample */
if ((stream->ctts_present =
!!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
&stream->ctts) ? TRUE : FALSE) == TRUE) {
/* skip version + flags */
if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
|| !gst_byte_reader_get_uint32_be (&stream->ctts,
&stream->n_composition_times))
goto corrupt_file;
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
4 + 4))
goto corrupt_file;
}
return TRUE;
corrupt_file:
{
GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
(_("This file is corrupt and cannot be played.")), (NULL));
return FALSE;
}
}
/* collect all samples for @stream by reading the info from @stbl
*/
static gboolean
qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
int sample_index = 0;
gint i, j, k;
int index = 0;
guint64 timestamp = 0;
if (stream->chunks_are_chunks) { if (stream->chunks_are_chunks) {
/* set the sample sizes */ /* set the sample sizes */
if (stream->sample_size == 0) { if (stream->sample_size == 0) {
/* different sizes for each sample */ /* different sizes for each sample */
if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
goto corrupt_file;
for (i = 0; i < stream->n_samples; i++) { for (i = 0; i < stream->n_samples; i++) {
stream->samples[i].size = stream->samples[i].size =
gst_byte_reader_get_uint32_be_unchecked (&stream->stsz); gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
@ -3827,21 +3940,6 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream,
} }
} }
/* set the sample offsets in the file */
if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
!gst_byte_reader_get_uint32_be (&stream->stsc,
&stream->n_samples_per_chunk))
goto corrupt_file;
if (!stream->chunks_are_chunks) {
GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
stream->n_samples_per_chunk);
}
if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
12))
goto corrupt_file;
for (i = 0; i < stream->n_samples_per_chunk; i++) { for (i = 0; i < stream->n_samples_per_chunk; i++) {
GstByteReader co_chunk; GstByteReader co_chunk;
guint32 first_chunk, last_chunk; guint32 first_chunk, last_chunk;
@ -3945,17 +4043,6 @@ done2:
{ {
guint32 time; guint32 time;
if (!gst_byte_reader_skip (&stream->stts, 4))
goto corrupt_file;
if (!gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
goto corrupt_file;
GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times,
2 * 4))
goto corrupt_file;
timestamp = 0; timestamp = 0;
stream->min_duration = 0; stream->min_duration = 0;
time = 0; time = 0;
@ -4005,22 +4092,10 @@ done2:
done3: done3:
{ {
/* sample sync, can be NULL */ /* sample sync, can be NULL */
if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss, &stream->stss)) { if (stream->stss_present == TRUE) {
/* mark keyframes */
if (!gst_byte_reader_skip (&stream->stss, 4))
goto corrupt_file;
if (!gst_byte_reader_get_uint32_be (&stream->stss,
&stream->n_sample_syncs))
goto corrupt_file;
if (!stream->n_sample_syncs) { if (!stream->n_sample_syncs) {
stream->all_keyframe = TRUE; stream->all_keyframe = TRUE;
} else { } else {
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs,
4))
goto corrupt_file;
for (i = 0; i < stream->n_sample_syncs; i++) { for (i = 0; i < stream->n_sample_syncs; i++) {
/* note that the first sample is index 1, not 0 */ /* note that the first sample is index 1, not 0 */
index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss); index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
@ -4028,23 +4103,12 @@ done3:
stream->samples[index - 1].keyframe = TRUE; stream->samples[index - 1].keyframe = TRUE;
} }
} }
/* stps marks partial sync frames like open GOP I-Frames */ /* stps marks partial sync frames like open GOP I-Frames */
if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps, if (stream->stps_present == TRUE) {
&stream->stps)) {
if (!gst_byte_reader_skip (&stream->stps, 4))
goto corrupt_file;
if (!gst_byte_reader_get_uint32_be (&stream->stps,
&stream->n_sample_partial_syncs))
goto corrupt_file;
/* if there are no entries, the stss table contains the real /* if there are no entries, the stss table contains the real
* sync samples */ * sync samples */
if (stream->n_sample_partial_syncs) { if (stream->n_sample_partial_syncs) {
/* make sure there's enough data */
if (!qt_atom_parser_has_chunks (&stream->stps,
stream->n_sample_partial_syncs, 4))
goto corrupt_file;
for (i = 0; i < stream->n_sample_partial_syncs; i++) { for (i = 0; i < stream->n_sample_partial_syncs; i++) {
/* note that the first sample is index 1, not 0 */ /* note that the first sample is index 1, not 0 */
index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps); index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
@ -4061,20 +4125,13 @@ done3:
ctts: ctts:
/* composition time to sample */ /* composition time to sample */
if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts, &stream->ctts)) { if (stream->ctts_present == TRUE) {
guint32 n_entries;
guint32 count; guint32 count;
gint32 soffset; gint32 soffset;
gst_byte_reader_skip (&stream->ctts, 1 + 3);
n_entries = gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
if (!qt_atom_parser_has_chunks (&stream->ctts, n_entries, 4 + 4))
goto corrupt_file;
/* Fill in the pts_offsets */ /* Fill in the pts_offsets */
index = 0; index = 0;
for (i = 0; i < n_entries; i++) { for (i = 0; i < stream->n_composition_times; i++) {
count = gst_byte_reader_get_uint32_be_unchecked (&stream->ctts); count = gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
soffset = gst_byte_reader_get_uint32_be_unchecked (&stream->ctts); soffset = gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
for (j = 0; j < count; j++) { for (j = 0; j < count; j++) {
@ -4097,24 +4154,6 @@ corrupt_file:
(_("This file is corrupt and cannot be played.")), (NULL)); (_("This file is corrupt and cannot be played.")), (NULL));
return FALSE; return FALSE;
} }
no_samples:
{
GST_WARNING_OBJECT (qtdemux, "stream has no samples");
return FALSE;
}
out_of_memory:
{
GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
stream->n_samples);
return FALSE;
}
index_too_big:
{
GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
"be larger than %uMB (broken file?)", stream->n_samples,
QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
return FALSE;
}
} }
/* collect all segment info for @stream. /* collect all segment info for @stream.
@ -5177,7 +5216,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
} }
/* collect sample information */ /* collect sample information */
if (!qtdemux_parse_samples (qtdemux, stream, stbl)) if (!qtdemux_stbl_init (qtdemux, stream, stbl) ||
!qtdemux_parse_samples (qtdemux, stream))
goto samples_failed; goto samples_failed;
/* configure segments */ /* configure segments */