mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
qtdemux: fragmented support; consider mvex and handle flags and offset fields
This commit is contained in:
parent
4bcad634f8
commit
4790603d31
1 changed files with 206 additions and 30 deletions
|
@ -334,6 +334,12 @@ struct _QtDemuxStream
|
||||||
guint32 ctts_sample_index;
|
guint32 ctts_sample_index;
|
||||||
guint32 ctts_count;
|
guint32 ctts_count;
|
||||||
gint32 ctts_soffset;
|
gint32 ctts_soffset;
|
||||||
|
|
||||||
|
/* fragmented */
|
||||||
|
gboolean parsed_trex;
|
||||||
|
guint32 def_sample_duration;
|
||||||
|
guint32 def_sample_size;
|
||||||
|
guint32 def_sample_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum QtDemuxState
|
enum QtDemuxState
|
||||||
|
@ -1854,18 +1860,120 @@ extract_initial_length_and_fourcc (const guint8 * data, guint64 * plength,
|
||||||
*pfourcc = fourcc;
|
*pfourcc = fourcc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
|
||||||
|
{
|
||||||
|
guint32 version;
|
||||||
|
guint64 duration;
|
||||||
|
|
||||||
|
if (!gst_byte_reader_get_uint32_be (br, &version))
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
version >>= 24;
|
||||||
|
if (version == 1) {
|
||||||
|
if (!gst_byte_reader_get_uint64_be (br, &duration))
|
||||||
|
goto failed;
|
||||||
|
} else {
|
||||||
|
guint32 dur;
|
||||||
|
|
||||||
|
if (!gst_byte_reader_get_uint32_be (br, &dur))
|
||||||
|
goto failed;
|
||||||
|
duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
|
||||||
|
qtdemux->duration = duration;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
|
guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
|
||||||
|
{
|
||||||
|
if (!stream->parsed_trex && qtdemux->moov_node) {
|
||||||
|
GNode *mvex, *trex;
|
||||||
|
GstByteReader trex_data;
|
||||||
|
|
||||||
|
mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
|
||||||
|
if (mvex) {
|
||||||
|
trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
|
||||||
|
&trex_data);
|
||||||
|
while (trex) {
|
||||||
|
guint32 id, dur, size, flags;
|
||||||
|
|
||||||
|
/* skip version/flags */
|
||||||
|
if (!gst_byte_reader_skip (&trex_data, 4))
|
||||||
|
goto next;
|
||||||
|
if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
|
||||||
|
goto next;
|
||||||
|
if (id != stream->track_id)
|
||||||
|
goto next;
|
||||||
|
/* sample description index; ignore */
|
||||||
|
if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
|
||||||
|
goto next;
|
||||||
|
if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
|
||||||
|
goto next;
|
||||||
|
if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
|
||||||
|
goto next;
|
||||||
|
if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
|
||||||
|
"duration %d, size %d, flags 0x%x", stream->track_id,
|
||||||
|
dur, size, flags);
|
||||||
|
|
||||||
|
stream->parsed_trex = TRUE;
|
||||||
|
stream->def_sample_duration = dur;
|
||||||
|
stream->def_sample_size = size;
|
||||||
|
stream->def_sample_flags = flags;
|
||||||
|
|
||||||
|
next:
|
||||||
|
/* iterate all siblings */
|
||||||
|
trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
|
||||||
|
&trex_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ds_duration = stream->def_sample_duration;
|
||||||
|
*ds_size = stream->def_sample_size;
|
||||||
|
*ds_size = stream->def_sample_size;
|
||||||
|
|
||||||
|
/* even then, above values are better than random ... */
|
||||||
|
if (G_UNLIKELY (!stream->parsed_trex)) {
|
||||||
|
GST_WARNING_OBJECT (qtdemux,
|
||||||
|
"failed to find fragment defaults for stream %d", stream->track_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
||||||
QtDemuxStream * stream, guint32 mdat_offset,
|
QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
|
||||||
guint32 d_sample_duration, guint32 d_sample_size, guint32 * samples_count)
|
guint32 d_sample_flags, guint32 * samples_count, gint64 * base_offset)
|
||||||
{
|
{
|
||||||
guint64 timestamp;
|
guint64 timestamp;
|
||||||
guint32 flags, data_offset;
|
gint32 data_offset;
|
||||||
|
guint32 flags, first_flags = 0;
|
||||||
gint i;
|
gint i;
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
guint entry_size, dur_offset, size_offset;
|
guint entry_size, dur_offset, size_offset, flags_offset, ct_offset;
|
||||||
QtDemuxSample *sample;
|
QtDemuxSample *sample;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
|
||||||
|
"default dur %d, size %d, flags 0x%x, base offset %" G_GUINT64_FORMAT,
|
||||||
|
stream->track_id, d_sample_duration, d_sample_size, d_sample_flags,
|
||||||
|
data_offset);
|
||||||
|
|
||||||
if (!gst_byte_reader_skip (trun, 1) ||
|
if (!gst_byte_reader_skip (trun, 1) ||
|
||||||
!gst_byte_reader_get_uint24_be (trun, &flags))
|
!gst_byte_reader_get_uint24_be (trun, &flags))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1873,32 +1981,50 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
||||||
if (!gst_byte_reader_get_uint32_be (trun, samples_count))
|
if (!gst_byte_reader_get_uint32_be (trun, samples_count))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/*FIXME: handle this flag properly */
|
|
||||||
if (flags & TR_DATA_OFFSET) {
|
if (flags & TR_DATA_OFFSET) {
|
||||||
if (!gst_byte_reader_get_uint32_be (trun, &data_offset))
|
/* note this is really signed */
|
||||||
|
if (!gst_byte_reader_get_int32_be (trun, &data_offset))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
*base_offset += data_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & TR_FIRST_SAMPLE_FLAGS)
|
GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
|
||||||
if (!gst_byte_reader_skip (trun, 4))
|
data_offset, flags, *samples_count);
|
||||||
goto fail;
|
|
||||||
|
if (flags & TR_FIRST_SAMPLE_FLAGS) {
|
||||||
|
if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
|
||||||
|
GST_DEBUG_OBJECT (qtdemux,
|
||||||
|
"invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
|
||||||
|
flags ^= TR_FIRST_SAMPLE_FLAGS;
|
||||||
|
} else {
|
||||||
|
if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
|
||||||
|
goto fail;
|
||||||
|
GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME ? spec says other bits should also be checked to determine
|
/* FIXME ? spec says other bits should also be checked to determine
|
||||||
* entry size (and prefix size for that matter) */
|
* entry size (and prefix size for that matter) */
|
||||||
entry_size = 0;
|
entry_size = 0;
|
||||||
dur_offset = size_offset = 0;
|
dur_offset = size_offset = 0;
|
||||||
if (flags & TR_SAMPLE_DURATION) {
|
if (flags & TR_SAMPLE_DURATION) {
|
||||||
|
GST_LOG_OBJECT (qtdemux, "entry duration present");
|
||||||
dur_offset = entry_size;
|
dur_offset = entry_size;
|
||||||
entry_size += 4;
|
entry_size += 4;
|
||||||
}
|
}
|
||||||
if (flags & TR_SAMPLE_SIZE) {
|
if (flags & TR_SAMPLE_SIZE) {
|
||||||
|
GST_LOG_OBJECT (qtdemux, "entry size present");
|
||||||
size_offset = entry_size;
|
size_offset = entry_size;
|
||||||
entry_size += 4;
|
entry_size += 4;
|
||||||
}
|
}
|
||||||
if (flags & TR_SAMPLE_FLAGS) {
|
if (flags & TR_SAMPLE_FLAGS) {
|
||||||
|
GST_LOG_OBJECT (qtdemux, "entry flags present");
|
||||||
|
flags_offset = entry_size;
|
||||||
entry_size += 4;
|
entry_size += 4;
|
||||||
}
|
}
|
||||||
if (flags & TR_COMPOSITION_TIME_OFFSETS) {
|
if (flags & TR_COMPOSITION_TIME_OFFSETS) {
|
||||||
|
GST_LOG_OBJECT (qtdemux, "entry ct offset present");
|
||||||
|
ct_offset = entry_size;
|
||||||
entry_size += 4;
|
entry_size += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1906,8 +2032,6 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
||||||
goto fail;
|
goto fail;
|
||||||
data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
|
data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
|
||||||
|
|
||||||
data_offset = mdat_offset + 8;
|
|
||||||
|
|
||||||
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;
|
goto index_too_big;
|
||||||
|
@ -1937,7 +2061,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
||||||
}
|
}
|
||||||
sample = stream->samples + stream->n_samples;
|
sample = stream->samples + stream->n_samples;
|
||||||
for (i = 0; i < *samples_count; i++) {
|
for (i = 0; i < *samples_count; i++) {
|
||||||
guint32 dur, size;
|
guint32 dur, size, sflags, ct;
|
||||||
|
|
||||||
/* first read sample data */
|
/* first read sample data */
|
||||||
if (flags & TR_SAMPLE_DURATION) {
|
if (flags & TR_SAMPLE_DURATION) {
|
||||||
|
@ -1950,16 +2074,33 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
|
||||||
} else {
|
} else {
|
||||||
size = d_sample_size;
|
size = d_sample_size;
|
||||||
}
|
}
|
||||||
|
if (flags & TR_FIRST_SAMPLE_FLAGS) {
|
||||||
|
if (i == 0) {
|
||||||
|
sflags = first_flags;
|
||||||
|
} else {
|
||||||
|
sflags = d_sample_flags;
|
||||||
|
}
|
||||||
|
} else if (flags & TR_SAMPLE_FLAGS) {
|
||||||
|
sflags = QT_UINT32 (data + flags_offset);
|
||||||
|
} else {
|
||||||
|
sflags = d_sample_flags;
|
||||||
|
}
|
||||||
|
if (flags & TR_COMPOSITION_TIME_OFFSETS) {
|
||||||
|
ct = QT_UINT32 (data + ct_offset);
|
||||||
|
} else {
|
||||||
|
ct = 0;
|
||||||
|
}
|
||||||
data += entry_size;
|
data += entry_size;
|
||||||
|
|
||||||
/* fill the sample information */
|
/* fill the sample information */
|
||||||
sample->offset = data_offset;
|
sample->offset = *base_offset;
|
||||||
sample->pts_offset = 0;
|
sample->pts_offset = ct;
|
||||||
sample->size = size;
|
sample->size = size;
|
||||||
sample->timestamp = timestamp;
|
sample->timestamp = timestamp;
|
||||||
sample->duration = dur;
|
sample->duration = dur;
|
||||||
sample->keyframe = (i == 0);
|
/* sample-is-difference-sample */
|
||||||
data_offset += size;
|
sample->keyframe = !(sflags & 0x10000);
|
||||||
|
*base_offset += size;
|
||||||
timestamp += dur;
|
timestamp += dur;
|
||||||
sample++;
|
sample++;
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +2132,8 @@ index_too_big:
|
||||||
static gboolean
|
static gboolean
|
||||||
qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
|
qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
|
||||||
guint32 * track_id, guint32 * default_sample_duration,
|
guint32 * track_id, guint32 * default_sample_duration,
|
||||||
guint32 * default_sample_size)
|
guint32 * default_sample_size, guint32 * default_sample_flags,
|
||||||
|
gint64 * base_offset)
|
||||||
{
|
{
|
||||||
guint32 flags;
|
guint32 flags;
|
||||||
|
|
||||||
|
@ -2002,9 +2144,8 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
|
||||||
if (!gst_byte_reader_get_uint32_be (tfhd, track_id))
|
if (!gst_byte_reader_get_uint32_be (tfhd, track_id))
|
||||||
goto invalid_track;
|
goto invalid_track;
|
||||||
|
|
||||||
/* FIXME: Handle TF_BASE_DATA_OFFSET properly */
|
|
||||||
if (flags & TF_BASE_DATA_OFFSET)
|
if (flags & TF_BASE_DATA_OFFSET)
|
||||||
if (!gst_byte_reader_skip (tfhd, 4))
|
if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
|
||||||
goto invalid_track;
|
goto invalid_track;
|
||||||
|
|
||||||
/* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
|
/* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
|
||||||
|
@ -2020,6 +2161,10 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
|
||||||
if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
|
if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
|
||||||
goto invalid_track;
|
goto invalid_track;
|
||||||
|
|
||||||
|
if (flags & TF_DEFAULT_SAMPLE_FLAGS)
|
||||||
|
if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
|
||||||
|
goto invalid_track;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
invalid_track:
|
invalid_track:
|
||||||
|
@ -2035,16 +2180,20 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
|
||||||
{
|
{
|
||||||
GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
|
GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
|
||||||
GstByteReader trun_data, tfhd_data;
|
GstByteReader trun_data, tfhd_data;
|
||||||
guint32 id = 0, default_sample_size = 0, default_sample_duration = 0;
|
guint32 id = 0;
|
||||||
|
guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
|
||||||
guint32 samples_count = 0;
|
guint32 samples_count = 0;
|
||||||
guint64 mdat_offset;
|
gint64 base_offset;
|
||||||
|
|
||||||
mdat_offset = moof_offset + length;
|
/* obtain stream defaults */
|
||||||
|
qtdemux_parse_trex (qtdemux, stream, &ds_duration, &ds_size, &ds_flags);
|
||||||
|
|
||||||
moof_node = g_node_new ((guint8 *) buffer);
|
moof_node = g_node_new ((guint8 *) buffer);
|
||||||
qtdemux_parse_node (qtdemux, moof_node, buffer, length);
|
qtdemux_parse_node (qtdemux, moof_node, buffer, length);
|
||||||
qtdemux_node_dump (qtdemux, moof_node);
|
qtdemux_node_dump (qtdemux, moof_node);
|
||||||
|
|
||||||
|
/* default base offset = first byte of moof */
|
||||||
|
base_offset = moof_offset;
|
||||||
traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
|
traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
|
||||||
while (traf_node) {
|
while (traf_node) {
|
||||||
/* Fragment Header node */
|
/* Fragment Header node */
|
||||||
|
@ -2053,19 +2202,26 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
|
||||||
&tfhd_data);
|
&tfhd_data);
|
||||||
if (!tfhd_node)
|
if (!tfhd_node)
|
||||||
goto missing_tfhd;
|
goto missing_tfhd;
|
||||||
if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &id, &default_sample_duration,
|
if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &id, &ds_duration,
|
||||||
&default_sample_size))
|
&ds_size, &ds_flags, &base_offset))
|
||||||
goto missing_tfhd;
|
goto missing_tfhd;
|
||||||
/* skip trun atoms that don't match the track ID */
|
/* skip trun atoms that don't match the track ID */
|
||||||
if (id != stream->track_id)
|
if (id != stream->track_id) {
|
||||||
|
/* lost track of offset here */
|
||||||
|
base_offset = -1;
|
||||||
goto next;
|
goto next;
|
||||||
|
} else if (base_offset == -1) {
|
||||||
|
GST_WARNING_OBJECT (qtdemux, "FIXME: no base_offset for data");
|
||||||
|
/* FIXME modify parsing so we don't have this limitation */
|
||||||
|
goto missing_tfhd;
|
||||||
|
}
|
||||||
/* Track Run node */
|
/* Track Run node */
|
||||||
trun_node =
|
trun_node =
|
||||||
qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
|
qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
|
||||||
&trun_data);
|
&trun_data);
|
||||||
while (trun_node) {
|
while (trun_node) {
|
||||||
qtdemux_parse_trun (qtdemux, &trun_data, stream, mdat_offset,
|
qtdemux_parse_trun (qtdemux, &trun_data, stream,
|
||||||
default_sample_duration, default_sample_size, &samples_count);
|
ds_duration, ds_size, ds_flags, &samples_count, &base_offset);
|
||||||
/* iterate all siblings */
|
/* iterate all siblings */
|
||||||
trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
|
trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
|
||||||
&trun_data);
|
&trun_data);
|
||||||
|
@ -7813,6 +7969,7 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
|
||||||
GNode *mvhd;
|
GNode *mvhd;
|
||||||
GNode *trak;
|
GNode *trak;
|
||||||
GNode *udta;
|
GNode *udta;
|
||||||
|
GNode *mvex;
|
||||||
gint64 duration;
|
gint64 duration;
|
||||||
guint64 creation_time;
|
guint64 creation_time;
|
||||||
GstDateTime *datetime = NULL;
|
GstDateTime *datetime = NULL;
|
||||||
|
@ -7861,9 +8018,12 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
|
||||||
GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
|
GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
|
||||||
GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
|
GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
|
||||||
|
|
||||||
/* set duration in the segment info */
|
/* check for fragmented file and get some (default) data */
|
||||||
gst_qtdemux_get_duration (qtdemux, &duration);
|
mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
|
||||||
gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
|
if (mvex) {
|
||||||
|
/* let track parsing or anyone know weird stuff might happen ... */
|
||||||
|
qtdemux->fragmented = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* parse all traks */
|
/* parse all traks */
|
||||||
trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
|
trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
|
||||||
|
@ -7872,6 +8032,22 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
|
||||||
/* iterate all siblings */
|
/* iterate all siblings */
|
||||||
trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
|
trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* compensate for total duration */
|
||||||
|
if (mvex) {
|
||||||
|
GNode *mehd;
|
||||||
|
GstByteReader mehd_data;
|
||||||
|
|
||||||
|
mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
|
||||||
|
if (mehd)
|
||||||
|
qtdemux_parse_mehd (qtdemux, &mehd_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set duration in the segment info */
|
||||||
|
gst_qtdemux_get_duration (qtdemux, &duration);
|
||||||
|
if (duration)
|
||||||
|
gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
|
||||||
|
|
||||||
gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
|
gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
|
||||||
|
|
||||||
/* find and push tags, we do this after adding the pads so we can push the
|
/* find and push tags, we do this after adding the pads so we can push the
|
||||||
|
|
Loading…
Reference in a new issue