X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fisomp4%2Fqtdemux.c;h=f801b8fa88ea469c0c8338b330543e55c9a04cfe;hb=5f438c5ff5c156efa93bae97d90bd07ba0b6a11f;hp=d4fb29204b0fc41e0c40ee13ffa3f16e21584d9b;hpb=5404304facbec97e8d6d31370b8ea6ba89572336;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index d4fb292..f801b8f 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -79,7 +79,7 @@ #include #ifdef HAVE_ZLIB -# include +#include #endif /* max. size considered 'sane' for non-mdat atoms */ @@ -94,6 +94,8 @@ #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \ QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY) +#define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4)) + #define STREAM_IS_EOS(s) (s->time_position == GST_CLOCK_TIME_NONE) #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) @@ -115,6 +117,34 @@ struct _QtDemuxSample gboolean keyframe; /* TRUE when this packet is a keyframe */ }; +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION +typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata; + +struct _QtDemuxSphericalMetadata +{ + gboolean is_spherical; + gboolean is_stitched; + char *stitching_software; + char *projection_type; + char *stereo_mode; + int source_count; + int init_view_heading; + int init_view_pitch; + int init_view_roll; + int timestamp; + int full_pano_width_pixels; + int full_pano_height_pixels; + int cropped_area_image_width; + int cropped_area_image_height; + int cropped_area_left; + int cropped_area_top; + QTDEMUX_AMBISONIC_TYPE ambisonic_type; + QTDEMUX_AMBISONIC_FORMAT ambisonic_format; + QTDEMUX_AMBISONIC_ORDER ambisonic_order; +}; + +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + /* Macros for converting to/from timescale */ #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale)) #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND)) @@ -214,15 +244,55 @@ typedef struct guint64 moof_offset; } QtDemuxRandomAccessEntry; +typedef struct _QtDemuxStreamStsdEntry +{ + GstCaps *caps; + guint32 fourcc; + gboolean sparse; + + /* video info */ + gint width; + gint height; + gint par_w; + gint par_h; + /* Numerator/denominator framerate */ + gint fps_n; + gint fps_d; + GstVideoColorimetry colorimetry; + guint16 bits_per_sample; + guint16 color_table_id; + GstMemory *rgb8_palette; + guint interlace_mode; + guint field_order; + + /* audio info */ + gdouble rate; + gint n_channels; + guint samples_per_packet; + guint samples_per_frame; + guint bytes_per_packet; + guint bytes_per_sample; + guint bytes_per_frame; + guint compression; + + /* if we use chunks or samples */ + gboolean sampled; + guint padding; + +} QtDemuxStreamStsdEntry; + +#define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index])) + struct _QtDemuxStream { GstPad *pad; + QtDemuxStreamStsdEntry *stsd_entries; + guint stsd_entries_length; + guint cur_stsd_entry_index; + /* stream type */ guint32 subtype; - GstCaps *caps; - guint32 fourcc; - gboolean sparse; gboolean new_caps; /* If TRUE, caps need to be generated (by * calling _configure_stream()) This happens @@ -265,37 +335,10 @@ struct _QtDemuxStream guint32 max_buffer_size; /* Maximum allowed size for output buffers. * Currently only set for raw audio streams*/ - /* if we use chunks or samples */ - gboolean sampled; - guint padding; - /* video info */ - gint width; - gint height; /* aspect ratio */ gint display_width; gint display_height; - gint par_w; - gint par_h; - /* Numerator/denominator framerate */ - gint fps_n; - gint fps_d; - GstVideoColorimetry colorimetry; - guint16 bits_per_sample; - guint16 color_table_id; - GstMemory *rgb8_palette; - guint interlace_mode; - guint field_order; - - /* audio info */ - gdouble rate; - gint n_channels; - guint samples_per_packet; - guint samples_per_frame; - guint bytes_per_packet; - guint bytes_per_sample; - guint bytes_per_frame; - guint compression; /* allocation */ gboolean use_allocator; @@ -334,7 +377,7 @@ struct _QtDemuxStream guint32 to_sample; gboolean sent_eos; - GstTagList *pending_tags; + GstTagList *stream_tags; gboolean send_global_tags; GstEvent *pending_event; @@ -356,6 +399,7 @@ struct _QtDemuxStream guint32 current_chunk; guint32 last_chunk; guint32 samples_per_chunk; + guint32 stsd_sample_description_id; guint32 stco_sample_index; /* stsz */ guint32 sample_size; /* 0 means variable sizes are stored in stsz */ @@ -398,6 +442,7 @@ struct _QtDemuxStream /* fragmented */ gboolean parsed_trex; + guint32 def_sample_description_index; /* index is 1-based */ guint32 def_sample_duration; guint32 def_sample_size; guint32 def_sample_flags; @@ -508,6 +553,8 @@ static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent, static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps); static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream); +static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux, + QtDemuxStream * stream); static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force); @@ -520,19 +567,20 @@ static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta); static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux, - QtDemuxStream * stream, GNode * esds, GstTagList * list); + QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds, + GstTagList * list); static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, - QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data, - gchar ** codec_name); + QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * stsd_entry_data, gchar ** codec_name); static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux, - QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len, - gchar ** codec_name); -static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, - QtDemuxStream * stream, guint32 fourcc, const guint8 * data, + QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * data, int len, gchar ** codec_name); +static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, + QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data, gchar ** codec_name); static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux, - QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data, - gchar ** codec_name); + QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * stsd_entry_data, gchar ** codec_name); static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n); @@ -564,6 +612,10 @@ static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux, const gchar * id); static void qtdemux_gst_structure_free (GstStructure * gststructure); +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION +static void gst_tag_register_spherical_tags (void); +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + static void gst_qtdemux_class_init (GstQTDemuxClass * klass) { @@ -585,6 +637,10 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass) gst_tag_register_musicbrainz_tags (); +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + gst_tag_register_spherical_tags (); +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + gst_element_class_add_static_pad_template (gstelement_class, &gst_qtdemux_sink_template); gst_element_class_add_static_pad_template (gstelement_class, @@ -642,8 +698,37 @@ gst_qtdemux_init (GstQTDemux * qtdemux) qtdemux->protection_system_ids = NULL; g_queue_init (&qtdemux->protection_event_queue); gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); + qtdemux->tag_list = gst_tag_list_new_empty (); + gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL); qtdemux->flowcombiner = gst_flow_combiner_new (); +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *) + malloc (sizeof (QtDemuxSphericalMetadata)); + + if (qtdemux->spherical_metadata) { + qtdemux->spherical_metadata->is_spherical = FALSE; + qtdemux->spherical_metadata->is_stitched = FALSE; + qtdemux->spherical_metadata->stitching_software = NULL; + qtdemux->spherical_metadata->projection_type = NULL; + qtdemux->spherical_metadata->stereo_mode = NULL; + qtdemux->spherical_metadata->source_count = 0; + qtdemux->spherical_metadata->init_view_heading = 0; + qtdemux->spherical_metadata->init_view_pitch = 0; + qtdemux->spherical_metadata->init_view_roll = 0; + qtdemux->spherical_metadata->timestamp = 0; + qtdemux->spherical_metadata->full_pano_width_pixels = 0; + qtdemux->spherical_metadata->full_pano_height_pixels = 0; + qtdemux->spherical_metadata->cropped_area_image_width = 0; + qtdemux->spherical_metadata->cropped_area_image_height = 0; + qtdemux->spherical_metadata->cropped_area_left = 0; + qtdemux->spherical_metadata->cropped_area_top = 0; + qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN; + qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN; + qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN; + } +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE); } @@ -652,10 +737,25 @@ gst_qtdemux_dispose (GObject * object) { GstQTDemux *qtdemux = GST_QTDEMUX (object); +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + if (qtdemux->spherical_metadata) { + if (qtdemux->spherical_metadata->stitching_software) + free(qtdemux->spherical_metadata->stitching_software); + if (qtdemux->spherical_metadata->projection_type) + free(qtdemux->spherical_metadata->projection_type); + if (qtdemux->spherical_metadata->stereo_mode) + free(qtdemux->spherical_metadata->stereo_mode); + + free(qtdemux->spherical_metadata); + qtdemux->spherical_metadata = NULL; + } +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + if (qtdemux->adapter) { g_object_unref (G_OBJECT (qtdemux->adapter)); qtdemux->adapter = NULL; } + gst_tag_list_unref (qtdemux->tag_list); gst_flow_combiner_free (qtdemux->flowcombiner); g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref, NULL); @@ -959,15 +1059,20 @@ gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream) GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags", GST_DEBUG_PAD_NAME (stream->pad)); - if (G_UNLIKELY (stream->pending_tags)) { + if (!gst_tag_list_is_empty (stream->stream_tags)) { GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT, - stream->pending_tags); + stream->stream_tags); gst_pad_push_event (stream->pad, - gst_event_new_tag (stream->pending_tags)); - stream->pending_tags = NULL; + gst_event_new_tag (gst_tag_list_ref (stream->stream_tags))); +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + /* post message qtdemux tag (for early recive application) */ + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), + gst_message_new_tag (GST_OBJECT_CAST (qtdemux), + gst_tag_list_copy (stream->stream_tags))); +#endif } - if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) { + if (G_UNLIKELY (stream->send_global_tags)) { GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT, qtdemux->tag_list); gst_pad_push_event (stream->pad, @@ -1294,7 +1399,7 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, * and move back to the previous keyframe. */ for (n = 0; n < qtdemux->n_streams; n++) { QtDemuxStream *str; - guint32 index; + guint32 index, kindex; guint32 seg_idx; GstClockTime media_start; GstClockTime media_time; @@ -1304,7 +1409,7 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, str = qtdemux->streams[n]; - if (str->sparse && !use_sparse) + if (CUR_STREAM (str)->sparse && !use_sparse) continue; seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time); @@ -1348,33 +1453,37 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, if (!empty_segment) { /* find previous keyframe */ - index = gst_qtdemux_find_keyframe (qtdemux, str, index, next); + kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next); /* we will settle for one before if none found after */ - if (next && index == -1) - index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE); - - /* Snap to the start ts of the keyframe we found */ + if (next && kindex == -1) + kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE); - /* get timestamp of keyframe */ - media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]); - GST_DEBUG_OBJECT (qtdemux, - "keyframe at %u with time %" GST_TIME_FORMAT " at offset %" - G_GUINT64_FORMAT, index, GST_TIME_ARGS (media_time), - str->samples[index].offset); - - /* keyframes in the segment get a chance to change the - * desired_offset. keyframes out of the segment are - * ignored. */ - if (media_time >= seg->media_start) { - GstClockTime seg_time; + /* if the keyframe is at a different position, we need to update the + * requested seek time */ + if (index != kindex) { + index = kindex; - /* this keyframe is inside the segment, convert back to - * segment time */ - seg_time = (media_time - seg->media_start) + seg->time; - if ((!next && (seg_time < min_offset)) || - (next && (seg_time > min_offset))) - min_offset = seg_time; + /* get timestamp of keyframe */ + media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]); + GST_DEBUG_OBJECT (qtdemux, + "keyframe at %u with time %" GST_TIME_FORMAT " at offset %" + G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time), + str->samples[kindex].offset); + + /* keyframes in the segment get a chance to change the + * desired_offset. keyframes out of the segment are + * ignored. */ + if (media_time >= seg->media_start) { + GstClockTime seg_time; + + /* this keyframe is inside the segment, convert back to + * segment time */ + seg_time = (media_time - seg->media_start) + seg->time; + if ((!next && (seg_time < min_offset)) || + (next && (seg_time > min_offset))) + min_offset = seg_time; + } } } @@ -1602,6 +1711,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) GstSegment seeksegment; guint32 seqnum = 0; GstEvent *flush_event; + gboolean ret; if (event) { GST_DEBUG_OBJECT (qtdemux, "doing seek with event"); @@ -1648,13 +1758,19 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) if (event) { /* configure the segment with the seek variables */ GST_DEBUG_OBJECT (qtdemux, "configuring seek"); - gst_segment_do_seek (&seeksegment, rate, format, flags, - cur_type, cur, stop_type, stop, &update); + if (!gst_segment_do_seek (&seeksegment, rate, format, flags, + cur_type, cur, stop_type, stop, &update)) { + ret = FALSE; + GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing"); + } else { + /* now do the seek */ + ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags); + } + } else { + /* now do the seek */ + ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags); } - /* now do the seek, this actually never returns FALSE */ - gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags); - /* prepare for streaming again */ if (flush) { flush_event = gst_event_new_flush_stop (TRUE); @@ -1682,7 +1798,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad); - return TRUE; + return ret; /* ERRORS */ no_format: @@ -1730,6 +1846,14 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent, #ifndef GST_DISABLE_GST_DEBUG GstClockTime ts = gst_util_get_timestamp (); #endif + guint32 seqnum = gst_event_get_seqnum (event); + + if (seqnum == qtdemux->segment_seqnum) { + GST_LOG_OBJECT (pad, + "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum); + gst_event_unref (event); + return TRUE; + } if (qtdemux->upstream_format_is_time && qtdemux->fragmented) { /* seek should be handled by upstream, we might need to re-download fragments */ @@ -1831,7 +1955,7 @@ gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw, } /* avoid index from sparse streams since they might be far away */ - if (!str->sparse) { + if (!CUR_STREAM (str)->sparse) { /* determine min/max time */ time = QTSAMPLE_PTS (str, &str->samples[i]); if (min_time == -1 || (!fw && time > min_time) || @@ -1889,6 +2013,8 @@ _create_stream (void) stream->duration_moof = 0; stream->duration_last_moof = 0; stream->alignment = 1; + stream->stream_tags = gst_tag_list_new_empty (); + gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM); g_queue_init (&stream->protection_scheme_event_queue); return stream; } @@ -1928,6 +2054,10 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps) stream = _create_stream (); demux->streams[demux->n_streams] = stream; demux->n_streams = 1; + /* mss has no stsd/stsd entry, use id 0 as default */ + stream->stsd_entries_length = 1; + stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0; + stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1); } else { stream = demux->streams[0]; } @@ -1942,26 +2072,29 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps) demux->timescale = stream->timescale; mediacaps = gst_value_get_caps (value); - if (!stream->caps || !gst_caps_is_equal_fixed (mediacaps, stream->caps)) { + if (!CUR_STREAM (stream)->caps + || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) { GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT, mediacaps); stream->new_caps = TRUE; } - gst_caps_replace (&stream->caps, (GstCaps *) mediacaps); + gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps); structure = gst_caps_get_structure (mediacaps, 0); if (g_str_has_prefix (gst_structure_get_name (structure), "video")) { stream->subtype = FOURCC_vide; - gst_structure_get_int (structure, "width", &stream->width); - gst_structure_get_int (structure, "height", &stream->height); - gst_structure_get_fraction (structure, "framerate", &stream->fps_n, - &stream->fps_d); + gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width); + gst_structure_get_int (structure, "height", + &CUR_STREAM (stream)->height); + gst_structure_get_fraction (structure, "framerate", + &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d); } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) { gint rate = 0; stream->subtype = FOURCC_soun; - gst_structure_get_int (structure, "channels", &stream->n_channels); + gst_structure_get_int (structure, "channels", + &CUR_STREAM (stream)->n_channels); gst_structure_get_int (structure, "rate", &rate); - stream->rate = rate; + CUR_STREAM (stream)->rate = rate; } } gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps); @@ -1997,6 +2130,7 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) qtdemux->mdatbuffer = NULL; qtdemux->restoredata_buffer = NULL; qtdemux->mdatleft = 0; + qtdemux->mdatsize = 0; if (qtdemux->comp_brands) gst_buffer_unref (qtdemux->comp_brands); qtdemux->comp_brands = NULL; @@ -2012,7 +2146,8 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) qtdemux->moov_node = NULL; if (qtdemux->tag_list) gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list)); - qtdemux->tag_list = NULL; + qtdemux->tag_list = gst_tag_list_new_empty (); + gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL); #if 0 if (qtdemux->element_index) gst_object_unref (qtdemux->element_index); @@ -2435,24 +2570,28 @@ gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux, static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream) { + gint i; if (stream->allocator) gst_object_unref (stream->allocator); while (stream->buffers) { gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data)); stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers); } - if (stream->rgb8_palette) { - gst_memory_unref (stream->rgb8_palette); - stream->rgb8_palette = NULL; + for (i = 0; i < stream->stsd_entries_length; i++) { + QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i]; + if (entry->rgb8_palette) { + gst_memory_unref (entry->rgb8_palette); + entry->rgb8_palette = NULL; + } + entry->sparse = FALSE; } - if (stream->pending_tags) - gst_tag_list_unref (stream->pending_tags); - stream->pending_tags = NULL; + gst_tag_list_unref (stream->stream_tags); + stream->stream_tags = gst_tag_list_new_empty (); + gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM); g_free (stream->redirect_uri); stream->redirect_uri = NULL; stream->sent_eos = FALSE; - stream->sparse = FALSE; stream->protected = FALSE; if (stream->protection_scheme_info) { if (stream->protection_scheme_type == FOURCC_cenc) { @@ -2478,14 +2617,21 @@ gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream) static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream) { + gint i; gst_qtdemux_stream_clear (qtdemux, stream); - if (stream->caps) - gst_caps_unref (stream->caps); - stream->caps = NULL; + for (i = 0; i < stream->stsd_entries_length; i++) { + QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i]; + if (entry->caps) { + gst_caps_unref (entry->caps); + entry->caps = NULL; + } + } + gst_tag_list_unref (stream->stream_tags); if (stream->pad) { gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad); } + g_free (stream->stsd_entries); g_free (stream); } @@ -2568,6 +2714,482 @@ qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist, } } +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION +static void +_get_int_value_from_xml_string (GstQTDemux * qtdemux, + const char *xml_str, const char *param_name, int *value) +{ + char *value_start, *value_end, *endptr; + const short value_length_max = 12; + char init_view_ret[12]; + int value_length = 0; + int i = 0; + + value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL; + + if (!value_start) { + GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n", + param_name); + return; + } + + value_start += strlen (param_name); + while ((value_start[0] == ' ') || (value_start[0] == '\t')) + value_start++; + + value_end = strchr (value_start, '<'); + if (!value_end) { + GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n"); + return; + } + + value_length = value_end - value_start; + while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') + || (value_start[value_length - 1] == '\t'))) + value_length--; + + if (value_start[i] == '+' || value_start[i] == '-') + i++; + while (i < value_length) { + if (value_start[i] < '0' || value_start[i] > '9') { + GST_ERROR_OBJECT (qtdemux, + "error: incorrect value, integer was expected\n"); + return; + } + i++; + } + + if (value_length >= value_length_max || value_length < 1) { + GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n"); + return; + } + + strncpy (init_view_ret, value_start, value_length_max); + init_view_ret[value_length] = '\0'; + + *value = strtol (init_view_ret, &endptr, 10); + if (endptr == init_view_ret) { + GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n"); + return; + } + + return; +} + +static void +_get_string_value_from_xml_string (GstQTDemux * qtdemux, + const char *xml_str, const char *param_name, char **value) +{ + char *value_start, *value_end; + const short value_length_max = 256; + int value_length = 0; + + value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL; + + if (!value_start) { + GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n", + param_name); + return; + } + + value_start += strlen (param_name); + while ((value_start[0] == ' ') || (value_start[0] == '\t')) + value_start++; + + value_end = strchr (value_start, '<'); + if (!value_end) { + GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n"); + return; + } + + value_length = value_end - value_start; + while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') + || (value_start[value_length - 1] == '\t'))) + value_length--; + + if (value_length >= value_length_max || value_length < 1) { + GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n"); + return; + } + + *value = (char *) malloc (value_length); + + if (*value == NULL) { + GST_ERROR_OBJECT (qtdemux, "error: malloc failed\n"); + return; + } + + strncpy (*value, value_start, value_length); + + return; +} + +static void +_get_bool_value_from_xml_string (GstQTDemux * qtdemux, + const char *xml_str, const char *param_name, gboolean * value) +{ + char *value_start, *value_end; + int value_length = 0; + + value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL; + + if (!value_start) { + GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n", + param_name); + return; + } + + value_start += strlen (param_name); + while ((value_start[0] == ' ') || (value_start[0] == '\t')) + value_start++; + + value_end = strchr (value_start, '<'); + if (!value_end) { + GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n"); + return; + } + + value_length = value_end - value_start; + while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') + || (value_start[value_length - 1] == '\t'))) + value_length--; + + if (value_length < 1) { + GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n"); + return; + } + + *value = strstr (value_start, "true") ? TRUE : FALSE; + + return; +} + +static void +_parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr) +{ + const char is_spherical_str[] = ""; + const char is_stitched_str[] = ""; + const char stitching_software_str[] = ""; + const char projection_type_str[] = ""; + const char stereo_mode_str[] = ""; + const char source_count_str[] = ""; + const char init_view_heading_str[] = ""; + const char init_view_pitch_str[] = ""; + const char init_view_roll_str[] = ""; + const char timestamp_str[] = ""; + const char full_pano_width_str[] = ""; + const char full_pano_height_str[] = ""; + const char cropped_area_image_width_str[] = + ""; + const char cropped_area_image_height_str[] = + ""; + const char cropped_area_left_str[] = ""; + const char cropped_area_top_str[] = ""; + + QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata; + + _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str, + (gboolean *) & spherical_metadata->is_spherical); + _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str, + (gboolean *) & spherical_metadata->is_stitched); + + if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) { + _get_string_value_from_xml_string (qtdemux, xmlStr, + stitching_software_str, &spherical_metadata->stitching_software); + _get_string_value_from_xml_string (qtdemux, xmlStr, + projection_type_str, &spherical_metadata->projection_type); + _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str, + &spherical_metadata->stereo_mode); + _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str, + &spherical_metadata->source_count); + _get_int_value_from_xml_string (qtdemux, xmlStr, + init_view_heading_str, &spherical_metadata->init_view_heading); + _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str, + &spherical_metadata->init_view_pitch); + _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str, + &spherical_metadata->init_view_roll); + _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str, + &spherical_metadata->timestamp); + _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str, + &spherical_metadata->full_pano_width_pixels); + _get_int_value_from_xml_string (qtdemux, xmlStr, + full_pano_height_str, &spherical_metadata->full_pano_height_pixels); + _get_int_value_from_xml_string (qtdemux, xmlStr, + cropped_area_image_width_str, + &spherical_metadata->cropped_area_image_width); + _get_int_value_from_xml_string (qtdemux, xmlStr, + cropped_area_image_height_str, + &spherical_metadata->cropped_area_image_height); + _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str, + &spherical_metadata->cropped_area_left); + _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str, + &spherical_metadata->cropped_area_top); + } + + return; +} + +static void +gst_tag_register_spherical_tags (void) { + gst_tag_register ("is_spherical", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-spherical"), + _("Flag indicating if the video is a spherical video"), + NULL); + gst_tag_register ("is_stitched", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-stitched"), + _("Flag indicating if the video is stitched"), + NULL); + gst_tag_register ("stitching_software", GST_TAG_FLAG_META, + G_TYPE_STRING, + _("tag-stitching-software"), + _("Software used to stitch the spherical video"), + NULL); + gst_tag_register ("projection_type", GST_TAG_FLAG_META, + G_TYPE_STRING, + _("tag-projection-type"), + _("Projection type used in the video frames"), + NULL); + gst_tag_register ("stereo_mode", GST_TAG_FLAG_META, + G_TYPE_STRING, + _("tag-stereo-mode"), + _("Description of stereoscopic 3D layout"), + NULL); + gst_tag_register ("source_count", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-source-count"), + _("Number of cameras used to create the spherical video"), + NULL); + gst_tag_register ("init_view_heading", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-init-view-heading"), + _("The heading angle of the initial view in degrees"), + NULL); + gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-init-view-pitch"), + _("The pitch angle of the initial view in degrees"), + NULL); + gst_tag_register ("init_view_roll", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-init-view-roll"), + _("The roll angle of the initial view in degrees"), + NULL); + gst_tag_register ("timestamp", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-timestamp"), + _("Epoch timestamp of when the first frame in the video was recorded"), + NULL); + gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-full-pano-width"), + _("Width of the encoded video frame in pixels"), + NULL); + gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-full-pano-height"), + _("Height of the encoded video frame in pixels"), + NULL); + gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-cropped-area-image-width"), + _("Width of the video frame to display (e.g. cropping)"), + NULL); + gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-cropped-area-image-height"), + _("Height of the video frame to display (e.g. cropping)"), + NULL); + gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-cropped-area-left"), + _("Column where the left edge of the image was cropped from the" + " full sized panorama"), + NULL); + gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-cropped-area-top"), + _("Row where the top edge of the image was cropped from the" + " full sized panorama"), + NULL); + gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-ambisonic-type"), + _("Specifies the type of ambisonic audio represented"), + NULL); + gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-ambisonic-format"), + _("Specifies the ambisonic audio format"), + NULL); + gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META, + G_TYPE_INT, + _("tag-ambisonic-order"), + _("Specifies the ambisonic audio channel order"), + NULL); + + return; +} + +static void +_send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux) +{ + GstTagList *taglist; + QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata; + + GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d", + spherical_metadata->is_spherical); + GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d", + spherical_metadata->is_stitched); + GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s", + spherical_metadata->stitching_software); + GST_DEBUG_OBJECT (qtdemux, "projection_type = %s", + spherical_metadata->projection_type); + GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s", + spherical_metadata->stereo_mode); + GST_DEBUG_OBJECT (qtdemux, "source_count %d", + spherical_metadata->source_count); + GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d", + spherical_metadata->init_view_heading); + GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d", + spherical_metadata->init_view_pitch); + GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d", + spherical_metadata->init_view_roll); + GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp); + GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d", + spherical_metadata->full_pano_width_pixels); + GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d", + spherical_metadata->full_pano_height_pixels); + GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d", + spherical_metadata->cropped_area_image_width); + GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d", + spherical_metadata->cropped_area_image_height); + GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d", + spherical_metadata->cropped_area_left); + GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d", + spherical_metadata->cropped_area_top); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d", + spherical_metadata->ambisonic_type); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d", + spherical_metadata->ambisonic_order); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d", + spherical_metadata->ambisonic_format); + + taglist = gst_tag_list_new_empty (); + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + "is_spherical", spherical_metadata->is_spherical, + "is_stitched", spherical_metadata->is_stitched, + "source_count", spherical_metadata->source_count, + "init_view_heading", spherical_metadata->init_view_heading, + "init_view_pitch", spherical_metadata->init_view_pitch, + "init_view_roll", spherical_metadata->init_view_roll, + "timestamp", spherical_metadata->timestamp, + "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels, + "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels, + "cropped_area_image_width", spherical_metadata->cropped_area_image_width, + "cropped_area_image_height", spherical_metadata->cropped_area_image_height, + "cropped_area_left", spherical_metadata->cropped_area_left, + "cropped_area_top", spherical_metadata->cropped_area_top, + "ambisonic_type", spherical_metadata->ambisonic_type, + "ambisonic_format", spherical_metadata->ambisonic_format, + "ambisonic_order", spherical_metadata->ambisonic_order, + NULL); + + if (spherical_metadata->stitching_software) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + "stitching_software", spherical_metadata->stitching_software, + NULL); + if (spherical_metadata->projection_type) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + "projection_type", spherical_metadata->projection_type, + NULL); + if (spherical_metadata->stereo_mode) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + "stereo_mode", spherical_metadata->stereo_mode, + NULL); + + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), + gst_message_new_tag (GST_OBJECT_CAST (qtdemux), + gst_tag_list_copy (taglist))); + + gst_tag_list_unref(taglist); + + return; +} + +static void +qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length) +{ + guint offset = 0; + + guint8 version = 0; + guint8 ambisonic_type = 0; + guint32 ambisonic_order = 0; + guint8 ambisonic_channel_ordering = 0; + guint8 ambisonic_normalization = 0; + guint32 num_channels = 0; + guint32 channel_map[49] = { 0 }; /* Up to 6th order */ + + int i; + + GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D"); + + qtdemux->header_size += length; + offset = (QT_UINT32 (buffer) == 0) ? 16 : 8; + + if (length <= offset + 16) { + GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping"); + return; + } + + version = QT_UINT8 (buffer + offset); + ambisonic_type = QT_UINT8 (buffer + offset + 1); + ambisonic_order = QT_UINT32 (buffer + offset + 2); + ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6); + ambisonic_normalization = QT_UINT8 (buffer + offset + 7); + num_channels = QT_UINT32 (buffer + offset + 8); + for (i = 0; i < num_channels; ++i) + channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4); + + GST_DEBUG_OBJECT (qtdemux, "version: %d", version); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d", + ambisonic_channel_ordering); + GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d", + ambisonic_normalization); + GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels); + for (i = 0; i < num_channels; ++i) + GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]); + + if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) { + if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC) + qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC; + + if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) { + if (num_channels == 4) { + qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA; + + if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) + && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) + && (channel_map[0] == 0) && (channel_map[1] == 1) + && (channel_map[2] == 2) && (channel_map[3] == 3)) + qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX; + + if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) + && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) + && (channel_map[0] == 0) && (channel_map[1] == 3) + && (channel_map[2] == 1) && (channel_map[3] == 2)) + qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB; + } + } + } + + return; +} +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + static void qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length, guint offset) @@ -2588,7 +3210,7 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length, stream = qtdemux->streams[0]; - structure = gst_caps_get_structure (stream->caps, 0); + structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0); if (!gst_structure_has_name (structure, "application/x-cenc")) { GST_WARNING_OBJECT (qtdemux, "Attempting PIFF box parsing on an unencrypted stream."); @@ -2754,6 +3376,13 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length) 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4 }; +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + static const guint8 spherical_uuid[] = { + 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93, + 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd + }; +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + guint offset; /* counts as header data */ @@ -2766,6 +3395,21 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length) return; } +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + if (memcmp (buffer + offset, spherical_uuid, 16) == 0) { + const char *contents; + + GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found"); + contents = (char *) (buffer + offset + 16); + GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents); + + if (qtdemux->spherical_metadata) + _parse_spatial_video_metadata_from_xml_string (qtdemux, contents); + + return; + } +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ + if (memcmp (buffer + offset, xmp_uuid, 16) == 0) { GstBuffer *buf; GstTagList *taglist; @@ -2775,7 +3419,10 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length) taglist = gst_tag_list_from_xmp_buffer (buf); gst_buffer_unref (buf); - qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist); + /* make sure we have a usable taglist */ + qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); + + qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist); } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) { int len; @@ -2896,7 +3543,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream, trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex, &trex_data); while (trex) { - guint32 id = 0, dur = 0, size = 0, flags = 0, dummy = 0; + guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0; /* skip version/flags */ if (!gst_byte_reader_skip (&trex_data, 4)) @@ -2905,8 +3552,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream, goto next; if (id != stream->track_id) goto next; - /* sample description index; ignore */ - if (!gst_byte_reader_get_uint32_be (&trex_data, &dummy)) + if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi)) goto next; if (!gst_byte_reader_get_uint32_be (&trex_data, &dur)) goto next; @@ -2920,6 +3566,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream, dur, size, flags); stream->parsed_trex = TRUE; + stream->def_sample_description_index = sdi; stream->def_sample_duration = dur; stream->def_sample_size = size; stream->def_sample_flags = flags; @@ -3017,7 +3664,8 @@ static gboolean qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size, guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length, - gint64 * base_offset, gint64 * running_offset, gint64 decode_ts) + gint64 * base_offset, gint64 * running_offset, gint64 decode_ts, + gboolean has_tfdt) { GstClockTime gst_ts = GST_CLOCK_TIME_NONE; guint64 timestamp; @@ -3178,7 +3826,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, /* If this is a GST_FORMAT_BYTES stream and there's a significant * difference (1 sec.) between decode_ts and timestamp, prefer the * former */ - if (decode_ts != 0 && !qtdemux->upstream_format_is_time + if (has_tfdt && !qtdemux->upstream_format_is_time && ABSDIFF (decode_ts, timestamp) > MAX (stream->duration_last_moof / 2, GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) { @@ -3259,6 +3907,8 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, * size, else we will still be able to use this when dealing with gap'ed * input */ qtdemux->mdatleft = *running_offset - initial_offset; + qtdemux->mdatoffset = initial_offset; + qtdemux->mdatsize = qtdemux->mdatleft; stream->n_samples += samples_count; stream->n_samples_moof += samples_count; @@ -3369,10 +4019,20 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd, qtdemux_parse_trex (qtdemux, *stream, default_sample_duration, default_sample_size, default_sample_flags); - /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */ - if (flags & TF_SAMPLE_DESCRIPTION_INDEX) - if (!gst_byte_reader_skip (tfhd, 4)) + (*stream)->stsd_sample_description_id = + (*stream)->def_sample_description_index - 1; + + if (flags & TF_SAMPLE_DESCRIPTION_INDEX) { + guint32 sample_description_index; + if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index)) goto invalid_track; + (*stream)->stsd_sample_description_id = sample_description_index - 1; + } + + if (qtdemux->mss_mode) { + /* mss has no stsd entry */ + (*stream)->stsd_sample_description_id = 0; + } if (flags & TF_DEFAULT_SAMPLE_DURATION) if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration)) @@ -3536,7 +4196,7 @@ qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream, } else if (stream->protected) { aux_info_type = stream->protection_scheme_type; } else { - aux_info_type = stream->fourcc; + aux_info_type = CUR_STREAM (stream)->fourcc; } if (info_type) @@ -3588,6 +4248,8 @@ qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream, QtDemuxCencSampleSetInfo *ss_info = NULL; guint8 size; gint i; + GPtrArray *old_crypto_info = NULL; + guint old_entries = 0; g_return_val_if_fail (qtdemux != NULL, FALSE); g_return_val_if_fail (stream != NULL, FALSE); @@ -3598,14 +4260,38 @@ qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream, ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info; if (ss_info->crypto_info) { - GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info"); - g_ptr_array_free (ss_info->crypto_info, TRUE); + old_crypto_info = ss_info->crypto_info; + /* Count number of non-null entries remaining at the tail end */ + for (i = old_crypto_info->len - 1; i >= 0; i--) { + if (g_ptr_array_index (old_crypto_info, i) == NULL) + break; + old_entries++; + } } ss_info->crypto_info = - g_ptr_array_new_full (sample_count, + g_ptr_array_new_full (sample_count + old_entries, (GDestroyNotify) qtdemux_gst_structure_free); + /* We preserve old entries because we parse the next moof in advance + * of consuming all samples from the previous moof, and otherwise + * we'd discard the corresponding crypto info for the samples + * from the previous fragment. */ + if (old_entries) { + GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries", + old_entries); + for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) { + g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info, + i)); + g_ptr_array_index (old_crypto_info, i) = NULL; + } + } + + if (old_crypto_info) { + /* Everything now belongs to the new array */ + g_ptr_array_free (old_crypto_info, TRUE); + } + for (i = 0; i < sample_count; ++i) { GstStructure *properties; guint16 n_subsamples = 0; @@ -3837,7 +4523,8 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT " (%" GST_TIME_FORMAT ")", decode_time, - GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_time))); + GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream, + decode_time) : GST_CLOCK_TIME_NONE)); /* Discard the fragment buffer timestamp info to avoid using it. * Rely on tfdt instead as it is more accurate than the timestamp @@ -3870,7 +4557,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length, while (trun_node) { qtdemux_parse_trun (qtdemux, &trun_data, stream, ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset, - &running_offset, decode_time); + &running_offset, decode_time, (tfdt_node != NULL)); /* iterate all siblings */ trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun, &trun_data); @@ -4341,6 +5028,10 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) beach: if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) { /* digested all data, show what we have */ +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + if (qtdemux->spherical_metadata) + _send_spherical_metadata_msg_to_bus (qtdemux); +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ qtdemux_prepare_streams (qtdemux); ret = qtdemux_expose_streams (qtdemux); @@ -5077,14 +5768,14 @@ gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, /* depending on the type, setup the clip parameters */ if (stream->subtype == FOURCC_soun) { - frame_size = stream->bytes_per_frame; + frame_size = CUR_STREAM (stream)->bytes_per_frame; num_rate = GST_SECOND; - denom_rate = (gint) stream->rate; + denom_rate = (gint) CUR_STREAM (stream)->rate; clip_data = TRUE; } else if (stream->subtype == FOURCC_vide) { frame_size = size; - num_rate = stream->fps_n; - denom_rate = stream->fps_d; + num_rate = CUR_STREAM (stream)->fps_n; + denom_rate = CUR_STREAM (stream)->fps_d; clip_data = FALSE; } else goto wrong_type; @@ -5227,7 +5918,7 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, gchar *str; /* not many cases for now */ - if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) { + if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) { /* send a one time dvd clut event */ if (stream->pending_event && stream->pad) gst_pad_push_event (stream->pad, stream->pending_event); @@ -5292,7 +5983,7 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, /* offset the timestamps according to the edit list */ - if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) { + if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) { gchar *url; GstMapInfo map; @@ -5360,11 +6051,12 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, GST_BUFFER_OFFSET (buf) = -1; GST_BUFFER_OFFSET_END (buf) = -1; - if (G_UNLIKELY (stream->rgb8_palette)) - gst_buffer_append_memory (buf, gst_memory_ref (stream->rgb8_palette)); + if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette)) + gst_buffer_append_memory (buf, + gst_memory_ref (CUR_STREAM (stream)->rgb8_palette)); - if (G_UNLIKELY (stream->padding)) { - gst_buffer_resize (buf, stream->padding, -1); + if (G_UNLIKELY (CUR_STREAM (stream)->padding)) { + gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1); } #if 0 if (G_UNLIKELY (qtdemux->element_index)) { @@ -5431,14 +6123,20 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, goto exit; } - index = stream->sample_index - (stream->n_samples - info->crypto_info->len); + /* The end of the crypto_info array matches our n_samples position, + * so count backward from there */ + index = stream->sample_index - stream->n_samples + info->crypto_info->len; if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) { /* steal structure from array */ crypto_info = g_ptr_array_index (info->crypto_info, index); g_ptr_array_index (info->crypto_info, index) = NULL; - GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u]", index); + GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index, + info->crypto_info->len); if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer"); + } else { + GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d", + index, stream->sample_index); } } @@ -5504,6 +6202,18 @@ gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux) stream->stbl_index = -1; /* no samples have yet been parsed */ stream->sample_index = -1; + if (stream->protection_scheme_info) { + /* Clear out any old cenc crypto info entries as we'll move to a new moof */ + if (stream->protection_scheme_type == FOURCC_cenc) { + QtDemuxCencSampleSetInfo *info = + (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info; + if (info->crypto_info) { + g_ptr_array_free (info->crypto_info, TRUE); + info->crypto_info = NULL; + } + } + } + if (stream->ra_entries == NULL) continue; @@ -5627,16 +6337,17 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) } stream = qtdemux->streams[index]; - if (stream->new_caps) { - gst_qtdemux_configure_stream (qtdemux, stream); - qtdemux_do_allocation (qtdemux, stream); - } - /* fetch info for the current sample of this stream */ if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty, &offset, &sample_size, &dts, &pts, &duration, &keyframe))) goto eos_stream; + gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream); + if (stream->new_caps) { + gst_qtdemux_configure_stream (qtdemux, stream); + qtdemux_do_allocation (qtdemux, stream); + } + /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */ if (G_UNLIKELY (qtdemux-> segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) { @@ -5718,11 +6429,15 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) if (size != sample_size) { pts += gst_util_uint64_scale_int (GST_SECOND, - stream->offset_in_sample / stream->bytes_per_frame, stream->timescale); - dts += gst_util_uint64_scale_int (GST_SECOND, - stream->offset_in_sample / stream->bytes_per_frame, stream->timescale); - duration = gst_util_uint64_scale_int (GST_SECOND, - size / stream->bytes_per_frame, stream->timescale); + stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame, + stream->timescale); + dts += + gst_util_uint64_scale_int (GST_SECOND, + stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame, + stream->timescale); + duration = + gst_util_uint64_scale_int (GST_SECOND, + size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale); } ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf, @@ -5733,7 +6448,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) QtDemuxSegment *segment = &stream->segments[stream->segment_index]; GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream, - sample->timestamp + stream->offset_in_sample / stream->bytes_per_frame); + sample->timestamp + + stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame); if (time_position >= segment->media_start) { /* inside the segment, update time_position, looks very familiar to * GStreamer segments, doesn't it? */ @@ -6043,6 +6759,9 @@ gst_qtdemux_check_seekability (GstQTDemux * demux) if (demux->upstream_size) return; + if (demux->upstream_format_is_time) + return; + query = gst_query_new_seeking (GST_FORMAT_BYTES); if (!gst_pad_peer_query (demux->sinkpad, query)) { GST_DEBUG_OBJECT (demux, "seeking query failed"); @@ -6097,7 +6816,7 @@ gst_qtdemux_check_send_pending_segment (GstQTDemux * demux) QtDemuxStream *stream; stream = demux->streams[i]; gst_qtdemux_push_tags (demux, stream); - if (stream->sparse) { + if (CUR_STREAM (stream)->sparse) { GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i); gst_pad_push_event (stream->pad, gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE)); @@ -6204,16 +6923,20 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf) demux->streams[i]->sample_index = res; /* Finally update all push-based values to the expected values */ demux->neededbytes = demux->streams[i]->samples[res].size; - demux->todrop = 0; demux->offset = GST_BUFFER_OFFSET (inbuf); + demux->mdatleft = + demux->mdatsize - demux->offset + demux->mdatoffset; + demux->todrop = 0; } } } if (!is_gap_input) { + GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT"); /* Reset state if it's a real discont */ demux->neededbytes = 16; demux->state = QTDEMUX_STATE_INITIAL; demux->offset = GST_BUFFER_OFFSET (inbuf); + gst_adapter_clear (demux->adapter); } } /* Reverse fragmented playback, need to flush all we have before @@ -6299,6 +7022,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) demux->state = QTDEMUX_STATE_MOVIE; demux->neededbytes = next_entry; demux->mdatleft = size; + demux->mdatsize = demux->mdatleft; } else { /* no headers yet, try to get them */ guint bs; @@ -6601,6 +7325,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) demux->neededbytes = next_entry_size (demux); demux->state = QTDEMUX_STATE_MOVIE; demux->mdatleft = gst_adapter_available (demux->adapter); + demux->mdatsize = demux->mdatleft; } else { GST_DEBUG_OBJECT (demux, "Carrying on normally"); gst_adapter_flush (demux->adapter, demux->neededbytes); @@ -6742,6 +7467,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) if (G_UNLIKELY (stream == NULL || i == demux->n_streams)) goto unknown_stream; + gst_qtdemux_stream_check_and_change_stsd_index (demux, stream); + if (stream->new_caps) { gst_qtdemux_configure_stream (demux, stream); } @@ -6751,7 +7478,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) if (G_LIKELY (!(STREAM_IS_EOS (stream)))) { GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (stream->fourcc)); + GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc)); dts = QTSAMPLE_DTS (stream, sample); pts = QTSAMPLE_PTS (stream, sample); @@ -6766,6 +7493,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) /* skip this data, stream is EOS */ gst_adapter_flush (demux->adapter, demux->neededbytes); + demux->offset += demux->neededbytes; /* check if all streams are eos */ ret = GST_FLOW_EOS; @@ -6775,11 +7503,6 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) break; } } - - if (ret == GST_FLOW_EOS) { - GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream"); - goto eos; - } } else { GstBuffer *outbuf; @@ -6795,8 +7518,6 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) /* combine flows */ ret = gst_qtdemux_combine_flows (demux, stream, ret); - if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) - goto non_ok_unlinked_flow; } else { /* skip this data, stream is EOS */ gst_adapter_flush (demux->adapter, demux->neededbytes); @@ -6812,6 +7533,13 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT, demux->offset); + + if (ret == GST_FLOW_EOS) { + GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream"); + demux->neededbytes = -1; + goto eos; + } + if ((demux->neededbytes = next_entry_size (demux)) == -1) { if (demux->fragmented) { GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples"); @@ -6822,6 +7550,9 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force) } goto eos; } + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) { + goto non_ok_unlinked_flow; + } break; } default: @@ -7388,6 +8119,13 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, qtdemux_parse_container (qtdemux, node, buffer + 36, end); break; } +#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION + case FOURCC_SA3D: + { + qtdemux_parse_SA3D (qtdemux, buffer, end - buffer); + break; + } +#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */ default: if (!strcmp (type->name, "unknown")) GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4); @@ -7464,6 +8202,12 @@ qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc, } static GNode * +qtdemux_tree_get_child_by_index (GNode * node, guint index) +{ + return g_node_nth_child (node, index); +} + +static GNode * qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc, GstByteReader * parser) { @@ -7543,7 +8287,8 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux, g_return_val_if_fail (qtdemux != NULL, FALSE); g_return_val_if_fail (stream != NULL, FALSE); - g_return_val_if_fail (gst_caps_get_size (stream->caps) == 1, FALSE); + g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1, + FALSE); if (stream->protection_scheme_type != FOURCC_cenc) { GST_ERROR_OBJECT (qtdemux, "unsupported protection scheme"); @@ -7565,7 +8310,7 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux, return FALSE; } - s = gst_caps_get_structure (stream->caps, 0); + s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0); if (!gst_structure_has_name (s, "application/x-cenc")) { gst_structure_set (s, "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), @@ -7586,12 +8331,12 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) if ((stream->n_samples == 1) && (stream->first_duration == 0)) { /* still frame */ - stream->fps_n = 0; - stream->fps_d = 1; + CUR_STREAM (stream)->fps_n = 0; + CUR_STREAM (stream)->fps_d = 1; } else { if (stream->duration == 0 || stream->n_samples < 2) { - stream->fps_n = stream->timescale; - stream->fps_d = 1; + CUR_STREAM (stream)->fps_n = stream->timescale; + CUR_STREAM (stream)->fps_d = 1; fps_available = FALSE; } else { GstClockTime avg_duration; @@ -7623,95 +8368,105 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) GST_TIME_FORMAT, duration, stream->first_duration, n_samples - 1, GST_TIME_ARGS (avg_duration)); - gst_video_guess_framerate (avg_duration, &stream->fps_n, - &stream->fps_d); + gst_video_guess_framerate (avg_duration, &CUR_STREAM (stream)->fps_n, + &CUR_STREAM (stream)->fps_d); GST_DEBUG_OBJECT (qtdemux, "Calculating framerate, timescale %u gave fps_n %d fps_d %d", - stream->timescale, stream->fps_n, stream->fps_d); + stream->timescale, CUR_STREAM (stream)->fps_n, + CUR_STREAM (stream)->fps_d); } } - if (stream->caps) { - stream->caps = gst_caps_make_writable (stream->caps); + if (CUR_STREAM (stream)->caps) { + CUR_STREAM (stream)->caps = + gst_caps_make_writable (CUR_STREAM (stream)->caps); - gst_caps_set_simple (stream->caps, - "width", G_TYPE_INT, stream->width, - "height", G_TYPE_INT, stream->height, NULL); + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "width", G_TYPE_INT, CUR_STREAM (stream)->width, + "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL); /* set framerate if calculated framerate is reliable */ if (fps_available) { - gst_caps_set_simple (stream->caps, - "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL); + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n, + CUR_STREAM (stream)->fps_d, NULL); } /* calculate pixel-aspect-ratio using display width and height */ GST_DEBUG_OBJECT (qtdemux, - "video size %dx%d, target display size %dx%d", stream->width, - stream->height, stream->display_width, stream->display_height); + "video size %dx%d, target display size %dx%d", + CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, + stream->display_width, stream->display_height); /* qt file might have pasp atom */ - if (stream->par_w > 0 && stream->par_h > 0) { - GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h); - gst_caps_set_simple (stream->caps, "pixel-aspect-ratio", - GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL); - } else if (stream->display_width > 0 && stream->display_height > 0 && - stream->width > 0 && stream->height > 0) { + if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) { + GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w, + CUR_STREAM (stream)->par_h); + gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio", + GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w, + CUR_STREAM (stream)->par_h, NULL); + } else if (stream->display_width > 0 && stream->display_height > 0 + && CUR_STREAM (stream)->width > 0 + && CUR_STREAM (stream)->height > 0) { gint n, d; /* calculate the pixel aspect ratio using the display and pixel w/h */ - n = stream->display_width * stream->height; - d = stream->display_height * stream->width; + n = stream->display_width * CUR_STREAM (stream)->height; + d = stream->display_height * CUR_STREAM (stream)->width; if (n == d) n = d = 1; GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d); - stream->par_w = n; - stream->par_h = d; - gst_caps_set_simple (stream->caps, "pixel-aspect-ratio", - GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL); - } - - if (stream->interlace_mode > 0) { - if (stream->interlace_mode == 1) { - gst_caps_set_simple (stream->caps, "interlace-mode", G_TYPE_STRING, - "progressive", NULL); - } else if (stream->interlace_mode == 2) { - gst_caps_set_simple (stream->caps, "interlace-mode", G_TYPE_STRING, - "interleaved", NULL); - if (stream->field_order == 9) { - gst_caps_set_simple (stream->caps, "field-order", G_TYPE_STRING, - "top-field-first", NULL); - } else if (stream->field_order == 14) { - gst_caps_set_simple (stream->caps, "field-order", G_TYPE_STRING, - "bottom-field-first", NULL); + CUR_STREAM (stream)->par_w = n; + CUR_STREAM (stream)->par_h = d; + gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio", + GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w, + CUR_STREAM (stream)->par_h, NULL); + } + + if (CUR_STREAM (stream)->interlace_mode > 0) { + if (CUR_STREAM (stream)->interlace_mode == 1) { + gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode", + G_TYPE_STRING, "progressive", NULL); + } else if (CUR_STREAM (stream)->interlace_mode == 2) { + gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode", + G_TYPE_STRING, "interleaved", NULL); + if (CUR_STREAM (stream)->field_order == 9) { + gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order", + G_TYPE_STRING, "top-field-first", NULL); + } else if (CUR_STREAM (stream)->field_order == 14) { + gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order", + G_TYPE_STRING, "bottom-field-first", NULL); } } } /* Create incomplete colorimetry here if needed */ - if (stream->colorimetry.range || - stream->colorimetry.matrix || - stream->colorimetry.transfer || stream->colorimetry.primaries) { + if (CUR_STREAM (stream)->colorimetry.range || + CUR_STREAM (stream)->colorimetry.matrix || + CUR_STREAM (stream)->colorimetry.transfer + || CUR_STREAM (stream)->colorimetry.primaries) { gchar *colorimetry = - gst_video_colorimetry_to_string (&stream->colorimetry); - gst_caps_set_simple (stream->caps, "colorimetry", G_TYPE_STRING, - colorimetry, NULL); + gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry); + gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry", + G_TYPE_STRING, colorimetry, NULL); g_free (colorimetry); } if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) { guint par_w = 1, par_h = 1; - if (stream->par_w > 0 && stream->par_h > 0) { - par_w = stream->par_w; - par_h = stream->par_h; + if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) { + par_w = CUR_STREAM (stream)->par_w; + par_h = CUR_STREAM (stream)->par_h; } if (gst_video_multiview_guess_half_aspect (stream->multiview_mode, - stream->width, stream->height, par_w, par_h)) { + CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w, + par_h)) { stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT; } - gst_caps_set_simple (stream->caps, + gst_caps_set_simple (CUR_STREAM (stream)->caps, "multiview-mode", G_TYPE_STRING, gst_video_multiview_mode_to_caps_string (stream->multiview_mode), "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, @@ -7721,19 +8476,20 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) } else if (stream->subtype == FOURCC_soun) { - if (stream->caps) { - stream->caps = gst_caps_make_writable (stream->caps); - if (stream->rate > 0) - gst_caps_set_simple (stream->caps, - "rate", G_TYPE_INT, (int) stream->rate, NULL); - if (stream->n_channels > 0) - gst_caps_set_simple (stream->caps, - "channels", G_TYPE_INT, stream->n_channels, NULL); - if (stream->n_channels > 2) { + if (CUR_STREAM (stream)->caps) { + CUR_STREAM (stream)->caps = + gst_caps_make_writable (CUR_STREAM (stream)->caps); + if (CUR_STREAM (stream)->rate > 0) + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL); + if (CUR_STREAM (stream)->n_channels > 0) + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL); + if (CUR_STREAM (stream)->n_channels > 2) { /* FIXME: Need to parse the 'chan' atom to get channel layouts * correctly; this is just the minimum we can do - assume * we don't actually have any channel positions. */ - gst_caps_set_simple (stream->caps, + gst_caps_set_simple (CUR_STREAM (stream)->caps, "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL); } } @@ -7757,15 +8513,18 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) } } + GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, + CUR_STREAM (stream)->caps); if (stream->new_stream) { gchar *stream_id; GstEvent *event; - GstStreamFlags stream_flags; + GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE; event = gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START, 0); if (event) { + gst_event_parse_stream_flags (event, &stream_flags); if (gst_event_parse_group_id (event, &qtdemux->group_id)) qtdemux->have_group_id = TRUE; else @@ -7783,11 +8542,13 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) event = gst_event_new_stream_start (stream_id); if (qtdemux->have_group_id) gst_event_set_group_id (event, qtdemux->group_id); - stream_flags = GST_STREAM_FLAG_NONE; if (stream->disabled) stream_flags |= GST_STREAM_FLAG_UNSELECT; - if (stream->sparse) + if (CUR_STREAM (stream)->sparse) { stream_flags |= GST_STREAM_FLAG_SPARSE; + } else { + stream_flags &= ~GST_STREAM_FLAG_SPARSE; + } gst_event_set_stream_flags (event, stream_flags); gst_pad_push_event (stream->pad, event); g_free (stream_id); @@ -7795,11 +8556,17 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) prev_caps = gst_pad_get_current_caps (stream->pad); - if (!prev_caps || !gst_caps_is_equal_fixed (prev_caps, stream->caps)) { - GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps); - gst_pad_set_caps (stream->pad, stream->caps); + if (CUR_STREAM (stream)->caps) { + if (!prev_caps + || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) { + GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, + CUR_STREAM (stream)->caps); + gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps); + } else { + GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps"); + } } else { - GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps"); + GST_WARNING_OBJECT (qtdemux, "stream without caps"); } if (prev_caps) @@ -7809,6 +8576,27 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) return TRUE; } +static void +gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux, + QtDemuxStream * stream) +{ + if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id) + return; + + GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'", + stream->cur_stsd_entry_index, stream->stsd_sample_description_id); + if (G_UNLIKELY (stream->stsd_sample_description_id >= + stream->stsd_entries_length)) { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, + (_("This file is invalid and cannot be played.")), + ("New sample description id is out of bounds (%d >= %d)", + stream->stsd_sample_description_id, stream->stsd_entries_length)); + } else { + stream->cur_stsd_entry_index = stream->stsd_sample_description_id; + stream->new_caps = TRUE; + } +} + static gboolean gst_qtdemux_add_stream (GstQTDemux * qtdemux, QtDemuxStream * stream, GstTagList * list) @@ -7861,7 +8649,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, goto done; } qtdemux->n_sub_streams++; - } else if (stream->caps) { + } else if (CUR_STREAM (stream)->caps) { gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams); stream->pad = @@ -7887,9 +8675,9 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad); - if (stream->pending_tags) - gst_tag_list_unref (stream->pending_tags); - stream->pending_tags = list; + if (stream->stream_tags) + gst_tag_list_unref (stream->stream_tags); + stream->stream_tags = list; list = NULL; /* global tags go on each pad anyway */ stream->send_global_tags = TRUE; @@ -8167,7 +8955,8 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) goto corrupt_file; /* chunks_are_samples == TRUE means treat chunks as samples */ - stream->chunks_are_samples = stream->sample_size && !stream->sampled; + stream->chunks_are_samples = stream->sample_size + && !CUR_STREAM (stream)->sampled; if (stream->chunks_are_samples) { /* treat chunks as samples */ if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples)) @@ -8303,7 +9092,8 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n) guint32 n_samples; GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %" - GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc), + GST_FOURCC_FORMAT ", pad %s", + GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc), stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)"); n_samples = stream->n_samples; @@ -8363,7 +9153,9 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n) gst_byte_reader_get_uint32_be_unchecked (&stream->stsc); stream->samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc); - gst_byte_reader_skip_unchecked (&stream->stsc, 4); + /* starts from 1 */ + stream->stsd_sample_description_id = + gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1; /* chunk numbers are counted from 1 it seems */ if (G_UNLIKELY (stream->first_chunk == 0)) @@ -8386,8 +9178,9 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n) } GST_LOG_OBJECT (qtdemux, - "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i, - stream->first_chunk, stream->last_chunk, stream->samples_per_chunk); + "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d" + "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk, + stream->samples_per_chunk, stream->stsd_sample_description_id); if (G_UNLIKELY (stream->last_chunk < stream->first_chunk)) goto corrupt_file; @@ -8428,10 +9221,12 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n) GST_LOG_OBJECT (qtdemux, "Created entry %d with offset " "%" G_GUINT64_FORMAT, j, cur->offset); - if (stream->samples_per_frame * stream->bytes_per_frame) { + if (CUR_STREAM (stream)->samples_per_frame > 0 && + CUR_STREAM (stream)->bytes_per_frame > 0) { cur->size = - (stream->samples_per_chunk * stream->n_channels) / - stream->samples_per_frame * stream->bytes_per_frame; + (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) / + CUR_STREAM (stream)->samples_per_frame * + CUR_STREAM (stream)->bytes_per_frame; } else { cur->size = stream->samples_per_chunk; } @@ -8536,6 +9331,8 @@ done2: /* save values */ stream->stts_time = stts_time; stream->stts_sample_index = j + 1; + if (stream->stts_sample_index >= stream->stts_samples) + stream->stts_index++; goto done3; } } @@ -8755,7 +9552,7 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream, n_segments = QT_UINT32 (buffer + 12); - if (size < 16 + n_segments * entry_size) { + if (n_segments > 100000 || size < 16 + n_segments * entry_size) { GST_WARNING_OBJECT (qtdemux, "Invalid edit list"); goto done; } @@ -8908,12 +9705,12 @@ done: * the SMI and gama atoms. */ static void -qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd, - guint8 ** gamma, GstBuffer ** seqh) +qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, + const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh) { - guint8 *_gamma = NULL; + const guint8 *_gamma = NULL; GstBuffer *_seqh = NULL; - guint8 *stsd_data = stsd->data; + const guint8 *stsd_data = stsd_entry_data; guint32 length = QT_UINT32 (stsd_data); guint16 version; @@ -8922,8 +9719,8 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd, goto end; } - stsd_data += 32; - length -= 32; + stsd_data += 16; + length -= 16; version = QT_UINT16 (stsd_data); if (version == 3) { if (length >= 70) { @@ -8931,7 +9728,7 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd, stsd_data += 70; while (length > 8) { guint32 fourcc, size; - guint8 *data; + const guint8 *data; size = QT_UINT32 (stsd_data); fourcc = QT_FOURCC (stsd_data + 4); data = stsd_data + 8; @@ -9368,10 +10165,15 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) gboolean new_stream = FALSE; gchar *codec = NULL; const guint8 *stsd_data; + const guint8 *stsd_entry_data; + guint remaining_stsd_len; + guint stsd_entry_count; + guint stsd_index; guint16 lang_code; /* quicktime lang code or packed iso code */ guint32 version; guint32 tkhd_flags = 0; guint8 tkhd_version = 0; + guint32 w = 0, h = 0; guint32 fourcc; guint value_size, stsd_len, len; guint32 track_id; @@ -9403,6 +10205,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) goto skip_track; } + stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags); + /* flush samples data from this track from previous moov */ gst_qtdemux_stream_flush_segments_data (qtdemux, stream); gst_qtdemux_stream_flush_samples_data (qtdemux, stream); @@ -9410,9 +10214,6 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) /* need defaults for fragments */ qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy); - if (stream->pending_tags == NULL) - stream->pending_tags = gst_tag_list_new_empty (); - if ((tkhd_flags & 1) == 0) stream->disabled = TRUE; @@ -9571,6 +10372,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } } + /* parse rest of tkhd */ + if (stream->subtype == FOURCC_vide) { + guint32 matrix[9]; + + /* version 1 uses some 64-bit ints */ + if (!gst_byte_reader_skip (&tkhd, 20 + value_size)) + goto corrupt_file; + + if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd")) + goto corrupt_file; + + if (!gst_byte_reader_get_uint32_be (&tkhd, &w) + || !gst_byte_reader_get_uint32_be (&tkhd, &h)) + goto corrupt_file; + + qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix, + &stream->stream_tags); + } + /* parse stsd */ if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd))) goto corrupt_file; @@ -9589,1609 +10409,1694 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } } + stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12); + stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count); GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len); + GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count); - /* and that entry should fit within stsd */ - len = QT_UINT32 (stsd_data + 16); - if (len > stsd_len + 16) - goto corrupt_file; - - stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4); - GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (stream->fourcc)); - GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len); - - if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi)) - goto error_encrypted; - - if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) { - GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc); - stream->protected = TRUE; - if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc)) - GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info"); - } - - if (stream->subtype == FOURCC_vide) { - guint32 w = 0, h = 0; - gboolean gray; - gint depth, palette_size, palette_count; - guint32 matrix[9]; - guint32 *palette_data = NULL; - - stream->sampled = TRUE; + stsd_entry_data = stsd_data + 16; + remaining_stsd_len = stsd_len - 16; + for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) { + QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index]; - /* version 1 uses some 64-bit ints */ - if (!gst_byte_reader_skip (&tkhd, 20 + value_size)) - goto corrupt_file; - - if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd")) - goto corrupt_file; - - if (!gst_byte_reader_get_uint32_be (&tkhd, &w) - || !gst_byte_reader_get_uint32_be (&tkhd, &h)) + /* and that entry should fit within stsd */ + len = QT_UINT32 (stsd_entry_data); + if (len > remaining_stsd_len) goto corrupt_file; - stream->display_width = w >> 16; - stream->display_height = h >> 16; - - qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix, - &stream->pending_tags); - - offset = 16; - if (len < 86) - goto corrupt_file; + entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4); + GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (entry->fourcc)); + GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len); - stream->width = QT_UINT16 (stsd_data + offset + 32); - stream->height = QT_UINT16 (stsd_data + offset + 34); - stream->fps_n = 0; /* this is filled in later */ - stream->fps_d = 0; /* this is filled in later */ - stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82); - stream->color_table_id = QT_UINT16 (stsd_data + offset + 84); + if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi)) + goto error_encrypted; - /* if color_table_id is 0, ctab atom must follow; however some files - * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so - * if color table is not present we'll correct the value */ - if (stream->color_table_id == 0 && - (len < 90 || QT_FOURCC (stsd_data + offset + 86) != FOURCC_ctab)) { - stream->color_table_id = -1; + if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) { + /* FIXME this looks wrong, there might be multiple children + * with the same type */ + GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc); + stream->protected = TRUE; + if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc)) + GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info"); } - GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d", - stream->width, stream->height, stream->bits_per_sample, - stream->color_table_id); + if (stream->subtype == FOURCC_vide) { + gboolean gray; + gint depth, palette_size, palette_count; + guint32 *palette_data = NULL; - depth = stream->bits_per_sample; + entry->sampled = TRUE; - /* more than 32 bits means grayscale */ - gray = (depth > 32); - /* low 32 bits specify the depth */ - depth &= 0x1F; + stream->display_width = w >> 16; + stream->display_height = h >> 16; - /* different number of palette entries is determined by depth. */ - palette_count = 0; - if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8)) - palette_count = (1 << depth); - palette_size = palette_count * 4; + offset = 16; + if (len < 86) /* TODO verify */ + goto corrupt_file; - if (stream->color_table_id) { - switch (palette_count) { - case 0: - break; - case 2: - palette_data = g_memdup (ff_qt_default_palette_2, palette_size); - break; - case 4: - palette_data = g_memdup (ff_qt_default_palette_4, palette_size); - break; - case 16: - if (gray) - palette_data = g_memdup (ff_qt_grayscale_palette_16, palette_size); - else - palette_data = g_memdup (ff_qt_default_palette_16, palette_size); - break; - case 256: - if (gray) - palette_data = g_memdup (ff_qt_grayscale_palette_256, palette_size); - else - palette_data = g_memdup (ff_qt_default_palette_256, palette_size); - break; - default: - GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX, - (_("The video in this file might not play correctly.")), - ("unsupported palette depth %d", depth)); - break; - } - } else { - gint i, j, start, end; + entry->width = QT_UINT16 (stsd_entry_data + offset + 16); + entry->height = QT_UINT16 (stsd_entry_data + offset + 18); + entry->fps_n = 0; /* this is filled in later */ + entry->fps_d = 0; /* this is filled in later */ + entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66); + entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68); + + /* if color_table_id is 0, ctab atom must follow; however some files + * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so + * if color table is not present we'll correct the value */ + if (entry->color_table_id == 0 && + (len < 90 + || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) { + entry->color_table_id = -1; + } + + GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d", + entry->width, entry->height, entry->bits_per_sample, + entry->color_table_id); + + depth = entry->bits_per_sample; + + /* more than 32 bits means grayscale */ + gray = (depth > 32); + /* low 32 bits specify the depth */ + depth &= 0x1F; + + /* different number of palette entries is determined by depth. */ + palette_count = 0; + if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8)) + palette_count = (1 << depth); + palette_size = palette_count * 4; + + if (entry->color_table_id) { + switch (palette_count) { + case 0: + break; + case 2: + palette_data = g_memdup (ff_qt_default_palette_2, palette_size); + break; + case 4: + palette_data = g_memdup (ff_qt_default_palette_4, palette_size); + break; + case 16: + if (gray) + palette_data = + g_memdup (ff_qt_grayscale_palette_16, palette_size); + else + palette_data = g_memdup (ff_qt_default_palette_16, palette_size); + break; + case 256: + if (gray) + palette_data = + g_memdup (ff_qt_grayscale_palette_256, palette_size); + else + palette_data = g_memdup (ff_qt_default_palette_256, palette_size); + break; + default: + GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX, + (_("The video in this file might not play correctly.")), + ("unsupported palette depth %d", depth)); + break; + } + } else { + gint i, j, start, end; - if (len < 94) - goto corrupt_file; + if (len < 94) + goto corrupt_file; - /* read table */ - start = QT_UINT32 (stsd_data + offset + 86); - palette_count = QT_UINT16 (stsd_data + offset + 90); - end = QT_UINT16 (stsd_data + offset + 92); + /* read table */ + start = QT_UINT32 (stsd_entry_data + offset + 70); + palette_count = QT_UINT16 (stsd_entry_data + offset + 74); + end = QT_UINT16 (stsd_entry_data + offset + 76); - GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d", - start, end, palette_count); + GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d", + start, end, palette_count); - if (end > 255) - end = 255; - if (start > end) - start = end; + if (end > 255) + end = 255; + if (start > end) + start = end; - if (len < 94 + (end - start) * 8) - goto corrupt_file; + if (len < 94 + (end - start) * 8) + goto corrupt_file; - /* palette is always the same size */ - palette_data = g_malloc0 (256 * 4); - palette_size = 256 * 4; + /* palette is always the same size */ + palette_data = g_malloc0 (256 * 4); + palette_size = 256 * 4; - for (j = 0, i = start; i <= end; j++, i++) { - guint32 a, r, g, b; + for (j = 0, i = start; i <= end; j++, i++) { + guint32 a, r, g, b; - a = QT_UINT16 (stsd_data + offset + 94 + (j * 8)); - r = QT_UINT16 (stsd_data + offset + 96 + (j * 8)); - g = QT_UINT16 (stsd_data + offset + 98 + (j * 8)); - b = QT_UINT16 (stsd_data + offset + 100 + (j * 8)); + a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8)); + r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8)); + g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8)); + b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8)); - palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) | - (g & 0xff00) | (b >> 8); + palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) | + (g & 0xff00) | (b >> 8); + } } - } - - if (stream->caps) - gst_caps_unref (stream->caps); - stream->caps = - qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec); - if (G_UNLIKELY (!stream->caps)) { - g_free (palette_data); - goto unknown_stream; - } + if (entry->caps) + gst_caps_unref (entry->caps); - if (codec) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_VIDEO_CODEC, codec, NULL); - g_free (codec); - codec = NULL; - } + entry->caps = + qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data, + &codec); + if (G_UNLIKELY (!entry->caps)) { + g_free (palette_data); + goto unknown_stream; + } + if (codec) { + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_VIDEO_CODEC, codec, NULL); + g_free (codec); + codec = NULL; + } - if (palette_data) { - GstStructure *s; + if (palette_data) { + GstStructure *s; - if (stream->rgb8_palette) - gst_memory_unref (stream->rgb8_palette); - stream->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, - palette_data, palette_size, 0, palette_size, palette_data, g_free); + if (entry->rgb8_palette) + gst_memory_unref (entry->rgb8_palette); + entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, + palette_data, palette_size, 0, palette_size, palette_data, g_free); - s = gst_caps_get_structure (stream->caps, 0); + s = gst_caps_get_structure (entry->caps, 0); - /* non-raw video has a palette_data property. raw video has the palette as - * an extra plane that we append to the output buffers before we push - * them*/ - if (!gst_structure_has_name (s, "video/x-raw")) { - GstBuffer *palette; + /* non-raw video has a palette_data property. raw video has the palette as + * an extra plane that we append to the output buffers before we push + * them*/ + if (!gst_structure_has_name (s, "video/x-raw")) { + GstBuffer *palette; - palette = gst_buffer_new (); - gst_buffer_append_memory (palette, stream->rgb8_palette); - stream->rgb8_palette = NULL; + palette = gst_buffer_new (); + gst_buffer_append_memory (palette, entry->rgb8_palette); + entry->rgb8_palette = NULL; - gst_caps_set_simple (stream->caps, "palette_data", - GST_TYPE_BUFFER, palette, NULL); - gst_buffer_unref (palette); + gst_caps_set_simple (entry->caps, "palette_data", + GST_TYPE_BUFFER, palette, NULL); + gst_buffer_unref (palette); + } + } else if (palette_count != 0) { + GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED, + (NULL), ("Unsupported palette depth %d", depth)); + } + + GST_LOG_OBJECT (qtdemux, "frame count: %u", + QT_UINT16 (stsd_entry_data + offset + 32)); + + esds = NULL; + pasp = NULL; + colr = NULL; + fiel = NULL; + /* pick 'the' stsd child */ + mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index); + if (!stream->protected) { + if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) { + mp4v = NULL; + } + } else { + if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) { + mp4v = NULL; + } } - } else if (palette_count != 0) { - GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED, - (NULL), ("Unsupported palette depth %d", depth)); - } - - GST_LOG_OBJECT (qtdemux, "frame count: %u", - QT_UINT16 (stsd_data + offset + 48)); - - esds = NULL; - pasp = NULL; - colr = NULL; - fiel = NULL; - /* pick 'the' stsd child */ - if (!stream->protected) - mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc); - else - mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_encv); - if (mp4v) { - esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds); - pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp); - colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr); - fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel); - } + if (mp4v) { + esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds); + pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp); + colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr); + fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel); + } - if (pasp) { - const guint8 *pasp_data = (const guint8 *) pasp->data; - gint len = QT_UINT32 (pasp_data); + if (pasp) { + const guint8 *pasp_data = (const guint8 *) pasp->data; + gint len = QT_UINT32 (pasp_data); - if (len == 16) { - stream->par_w = QT_UINT32 (pasp_data + 8); - stream->par_h = QT_UINT32 (pasp_data + 12); + if (len == 16) { + CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8); + CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12); + } else { + CUR_STREAM (stream)->par_w = 0; + CUR_STREAM (stream)->par_h = 0; + } } else { - stream->par_w = 0; - stream->par_h = 0; + CUR_STREAM (stream)->par_w = 0; + CUR_STREAM (stream)->par_h = 0; } - } else { - stream->par_w = 0; - stream->par_h = 0; - } - if (fiel) { - const guint8 *fiel_data = (const guint8 *) fiel->data; - gint len = QT_UINT32 (fiel_data); + if (fiel) { + const guint8 *fiel_data = (const guint8 *) fiel->data; + gint len = QT_UINT32 (fiel_data); - if (len == 10) { - stream->interlace_mode = GST_READ_UINT8 (fiel_data + 8); - stream->field_order = GST_READ_UINT8 (fiel_data + 9); + if (len == 10) { + CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8); + CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9); + } } - } - if (colr) { - const guint8 *colr_data = (const guint8 *) colr->data; - gint len = QT_UINT32 (colr_data); + if (colr) { + const guint8 *colr_data = (const guint8 *) colr->data; + gint len = QT_UINT32 (colr_data); - if (len == 19 || len == 18) { - guint32 color_type = GST_READ_UINT32_LE (colr_data + 8); + if (len == 19 || len == 18) { + guint32 color_type = GST_READ_UINT32_LE (colr_data + 8); - if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) { - guint16 primaries = GST_READ_UINT16_BE (colr_data + 12); - guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14); - guint16 matrix = GST_READ_UINT16_BE (colr_data + 16); - gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE; + if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) { + guint16 primaries = GST_READ_UINT16_BE (colr_data + 12); + guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14); + guint16 matrix = GST_READ_UINT16_BE (colr_data + 16); + gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE; - switch (primaries) { - case 1: - stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709; - break; - case 5: - stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG; - break; - case 6: - stream->colorimetry.primaries = - GST_VIDEO_COLOR_PRIMARIES_SMPTE170M; - break; - case 9: - stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020; - break; - default: - break; - } + switch (primaries) { + case 1: + CUR_STREAM (stream)->colorimetry.primaries = + GST_VIDEO_COLOR_PRIMARIES_BT709; + break; + case 5: + CUR_STREAM (stream)->colorimetry.primaries = + GST_VIDEO_COLOR_PRIMARIES_BT470BG; + break; + case 6: + CUR_STREAM (stream)->colorimetry.primaries = + GST_VIDEO_COLOR_PRIMARIES_SMPTE170M; + break; + case 9: + CUR_STREAM (stream)->colorimetry.primaries = + GST_VIDEO_COLOR_PRIMARIES_BT2020; + break; + default: + break; + } - switch (transfer_function) { - case 1: - stream->colorimetry.transfer = GST_VIDEO_TRANSFER_BT709; - break; - case 7: - stream->colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE240M; - break; - default: - break; - } + switch (transfer_function) { + case 1: + CUR_STREAM (stream)->colorimetry.transfer = + GST_VIDEO_TRANSFER_BT709; + break; + case 7: + CUR_STREAM (stream)->colorimetry.transfer = + GST_VIDEO_TRANSFER_SMPTE240M; + break; + default: + break; + } - switch (matrix) { - case 1: - stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709; - break; - case 6: - stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601; - break; - case 7: - stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_SMPTE240M; - break; - case 9: - stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020; - break; - default: - break; - } + switch (matrix) { + case 1: + CUR_STREAM (stream)->colorimetry.matrix = + GST_VIDEO_COLOR_MATRIX_BT709; + break; + case 6: + CUR_STREAM (stream)->colorimetry.matrix = + GST_VIDEO_COLOR_MATRIX_BT601; + break; + case 7: + CUR_STREAM (stream)->colorimetry.matrix = + GST_VIDEO_COLOR_MATRIX_SMPTE240M; + break; + case 9: + CUR_STREAM (stream)->colorimetry.matrix = + GST_VIDEO_COLOR_MATRIX_BT2020; + break; + default: + break; + } - stream->colorimetry.range = - full_range ? GST_VIDEO_COLOR_RANGE_0_255 : - GST_VIDEO_COLOR_RANGE_16_235; + CUR_STREAM (stream)->colorimetry.range = + full_range ? GST_VIDEO_COLOR_RANGE_0_255 : + GST_VIDEO_COLOR_RANGE_16_235; + } else { + GST_DEBUG_OBJECT (qtdemux, "Unsupported color type"); + } } else { - GST_DEBUG_OBJECT (qtdemux, "Unsupported color type"); + GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size"); } - } else { - GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size"); } - } - - if (esds) { - gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags); - } else { - switch (fourcc) { - case FOURCC_H264: - case FOURCC_avc1: - case FOURCC_avc3: - { - gint len = QT_UINT32 (stsd_data) - 0x66; - const guint8 *avc_data = stsd_data + 0x66; - - /* find avcC */ - while (len >= 0x8) { - gint size; - - if (QT_UINT32 (avc_data) <= len) - size = QT_UINT32 (avc_data) - 0x8; - else - size = len - 0x8; - - if (size < 1) - /* No real data, so break out */ - break; - - switch (QT_FOURCC (avc_data + 0x4)) { - case FOURCC_avcC: - { - /* parse, if found */ - GstBuffer *buf; - - GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd"); - /* First 4 bytes are the length of the atom, the next 4 bytes - * are the fourcc, the next 1 byte is the version, and the - * subsequent bytes are profile_tier_level structure like data. */ - gst_codec_utils_h264_caps_set_level_and_profile (stream->caps, - avc_data + 8 + 1, size - 1); - buf = gst_buffer_new_and_alloc (size); - gst_buffer_fill (buf, 0, avc_data + 0x8, size); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); + if (esds) { + gst_qtdemux_handle_esds (qtdemux, stream, entry, esds, + stream->stream_tags); + } else { + switch (fourcc) { + case FOURCC_H264: + case FOURCC_avc1: + case FOURCC_avc3: + { + gint len = QT_UINT32 (stsd_entry_data) - 0x56; + const guint8 *avc_data = stsd_entry_data + 0x56; + + /* find avcC */ + while (len >= 0x8) { + gint size; + + if (QT_UINT32 (avc_data) <= len) + size = QT_UINT32 (avc_data) - 0x8; + else + size = len - 0x8; + if (size < 1) + /* No real data, so break out */ break; - } - case FOURCC_strf: - { - GstBuffer *buf; - GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd"); + switch (QT_FOURCC (avc_data + 0x4)) { + case FOURCC_avcC: + { + /* parse, if found */ + GstBuffer *buf; - /* First 4 bytes are the length of the atom, the next 4 bytes - * are the fourcc, next 40 bytes are BITMAPINFOHEADER, - * next 1 byte is the version, and the - * subsequent bytes are sequence parameter set like data. */ - - size -= 40; /* we'll be skipping BITMAPINFOHEADER */ - if (size > 1) { - gst_codec_utils_h264_caps_set_level_and_profile (stream->caps, - avc_data + 8 + 40 + 1, size - 1); + GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd"); + /* First 4 bytes are the length of the atom, the next 4 bytes + * are the fourcc, the next 1 byte is the version, and the + * subsequent bytes are profile_tier_level structure like data. */ + gst_codec_utils_h264_caps_set_level_and_profile (entry->caps, + avc_data + 8 + 1, size - 1); buf = gst_buffer_new_and_alloc (size); - gst_buffer_fill (buf, 0, avc_data + 8 + 40, size); - gst_caps_set_simple (stream->caps, + gst_buffer_fill (buf, 0, avc_data + 0x8, size); + gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER, buf, NULL); gst_buffer_unref (buf); - } - break; - } - case FOURCC_btrt: - { - guint avg_bitrate, max_bitrate; - /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */ - if (size < 12) break; + } + case FOURCC_strf: + { + GstBuffer *buf; + + GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd"); + + /* First 4 bytes are the length of the atom, the next 4 bytes + * are the fourcc, next 40 bytes are BITMAPINFOHEADER, + * next 1 byte is the version, and the + * subsequent bytes are sequence parameter set like data. */ + + size -= 40; /* we'll be skipping BITMAPINFOHEADER */ + if (size > 1) { + gst_codec_utils_h264_caps_set_level_and_profile + (entry->caps, avc_data + 8 + 40 + 1, size - 1); + + buf = gst_buffer_new_and_alloc (size); + gst_buffer_fill (buf, 0, avc_data + 8 + 40, size); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } + break; + } + case FOURCC_btrt: + { + guint avg_bitrate, max_bitrate; + + /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */ + if (size < 12) + break; - max_bitrate = QT_UINT32 (avc_data + 0xc); - avg_bitrate = QT_UINT32 (avc_data + 0x10); + max_bitrate = QT_UINT32 (avc_data + 0xc); + avg_bitrate = QT_UINT32 (avc_data + 0x10); - if (!max_bitrate && !avg_bitrate) - break; + if (!max_bitrate && !avg_bitrate) + break; - /* Some muxers seem to swap the average and maximum bitrates - * (I'm looking at you, YouTube), so we swap for sanity. */ - if (max_bitrate > 0 && max_bitrate < avg_bitrate) { - guint temp = avg_bitrate; + /* Some muxers seem to swap the average and maximum bitrates + * (I'm looking at you, YouTube), so we swap for sanity. */ + if (max_bitrate > 0 && max_bitrate < avg_bitrate) { + guint temp = avg_bitrate; - avg_bitrate = max_bitrate; - max_bitrate = temp; - } + avg_bitrate = max_bitrate; + max_bitrate = temp; + } - if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL); - } - if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, avg_bitrate, NULL); + if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) { + gst_tag_list_add (stream->stream_tags, + GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE, + max_bitrate, NULL); + } + if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) { + gst_tag_list_add (stream->stream_tags, + GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate, + NULL); + } + + break; } - break; + default: + break; } - default: - break; + len -= size + 8; + avc_data += size + 8; } - len -= size + 8; - avc_data += size + 8; + break; } + case FOURCC_H265: + case FOURCC_hvc1: + case FOURCC_hev1: + { + gint len = QT_UINT32 (stsd_entry_data) - 0x56; + const guint8 *hevc_data = stsd_entry_data + 0x56; + + /* find hevc */ + while (len >= 0x8) { + gint size; + + if (QT_UINT32 (hevc_data) <= len) + size = QT_UINT32 (hevc_data) - 0x8; + else + size = len - 0x8; - break; - } - case FOURCC_H265: - case FOURCC_hvc1: - case FOURCC_hev1: - { - gint len = QT_UINT32 (stsd_data) - 0x66; - const guint8 *hevc_data = stsd_data + 0x66; - - /* find hevc */ - while (len >= 0x8) { - gint size; - - if (QT_UINT32 (hevc_data) <= len) - size = QT_UINT32 (hevc_data) - 0x8; - else - size = len - 0x8; - - if (size < 1) - /* No real data, so break out */ - break; + if (size < 1) + /* No real data, so break out */ + break; - switch (QT_FOURCC (hevc_data + 0x4)) { - case FOURCC_hvcC: - { - /* parse, if found */ - GstBuffer *buf; + switch (QT_FOURCC (hevc_data + 0x4)) { + case FOURCC_hvcC: + { + /* parse, if found */ + GstBuffer *buf; - GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd"); + GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd"); - /* First 4 bytes are the length of the atom, the next 4 bytes - * are the fourcc, the next 1 byte is the version, and the - * subsequent bytes are sequence parameter set like data. */ - gst_codec_utils_h265_caps_set_level_tier_and_profile - (stream->caps, hevc_data + 8 + 1, size - 1); + /* First 4 bytes are the length of the atom, the next 4 bytes + * are the fourcc, the next 1 byte is the version, and the + * subsequent bytes are sequence parameter set like data. */ + gst_codec_utils_h265_caps_set_level_tier_and_profile + (entry->caps, hevc_data + 8 + 1, size - 1); - buf = gst_buffer_new_and_alloc (size); - gst_buffer_fill (buf, 0, hevc_data + 0x8, size); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - break; + buf = gst_buffer_new_and_alloc (size); + gst_buffer_fill (buf, 0, hevc_data + 0x8, size); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + break; + } + default: + break; } - default: - break; + len -= size + 8; + hevc_data += size + 8; } - len -= size + 8; - hevc_data += size + 8; + break; } - break; - } - case FOURCC_mp4v: - case FOURCC_MP4V: - case FOURCC_fmp4: - case FOURCC_FMP4: - case FOURCC_xvid: - case FOURCC_XVID: - { - GNode *glbl; - - GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (fourcc)); + case FOURCC_mp4v: + case FOURCC_MP4V: + case FOURCC_fmp4: + case FOURCC_FMP4: + case FOURCC_xvid: + case FOURCC_XVID: + { + GNode *glbl; + + GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fourcc)); - /* codec data might be in glbl extension atom */ - glbl = mp4v ? - qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL; - if (glbl) { - guint8 *data; - GstBuffer *buf; - gint len; - - GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd"); - data = glbl->data; - len = QT_UINT32 (data); - if (len > 0x8) { - len -= 0x8; - buf = gst_buffer_new_and_alloc (len); - gst_buffer_fill (buf, 0, data + 8, len); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); + /* codec data might be in glbl extension atom */ + glbl = mp4v ? + qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL; + if (glbl) { + guint8 *data; + GstBuffer *buf; + gint len; + + GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd"); + data = glbl->data; + len = QT_UINT32 (data); + if (len > 0x8) { + len -= 0x8; + buf = gst_buffer_new_and_alloc (len); + gst_buffer_fill (buf, 0, data + 8, len); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } } - } - break; - } - case FOURCC_mjp2: - { - /* see annex I of the jpeg2000 spec */ - GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef; - const guint8 *data; - const gchar *colorspace = NULL; - gint ncomp = 0; - guint32 ncomp_map = 0; - gint32 *comp_map = NULL; - guint32 nchan_def = 0; - gint32 *chan_def = NULL; - - GST_DEBUG_OBJECT (qtdemux, "found mjp2"); - /* some required atoms */ - mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2); - if (!mjp2) - break; - jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h); - if (!jp2h) break; + } + case FOURCC_mjp2: + { + /* see annex I of the jpeg2000 spec */ + GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef; + const guint8 *data; + const gchar *colorspace = NULL; + gint ncomp = 0; + guint32 ncomp_map = 0; + gint32 *comp_map = NULL; + guint32 nchan_def = 0; + gint32 *chan_def = NULL; + + GST_DEBUG_OBJECT (qtdemux, "found mjp2"); + /* some required atoms */ + mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index); + if (!mjp2) + break; + jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h); + if (!jp2h) + break; - /* number of components; redundant with info in codestream, but useful - to a muxer */ - ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr); - if (!ihdr || QT_UINT32 (ihdr->data) != 22) - break; - ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16); + /* number of components; redundant with info in codestream, but useful + to a muxer */ + ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr); + if (!ihdr || QT_UINT32 (ihdr->data) != 22) + break; + ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16); - colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr); - if (!colr) - break; - GST_DEBUG_OBJECT (qtdemux, "found colr"); - /* extract colour space info */ - if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) { - switch (QT_UINT32 ((guint8 *) colr->data + 11)) { - case 16: - colorspace = "sRGB"; - break; - case 17: - colorspace = "GRAY"; - break; - case 18: - colorspace = "sYUV"; - break; - default: - colorspace = NULL; - break; + colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr); + if (!colr) + break; + GST_DEBUG_OBJECT (qtdemux, "found colr"); + /* extract colour space info */ + if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) { + switch (QT_UINT32 ((guint8 *) colr->data + 11)) { + case 16: + colorspace = "sRGB"; + break; + case 17: + colorspace = "GRAY"; + break; + case 18: + colorspace = "sYUV"; + break; + default: + colorspace = NULL; + break; + } } - } - if (!colorspace) - /* colr is required, and only values 16, 17, and 18 are specified, - so error if we have no colorspace */ - break; + if (!colorspace) + /* colr is required, and only values 16, 17, and 18 are specified, + so error if we have no colorspace */ + break; - /* extract component mapping */ - cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap); - if (cmap) { - guint32 cmap_len = 0; - int i; - cmap_len = QT_UINT32 (cmap->data); - if (cmap_len >= 8) { - /* normal box, subtract off header */ - cmap_len -= 8; - /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */ - if (cmap_len % 4 == 0) { - ncomp_map = (cmap_len / 4); - comp_map = g_new0 (gint32, ncomp_map); - for (i = 0; i < ncomp_map; i++) { - guint16 cmp; - guint8 mtyp, pcol; - cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4); - mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2); - pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3); - comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp; + /* extract component mapping */ + cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap); + if (cmap) { + guint32 cmap_len = 0; + int i; + cmap_len = QT_UINT32 (cmap->data); + if (cmap_len >= 8) { + /* normal box, subtract off header */ + cmap_len -= 8; + /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */ + if (cmap_len % 4 == 0) { + ncomp_map = (cmap_len / 4); + comp_map = g_new0 (gint32, ncomp_map); + for (i = 0; i < ncomp_map; i++) { + guint16 cmp; + guint8 mtyp, pcol; + cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4); + mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2); + pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3); + comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp; + } } } } - } - /* extract channel definitions */ - cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef); - if (cdef) { - guint32 cdef_len = 0; - int i; - cdef_len = QT_UINT32 (cdef->data); - if (cdef_len >= 10) { - /* normal box, subtract off header and len */ - cdef_len -= 10; - /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */ - if (cdef_len % 6 == 0) { - nchan_def = (cdef_len / 6); - chan_def = g_new0 (gint32, nchan_def); - for (i = 0; i < nchan_def; i++) - chan_def[i] = -1; - for (i = 0; i < nchan_def; i++) { - guint16 cn, typ, asoc; - cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6); - typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2); - asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4); - if (cn < nchan_def) { - switch (typ) { - case 0: - chan_def[cn] = asoc; - break; - case 1: - chan_def[cn] = 0; /* alpha */ - break; - default: - chan_def[cn] = -typ; + /* extract channel definitions */ + cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef); + if (cdef) { + guint32 cdef_len = 0; + int i; + cdef_len = QT_UINT32 (cdef->data); + if (cdef_len >= 10) { + /* normal box, subtract off header and len */ + cdef_len -= 10; + /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */ + if (cdef_len % 6 == 0) { + nchan_def = (cdef_len / 6); + chan_def = g_new0 (gint32, nchan_def); + for (i = 0; i < nchan_def; i++) + chan_def[i] = -1; + for (i = 0; i < nchan_def; i++) { + guint16 cn, typ, asoc; + cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6); + typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2); + asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4); + if (cn < nchan_def) { + switch (typ) { + case 0: + chan_def[cn] = asoc; + break; + case 1: + chan_def[cn] = 0; /* alpha */ + break; + default: + chan_def[cn] = -typ; + } } } } } } - } - gst_caps_set_simple (stream->caps, - "num-components", G_TYPE_INT, ncomp, NULL); - gst_caps_set_simple (stream->caps, - "colorspace", G_TYPE_STRING, colorspace, NULL); - - if (comp_map) { - GValue arr = { 0, }; - GValue elt = { 0, }; - int i; - g_value_init (&arr, GST_TYPE_ARRAY); - g_value_init (&elt, G_TYPE_INT); - for (i = 0; i < ncomp_map; i++) { - g_value_set_int (&elt, comp_map[i]); - gst_value_array_append_value (&arr, &elt); + gst_caps_set_simple (entry->caps, + "num-components", G_TYPE_INT, ncomp, NULL); + gst_caps_set_simple (entry->caps, + "colorspace", G_TYPE_STRING, colorspace, NULL); + + if (comp_map) { + GValue arr = { 0, }; + GValue elt = { 0, }; + int i; + g_value_init (&arr, GST_TYPE_ARRAY); + g_value_init (&elt, G_TYPE_INT); + for (i = 0; i < ncomp_map; i++) { + g_value_set_int (&elt, comp_map[i]); + gst_value_array_append_value (&arr, &elt); + } + gst_structure_set_value (gst_caps_get_structure (entry->caps, 0), + "component-map", &arr); + g_value_unset (&elt); + g_value_unset (&arr); + g_free (comp_map); } - gst_structure_set_value (gst_caps_get_structure (stream->caps, 0), - "component-map", &arr); - g_value_unset (&elt); - g_value_unset (&arr); - g_free (comp_map); - } - if (chan_def) { - GValue arr = { 0, }; - GValue elt = { 0, }; - int i; - g_value_init (&arr, GST_TYPE_ARRAY); - g_value_init (&elt, G_TYPE_INT); - for (i = 0; i < nchan_def; i++) { - g_value_set_int (&elt, chan_def[i]); - gst_value_array_append_value (&arr, &elt); + if (chan_def) { + GValue arr = { 0, }; + GValue elt = { 0, }; + int i; + g_value_init (&arr, GST_TYPE_ARRAY); + g_value_init (&elt, G_TYPE_INT); + for (i = 0; i < nchan_def; i++) { + g_value_set_int (&elt, chan_def[i]); + gst_value_array_append_value (&arr, &elt); + } + gst_structure_set_value (gst_caps_get_structure (entry->caps, 0), + "channel-definitions", &arr); + g_value_unset (&elt); + g_value_unset (&arr); + g_free (chan_def); } - gst_structure_set_value (gst_caps_get_structure (stream->caps, 0), - "channel-definitions", &arr); - g_value_unset (&elt); - g_value_unset (&arr); - g_free (chan_def); - } - /* some optional atoms */ - field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel); - prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x); + /* some optional atoms */ + field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel); + prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x); - /* indicate possible fields in caps */ - if (field) { - data = (guint8 *) field->data + 8; - if (*data != 1) - gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT, - (gint) * data, NULL); + /* indicate possible fields in caps */ + if (field) { + data = (guint8 *) field->data + 8; + if (*data != 1) + gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT, + (gint) * data, NULL); + } + /* add codec_data if provided */ + if (prefix) { + GstBuffer *buf; + gint len; + + GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd"); + data = prefix->data; + len = QT_UINT32 (data); + if (len > 0x8) { + len -= 0x8; + buf = gst_buffer_new_and_alloc (len); + gst_buffer_fill (buf, 0, data + 8, len); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } + } + break; } - /* add codec_data if provided */ - if (prefix) { + case FOURCC_SVQ3: + case FOURCC_VP31: + { GstBuffer *buf; - gint len; - - GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd"); - data = prefix->data; - len = QT_UINT32 (data); - if (len > 0x8) { - len -= 0x8; - buf = gst_buffer_new_and_alloc (len); - gst_buffer_fill (buf, 0, data + 8, len); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); + GstBuffer *seqh = NULL; + const guint8 *gamma_data = NULL; + gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */ + + qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data, + &seqh); + if (gamma_data) { + gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE, + QT_FP32 (gamma_data), NULL); } - } - break; - } - case FOURCC_jpeg: - { - /* https://developer.apple.com/standards/qtff-2001.pdf, - * page 92, "Video Sample Description", under table 3.1 */ - GstByteReader br; - - const gint compressor_offset = 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2; - const gint min_size = compressor_offset + 32 + 2 + 2; - GNode *jpeg; - guint32 len; - guint16 color_table_id = 0; - gboolean ok; - - GST_DEBUG_OBJECT (qtdemux, "found jpeg"); - - /* recover information on interlaced/progressive */ - jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg); - if (!jpeg) + if (seqh) { + /* sorry for the bad name, but we don't know what this is, other + * than its own fourcc */ + gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh, + NULL); + gst_buffer_unref (seqh); + } + + GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd"); + buf = gst_buffer_new_and_alloc (len); + gst_buffer_fill (buf, 0, stsd_data, len); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); break; + } + case FOURCC_jpeg: + { + /* https://developer.apple.com/standards/qtff-2001.pdf, + * page 92, "Video Sample Description", under table 3.1 */ + GstByteReader br; - len = QT_UINT32 (jpeg->data); - GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len, - min_size); - if (len >= min_size) { - gst_byte_reader_init (&br, jpeg->data, len); - - gst_byte_reader_skip (&br, compressor_offset + 32 + 2); - gst_byte_reader_get_uint16_le (&br, &color_table_id); - if (color_table_id != 0) { - /* the spec says there can be concatenated chunks in the data, and we want - * to find one called field. Walk through them. */ - gint offset = min_size; - while (offset + 8 < len) { - guint32 size = 0, tag; - ok = gst_byte_reader_get_uint32_le (&br, &size); - ok &= gst_byte_reader_get_uint32_le (&br, &tag); - if (!ok || size < 8) { - GST_WARNING_OBJECT (qtdemux, - "Failed to walk optional chunk list"); - break; - } - GST_DEBUG_OBJECT (qtdemux, - "Found optional %4.4s chunk, size %u", (const char *) &tag, - size); - if (tag == FOURCC_fiel) { - guint8 n_fields, ordering; - gst_byte_reader_get_uint8 (&br, &n_fields); - gst_byte_reader_get_uint8 (&br, &ordering); - if (n_fields == 1 || n_fields == 2) { - GST_DEBUG_OBJECT (qtdemux, - "Found fiel tag with %u fields, ordering %u", n_fields, - ordering); - if (n_fields == 2) - gst_caps_set_simple (stream->caps, "interlace-mode", - G_TYPE_STRING, "interleaved", NULL); - } else { + const gint compressor_offset = + 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2; + const gint min_size = compressor_offset + 32 + 2 + 2; + GNode *jpeg; + guint32 len; + guint16 color_table_id = 0; + gboolean ok; + + GST_DEBUG_OBJECT (qtdemux, "found jpeg"); + + /* recover information on interlaced/progressive */ + jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg); + if (!jpeg) + break; + + len = QT_UINT32 (jpeg->data); + GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len, + min_size); + if (len >= min_size) { + gst_byte_reader_init (&br, jpeg->data, len); + + gst_byte_reader_skip (&br, compressor_offset + 32 + 2); + gst_byte_reader_get_uint16_le (&br, &color_table_id); + if (color_table_id != 0) { + /* the spec says there can be concatenated chunks in the data, and we want + * to find one called field. Walk through them. */ + gint offset = min_size; + while (offset + 8 < len) { + guint32 size = 0, tag; + ok = gst_byte_reader_get_uint32_le (&br, &size); + ok &= gst_byte_reader_get_uint32_le (&br, &tag); + if (!ok || size < 8) { GST_WARNING_OBJECT (qtdemux, - "Found fiel tag with invalid fields (%u)", n_fields); + "Failed to walk optional chunk list"); + break; + } + GST_DEBUG_OBJECT (qtdemux, + "Found optional %4.4s chunk, size %u", + (const char *) &tag, size); + if (tag == FOURCC_fiel) { + guint8 n_fields = 0, ordering = 0; + gst_byte_reader_get_uint8 (&br, &n_fields); + gst_byte_reader_get_uint8 (&br, &ordering); + if (n_fields == 1 || n_fields == 2) { + GST_DEBUG_OBJECT (qtdemux, + "Found fiel tag with %u fields, ordering %u", + n_fields, ordering); + if (n_fields == 2) + gst_caps_set_simple (CUR_STREAM (stream)->caps, + "interlace-mode", G_TYPE_STRING, "interleaved", + NULL); + } else { + GST_WARNING_OBJECT (qtdemux, + "Found fiel tag with invalid fields (%u)", n_fields); + } } + offset += size; } - offset += size; + } else { + GST_DEBUG_OBJECT (qtdemux, + "Color table ID is 0, not trying to get interlacedness"); } } else { - GST_DEBUG_OBJECT (qtdemux, - "Color table ID is 0, not trying to get interlacedness"); + GST_WARNING_OBJECT (qtdemux, + "Length of jpeg chunk is too small, not trying to get interlacedness"); } - } else { - GST_WARNING_OBJECT (qtdemux, - "Length of jpeg chunk is too small, not trying to get interlacedness"); - } - break; - } - case FOURCC_SVQ3: - case FOURCC_VP31: - { - GstBuffer *buf; - GstBuffer *seqh = NULL; - guint8 *gamma_data = NULL; - gint len = QT_UINT32 (stsd_data); - - qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh); - if (gamma_data) { - gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE, - QT_FP32 (gamma_data), NULL); + break; } - if (seqh) { - /* sorry for the bad name, but we don't know what this is, other - * than its own fourcc */ - gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh, + case FOURCC_rle_: + case FOURCC_WRLE: + { + gst_caps_set_simple (entry->caps, + "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66), NULL); - gst_buffer_unref (seqh); + break; } + case FOURCC_XiTh: + { + GNode *xith, *xdxt; - GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd"); - buf = gst_buffer_new_and_alloc (len); - gst_buffer_fill (buf, 0, stsd_data, len); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - break; - } - case FOURCC_rle_: - case FOURCC_WRLE: - { - gst_caps_set_simple (stream->caps, - "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL); - break; - } - case FOURCC_XiTh: - { - GNode *xith, *xdxt; + GST_DEBUG_OBJECT (qtdemux, "found XiTh"); + xith = qtdemux_tree_get_child_by_index (stsd, stsd_index); + if (!xith) + break; - GST_DEBUG_OBJECT (qtdemux, "found XiTh"); - xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh); - if (!xith) - break; + xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT); + if (!xdxt) + break; - xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT); - if (!xdxt) + GST_DEBUG_OBJECT (qtdemux, "found XdxT node"); + /* collect the headers and store them in a stream list so that we can + * send them out first */ + qtdemux_parse_theora_extension (qtdemux, stream, xdxt); break; + } + case FOURCC_ovc1: + { + GNode *ovc1; + guint8 *ovc1_data; + guint ovc1_len; + GstBuffer *buf; - GST_DEBUG_OBJECT (qtdemux, "found XdxT node"); - /* collect the headers and store them in a stream list so that we can - * send them out first */ - qtdemux_parse_theora_extension (qtdemux, stream, xdxt); - break; - } - case FOURCC_ovc1: - { - GNode *ovc1; - guint8 *ovc1_data; - guint ovc1_len; - GstBuffer *buf; - - GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header"); - ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1); - if (!ovc1) - break; - ovc1_data = ovc1->data; - ovc1_len = QT_UINT32 (ovc1_data); - if (ovc1_len <= 198) { - GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping"); + GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header"); + ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index); + if (!ovc1) + break; + ovc1_data = ovc1->data; + ovc1_len = QT_UINT32 (ovc1_data); + if (ovc1_len <= 198) { + GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping"); + break; + } + buf = gst_buffer_new_and_alloc (ovc1_len - 198); + gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); break; } - buf = gst_buffer_new_and_alloc (ovc1_len - 198); - gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - break; - } - case FOURCC_vc_1: - { - gint len = QT_UINT32 (stsd_data) - 0x66; - const guint8 *vc1_data = stsd_data + 0x66; + case FOURCC_vc_1: + { + gint len = QT_UINT32 (stsd_entry_data) - 0x56; + const guint8 *vc1_data = stsd_entry_data + 0x56; - /* find dvc1 */ - while (len >= 8) { - gint size; + /* find dvc1 */ + while (len >= 8) { + gint size; - if (QT_UINT32 (vc1_data) <= len) - size = QT_UINT32 (vc1_data) - 8; - else - size = len - 8; + if (QT_UINT32 (vc1_data) <= len) + size = QT_UINT32 (vc1_data) - 8; + else + size = len - 8; - if (size < 1) - /* No real data, so break out */ - break; + if (size < 1) + /* No real data, so break out */ + break; - switch (QT_FOURCC (vc1_data + 0x4)) { - case GST_MAKE_FOURCC ('d', 'v', 'c', '1'): - { - GstBuffer *buf; + switch (QT_FOURCC (vc1_data + 0x4)) { + case GST_MAKE_FOURCC ('d', 'v', 'c', '1'): + { + GstBuffer *buf; - GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd"); - buf = gst_buffer_new_and_alloc (size); - gst_buffer_fill (buf, 0, vc1_data + 8, size); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - break; + GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd"); + buf = gst_buffer_new_and_alloc (size); + gst_buffer_fill (buf, 0, vc1_data + 8, size); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + break; + } + default: + break; } - default: - break; + len -= size + 8; + vc1_data += size + 8; } - len -= size + 8; - vc1_data += size + 8; + break; } - break; + default: + break; } - default: - break; } - } - GST_INFO_OBJECT (qtdemux, - "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, - GST_FOURCC_ARGS (fourcc), stream->caps); - - } else if (stream->subtype == FOURCC_soun) { - int version, samplesize; - guint16 compression_id; - gboolean amrwb = FALSE; + GST_INFO_OBJECT (qtdemux, + "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, + GST_FOURCC_ARGS (fourcc), entry->caps); - offset = 32; - /* sample description entry (16) + sound sample description v0 (20) */ - if (len < 36) - goto corrupt_file; + } else if (stream->subtype == FOURCC_soun) { + int version, samplesize; + guint16 compression_id; + gboolean amrwb = FALSE; - version = QT_UINT32 (stsd_data + offset); - stream->n_channels = QT_UINT16 (stsd_data + offset + 8); - samplesize = QT_UINT16 (stsd_data + offset + 10); - compression_id = QT_UINT16 (stsd_data + offset + 12); - stream->rate = QT_FP32 (stsd_data + offset + 16); - - GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version); - GST_LOG_OBJECT (qtdemux, "vendor: %08x", - QT_UINT32 (stsd_data + offset + 4)); - GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels); - GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize); - GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id); - GST_LOG_OBJECT (qtdemux, "packet size: %d", - QT_UINT16 (stsd_data + offset + 14)); - GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate); - - if (compression_id == 0xfffe) - stream->sampled = TRUE; - - /* first assume uncompressed audio */ - stream->bytes_per_sample = samplesize / 8; - stream->samples_per_frame = stream->n_channels; - stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample; - stream->samples_per_packet = stream->samples_per_frame; - stream->bytes_per_packet = stream->bytes_per_sample; - - offset = 52; - switch (fourcc) { - /* Yes, these have to be hard-coded */ - case FOURCC_MAC6: - { - stream->samples_per_packet = 6; - stream->bytes_per_packet = 1; - stream->bytes_per_frame = 1 * stream->n_channels; - stream->bytes_per_sample = 1; - stream->samples_per_frame = 6 * stream->n_channels; - break; - } - case FOURCC_MAC3: - { - stream->samples_per_packet = 3; - stream->bytes_per_packet = 1; - stream->bytes_per_frame = 1 * stream->n_channels; - stream->bytes_per_sample = 1; - stream->samples_per_frame = 3 * stream->n_channels; - break; - } - case FOURCC_ima4: - { - stream->samples_per_packet = 64; - stream->bytes_per_packet = 34; - stream->bytes_per_frame = 34 * stream->n_channels; - stream->bytes_per_sample = 2; - stream->samples_per_frame = 64 * stream->n_channels; - break; - } - case FOURCC_ulaw: - case FOURCC_alaw: - { - stream->samples_per_packet = 1; - stream->bytes_per_packet = 1; - stream->bytes_per_frame = 1 * stream->n_channels; - stream->bytes_per_sample = 1; - stream->samples_per_frame = 1 * stream->n_channels; - break; - } - case FOURCC_agsm: - { - stream->samples_per_packet = 160; - stream->bytes_per_packet = 33; - stream->bytes_per_frame = 33 * stream->n_channels; - stream->bytes_per_sample = 2; - stream->samples_per_frame = 160 * stream->n_channels; - break; - } - default: - break; - } - - if (version == 0x00010000) { - /* sample description entry (16) + sound sample description v1 (20+16) */ - if (len < 52) + offset = 16; + /* sample description entry (16) + sound sample description v0 (20) */ + if (len < 36) goto corrupt_file; + version = QT_UINT32 (stsd_entry_data + offset); + entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8); + samplesize = QT_UINT16 (stsd_entry_data + offset + 10); + compression_id = QT_UINT16 (stsd_entry_data + offset + 12); + entry->rate = QT_FP32 (stsd_entry_data + offset + 16); + + GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version); + GST_LOG_OBJECT (qtdemux, "vendor: %08x", + QT_UINT32 (stsd_entry_data + offset + 4)); + GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels); + GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize); + GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id); + GST_LOG_OBJECT (qtdemux, "packet size: %d", + QT_UINT16 (stsd_entry_data + offset + 14)); + GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate); + + if (compression_id == 0xfffe) + entry->sampled = TRUE; + + /* first assume uncompressed audio */ + entry->bytes_per_sample = samplesize / 8; + entry->samples_per_frame = entry->n_channels; + entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample; + entry->samples_per_packet = entry->samples_per_frame; + entry->bytes_per_packet = entry->bytes_per_sample; + + offset = 36; switch (fourcc) { - case FOURCC_twos: - case FOURCC_sowt: - case FOURCC_raw_: + /* Yes, these have to be hard-coded */ + case FOURCC_MAC6: + { + entry->samples_per_packet = 6; + entry->bytes_per_packet = 1; + entry->bytes_per_frame = 1 * entry->n_channels; + entry->bytes_per_sample = 1; + entry->samples_per_frame = 6 * entry->n_channels; break; - default: + } + case FOURCC_MAC3: { - /* only parse extra decoding config for non-pcm audio */ - stream->samples_per_packet = QT_UINT32 (stsd_data + offset); - stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4); - stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8); - stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12); - - GST_LOG_OBJECT (qtdemux, "samples/packet: %d", - stream->samples_per_packet); - GST_LOG_OBJECT (qtdemux, "bytes/packet: %d", - stream->bytes_per_packet); - GST_LOG_OBJECT (qtdemux, "bytes/frame: %d", - stream->bytes_per_frame); - GST_LOG_OBJECT (qtdemux, "bytes/sample: %d", - stream->bytes_per_sample); - - if (!stream->sampled && stream->bytes_per_packet) { - stream->samples_per_frame = (stream->bytes_per_frame / - stream->bytes_per_packet) * stream->samples_per_packet; - GST_LOG_OBJECT (qtdemux, "samples/frame: %d", - stream->samples_per_frame); - } + entry->samples_per_packet = 3; + entry->bytes_per_packet = 1; + entry->bytes_per_frame = 1 * entry->n_channels; + entry->bytes_per_sample = 1; + entry->samples_per_frame = 3 * entry->n_channels; break; } + case FOURCC_ima4: + { + entry->samples_per_packet = 64; + entry->bytes_per_packet = 34; + entry->bytes_per_frame = 34 * entry->n_channels; + entry->bytes_per_sample = 2; + entry->samples_per_frame = 64 * entry->n_channels; + break; + } + case FOURCC_ulaw: + case FOURCC_alaw: + { + entry->samples_per_packet = 1; + entry->bytes_per_packet = 1; + entry->bytes_per_frame = 1 * entry->n_channels; + entry->bytes_per_sample = 1; + entry->samples_per_frame = 1 * entry->n_channels; + break; + } + case FOURCC_agsm: + { + entry->samples_per_packet = 160; + entry->bytes_per_packet = 33; + entry->bytes_per_frame = 33 * entry->n_channels; + entry->bytes_per_sample = 2; + entry->samples_per_frame = 160 * entry->n_channels; + break; + } + default: + break; } - } else if (version == 0x00020000) { - union - { - gdouble fp; - guint64 val; - } qtfp; - /* sample description entry (16) + sound sample description v2 (56) */ - if (len < 72) - goto corrupt_file; + if (version == 0x00010000) { + /* sample description entry (16) + sound sample description v1 (20+16) */ + if (len < 52) + goto corrupt_file; - qtfp.val = QT_UINT64 (stsd_data + offset + 4); - stream->rate = qtfp.fp; - stream->n_channels = QT_UINT32 (stsd_data + offset + 12); + switch (fourcc) { + case FOURCC_twos: + case FOURCC_sowt: + case FOURCC_raw_: + break; + default: + { + /* only parse extra decoding config for non-pcm audio */ + entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset); + entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4); + entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8); + entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12); + + GST_LOG_OBJECT (qtdemux, "samples/packet: %d", + entry->samples_per_packet); + GST_LOG_OBJECT (qtdemux, "bytes/packet: %d", + entry->bytes_per_packet); + GST_LOG_OBJECT (qtdemux, "bytes/frame: %d", + entry->bytes_per_frame); + GST_LOG_OBJECT (qtdemux, "bytes/sample: %d", + entry->bytes_per_sample); + + if (!entry->sampled && entry->bytes_per_packet) { + entry->samples_per_frame = (entry->bytes_per_frame / + entry->bytes_per_packet) * entry->samples_per_packet; + GST_LOG_OBJECT (qtdemux, "samples/frame: %d", + entry->samples_per_frame); + } + break; + } + } + } else if (version == 0x00020000) { + union + { + gdouble fp; + guint64 val; + } qtfp; - GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2"); - GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate); - GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels); - GST_LOG_OBJECT (qtdemux, "bits/channel: %d", - QT_UINT32 (stsd_data + offset + 20)); - GST_LOG_OBJECT (qtdemux, "format flags: %X", - QT_UINT32 (stsd_data + offset + 24)); - GST_LOG_OBJECT (qtdemux, "bytes/packet: %d", - QT_UINT32 (stsd_data + offset + 28)); - GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d", - QT_UINT32 (stsd_data + offset + 32)); - } else if (version != 0x00000) { - GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x", version); - } + /* sample description entry (16) + sound sample description v2 (56) */ + if (len < 72) + goto corrupt_file; - if (stream->caps) - gst_caps_unref (stream->caps); + qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4); + entry->rate = qtfp.fp; + entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12); - stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc, - stsd_data + 32, len - 16, &codec); + GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2"); + GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate); + GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels); + GST_LOG_OBJECT (qtdemux, "bits/channel: %d", + QT_UINT32 (stsd_entry_data + offset + 20)); + GST_LOG_OBJECT (qtdemux, "format flags: %X", + QT_UINT32 (stsd_entry_data + offset + 24)); + GST_LOG_OBJECT (qtdemux, "bytes/packet: %d", + QT_UINT32 (stsd_entry_data + offset + 28)); + GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d", + QT_UINT32 (stsd_entry_data + offset + 32)); + } else if (version != 0x00000) { + GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x", + version); + } - switch (fourcc) { - case FOURCC_in24: - { - GNode *enda; - GNode *in24; + if (entry->caps) + gst_caps_unref (entry->caps); - in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24); + entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc, + stsd_entry_data + 32, len - 16, &codec); - enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda); - if (!enda) { - wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave); - if (wave) - enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda); - } - if (enda) { - int enda_value = QT_UINT16 ((guint8 *) enda->data + 8); - gst_caps_set_simple (stream->caps, - "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE", NULL); - } - break; - } - case FOURCC_owma: - { - GNode *owma; - const guint8 *owma_data; - const gchar *codec_name = NULL; - guint owma_len; - GstBuffer *buf; - gint version = 1; - /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */ - /* FIXME this should also be gst_riff_strf_auds, - * but the latter one is actually missing bits-per-sample :( */ - typedef struct + switch (fourcc) { + case FOURCC_in24: { - gint16 wFormatTag; - gint16 nChannels; - gint32 nSamplesPerSec; - gint32 nAvgBytesPerSec; - gint16 nBlockAlign; - gint16 wBitsPerSample; - gint16 cbSize; - } WAVEFORMATEX; - WAVEFORMATEX *wfex; - - GST_DEBUG_OBJECT (qtdemux, "parse owma"); - owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma); - if (!owma) - break; - owma_data = owma->data; - owma_len = QT_UINT32 (owma_data); - if (owma_len <= 54) { - GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping"); + GNode *enda; + GNode *in24; + + in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24); + + enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda); + if (!enda) { + wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave); + if (wave) + enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda); + } + if (enda) { + int enda_value = QT_UINT16 ((guint8 *) enda->data + 8); + gst_caps_set_simple (entry->caps, + "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE", + NULL); + } break; } - wfex = (WAVEFORMATEX *) (owma_data + 36); - buf = gst_buffer_new_and_alloc (owma_len - 54); - gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54); - if (wfex->wFormatTag == 0x0161) { - codec_name = "Windows Media Audio"; - version = 2; - } else if (wfex->wFormatTag == 0x0162) { - codec_name = "Windows Media Audio 9 Pro"; - version = 3; - } else if (wfex->wFormatTag == 0x0163) { - codec_name = "Windows Media Audio 9 Lossless"; - /* is that correct? gstffmpegcodecmap.c is missing it, but - * fluendo codec seems to support it */ - version = 4; - } + case FOURCC_owma: + { + const guint8 *owma_data; + const gchar *codec_name = NULL; + guint owma_len; + GstBuffer *buf; + gint version = 1; + /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */ + /* FIXME this should also be gst_riff_strf_auds, + * but the latter one is actually missing bits-per-sample :( */ + typedef struct + { + gint16 wFormatTag; + gint16 nChannels; + gint32 nSamplesPerSec; + gint32 nAvgBytesPerSec; + gint16 nBlockAlign; + gint16 wBitsPerSample; + gint16 cbSize; + } WAVEFORMATEX; + WAVEFORMATEX *wfex; + + GST_DEBUG_OBJECT (qtdemux, "parse owma"); + owma_data = stsd_entry_data; + owma_len = QT_UINT32 (owma_data); + if (owma_len <= 54) { + GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping"); + break; + } + wfex = (WAVEFORMATEX *) (owma_data + 36); + buf = gst_buffer_new_and_alloc (owma_len - 54); + gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54); + if (wfex->wFormatTag == 0x0161) { + codec_name = "Windows Media Audio"; + version = 2; + } else if (wfex->wFormatTag == 0x0162) { + codec_name = "Windows Media Audio 9 Pro"; + version = 3; + } else if (wfex->wFormatTag == 0x0163) { + codec_name = "Windows Media Audio 9 Lossless"; + /* is that correct? gstffmpegcodecmap.c is missing it, but + * fluendo codec seems to support it */ + version = 4; + } - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, - "wmaversion", G_TYPE_INT, version, - "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign), - "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), - "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample), - "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample), - NULL); - gst_buffer_unref (buf); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, + "wmaversion", G_TYPE_INT, version, + "block_align", G_TYPE_INT, + GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT, + GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT, + GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT, + GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL); + gst_buffer_unref (buf); - if (codec_name) { - g_free (codec); - codec = g_strdup (codec_name); + if (codec_name) { + g_free (codec); + codec = g_strdup (codec_name); + } + break; } - break; - } - case FOURCC_wma_: - { - gint len = QT_UINT32 (stsd_data) - offset; - const guint8 *wfex_data = stsd_data + offset; - const gchar *codec_name = NULL; - gint version = 1; - /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */ - /* FIXME this should also be gst_riff_strf_auds, - * but the latter one is actually missing bits-per-sample :( */ - typedef struct + case FOURCC_wma_: { - gint16 wFormatTag; - gint16 nChannels; - gint32 nSamplesPerSec; - gint32 nAvgBytesPerSec; - gint16 nBlockAlign; - gint16 wBitsPerSample; - gint16 cbSize; - } WAVEFORMATEX; - WAVEFORMATEX wfex; - - /* FIXME: unify with similar wavformatex parsing code above */ - GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex"); - - /* find wfex */ - while (len >= 8) { - gint size; - - if (QT_UINT32 (wfex_data) <= len) - size = QT_UINT32 (wfex_data) - 8; - else - size = len - 8; + gint len = QT_UINT32 (stsd_entry_data) - offset; + const guint8 *wfex_data = stsd_entry_data + offset; + const gchar *codec_name = NULL; + gint version = 1; + /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */ + /* FIXME this should also be gst_riff_strf_auds, + * but the latter one is actually missing bits-per-sample :( */ + typedef struct + { + gint16 wFormatTag; + gint16 nChannels; + gint32 nSamplesPerSec; + gint32 nAvgBytesPerSec; + gint16 nBlockAlign; + gint16 wBitsPerSample; + gint16 cbSize; + } WAVEFORMATEX; + WAVEFORMATEX wfex; + + /* FIXME: unify with similar wavformatex parsing code above */ + GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex"); + + /* find wfex */ + while (len >= 8) { + gint size; - if (size < 1) - /* No real data, so break out */ - break; + if (QT_UINT32 (wfex_data) <= len) + size = QT_UINT32 (wfex_data) - 8; + else + size = len - 8; - switch (QT_FOURCC (wfex_data + 4)) { - case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'): - { - GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd"); + if (size < 1) + /* No real data, so break out */ + break; - if (size < 8 + 18) - break; + switch (QT_FOURCC (wfex_data + 4)) { + case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'): + { + GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd"); - wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0); - wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2); - wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4); - wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8); - wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12); - wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14); - wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16); - - GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:"); - GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, " - "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, " - "BitsPerSample = %u, Size = %u", wfex.wFormatTag, - wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec, - wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize); - - if (wfex.wFormatTag == 0x0161) { - codec_name = "Windows Media Audio"; - version = 2; - } else if (wfex.wFormatTag == 0x0162) { - codec_name = "Windows Media Audio 9 Pro"; - version = 3; - } else if (wfex.wFormatTag == 0x0163) { - codec_name = "Windows Media Audio 9 Lossless"; - /* is that correct? gstffmpegcodecmap.c is missing it, but - * fluendo codec seems to support it */ - version = 4; - } + if (size < 8 + 18) + break; - gst_caps_set_simple (stream->caps, - "wmaversion", G_TYPE_INT, version, - "block_align", G_TYPE_INT, wfex.nBlockAlign, - "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec, - "width", G_TYPE_INT, wfex.wBitsPerSample, - "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL); + wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0); + wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2); + wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4); + wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8); + wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12); + wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14); + wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16); + + GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:"); + GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, " + "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, " + "BitsPerSample = %u, Size = %u", wfex.wFormatTag, + wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec, + wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize); + + if (wfex.wFormatTag == 0x0161) { + codec_name = "Windows Media Audio"; + version = 2; + } else if (wfex.wFormatTag == 0x0162) { + codec_name = "Windows Media Audio 9 Pro"; + version = 3; + } else if (wfex.wFormatTag == 0x0163) { + codec_name = "Windows Media Audio 9 Lossless"; + /* is that correct? gstffmpegcodecmap.c is missing it, but + * fluendo codec seems to support it */ + version = 4; + } - if (size > wfex.cbSize) { - GstBuffer *buf; + gst_caps_set_simple (entry->caps, + "wmaversion", G_TYPE_INT, version, + "block_align", G_TYPE_INT, wfex.nBlockAlign, + "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec, + "width", G_TYPE_INT, wfex.wBitsPerSample, + "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL); - buf = gst_buffer_new_and_alloc (size - wfex.cbSize); - gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize, - size - wfex.cbSize); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - } else { - GST_WARNING_OBJECT (qtdemux, "no codec data"); - } + if (size > wfex.cbSize) { + GstBuffer *buf; + + buf = gst_buffer_new_and_alloc (size - wfex.cbSize); + gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize, + size - wfex.cbSize); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } else { + GST_WARNING_OBJECT (qtdemux, "no codec data"); + } - if (codec_name) { - g_free (codec); - codec = g_strdup (codec_name); + if (codec_name) { + g_free (codec); + codec = g_strdup (codec_name); + } + break; } - break; + default: + break; } - default: - break; + len -= size + 8; + wfex_data += size + 8; } - len -= size + 8; - wfex_data += size + 8; - } - break; - } - case FOURCC_opus: - { - GNode *opus; - const guint8 *opus_data; - guint8 *channel_mapping = NULL; - guint32 rate; - guint8 channels; - guint8 channel_mapping_family; - guint8 stream_count; - guint8 coupled_count; - guint8 i; - - opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus); - opus_data = opus->data; - - channels = GST_READ_UINT8 (opus_data + 45); - rate = GST_READ_UINT32_LE (opus_data + 48); - channel_mapping_family = GST_READ_UINT8 (opus_data + 54); - stream_count = GST_READ_UINT8 (opus_data + 55); - coupled_count = GST_READ_UINT8 (opus_data + 56); - - if (channels > 0) { - channel_mapping = g_malloc (channels * sizeof (guint8)); - for (i = 0; i < channels; i++) - channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57); + break; } + case FOURCC_opus: + { + const guint8 *opus_data; + guint8 *channel_mapping = NULL; + guint32 rate; + guint8 channels; + guint8 channel_mapping_family; + guint8 stream_count; + guint8 coupled_count; + guint8 i; + + opus_data = stsd_entry_data; + + channels = GST_READ_UINT8 (opus_data + 45); + rate = GST_READ_UINT32_LE (opus_data + 48); + channel_mapping_family = GST_READ_UINT8 (opus_data + 54); + stream_count = GST_READ_UINT8 (opus_data + 55); + coupled_count = GST_READ_UINT8 (opus_data + 56); + + if (channels > 0) { + channel_mapping = g_malloc (channels * sizeof (guint8)); + for (i = 0; i < channels; i++) + channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57); + } - stream->caps = gst_codec_utils_opus_create_caps (rate, channels, - channel_mapping_family, stream_count, coupled_count, - channel_mapping); - break; + entry->caps = gst_codec_utils_opus_create_caps (rate, channels, + channel_mapping_family, stream_count, coupled_count, + channel_mapping); + break; + } + default: + break; } - default: - break; - } - - if (codec) { - GstStructure *s; - gint bitrate = 0; - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_AUDIO_CODEC, codec, NULL); - g_free (codec); - codec = NULL; - - /* some bitrate info may have ended up in caps */ - s = gst_caps_get_structure (stream->caps, 0); - gst_structure_get_int (s, "bitrate", &bitrate); - if (bitrate > 0) - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, bitrate, NULL); - } + if (codec) { + GstStructure *s; + gint bitrate = 0; - if (stream->protected && fourcc == FOURCC_mp4a) - mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_enca); - else - mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a); - - wave = NULL; - esds = NULL; - if (mp4a) { - wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave); - if (wave) - esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds); - if (!esds) - esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds); - } + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, codec, NULL); + g_free (codec); + codec = NULL; + /* some bitrate info may have ended up in caps */ + s = gst_caps_get_structure (entry->caps, 0); + gst_structure_get_int (s, "bitrate", &bitrate); + if (bitrate > 0) + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, bitrate, NULL); + } - /* If the fourcc's bottom 16 bits gives 'sm', then the top - 16 bits is a byte-swapped wave-style codec identifier, - and we can find a WAVE header internally to a 'wave' atom here. - This can more clearly be thought of as 'ms' as the top 16 bits, and a - codec id as the bottom 16 bits - but byte-swapped to store in QT (which - is big-endian). - */ - if ((fourcc & 0xffff) == (('s' << 8) | 'm')) { - if (len < offset + 20) { - GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio"); + mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index); + if (!stream->protected) { } else { - guint32 datalen = QT_UINT32 (stsd_data + offset + 16); - const guint8 *data = stsd_data + offset + 16; - GNode *wavenode; - GNode *waveheadernode; - - wavenode = g_node_new ((guint8 *) data); - if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) { - const guint8 *waveheader; - guint32 headerlen; - - waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc); - if (waveheadernode) { - waveheader = (const guint8 *) waveheadernode->data; - headerlen = QT_UINT32 (waveheader); - - if (headerlen > 8) { - gst_riff_strf_auds *header = NULL; - GstBuffer *headerbuf; - GstBuffer *extra; - - waveheader += 8; - headerlen -= 8; - - headerbuf = gst_buffer_new_and_alloc (headerlen); - gst_buffer_fill (headerbuf, 0, waveheader, headerlen); - - if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux), - headerbuf, &header, &extra)) { - gst_caps_unref (stream->caps); - /* FIXME: Need to do something with the channel reorder map */ - stream->caps = gst_riff_create_audio_caps (header->format, NULL, - header, extra, NULL, NULL, NULL); - - if (extra) - gst_buffer_unref (extra); - g_free (header); - } - } - } else - GST_DEBUG ("Didn't find waveheadernode for this codec"); + if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) { + mp4v = NULL; } - g_node_destroy (wavenode); } - } else if (esds) { - gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags); - } else { - switch (fourcc) { -#if 0 - /* FIXME: what is in the chunk? */ - case FOURCC_QDMC: - { - gint len = QT_UINT32 (stsd_data); - - /* seems to be always = 116 = 0x74 */ - break; + if (stream->protected && fourcc == FOURCC_mp4a) { + if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) { + mp4a = NULL; } -#endif - case FOURCC_QDM2: - { - gint len = QT_UINT32 (stsd_data); + } else { + if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_mp4a) { + mp4a = NULL; + } + } - if (len > 0x4C) { - GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C); + wave = NULL; + esds = NULL; + if (mp4a) { + wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave); + if (wave) + esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds); + if (!esds) + esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds); + } - gst_buffer_fill (buf, 0, stsd_data + 0x4C, len - 0x4C); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); + + /* If the fourcc's bottom 16 bits gives 'sm', then the top + 16 bits is a byte-swapped wave-style codec identifier, + and we can find a WAVE header internally to a 'wave' atom here. + This can more clearly be thought of as 'ms' as the top 16 bits, and a + codec id as the bottom 16 bits - but byte-swapped to store in QT (which + is big-endian). + */ + if ((fourcc & 0xffff) == (('s' << 8) | 'm')) { + if (len < offset + 20) { + GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio"); + } else { + guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16); + const guint8 *data = stsd_entry_data + offset + 16; + GNode *wavenode; + GNode *waveheadernode; + + wavenode = g_node_new ((guint8 *) data); + if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) { + const guint8 *waveheader; + guint32 headerlen; + + waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc); + if (waveheadernode) { + waveheader = (const guint8 *) waveheadernode->data; + headerlen = QT_UINT32 (waveheader); + + if (headerlen > 8) { + gst_riff_strf_auds *header = NULL; + GstBuffer *headerbuf; + GstBuffer *extra; + + waveheader += 8; + headerlen -= 8; + + headerbuf = gst_buffer_new_and_alloc (headerlen); + gst_buffer_fill (headerbuf, 0, waveheader, headerlen); + + if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux), + headerbuf, &header, &extra)) { + gst_caps_unref (entry->caps); + /* FIXME: Need to do something with the channel reorder map */ + entry->caps = + gst_riff_create_audio_caps (header->format, NULL, header, + extra, NULL, NULL, NULL); + + if (extra) + gst_buffer_unref (extra); + g_free (header); + } + } + } else + GST_DEBUG ("Didn't find waveheadernode for this codec"); } - gst_caps_set_simple (stream->caps, - "samplesize", G_TYPE_INT, samplesize, NULL); - break; + g_node_destroy (wavenode); } - case FOURCC_alac: - { - GNode *alac, *wave = NULL; - - /* apparently, m4a has this atom appended directly in the stsd entry, - * while mov has it in a wave atom */ - alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac); - if (alac) { - /* alac now refers to stsd entry atom */ - wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave); - if (wave) - alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac); - else - alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac); + } else if (esds) { + gst_qtdemux_handle_esds (qtdemux, stream, entry, esds, + stream->stream_tags); + } else { + switch (fourcc) { +#if 0 + /* FIXME: what is in the chunk? */ + case FOURCC_QDMC: + { + gint len = QT_UINT32 (stsd_data); + + /* seems to be always = 116 = 0x74 */ + break; } - if (alac) { - const guint8 *alac_data = alac->data; - gint len = QT_UINT32 (alac->data); - GstBuffer *buf; +#endif + case FOURCC_QDM2: + { + gint len = QT_UINT32 (stsd_entry_data); - if (len < 36) { - GST_DEBUG_OBJECT (qtdemux, - "discarding alac atom with unexpected len %d", len); - } else { - /* codec-data contains alac atom size and prefix, - * ffmpeg likes it that way, not quite gst-ish though ...*/ - buf = gst_buffer_new_and_alloc (len); - gst_buffer_fill (buf, 0, alac->data, len); - gst_caps_set_simple (stream->caps, + if (len > 0x3C) { + GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C); + + gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C); + gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER, buf, NULL); gst_buffer_unref (buf); - - stream->bytes_per_frame = QT_UINT32 (alac_data + 12); - stream->n_channels = QT_UINT8 (alac_data + 21); - stream->rate = QT_UINT32 (alac_data + 32); } + gst_caps_set_simple (entry->caps, + "samplesize", G_TYPE_INT, samplesize, NULL); + break; } - gst_caps_set_simple (stream->caps, - "samplesize", G_TYPE_INT, samplesize, NULL); - break; - } - case FOURCC_fLaC: - { - /* The codingname of the sample entry is 'fLaC' */ - GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC); - - if (flac) { - /* The 'dfLa' box is added to the sample entry to convey - initializing information for the decoder. */ - const GNode *dfla = - qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa); - - if (dfla) { - const guint32 len = QT_UINT32 (dfla->data); + case FOURCC_alac: + { + GNode *alac, *wave = NULL; + + /* apparently, m4a has this atom appended directly in the stsd entry, + * while mov has it in a wave atom */ + alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac); + if (alac) { + /* alac now refers to stsd entry atom */ + wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave); + if (wave) + alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac); + else + alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac); + } + if (alac) { + const guint8 *alac_data = alac->data; + gint len = QT_UINT32 (alac->data); + GstBuffer *buf; - /* Must contain at least dfLa box header (12), - * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */ - if (len < 50) { + if (len < 36) { GST_DEBUG_OBJECT (qtdemux, - "discarding dfla atom with unexpected len %d", len); + "discarding alac atom with unexpected len %d", len); } else { - /* skip dfLa header to get the METADATA_BLOCKs */ - const guint8 *metadata_blocks = (guint8 *) dfla->data + 12; - const guint32 metadata_blocks_len = len - 12; + /* codec-data contains alac atom size and prefix, + * ffmpeg likes it that way, not quite gst-ish though ...*/ + buf = gst_buffer_new_and_alloc (len); + gst_buffer_fill (buf, 0, alac->data, len); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); - gchar *stream_marker = g_strdup ("fLaC"); - GstBuffer *block = gst_buffer_new_wrapped (stream_marker, - strlen (stream_marker)); + entry->bytes_per_frame = QT_UINT32 (alac_data + 12); + entry->n_channels = QT_UINT8 (alac_data + 21); + entry->rate = QT_UINT32 (alac_data + 32); + } + } + gst_caps_set_simple (entry->caps, + "samplesize", G_TYPE_INT, samplesize, NULL); + break; + } + case FOURCC_fLaC: + { + /* The codingname of the sample entry is 'fLaC' */ + GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC); + + if (flac) { + /* The 'dfLa' box is added to the sample entry to convey + initializing information for the decoder. */ + const GNode *dfla = + qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa); + + if (dfla) { + const guint32 len = QT_UINT32 (dfla->data); + + /* Must contain at least dfLa box header (12), + * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */ + if (len < 50) { + GST_DEBUG_OBJECT (qtdemux, + "discarding dfla atom with unexpected len %d", len); + } else { + /* skip dfLa header to get the METADATA_BLOCKs */ + const guint8 *metadata_blocks = (guint8 *) dfla->data + 12; + const guint32 metadata_blocks_len = len - 12; - guint32 index = 0; - guint32 remainder = 0; - guint32 block_size = 0; - gboolean is_last = FALSE; + gchar *stream_marker = g_strdup ("fLaC"); + GstBuffer *block = gst_buffer_new_wrapped (stream_marker, + strlen (stream_marker)); - GValue array = G_VALUE_INIT; - GValue value = G_VALUE_INIT; + guint32 index = 0; + guint32 remainder = 0; + guint32 block_size = 0; + gboolean is_last = FALSE; - g_value_init (&array, GST_TYPE_ARRAY); - g_value_init (&value, GST_TYPE_BUFFER); + GValue array = G_VALUE_INIT; + GValue value = G_VALUE_INIT; - gst_value_set_buffer (&value, block); - gst_value_array_append_value (&array, &value); - g_value_reset (&value); + g_value_init (&array, GST_TYPE_ARRAY); + g_value_init (&value, GST_TYPE_BUFFER); - gst_buffer_unref (block); + gst_value_set_buffer (&value, block); + gst_value_array_append_value (&array, &value); + g_value_reset (&value); - /* check there's at least one METADATA_BLOCK_HEADER's worth - * of data, and we haven't already finished parsing */ - while (!is_last && ((index + 3) < metadata_blocks_len)) { - remainder = metadata_blocks_len - index; + gst_buffer_unref (block); - /* add the METADATA_BLOCK_HEADER size to the signalled size */ - block_size = 4 + - (metadata_blocks[index + 1] << 16) + - (metadata_blocks[index + 2] << 8) + - metadata_blocks[index + 3]; + /* check there's at least one METADATA_BLOCK_HEADER's worth + * of data, and we haven't already finished parsing */ + while (!is_last && ((index + 3) < metadata_blocks_len)) { + remainder = metadata_blocks_len - index; - /* be careful not to read off end of box */ - if (block_size > remainder) { - break; - } + /* add the METADATA_BLOCK_HEADER size to the signalled size */ + block_size = 4 + + (metadata_blocks[index + 1] << 16) + + (metadata_blocks[index + 2] << 8) + + metadata_blocks[index + 3]; - is_last = metadata_blocks[index] >> 7; + /* be careful not to read off end of box */ + if (block_size > remainder) { + break; + } - block = gst_buffer_new_and_alloc (block_size); + is_last = metadata_blocks[index] >> 7; - gst_buffer_fill (block, 0, &metadata_blocks[index], - block_size); + block = gst_buffer_new_and_alloc (block_size); - gst_value_set_buffer (&value, block); - gst_value_array_append_value (&array, &value); - g_value_reset (&value); + gst_buffer_fill (block, 0, &metadata_blocks[index], + block_size); - gst_buffer_unref (block); + gst_value_set_buffer (&value, block); + gst_value_array_append_value (&array, &value); + g_value_reset (&value); - index += block_size; - } + gst_buffer_unref (block); - /* only append the metadata if we successfully read all of it */ - if (is_last) { - gst_structure_set_value (gst_caps_get_structure (stream->caps, - 0), "streamheader", &array); - } else { - GST_WARNING_OBJECT (qtdemux, - "discarding all METADATA_BLOCKs due to invalid " - "block_size %d at idx %d, rem %d", block_size, index, - remainder); - } + index += block_size; + } + + /* only append the metadata if we successfully read all of it */ + if (is_last) { + gst_structure_set_value (gst_caps_get_structure (CUR_STREAM + (stream)->caps, 0), "streamheader", &array); + } else { + GST_WARNING_OBJECT (qtdemux, + "discarding all METADATA_BLOCKs due to invalid " + "block_size %d at idx %d, rem %d", block_size, index, + remainder); + } - g_value_unset (&value); - g_value_unset (&array); + g_value_unset (&value); + g_value_unset (&array); - /* The sample rate obtained from the stsd may not be accurate - * since it cannot represent rates greater than 65535Hz, so - * override that value with the sample rate from the - * METADATA_BLOCK_STREAMINFO block */ - stream->rate = - (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF; + /* The sample rate obtained from the stsd may not be accurate + * since it cannot represent rates greater than 65535Hz, so + * override that value with the sample rate from the + * METADATA_BLOCK_STREAMINFO block */ + CUR_STREAM (stream)->rate = + (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF; + } } } + break; } - break; - } - case FOURCC_sawb: - /* Fallthrough! */ - amrwb = TRUE; - case FOURCC_samr: - { - gint len = QT_UINT32 (stsd_data); + case FOURCC_sawb: + /* Fallthrough! */ + amrwb = TRUE; + case FOURCC_samr: + { + gint len = QT_UINT32 (stsd_entry_data); + + if (len > 0x24) { + GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24); + guint bitrate; + + gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24); + + /* If we have enough data, let's try to get the 'damr' atom. See + * the 3GPP container spec (26.244) for more details. */ + if ((len - 0x34) > 8 && + (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) { + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_MAXIMUM_BITRATE, bitrate, NULL); + } + + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } + break; + } + case FOURCC_mp4a: + { + /* mp4a atom withtout ESDS; Attempt to build codec data from atom */ + gint len = QT_UINT32 (stsd_entry_data); + + if (len >= 34) { + guint16 sound_version = QT_UINT16 (stsd_entry_data + 16); + + if (sound_version == 1) { + guint16 channels = QT_UINT16 (stsd_entry_data + 24); + guint32 time_scale = QT_UINT32 (stsd_entry_data + 30); + guint8 codec_data[2]; + GstBuffer *buf; + gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */ - if (len > 0x34) { - GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34); - guint bitrate; + gint sample_rate_index = + gst_codec_utils_aac_get_index_from_sample_rate (time_scale); - gst_buffer_fill (buf, 0, stsd_data + 0x34, len - 0x34); + /* build AAC codec data */ + codec_data[0] = profile << 3; + codec_data[0] |= ((sample_rate_index >> 1) & 0x7); + codec_data[1] = (sample_rate_index & 0x01) << 7; + codec_data[1] |= (channels & 0xF) << 3; - /* If we have enough data, let's try to get the 'damr' atom. See - * the 3GPP container spec (26.244) for more details. */ - if ((len - 0x34) > 8 && - (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_MAXIMUM_BITRATE, bitrate, NULL); + buf = gst_buffer_new_and_alloc (2); + gst_buffer_fill (buf, 0, codec_data, 2); + gst_caps_set_simple (entry->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } } - - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); + break; } - break; + default: + GST_INFO_OBJECT (qtdemux, + "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); + break; } - case FOURCC_mp4a: - { - /* mp4a atom withtout ESDS; Attempt to build codec data from atom */ - gint len = QT_UINT32 (stsd_data); - - if (len >= 50) { - guint16 sound_version = QT_UINT16 (stsd_data + 32); + } + GST_INFO_OBJECT (qtdemux, + "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, + GST_FOURCC_ARGS (fourcc), entry->caps); - if (sound_version == 1) { - guint16 channels = QT_UINT16 (stsd_data + 40); - guint32 time_scale = QT_UINT32 (stsd_data + 46); - guint8 codec_data[2]; - GstBuffer *buf; - gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */ + } else if (stream->subtype == FOURCC_strm) { + if (fourcc == FOURCC_rtsp) { + stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf); + } else { + GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fourcc)); + goto unknown_stream; + } + entry->sampled = TRUE; + } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text + || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) { - gint sample_rate_index = - gst_codec_utils_aac_get_index_from_sample_rate (time_scale); + entry->sampled = TRUE; + entry->sparse = TRUE; - /* build AAC codec data */ - codec_data[0] = profile << 3; - codec_data[0] |= ((sample_rate_index >> 1) & 0x7); - codec_data[1] = (sample_rate_index & 0x01) << 7; - codec_data[1] |= (channels & 0xF) << 3; + entry->caps = + qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data, + &codec); + if (codec) { + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_SUBTITLE_CODEC, codec, NULL); + g_free (codec); + codec = NULL; + } - buf = gst_buffer_new_and_alloc (2); - gst_buffer_fill (buf, 0, codec_data, 2); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - } + /* hunt for sort-of codec data */ + switch (fourcc) { + case FOURCC_mp4s: + { + GNode *mp4s = NULL; + GNode *esds = NULL; + + /* look for palette in a stsd->mp4s->esds sub-atom */ + mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s); + if (mp4s) + esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds); + if (esds == NULL) { + /* Invalid STSD */ + GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child"); + break; } + + gst_qtdemux_handle_esds (qtdemux, stream, entry, esds, + stream->stream_tags); break; } default: @@ -11199,94 +12104,47 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); break; } - } - GST_INFO_OBJECT (qtdemux, - "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, - GST_FOURCC_ARGS (fourcc), stream->caps); - - } else if (stream->subtype == FOURCC_strm) { - if (fourcc == FOURCC_rtsp) { - stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf); + GST_INFO_OBJECT (qtdemux, + "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, + GST_FOURCC_ARGS (fourcc), entry->caps); } else { - GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (fourcc)); - goto unknown_stream; - } - stream->sampled = TRUE; - } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text - || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) { + /* everything in 1 sample */ + entry->sampled = TRUE; - stream->sampled = TRUE; - stream->sparse = TRUE; + entry->caps = + qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data, + &codec); - stream->caps = - qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec); - if (codec) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_SUBTITLE_CODEC, codec, NULL); - g_free (codec); - codec = NULL; - } + if (entry->caps == NULL) + goto unknown_stream; - /* hunt for sort-of codec data */ - switch (fourcc) { - case FOURCC_mp4s: - { - GNode *mp4s = NULL; - GNode *esds = NULL; - - /* look for palette in a stsd->mp4s->esds sub-atom */ - mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s); - if (mp4s) - esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds); - if (esds == NULL) { - /* Invalid STSD */ - GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child"); - break; - } - - gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags); - break; + if (codec) { + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_SUBTITLE_CODEC, codec, NULL); + g_free (codec); + codec = NULL; } - default: - GST_INFO_OBJECT (qtdemux, - "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); - break; } - GST_INFO_OBJECT (qtdemux, - "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, - GST_FOURCC_ARGS (fourcc), stream->caps); - } else { - /* everything in 1 sample */ - stream->sampled = TRUE; - stream->caps = - qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec); + /* promote to sampled format */ + if (entry->fourcc == FOURCC_samr) { + /* force mono 8000 Hz for AMR */ + entry->sampled = TRUE; + entry->n_channels = 1; + entry->rate = 8000; + } else if (entry->fourcc == FOURCC_sawb) { + /* force mono 16000 Hz for AMR-WB */ + entry->sampled = TRUE; + entry->n_channels = 1; + entry->rate = 16000; + } else if (entry->fourcc == FOURCC_mp4a) { + entry->sampled = TRUE; + } - if (stream->caps == NULL) - goto unknown_stream; - if (codec) { - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, - GST_TAG_SUBTITLE_CODEC, codec, NULL); - g_free (codec); - codec = NULL; - } - } + stsd_entry_data += len; + remaining_stsd_len -= len; - /* promote to sampled format */ - if (stream->fourcc == FOURCC_samr) { - /* force mono 8000 Hz for AMR */ - stream->sampled = TRUE; - stream->n_channels = 1; - stream->rate = 8000; - } else if (stream->fourcc == FOURCC_sawb) { - /* force mono 16000 Hz for AMR-WB */ - stream->sampled = TRUE; - stream->n_channels = 1; - stream->rate = 16000; - } else if (stream->fourcc == FOURCC_mp4a) { - stream->sampled = TRUE; } /* collect sample information */ @@ -11324,13 +12182,13 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) /* convert ISO 639-2 code to ISO 639-1 */ lang_code = gst_tag_get_language_code (stream->lang_id); - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL); } /* Check for UDTA tags */ if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) { - qtdemux_parse_udta (qtdemux, stream->pending_tags, udta); + qtdemux_parse_udta (qtdemux, stream->stream_tags, udta); } /* now we are ready to add the stream */ @@ -11448,17 +12306,17 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) case FOURCC_soun: case FOURCC_vide: GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT, - qtdemux->streams[i]->caps); + CUR_STREAM (qtdemux->streams[i])->caps); /* retrieve bitrate, prefer avg then max */ bitrate = 0; - if (qtdemux->streams[i]->pending_tags) { - gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags, + if (qtdemux->streams[i]->stream_tags) { + gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags, GST_TAG_MAXIMUM_BITRATE, &bitrate); GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate); - gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags, + gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags, GST_TAG_NOMINAL_BITRATE, &bitrate); GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate); - gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags, + gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags, GST_TAG_BITRATE, &bitrate); GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate); } @@ -11498,10 +12356,12 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT ", Stream bitrate = %u", sys_bitrate, bitrate); - if (!stream->pending_tags) - stream->pending_tags = gst_tag_list_new_empty (); + if (!stream->stream_tags) + stream->stream_tags = gst_tag_list_new_empty (); + else + stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags); - gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, + gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, bitrate, NULL); } @@ -11518,7 +12378,7 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux) guint32 sample_num = 0; GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, - i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); + i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc)); if (qtdemux->fragmented) { /* need all moov samples first */ @@ -11577,7 +12437,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) GstTagList *list; GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, - i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); + i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc)); if ((stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) && stream->track_id == qtdemux->chapters_track_id) { @@ -11589,8 +12449,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) } /* now we have all info and can expose */ - list = stream->pending_tags; - stream->pending_tags = NULL; + list = stream->stream_tags; + stream->stream_tags = NULL; if (oldpad) oldpads = g_slist_prepend (oldpads, oldpad); if (!gst_qtdemux_add_stream (qtdemux, stream, list)) @@ -12824,12 +13684,7 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) gint version; /* make sure we have a usable taglist */ - if (!qtdemux->tag_list) { - qtdemux->tag_list = gst_tag_list_new_empty (); - gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL); - } else { - qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); - } + qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd); if (mvhd == NULL) { @@ -12920,13 +13775,7 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak); } - if (!qtdemux->tag_list) { - GST_DEBUG_OBJECT (qtdemux, "new tag list"); - qtdemux->tag_list = gst_tag_list_new_empty (); - gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL); - } else { - qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); - } + qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list); /* find tags */ udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta); @@ -12983,7 +13832,7 @@ read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out) /* this can change the codec originally present in @list */ static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, - GNode * esds, GstTagList * list) + QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list) { int len = QT_UINT32 (esds->data); guint8 *ptr = esds->data; @@ -13100,7 +13949,7 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, * profile_and_level_indication */ if (data_ptr != NULL && data_len >= 5 && GST_READ_UINT32_BE (data_ptr) == 0x000001b0) { - gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps, + gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps, data_ptr + 4, data_len - 4); } break; /* Nothing special needed here */ @@ -13122,16 +13971,16 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, channels = gst_codec_utils_aac_get_channels (data_ptr, data_len); if (channels > 0) - stream->n_channels = channels; + entry->n_channels = channels; rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len); if (rate > 0) - stream->rate = rate; + entry->rate = rate; } /* Set level and profile if possible */ if (data_ptr != NULL && data_len >= 2) { - gst_codec_utils_aac_caps_set_level_and_profile (stream->caps, + gst_codec_utils_aac_caps_set_level_and_profile (entry->caps, data_ptr, data_len); } else { const gchar *profile_str = NULL; @@ -13150,7 +13999,8 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, codec_data = map.data; rate_idx = - gst_codec_utils_aac_get_index_from_sample_rate (stream->rate); + gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM + (stream)->rate); switch (object_type_id) { case 0x66: @@ -13171,16 +14021,17 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, } codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1); - codec_data[1] = ((rate_idx & 0x1) << 7) | (stream->n_channels << 3); + codec_data[1] = + ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3); gst_buffer_unmap (buffer, &map); - gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER, - buffer, NULL); + gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data", + GST_TYPE_BUFFER, buffer, NULL); gst_buffer_unref (buffer); if (profile_str) { - gst_caps_set_simple (stream->caps, "profile", G_TYPE_STRING, - profile_str, NULL); + gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile", + G_TYPE_STRING, profile_str, NULL); } } break; @@ -13249,8 +14100,8 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, /* If we have a replacement caps, then change our caps for this stream */ if (caps) { - gst_caps_unref (stream->caps); - stream->caps = caps; + gst_caps_unref (entry->caps); + entry->caps = caps; } if (codec_name && list) @@ -13267,7 +14118,7 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds"); GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len); - gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER, + gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL); gst_buffer_unref (buffer); } @@ -13301,7 +14152,8 @@ _get_unknown_codec_name (const gchar * type, guint32 fourcc) static GstCaps * qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, - guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name) + QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * stsd_entry_data, gchar ** codec_name) { GstCaps *caps = NULL; GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; @@ -13333,7 +14185,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, case FOURCC_mjp2: _codec ("JPEG-2000"); /* override to what it should be according to spec, avoid palette_data */ - stream->bits_per_sample = 24; + entry->bits_per_sample = 24; caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL); break; case FOURCC_SVQ3: @@ -13357,7 +14209,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, { guint16 bps; - bps = QT_UINT16 (stsd_data + 98); + bps = QT_UINT16 (stsd_entry_data + 82); switch (bps) { case 15: format = GST_VIDEO_FORMAT_RGB15; @@ -13629,7 +14481,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, caps = gst_caps_new_empty_simple ("video/x-theora"); /* theora uses one byte of padding in the data stream because it does not * allow 0 sized packets while theora does */ - stream->padding = 1; + entry->padding = 1; break; case FOURCC_drac: _codec ("Dirac"); @@ -13687,6 +14539,10 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "4444xq", NULL); break; + case FOURCC_cfhd: + _codec ("GoPro CineForm"); + caps = gst_caps_from_string ("video/x-cineform"); + break; case FOURCC_vc_1: case FOURCC_ovc1: _codec ("VC-1"); @@ -13705,7 +14561,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, GstVideoInfo info; gst_video_info_init (&info); - gst_video_info_set_format (&info, format, stream->width, stream->height); + gst_video_info_set_format (&info, format, entry->width, entry->height); caps = gst_video_info_to_caps (&info); *codec_name = gst_pb_utils_get_codec_description (caps); @@ -13732,7 +14588,8 @@ round_up_pow2 (guint n) static GstCaps * qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, - guint32 fourcc, const guint8 * data, int len, gchar ** codec_name) + QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data, + int len, gchar ** codec_name) { GstCaps *caps; const GstStructure *s; @@ -13743,7 +14600,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc)); - depth = stream->bytes_per_packet * 8; + depth = entry->bytes_per_packet * 8; switch (fourcc) { case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'): @@ -13806,6 +14663,13 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, "layout", G_TYPE_STRING, "interleaved", NULL); stream->alignment = 4; break; + case GST_MAKE_FOURCC ('s', '1', '6', 'l'): + _codec ("Raw 16-bit PCM audio"); + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + "layout", G_TYPE_STRING, "interleaved", NULL); + stream->alignment = 2; + break; case FOURCC_ulaw: _codec ("Mu-law audio"); caps = gst_caps_new_empty_simple ("audio/x-mulaw"); @@ -13854,28 +14718,28 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, _codec ("EAC-3 audio"); caps = gst_caps_new_simple ("audio/x-eac3", "framed", G_TYPE_BOOLEAN, TRUE, NULL); - stream->sampled = TRUE; + entry->sampled = TRUE; break; case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode case FOURCC_ac_3: _codec ("AC-3 audio"); caps = gst_caps_new_simple ("audio/x-ac3", "framed", G_TYPE_BOOLEAN, TRUE, NULL); - stream->sampled = TRUE; + entry->sampled = TRUE; break; case GST_MAKE_FOURCC ('d', 't', 's', 'c'): case GST_MAKE_FOURCC ('D', 'T', 'S', ' '): _codec ("DTS audio"); caps = gst_caps_new_simple ("audio/x-dts", "framed", G_TYPE_BOOLEAN, TRUE, NULL); - stream->sampled = TRUE; + entry->sampled = TRUE; break; case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless _codec ("DTS-HD audio"); caps = gst_caps_new_simple ("audio/x-dts", "framed", G_TYPE_BOOLEAN, TRUE, NULL); - stream->sampled = TRUE; + entry->sampled = TRUE; break; case FOURCC_MAC3: _codec ("MACE-3"); @@ -13976,7 +14840,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, if (data && len >= 56) { depth = QT_UINT32 (data + 40); flags = QT_UINT32 (data + 44); - width = QT_UINT32 (data + 48) * 8 / stream->n_channels; + width = QT_UINT32 (data + 48) * 8 / entry->n_channels; } if ((flags & FLAG_IS_FLOAT) == 0) { if (depth == 0) @@ -14037,7 +14901,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, name = gst_structure_get_name (s); if (g_str_has_prefix (name, "audio/x-raw")) { stream->need_clip = TRUE; - stream->max_buffer_size = 4096 * stream->bytes_per_frame; + stream->max_buffer_size = 4096 * entry->bytes_per_frame; GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size); } return caps; @@ -14045,7 +14909,8 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, static GstCaps * qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, - guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name) + QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * stsd_entry_data, gchar ** codec_name) { GstCaps *caps; @@ -14083,7 +14948,8 @@ qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, static GstCaps * qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, - guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name) + QtDemuxStreamStsdEntry * entry, guint32 fourcc, + const guint8 * stsd_entry_data, gchar ** codec_name) { GstCaps *caps;