2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
4 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
5 * Copyright (C) <2007> Julien Moutte <julien@fluendo.com>
6 * Copyright (C) <2009> Tim-Philipp Müller <tim centricular net>
7 * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.com>
8 * Copyright (C) <2013> Sreerenj Balachandran <sreerenj.balachandran@intel.com>
9 * Copyright (C) <2013> Intel Corporation
10 * Copyright (C) <2014> Centricular Ltd
11 * Copyright (C) <2015> YouView TV Ltd.
12 * Copyright (C) <2016> British Broadcasting Corporation
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with this library; if not, write to the
26 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
31 * SECTION:element-qtdemux
33 * Demuxes a .mov file into raw or compressed audio and/or video streams.
35 * This element supports both push and pull-based scheduling, depending on the
36 * capabilities of the upstream elements.
39 * <title>Example launch line</title>
41 * gst-launch-1.0 filesrc location=test.mov ! qtdemux name=demux demux.audio_0 ! queue ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
42 * ]| Play (parse and decode) a .mov file and try to output it to
43 * an automatically detected soundcard and videosink. If the MOV file contains
44 * compressed audio or video data, this will only work if you have the
45 * right decoder elements/plugins installed.
53 #include "gst/gst-i18n-plugin.h"
55 #include <glib/gprintf.h>
56 #include <gst/tag/tag.h>
57 #include <gst/audio/audio.h>
58 #include <gst/video/video.h>
60 #include "qtatomparser.h"
61 #include "qtdemux_types.h"
62 #include "qtdemux_dump.h"
64 #include "descriptors.h"
65 #include "qtdemux_lang.h"
67 #include "qtpalette.h"
69 #include "gst/riff/riff-media.h"
70 #include "gst/riff/riff-read.h"
72 #include <gst/pbutils/pbutils.h>
79 #include <gst/math-compat.h>
85 /* max. size considered 'sane' for non-mdat atoms */
86 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
88 /* if the sample index is larger than this, something is likely wrong */
89 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
91 /* For converting qt creation times to unix epoch times */
92 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
93 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
94 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
95 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
97 #define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))
99 #define STREAM_IS_EOS(s) (s->time_position == GST_CLOCK_TIME_NONE)
101 #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
103 GST_DEBUG_CATEGORY (qtdemux_debug);
105 typedef struct _QtDemuxSegment QtDemuxSegment;
106 typedef struct _QtDemuxSample QtDemuxSample;
108 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
110 struct _QtDemuxSample
113 gint32 pts_offset; /* Add this value to timestamp to get the pts */
115 guint64 timestamp; /* DTS In mov time */
116 guint32 duration; /* In mov time */
117 gboolean keyframe; /* TRUE when this packet is a keyframe */
120 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
121 typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
123 struct _QtDemuxSphericalMetadata
125 gboolean is_spherical;
126 gboolean is_stitched;
127 char *stitching_software;
128 char *projection_type;
131 int init_view_heading;
135 int full_pano_width_pixels;
136 int full_pano_height_pixels;
137 int cropped_area_image_width;
138 int cropped_area_image_height;
139 int cropped_area_left;
140 int cropped_area_top;
141 QTDEMUX_AMBISONIC_TYPE ambisonic_type;
142 QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
143 QTDEMUX_AMBISONIC_ORDER ambisonic_order;
146 QtDemuxSphericalMetadata *spherical_metadata;
147 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
149 /* Macros for converting to/from timescale */
150 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
151 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
153 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
154 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
156 /* timestamp is the DTS */
157 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
158 /* timestamp + offset + cslg_shift is the outgoing PTS */
159 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
160 /* timestamp + offset is the PTS used for internal seek calcuations */
161 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
162 /* timestamp + duration - dts is the duration */
163 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
165 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
168 * Quicktime has tracks and segments. A track is a continuous piece of
169 * multimedia content. The track is not always played from start to finish but
170 * instead, pieces of the track are 'cut out' and played in sequence. This is
171 * what the segments do.
173 * Inside the track we have keyframes (K) and delta frames. The track has its
174 * own timing, which starts from 0 and extends to end. The position in the track
175 * is called the media_time.
177 * The segments now describe the pieces that should be played from this track
178 * and are basically tuples of media_time/duration/rate entries. We can have
179 * multiple segments and they are all played after one another. An example:
181 * segment 1: media_time: 1 second, duration: 1 second, rate 1
182 * segment 2: media_time: 3 second, duration: 2 second, rate 2
184 * To correctly play back this track, one must play: 1 second of media starting
185 * from media_time 1 followed by 2 seconds of media starting from media_time 3
188 * Each of the segments will be played at a specific time, the first segment at
189 * time 0, the second one after the duration of the first one, etc.. Note that
190 * the time in resulting playback is not identical to the media_time of the
193 * Visually, assuming the track has 4 second of media_time:
196 * .-----------------------------------------------------------.
197 * track: | K.....K.........K........K.......K.......K...........K... |
198 * '-----------------------------------------------------------'
200 * .------------^ ^ .----------^ ^
201 * / .-------------' / .------------------'
203 * .--------------. .--------------.
204 * | segment 1 | | segment 2 |
205 * '--------------' '--------------'
207 * The challenge here is to cut out the right pieces of the track for each of
208 * the playback segments. This fortunately can easily be done with the SEGMENT
209 * events of GStreamer.
211 * For playback of segment 1, we need to provide the decoder with the keyframe
212 * (a), in the above figure, but we must instruct it only to output the decoded
213 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
214 * position set to the time of the segment: 0.
216 * We then proceed to push data from keyframe (a) to frame (b). The decoder
217 * decodes but clips all before media_time 1.
219 * After finishing a segment, we push out a new SEGMENT event with the clipping
220 * boundaries of the new data.
222 * This is a good usecase for the GStreamer accumulated SEGMENT events.
225 struct _QtDemuxSegment
227 /* global time and duration, all gst time */
229 GstClockTime stop_time;
230 GstClockTime duration;
231 /* media time of trak, all gst time */
232 GstClockTime media_start;
233 GstClockTime media_stop;
235 /* Media start time in trak timescale units */
236 guint32 trak_media_start;
239 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
241 /* Used with fragmented MP4 files (mfra atom) */
246 } QtDemuxRandomAccessEntry;
248 typedef struct _QtDemuxStreamStsdEntry
259 /* Numerator/denominator framerate */
262 GstVideoColorimetry colorimetry;
263 guint16 bits_per_sample;
264 guint16 color_table_id;
265 GstMemory *rgb8_palette;
266 guint interlace_mode;
272 guint samples_per_packet;
273 guint samples_per_frame;
274 guint bytes_per_packet;
275 guint bytes_per_sample;
276 guint bytes_per_frame;
279 /* if we use chunks or samples */
283 } QtDemuxStreamStsdEntry;
285 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
287 struct _QtDemuxStream
291 QtDemuxStreamStsdEntry *stsd_entries;
292 guint stsd_entries_length;
293 guint cur_stsd_entry_index;
298 gboolean new_caps; /* If TRUE, caps need to be generated (by
299 * calling _configure_stream()) This happens
300 * for MSS and fragmented streams */
302 gboolean new_stream; /* signals that a stream_start is required */
303 gboolean on_keyframe; /* if this stream last pushed buffer was a
304 * keyframe. This is important to identify
305 * where to stop pushing buffers after a
306 * segment stop time */
308 /* if the stream has a redirect URI in its headers, we store it here */
315 guint64 duration; /* in timescale units */
319 gchar lang_id[4]; /* ISO 639-2T language code */
323 QtDemuxSample *samples;
324 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
325 guint32 first_duration; /* duration in timescale of first sample, used for figuring out
327 guint32 n_samples_moof; /* sample count in a moof */
328 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
329 * the framerate of fragmented format stream */
330 guint64 duration_last_moof;
332 guint32 offset_in_sample; /* Offset in the current sample, used for
333 * streams which have got exceedingly big
334 * sample size (such as 24s of raw audio).
335 * Only used when max_buffer_size is non-NULL */
336 guint32 max_buffer_size; /* Maximum allowed size for output buffers.
337 * Currently only set for raw audio streams*/
345 gboolean use_allocator;
346 GstAllocator *allocator;
347 GstAllocationParams params;
351 /* when a discontinuity is pending */
354 /* list of buffers to push first */
357 /* if we need to clip this buffer. This is only needed for uncompressed
361 /* buffer needs some custom processing, e.g. subtitles */
362 gboolean need_process;
364 /* current position */
365 guint32 segment_index;
366 guint32 sample_index;
367 GstClockTime time_position; /* in gst time */
368 guint64 accumulated_base;
370 /* the Gst segment we are processing out, used for clipping */
373 /* quicktime segments */
375 QtDemuxSegment *segments;
376 gboolean dummy_segment;
381 GstTagList *stream_tags;
382 gboolean send_global_tags;
384 GstEvent *pending_event;
394 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
398 GstByteReader co_chunk;
400 guint32 current_chunk;
402 guint32 samples_per_chunk;
403 guint32 stsd_sample_description_id;
404 guint32 stco_sample_index;
406 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
409 guint32 n_samples_per_chunk;
410 guint32 stsc_chunk_index;
411 guint32 stsc_sample_index;
412 guint64 chunk_offset;
415 guint32 stts_samples;
416 guint32 n_sample_times;
417 guint32 stts_sample_index;
419 guint32 stts_duration;
421 gboolean stss_present;
422 guint32 n_sample_syncs;
425 gboolean stps_present;
426 guint32 n_sample_partial_syncs;
428 QtDemuxRandomAccessEntry *ra_entries;
431 const QtDemuxRandomAccessEntry *pending_seek;
434 gboolean ctts_present;
435 guint32 n_composition_times;
437 guint32 ctts_sample_index;
445 gboolean parsed_trex;
446 guint32 def_sample_description_index; /* index is 1-based */
447 guint32 def_sample_duration;
448 guint32 def_sample_size;
449 guint32 def_sample_flags;
453 /* stereoscopic video streams */
454 GstVideoMultiviewMode multiview_mode;
455 GstVideoMultiviewFlags multiview_flags;
457 /* protected streams */
459 guint32 protection_scheme_type;
460 guint32 protection_scheme_version;
461 gpointer protection_scheme_info; /* specific to the protection scheme */
462 GQueue protection_scheme_event_queue;
465 /* Contains properties and cryptographic info for a set of samples from a
466 * track protected using Common Encryption (cenc) */
467 struct _QtDemuxCencSampleSetInfo
469 GstStructure *default_properties;
471 /* @crypto_info holds one GstStructure per sample */
472 GPtrArray *crypto_info;
476 qt_demux_state_string (enum QtDemuxState state)
479 case QTDEMUX_STATE_INITIAL:
481 case QTDEMUX_STATE_HEADER:
483 case QTDEMUX_STATE_MOVIE:
485 case QTDEMUX_STATE_BUFFER_MDAT:
486 return "<BUFFER_MDAT>";
492 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
493 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
494 guint32 fourcc, GstByteReader * parser);
495 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
496 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
497 guint32 fourcc, GstByteReader * parser);
499 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
501 static GstStaticPadTemplate gst_qtdemux_sink_template =
502 GST_STATIC_PAD_TEMPLATE ("sink",
505 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
509 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
510 GST_STATIC_PAD_TEMPLATE ("video_%u",
513 GST_STATIC_CAPS_ANY);
515 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
516 GST_STATIC_PAD_TEMPLATE ("audio_%u",
519 GST_STATIC_CAPS_ANY);
521 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
522 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
525 GST_STATIC_CAPS_ANY);
527 #define gst_qtdemux_parent_class parent_class
528 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
530 static void gst_qtdemux_dispose (GObject * object);
533 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
534 GstClockTime media_time);
536 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
537 QtDemuxStream * str, gint64 media_offset);
540 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
541 static GstIndex *gst_qtdemux_get_index (GstElement * element);
543 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
544 GstStateChange transition);
545 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
546 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
547 GstObject * parent, GstPadMode mode, gboolean active);
549 static void gst_qtdemux_loop (GstPad * pad);
550 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
552 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
554 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
555 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
556 QtDemuxStream * stream);
557 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
558 QtDemuxStream * stream);
559 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
562 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
563 const guint8 * buffer, guint length);
564 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
565 const guint8 * buffer, guint length);
566 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
567 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
570 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
571 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
573 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
574 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
575 const guint8 * stsd_entry_data, gchar ** codec_name);
576 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
577 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
578 const guint8 * data, int len, gchar ** codec_name);
579 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
580 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
581 gchar ** codec_name);
582 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
583 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
584 const guint8 * stsd_entry_data, gchar ** codec_name);
586 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
587 QtDemuxStream * stream, guint32 n);
588 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
589 static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
590 QtDemuxStream * stream);
591 static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux,
592 QtDemuxStream * stream);
593 static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index);
594 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
595 static void qtdemux_do_allocation (GstQTDemux * qtdemux,
596 QtDemuxStream * stream);
597 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
598 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
599 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
600 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
601 GstClockTime * _start, GstClockTime * _stop);
602 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
603 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
605 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
606 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
608 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
610 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
611 QtDemuxStream * stream, guint sample_index);
612 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
614 static void qtdemux_gst_structure_free (GstStructure * gststructure);
616 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
617 static void gst_tag_register_spherical_tags (void);
618 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
621 gst_qtdemux_class_init (GstQTDemuxClass * klass)
623 GObjectClass *gobject_class;
624 GstElementClass *gstelement_class;
626 gobject_class = (GObjectClass *) klass;
627 gstelement_class = (GstElementClass *) klass;
629 parent_class = g_type_class_peek_parent (klass);
631 gobject_class->dispose = gst_qtdemux_dispose;
633 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
635 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
636 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
639 gst_tag_register_musicbrainz_tags ();
641 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
642 gst_tag_register_spherical_tags ();
643 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
645 gst_element_class_add_static_pad_template (gstelement_class,
646 &gst_qtdemux_sink_template);
647 gst_element_class_add_static_pad_template (gstelement_class,
648 &gst_qtdemux_videosrc_template);
649 gst_element_class_add_static_pad_template (gstelement_class,
650 &gst_qtdemux_audiosrc_template);
651 gst_element_class_add_static_pad_template (gstelement_class,
652 &gst_qtdemux_subsrc_template);
653 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
655 "Demultiplex a QuickTime file into audio and video streams",
656 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
658 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
663 gst_qtdemux_init (GstQTDemux * qtdemux)
666 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
667 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
668 gst_pad_set_activatemode_function (qtdemux->sinkpad,
669 qtdemux_sink_activate_mode);
670 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
671 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
672 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
674 qtdemux->state = QTDEMUX_STATE_INITIAL;
675 qtdemux->pullbased = FALSE;
676 qtdemux->posted_redirect = FALSE;
677 qtdemux->neededbytes = 16;
679 qtdemux->adapter = gst_adapter_new ();
681 qtdemux->first_mdat = -1;
682 qtdemux->got_moov = FALSE;
683 qtdemux->mdatoffset = -1;
684 qtdemux->mdatbuffer = NULL;
685 qtdemux->restoredata_buffer = NULL;
686 qtdemux->restoredata_offset = -1;
687 qtdemux->fragment_start = -1;
688 qtdemux->fragment_start_offset = -1;
689 qtdemux->media_caps = NULL;
690 qtdemux->exposed = FALSE;
691 qtdemux->mss_mode = FALSE;
692 qtdemux->pending_newsegment = NULL;
693 qtdemux->upstream_format_is_time = FALSE;
694 qtdemux->have_group_id = FALSE;
695 qtdemux->group_id = G_MAXUINT;
696 qtdemux->cenc_aux_info_offset = 0;
697 qtdemux->cenc_aux_info_sizes = NULL;
698 qtdemux->cenc_aux_sample_count = 0;
699 qtdemux->protection_system_ids = NULL;
700 g_queue_init (&qtdemux->protection_event_queue);
701 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
702 qtdemux->tag_list = gst_tag_list_new_empty ();
703 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
704 qtdemux->flowcombiner = gst_flow_combiner_new ();
706 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
707 spherical_metadata = (QtDemuxSphericalMetadata *)
708 malloc (sizeof (QtDemuxSphericalMetadata));
710 if (spherical_metadata) {
711 spherical_metadata->is_spherical = FALSE;
712 spherical_metadata->is_stitched = FALSE;
713 spherical_metadata->stitching_software = NULL;
714 spherical_metadata->projection_type = NULL;
715 spherical_metadata->stereo_mode = NULL;
716 spherical_metadata->source_count = 0;
717 spherical_metadata->init_view_heading = 0;
718 spherical_metadata->init_view_pitch = 0;
719 spherical_metadata->init_view_roll = 0;
720 spherical_metadata->timestamp = 0;
721 spherical_metadata->full_pano_width_pixels = 0;
722 spherical_metadata->full_pano_height_pixels = 0;
723 spherical_metadata->cropped_area_image_width = 0;
724 spherical_metadata->cropped_area_image_height = 0;
725 spherical_metadata->cropped_area_left = 0;
726 spherical_metadata->cropped_area_top = 0;
727 spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
728 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
729 spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
731 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
733 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
737 gst_qtdemux_dispose (GObject * object)
739 GstQTDemux *qtdemux = GST_QTDEMUX (object);
741 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
742 if (spherical_metadata->stitching_software)
743 free(spherical_metadata->stitching_software);
744 if (spherical_metadata->projection_type)
745 free(spherical_metadata->projection_type);
746 if (spherical_metadata->stereo_mode)
747 free(spherical_metadata->stereo_mode);
748 if (spherical_metadata)
749 free(spherical_metadata);
750 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
752 if (qtdemux->adapter) {
753 g_object_unref (G_OBJECT (qtdemux->adapter));
754 qtdemux->adapter = NULL;
756 gst_tag_list_unref (qtdemux->tag_list);
757 gst_flow_combiner_free (qtdemux->flowcombiner);
758 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
760 g_queue_clear (&qtdemux->protection_event_queue);
762 g_free (qtdemux->cenc_aux_info_sizes);
763 qtdemux->cenc_aux_info_sizes = NULL;
765 G_OBJECT_CLASS (parent_class)->dispose (object);
769 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
771 if (qtdemux->posted_redirect) {
772 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
773 (_("This file contains no playable streams.")),
774 ("no known streams found, a redirect message has been posted"));
776 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
777 (_("This file contains no playable streams.")),
778 ("no known streams found"));
783 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
785 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
786 mem, size, 0, size, mem, free_func);
790 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
797 if (G_UNLIKELY (size == 0)) {
799 GstBuffer *tmp = NULL;
801 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
802 if (ret != GST_FLOW_OK)
805 gst_buffer_map (tmp, &map, GST_MAP_READ);
806 size = QT_UINT32 (map.data);
807 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
809 gst_buffer_unmap (tmp, &map);
810 gst_buffer_unref (tmp);
813 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
814 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
815 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
816 /* we're pulling header but already got most interesting bits,
817 * so never mind the rest (e.g. tags) (that much) */
818 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
822 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
823 (_("This file is invalid and cannot be played.")),
824 ("atom has bogus size %" G_GUINT64_FORMAT, size));
825 return GST_FLOW_ERROR;
829 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
831 if (G_UNLIKELY (flow != GST_FLOW_OK))
834 bsize = gst_buffer_get_size (*buf);
835 /* Catch short reads - we don't want any partial atoms */
836 if (G_UNLIKELY (bsize < size)) {
837 GST_WARNING_OBJECT (qtdemux,
838 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
839 gst_buffer_unref (*buf);
849 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
850 GstFormat src_format, gint64 src_value, GstFormat dest_format,
854 QtDemuxStream *stream = gst_pad_get_element_private (pad);
857 if (stream->subtype != FOURCC_vide) {
862 switch (src_format) {
863 case GST_FORMAT_TIME:
864 switch (dest_format) {
865 case GST_FORMAT_BYTES:{
866 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
872 *dest_value = stream->samples[index].offset;
874 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
875 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
876 GST_TIME_ARGS (src_value), *dest_value);
884 case GST_FORMAT_BYTES:
885 switch (dest_format) {
886 case GST_FORMAT_TIME:{
888 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
897 QTSTREAMTIME_TO_GSTTIME (stream,
898 stream->samples[index].timestamp);
899 GST_DEBUG_OBJECT (qtdemux,
900 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
901 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
920 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
922 gboolean res = FALSE;
924 *duration = GST_CLOCK_TIME_NONE;
926 if (qtdemux->duration != 0 &&
927 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
928 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
931 *duration = GST_CLOCK_TIME_NONE;
938 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
941 gboolean res = FALSE;
942 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
944 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
946 switch (GST_QUERY_TYPE (query)) {
947 case GST_QUERY_POSITION:{
950 gst_query_parse_position (query, &fmt, NULL);
951 if (fmt == GST_FORMAT_TIME
952 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
953 gst_query_set_position (query, GST_FORMAT_TIME,
954 qtdemux->segment.position);
959 case GST_QUERY_DURATION:{
962 gst_query_parse_duration (query, &fmt, NULL);
963 if (fmt == GST_FORMAT_TIME) {
964 /* First try to query upstream */
965 res = gst_pad_query_default (pad, parent, query);
967 GstClockTime duration;
968 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
969 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
976 case GST_QUERY_CONVERT:{
977 GstFormat src_fmt, dest_fmt;
978 gint64 src_value, dest_value = 0;
980 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
982 res = gst_qtdemux_src_convert (qtdemux, pad,
983 src_fmt, src_value, dest_fmt, &dest_value);
985 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
990 case GST_QUERY_FORMATS:
991 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
994 case GST_QUERY_SEEKING:{
998 /* try upstream first */
999 res = gst_pad_query_default (pad, parent, query);
1002 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
1003 if (fmt == GST_FORMAT_TIME) {
1004 GstClockTime duration;
1006 gst_qtdemux_get_duration (qtdemux, &duration);
1008 if (!qtdemux->pullbased) {
1011 /* we might be able with help from upstream */
1013 q = gst_query_new_seeking (GST_FORMAT_BYTES);
1014 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
1015 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
1016 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
1018 gst_query_unref (q);
1020 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
1026 case GST_QUERY_SEGMENT:
1031 format = qtdemux->segment.format;
1034 gst_segment_to_stream_time (&qtdemux->segment, format,
1035 qtdemux->segment.start);
1036 if ((stop = qtdemux->segment.stop) == -1)
1037 stop = qtdemux->segment.duration;
1039 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
1041 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
1046 res = gst_pad_query_default (pad, parent, query);
1054 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
1056 if (G_LIKELY (stream->pad)) {
1057 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
1058 GST_DEBUG_PAD_NAME (stream->pad));
1060 if (!gst_tag_list_is_empty (stream->stream_tags)) {
1061 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
1062 stream->stream_tags);
1063 gst_pad_push_event (stream->pad,
1064 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
1065 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
1066 /* post message qtdemux tag (for early recive application) */
1067 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1068 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
1069 gst_tag_list_copy (stream->stream_tags)));
1073 if (G_UNLIKELY (stream->send_global_tags)) {
1074 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1076 gst_pad_push_event (stream->pad,
1077 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1078 stream->send_global_tags = FALSE;
1083 /* push event on all source pads; takes ownership of the event */
1085 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1088 gboolean has_valid_stream = FALSE;
1089 GstEventType etype = GST_EVENT_TYPE (event);
1091 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1092 GST_EVENT_TYPE_NAME (event));
1094 for (n = 0; n < qtdemux->n_streams; n++) {
1096 QtDemuxStream *stream = qtdemux->streams[n];
1097 GST_DEBUG_OBJECT (qtdemux, "pushing on pad %i", n);
1099 if ((pad = stream->pad)) {
1100 has_valid_stream = TRUE;
1102 if (etype == GST_EVENT_EOS) {
1103 /* let's not send twice */
1104 if (stream->sent_eos)
1106 stream->sent_eos = TRUE;
1109 gst_pad_push_event (pad, gst_event_ref (event));
1113 gst_event_unref (event);
1115 /* if it is EOS and there are no pads, post an error */
1116 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1117 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1121 /* push a pending newsegment event, if any from the streaming thread */
1123 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
1125 if (qtdemux->pending_newsegment) {
1126 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
1127 qtdemux->pending_newsegment = NULL;
1137 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1139 if ((gint64) s1->timestamp + s1->pts_offset > *media_time)
1141 if ((gint64) s1->timestamp + s1->pts_offset == *media_time)
1147 /* find the index of the sample that includes the data for @media_time using a
1148 * binary search. Only to be called in optimized cases of linear search below.
1150 * Returns the index of the sample.
1153 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1156 QtDemuxSample *result;
1159 /* convert media_time to mov format */
1161 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1163 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1164 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1165 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1167 if (G_LIKELY (result))
1168 index = result - str->samples;
1177 /* find the index of the sample that includes the data for @media_offset using a
1180 * Returns the index of the sample.
1183 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1184 QtDemuxStream * str, gint64 media_offset)
1186 QtDemuxSample *result = str->samples;
1189 if (result == NULL || str->n_samples == 0)
1192 if (media_offset == result->offset)
1196 while (index < str->n_samples - 1) {
1197 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1200 if (media_offset < result->offset)
1211 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1216 /* find the index of the sample that includes the data for @media_time using a
1217 * linear search, and keeping in mind that not all samples may have been parsed
1218 * yet. If possible, it will delegate to binary search.
1220 * Returns the index of the sample.
1223 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1224 GstClockTime media_time)
1228 QtDemuxSample *sample;
1230 /* convert media_time to mov format */
1232 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1234 sample = str->samples;
1235 if (mov_time == sample->timestamp + sample->pts_offset)
1238 /* use faster search if requested time in already parsed range */
1239 sample = str->samples + str->stbl_index;
1240 if (str->stbl_index >= 0 &&
1241 mov_time <= (sample->timestamp + sample->pts_offset))
1242 return gst_qtdemux_find_index (qtdemux, str, media_time);
1244 while (index < str->n_samples - 1) {
1245 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1248 sample = str->samples + index + 1;
1249 if (mov_time < (sample->timestamp + sample->pts_offset))
1259 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1264 /* find the index of the keyframe needed to decode the sample at @index
1265 * of stream @str, or of a subsequent keyframe (depending on @next)
1267 * Returns the index of the keyframe.
1270 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1271 guint32 index, gboolean next)
1273 guint32 new_index = index;
1275 if (index >= str->n_samples) {
1276 new_index = str->n_samples;
1280 /* all keyframes, return index */
1281 if (str->all_keyframe) {
1286 /* else search until we have a keyframe */
1287 while (new_index < str->n_samples) {
1288 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1291 if (str->samples[new_index].keyframe)
1303 if (new_index == str->n_samples) {
1304 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1309 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1310 "gave %u", next ? "after" : "before", index, new_index);
1317 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1322 /* find the segment for @time_position for @stream
1324 * Returns the index of the segment containing @time_position.
1325 * Returns the last segment and sets the @eos variable to TRUE
1326 * if the time is beyond the end. @eos may be NULL
1329 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1330 GstClockTime time_position)
1335 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1336 GST_TIME_ARGS (time_position));
1339 for (i = 0; i < stream->n_segments; i++) {
1340 QtDemuxSegment *segment = &stream->segments[i];
1342 GST_LOG_OBJECT (stream->pad,
1343 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1344 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1346 /* For the last segment we include stop_time in the last segment */
1347 if (i < stream->n_segments - 1) {
1348 if (segment->time <= time_position && time_position < segment->stop_time) {
1349 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1354 /* Last segment always matches */
1362 /* move the stream @str to the sample position @index.
1364 * Updates @str->sample_index and marks discontinuity if needed.
1367 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1370 /* no change needed */
1371 if (index == str->sample_index)
1374 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1377 /* position changed, we have a discont */
1378 str->sample_index = index;
1379 str->offset_in_sample = 0;
1380 /* Each time we move in the stream we store the position where we are
1382 str->from_sample = index;
1383 str->discont = TRUE;
1387 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1388 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1391 gint64 min_byte_offset = -1;
1394 min_offset = desired_time;
1396 /* for each stream, find the index of the sample in the segment
1397 * and move back to the previous keyframe. */
1398 for (n = 0; n < qtdemux->n_streams; n++) {
1400 guint32 index, kindex;
1402 GstClockTime media_start;
1403 GstClockTime media_time;
1404 GstClockTime seg_time;
1405 QtDemuxSegment *seg;
1406 gboolean empty_segment = FALSE;
1408 str = qtdemux->streams[n];
1410 if (CUR_STREAM (str)->sparse && !use_sparse)
1413 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1414 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1416 /* get segment and time in the segment */
1417 seg = &str->segments[seg_idx];
1418 seg_time = (desired_time - seg->time) * seg->rate;
1420 while (QTSEGMENT_IS_EMPTY (seg)) {
1422 empty_segment = TRUE;
1423 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1426 if (seg_idx == str->n_segments)
1428 seg = &str->segments[seg_idx];
1431 if (seg_idx == str->n_segments) {
1432 /* FIXME track shouldn't have the last segment as empty, but if it
1433 * happens we better handle it */
1437 /* get the media time in the segment */
1438 media_start = seg->media_start + seg_time;
1440 /* get the index of the sample with media time */
1441 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1442 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1443 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1444 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1447 /* shift to next frame if we are looking for next keyframe */
1448 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1449 && index < str->stbl_index)
1452 if (!empty_segment) {
1453 /* find previous keyframe */
1454 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1456 /* we will settle for one before if none found after */
1457 if (next && kindex == -1)
1458 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1460 /* if the keyframe is at a different position, we need to update the
1461 * requested seek time */
1462 if (index != kindex) {
1465 /* get timestamp of keyframe */
1466 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1467 GST_DEBUG_OBJECT (qtdemux,
1468 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1469 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1470 str->samples[kindex].offset);
1472 /* keyframes in the segment get a chance to change the
1473 * desired_offset. keyframes out of the segment are
1475 if (media_time >= seg->media_start) {
1476 GstClockTime seg_time;
1478 /* this keyframe is inside the segment, convert back to
1480 seg_time = (media_time - seg->media_start) + seg->time;
1481 if ((!next && (seg_time < min_offset)) ||
1482 (next && (seg_time > min_offset)))
1483 min_offset = seg_time;
1488 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1489 min_byte_offset = str->samples[index].offset;
1493 *key_time = min_offset;
1495 *key_offset = min_byte_offset;
1499 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1500 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1504 g_return_val_if_fail (format != NULL, FALSE);
1505 g_return_val_if_fail (cur != NULL, FALSE);
1506 g_return_val_if_fail (stop != NULL, FALSE);
1508 if (*format == GST_FORMAT_TIME)
1512 if (cur_type != GST_SEEK_TYPE_NONE)
1513 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1514 if (res && stop_type != GST_SEEK_TYPE_NONE)
1515 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1518 *format = GST_FORMAT_TIME;
1523 /* perform seek in push based mode:
1524 find BYTE position to move to based on time and delegate to upstream
1527 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1532 GstSeekType cur_type, stop_type;
1533 gint64 cur, stop, key_cur;
1536 gint64 original_stop;
1539 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1541 gst_event_parse_seek (event, &rate, &format, &flags,
1542 &cur_type, &cur, &stop_type, &stop);
1543 seqnum = gst_event_get_seqnum (event);
1545 /* only forward streaming and seeking is possible */
1547 goto unsupported_seek;
1549 /* convert to TIME if needed and possible */
1550 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1554 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1555 * the original stop position to use when upstream pushes the new segment
1557 original_stop = stop;
1560 /* find reasonable corresponding BYTE position,
1561 * also try to mind about keyframes, since we can not go back a bit for them
1563 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1564 * mostly just work, but let's not yet boldly go there ... */
1565 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1570 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1571 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1574 GST_OBJECT_LOCK (qtdemux);
1575 qtdemux->seek_offset = byte_cur;
1576 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1577 qtdemux->push_seek_start = cur;
1579 qtdemux->push_seek_start = key_cur;
1582 if (stop_type == GST_SEEK_TYPE_NONE) {
1583 qtdemux->push_seek_stop = qtdemux->segment.stop;
1585 qtdemux->push_seek_stop = original_stop;
1587 GST_OBJECT_UNLOCK (qtdemux);
1589 /* BYTE seek event */
1590 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1592 gst_event_set_seqnum (event, seqnum);
1593 res = gst_pad_push_event (qtdemux->sinkpad, event);
1600 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1606 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1611 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1616 /* perform the seek.
1618 * We set all segment_indexes in the streams to unknown and
1619 * adjust the time_position to the desired position. this is enough
1620 * to trigger a segment switch in the streaming thread to start
1621 * streaming from the desired position.
1623 * Keyframe seeking is a little more complicated when dealing with
1624 * segments. Ideally we want to move to the previous keyframe in
1625 * the segment but there might not be a keyframe in the segment. In
1626 * fact, none of the segments could contain a keyframe. We take a
1627 * practical approach: seek to the previous keyframe in the segment,
1628 * if there is none, seek to the beginning of the segment.
1630 * Called with STREAM_LOCK
1633 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1634 guint32 seqnum, GstSeekFlags flags)
1636 gint64 desired_offset;
1639 desired_offset = segment->position;
1641 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1642 GST_TIME_ARGS (desired_offset));
1644 /* may not have enough fragmented info to do this adjustment,
1645 * and we can't scan (and probably should not) at this time with
1646 * possibly flushing upstream */
1647 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1649 gboolean next, before, after;
1651 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1652 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1653 next = after && !before;
1654 if (segment->rate < 0)
1657 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1659 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1660 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1661 desired_offset = min_offset;
1664 /* and set all streams to the final position */
1665 gst_flow_combiner_reset (qtdemux->flowcombiner);
1666 qtdemux->segment_seqnum = seqnum;
1667 for (n = 0; n < qtdemux->n_streams; n++) {
1668 QtDemuxStream *stream = qtdemux->streams[n];
1670 stream->time_position = desired_offset;
1671 stream->accumulated_base = 0;
1672 stream->sample_index = -1;
1673 stream->offset_in_sample = 0;
1674 stream->segment_index = -1;
1675 stream->sent_eos = FALSE;
1677 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1678 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1680 segment->position = desired_offset;
1681 segment->time = desired_offset;
1682 if (segment->rate >= 0) {
1683 segment->start = desired_offset;
1685 /* we stop at the end */
1686 if (segment->stop == -1)
1687 segment->stop = segment->duration;
1689 segment->stop = desired_offset;
1692 if (qtdemux->fragmented)
1693 qtdemux->fragmented_seek_pending = TRUE;
1698 /* do a seek in pull based mode */
1700 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1705 GstSeekType cur_type, stop_type;
1709 GstSegment seeksegment;
1711 GstEvent *flush_event;
1715 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1717 gst_event_parse_seek (event, &rate, &format, &flags,
1718 &cur_type, &cur, &stop_type, &stop);
1719 seqnum = gst_event_get_seqnum (event);
1721 /* we have to have a format as the segment format. Try to convert
1723 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1727 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1729 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1733 flush = flags & GST_SEEK_FLAG_FLUSH;
1735 /* stop streaming, either by flushing or by pausing the task */
1737 flush_event = gst_event_new_flush_start ();
1739 gst_event_set_seqnum (flush_event, seqnum);
1740 /* unlock upstream pull_range */
1741 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1742 /* make sure out loop function exits */
1743 gst_qtdemux_push_event (qtdemux, flush_event);
1745 /* non flushing seek, pause the task */
1746 gst_pad_pause_task (qtdemux->sinkpad);
1749 /* wait for streaming to finish */
1750 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1752 /* copy segment, we need this because we still need the old
1753 * segment when we close the current segment. */
1754 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1757 /* configure the segment with the seek variables */
1758 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1759 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1760 cur_type, cur, stop_type, stop, &update)) {
1762 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1764 /* now do the seek */
1765 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1768 /* now do the seek */
1769 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1772 /* prepare for streaming again */
1774 flush_event = gst_event_new_flush_stop (TRUE);
1776 gst_event_set_seqnum (flush_event, seqnum);
1778 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1779 gst_qtdemux_push_event (qtdemux, flush_event);
1782 /* commit the new segment */
1783 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1785 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1786 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1787 qtdemux->segment.format, qtdemux->segment.position);
1789 gst_message_set_seqnum (msg, seqnum);
1790 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1793 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1794 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1795 qtdemux->sinkpad, NULL);
1797 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1804 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1810 qtdemux_ensure_index (GstQTDemux * qtdemux)
1814 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1816 /* Build complete index */
1817 for (i = 0; i < qtdemux->n_streams; i++) {
1818 QtDemuxStream *stream = qtdemux->streams[i];
1820 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1828 GST_LOG_OBJECT (qtdemux,
1829 "Building complete index of stream %u for seeking failed!", i);
1835 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1838 gboolean res = TRUE;
1839 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1841 switch (GST_EVENT_TYPE (event)) {
1842 case GST_EVENT_SEEK:
1844 #ifndef GST_DISABLE_GST_DEBUG
1845 GstClockTime ts = gst_util_get_timestamp ();
1847 guint32 seqnum = gst_event_get_seqnum (event);
1849 if (seqnum == qtdemux->segment_seqnum) {
1850 GST_LOG_OBJECT (pad,
1851 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1852 gst_event_unref (event);
1856 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1857 /* seek should be handled by upstream, we might need to re-download fragments */
1858 GST_DEBUG_OBJECT (qtdemux,
1859 "let upstream handle seek for fragmented playback");
1863 /* Build complete index for seeking;
1864 * if not a fragmented file at least */
1865 if (!qtdemux->fragmented)
1866 if (!qtdemux_ensure_index (qtdemux))
1868 #ifndef GST_DISABLE_GST_DEBUG
1869 ts = gst_util_get_timestamp () - ts;
1870 GST_INFO_OBJECT (qtdemux,
1871 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1874 if (qtdemux->pullbased) {
1875 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1876 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1877 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1879 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
1880 && !qtdemux->fragmented) {
1881 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1883 GST_DEBUG_OBJECT (qtdemux,
1884 "ignoring seek in push mode in current state");
1887 gst_event_unref (event);
1891 res = gst_pad_event_default (pad, parent, event);
1901 GST_ERROR_OBJECT (qtdemux, "Index failed");
1902 gst_event_unref (event);
1908 /* stream/index return sample that is min/max w.r.t. byte position,
1909 * time is min/max w.r.t. time of samples,
1910 * the latter need not be time of the former sample */
1912 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1913 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1916 gint64 time, min_time;
1917 QtDemuxStream *stream;
1923 for (n = 0; n < qtdemux->n_streams; ++n) {
1926 gboolean set_sample;
1928 str = qtdemux->streams[n];
1935 i = str->n_samples - 1;
1939 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1940 if (str->samples[i].size == 0)
1943 if (fw && (str->samples[i].offset < byte_pos))
1946 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1949 /* move stream to first available sample */
1951 gst_qtdemux_move_stream (qtdemux, str, i);
1955 /* avoid index from sparse streams since they might be far away */
1956 if (!CUR_STREAM (str)->sparse) {
1957 /* determine min/max time */
1958 time = QTSAMPLE_PTS (str, &str->samples[i]);
1959 if (min_time == -1 || (!fw && time > min_time) ||
1960 (fw && time < min_time)) {
1964 /* determine stream with leading sample, to get its position */
1966 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1967 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1975 /* no sample for this stream, mark eos */
1977 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1988 static QtDemuxStream *
1989 _create_stream (void)
1991 QtDemuxStream *stream;
1993 stream = g_new0 (QtDemuxStream, 1);
1994 /* new streams always need a discont */
1995 stream->discont = TRUE;
1996 /* we enable clipping for raw audio/video streams */
1997 stream->need_clip = FALSE;
1998 stream->need_process = FALSE;
1999 stream->segment_index = -1;
2000 stream->time_position = 0;
2001 stream->sample_index = -1;
2002 stream->offset_in_sample = 0;
2003 stream->new_stream = TRUE;
2004 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
2005 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2006 stream->protected = FALSE;
2007 stream->protection_scheme_type = 0;
2008 stream->protection_scheme_version = 0;
2009 stream->protection_scheme_info = NULL;
2010 stream->n_samples_moof = 0;
2011 stream->duration_moof = 0;
2012 stream->duration_last_moof = 0;
2013 stream->alignment = 1;
2014 stream->stream_tags = gst_tag_list_new_empty ();
2015 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2016 g_queue_init (&stream->protection_scheme_event_queue);
2021 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
2023 GstStructure *structure;
2024 const gchar *variant;
2025 const GstCaps *mediacaps = NULL;
2027 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
2029 structure = gst_caps_get_structure (caps, 0);
2030 variant = gst_structure_get_string (structure, "variant");
2032 if (variant && strcmp (variant, "mss-fragmented") == 0) {
2033 QtDemuxStream *stream;
2034 const GValue *value;
2036 demux->fragmented = TRUE;
2037 demux->mss_mode = TRUE;
2039 if (demux->n_streams > 1) {
2040 /* can't do this, we can only renegotiate for another mss format */
2044 value = gst_structure_get_value (structure, "media-caps");
2047 const GValue *timescale_v;
2049 /* TODO update when stream changes during playback */
2051 if (demux->n_streams == 0) {
2052 stream = _create_stream ();
2053 demux->streams[demux->n_streams] = stream;
2054 demux->n_streams = 1;
2055 /* mss has no stsd/stsd entry, use id 0 as default */
2056 stream->stsd_entries_length = 1;
2057 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2058 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2060 stream = demux->streams[0];
2063 timescale_v = gst_structure_get_value (structure, "timescale");
2065 stream->timescale = g_value_get_uint64 (timescale_v);
2067 /* default mss timescale */
2068 stream->timescale = 10000000;
2070 demux->timescale = stream->timescale;
2072 mediacaps = gst_value_get_caps (value);
2073 if (!CUR_STREAM (stream)->caps
2074 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2075 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2077 stream->new_caps = TRUE;
2079 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2080 structure = gst_caps_get_structure (mediacaps, 0);
2081 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2082 stream->subtype = FOURCC_vide;
2084 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2085 gst_structure_get_int (structure, "height",
2086 &CUR_STREAM (stream)->height);
2087 gst_structure_get_fraction (structure, "framerate",
2088 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2089 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2091 stream->subtype = FOURCC_soun;
2092 gst_structure_get_int (structure, "channels",
2093 &CUR_STREAM (stream)->n_channels);
2094 gst_structure_get_int (structure, "rate", &rate);
2095 CUR_STREAM (stream)->rate = rate;
2098 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2100 demux->mss_mode = FALSE;
2107 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2111 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2112 gst_pad_stop_task (qtdemux->sinkpad);
2114 if (hard || qtdemux->upstream_format_is_time) {
2115 qtdemux->state = QTDEMUX_STATE_INITIAL;
2116 qtdemux->neededbytes = 16;
2117 qtdemux->todrop = 0;
2118 qtdemux->pullbased = FALSE;
2119 qtdemux->posted_redirect = FALSE;
2120 qtdemux->first_mdat = -1;
2121 qtdemux->header_size = 0;
2122 qtdemux->mdatoffset = -1;
2123 qtdemux->restoredata_offset = -1;
2124 if (qtdemux->mdatbuffer)
2125 gst_buffer_unref (qtdemux->mdatbuffer);
2126 if (qtdemux->restoredata_buffer)
2127 gst_buffer_unref (qtdemux->restoredata_buffer);
2128 qtdemux->mdatbuffer = NULL;
2129 qtdemux->restoredata_buffer = NULL;
2130 qtdemux->mdatleft = 0;
2131 qtdemux->mdatsize = 0;
2132 if (qtdemux->comp_brands)
2133 gst_buffer_unref (qtdemux->comp_brands);
2134 qtdemux->comp_brands = NULL;
2135 qtdemux->last_moov_offset = -1;
2136 if (qtdemux->moov_node_compressed) {
2137 g_node_destroy (qtdemux->moov_node_compressed);
2138 if (qtdemux->moov_node)
2139 g_free (qtdemux->moov_node->data);
2141 qtdemux->moov_node_compressed = NULL;
2142 if (qtdemux->moov_node)
2143 g_node_destroy (qtdemux->moov_node);
2144 qtdemux->moov_node = NULL;
2145 if (qtdemux->tag_list)
2146 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2147 qtdemux->tag_list = gst_tag_list_new_empty ();
2148 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2150 if (qtdemux->element_index)
2151 gst_object_unref (qtdemux->element_index);
2152 qtdemux->element_index = NULL;
2154 qtdemux->major_brand = 0;
2155 if (qtdemux->pending_newsegment)
2156 gst_event_unref (qtdemux->pending_newsegment);
2157 qtdemux->pending_newsegment = NULL;
2158 qtdemux->upstream_format_is_time = FALSE;
2159 qtdemux->upstream_seekable = FALSE;
2160 qtdemux->upstream_size = 0;
2162 qtdemux->fragment_start = -1;
2163 qtdemux->fragment_start_offset = -1;
2164 qtdemux->duration = 0;
2165 qtdemux->moof_offset = 0;
2166 qtdemux->chapters_track_id = 0;
2167 qtdemux->have_group_id = FALSE;
2168 qtdemux->group_id = G_MAXUINT;
2170 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2172 g_queue_clear (&qtdemux->protection_event_queue);
2174 qtdemux->offset = 0;
2175 gst_adapter_clear (qtdemux->adapter);
2176 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2177 qtdemux->segment_seqnum = 0;
2180 for (n = 0; n < qtdemux->n_streams; n++) {
2181 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2182 qtdemux->streams[n] = NULL;
2184 qtdemux->n_streams = 0;
2185 qtdemux->n_video_streams = 0;
2186 qtdemux->n_audio_streams = 0;
2187 qtdemux->n_sub_streams = 0;
2188 qtdemux->exposed = FALSE;
2189 qtdemux->fragmented = FALSE;
2190 qtdemux->mss_mode = FALSE;
2191 gst_caps_replace (&qtdemux->media_caps, NULL);
2192 qtdemux->timescale = 0;
2193 qtdemux->got_moov = FALSE;
2194 if (qtdemux->protection_system_ids) {
2195 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2196 qtdemux->protection_system_ids = NULL;
2198 } else if (qtdemux->mss_mode) {
2199 gst_flow_combiner_reset (qtdemux->flowcombiner);
2200 for (n = 0; n < qtdemux->n_streams; n++)
2201 gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]);
2203 gst_flow_combiner_reset (qtdemux->flowcombiner);
2204 for (n = 0; n < qtdemux->n_streams; n++) {
2205 qtdemux->streams[n]->sent_eos = FALSE;
2206 qtdemux->streams[n]->time_position = 0;
2207 qtdemux->streams[n]->accumulated_base = 0;
2209 if (!qtdemux->pending_newsegment) {
2210 qtdemux->pending_newsegment = gst_event_new_segment (&qtdemux->segment);
2211 if (qtdemux->segment_seqnum)
2212 gst_event_set_seqnum (qtdemux->pending_newsegment,
2213 qtdemux->segment_seqnum);
2219 /* Maps the @segment to the qt edts internal segments and pushes
2220 * the correspnding segment event.
2222 * If it ends up being at a empty segment, a gap will be pushed and the next
2223 * edts segment will be activated in sequence.
2225 * To be used in push-mode only */
2227 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2231 for (n = 0; n < qtdemux->n_streams; n++) {
2232 QtDemuxStream *stream = qtdemux->streams[n];
2234 stream->time_position = segment->start;
2236 /* in push mode we should be guaranteed that we will have empty segments
2237 * at the beginning and then one segment after, other scenarios are not
2238 * supported and are discarded when parsing the edts */
2239 for (i = 0; i < stream->n_segments; i++) {
2240 if (stream->segments[i].stop_time > segment->start) {
2241 gst_qtdemux_activate_segment (qtdemux, stream, i,
2242 stream->time_position);
2243 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2244 /* push the empty segment and move to the next one */
2245 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2246 stream->time_position);
2250 g_assert (i == stream->n_segments - 1);
2257 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2260 GstQTDemux *demux = GST_QTDEMUX (parent);
2261 gboolean res = TRUE;
2263 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2265 switch (GST_EVENT_TYPE (event)) {
2266 case GST_EVENT_SEGMENT:
2269 QtDemuxStream *stream;
2273 /* some debug output */
2274 gst_event_copy_segment (event, &segment);
2275 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2278 /* erase any previously set segment */
2279 gst_event_replace (&demux->pending_newsegment, NULL);
2281 if (segment.format == GST_FORMAT_TIME) {
2282 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
2283 gst_event_replace (&demux->pending_newsegment, event);
2284 demux->upstream_format_is_time = TRUE;
2286 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2287 "not in time format");
2289 /* chain will send initial newsegment after pads have been added */
2290 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
2291 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2296 /* check if this matches a time seek we received previously
2297 * FIXME for backwards compatibility reasons we use the
2298 * seek_offset here to compare. In the future we might want to
2299 * change this to use the seqnum as it uniquely should identify
2300 * the segment that corresponds to the seek. */
2301 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2302 ", received segment offset %" G_GINT64_FORMAT,
2303 demux->seek_offset, segment.start);
2304 if (segment.format == GST_FORMAT_BYTES
2305 && demux->seek_offset == segment.start) {
2306 GST_OBJECT_LOCK (demux);
2307 offset = segment.start;
2309 segment.format = GST_FORMAT_TIME;
2310 segment.start = demux->push_seek_start;
2311 segment.stop = demux->push_seek_stop;
2312 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2313 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2314 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2315 GST_OBJECT_UNLOCK (demux);
2318 /* we only expect a BYTE segment, e.g. following a seek */
2319 if (segment.format == GST_FORMAT_BYTES) {
2320 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2321 offset = segment.start;
2323 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2324 NULL, (gint64 *) & segment.start);
2325 if ((gint64) segment.start < 0)
2328 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2329 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2330 NULL, (gint64 *) & segment.stop);
2331 /* keyframe seeking should already arrange for start >= stop,
2332 * but make sure in other rare cases */
2333 segment.stop = MAX (segment.stop, segment.start);
2335 } else if (segment.format == GST_FORMAT_TIME) {
2336 /* push all data on the adapter before starting this
2338 gst_qtdemux_process_adapter (demux, TRUE);
2340 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2344 /* We shouldn't modify upstream driven TIME FORMAT segment */
2345 if (!demux->upstream_format_is_time) {
2346 /* accept upstream's notion of segment and distribute along */
2347 segment.format = GST_FORMAT_TIME;
2348 segment.position = segment.time = segment.start;
2349 segment.duration = demux->segment.duration;
2350 segment.base = gst_segment_to_running_time (&demux->segment,
2351 GST_FORMAT_TIME, demux->segment.position);
2354 gst_segment_copy_into (&segment, &demux->segment);
2355 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2357 /* map segment to internal qt segments and push on each stream */
2358 if (demux->n_streams) {
2359 if (demux->fragmented) {
2360 GstEvent *segment_event = gst_event_new_segment (&segment);
2362 gst_event_replace (&demux->pending_newsegment, NULL);
2363 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
2364 gst_qtdemux_push_event (demux, segment_event);
2366 gst_event_replace (&demux->pending_newsegment, NULL);
2367 gst_qtdemux_map_and_push_segments (demux, &segment);
2371 /* clear leftover in current segment, if any */
2372 gst_adapter_clear (demux->adapter);
2374 /* set up streaming thread */
2375 demux->offset = offset;
2376 if (demux->upstream_format_is_time) {
2377 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2378 "set values to restart reading from a new atom");
2379 demux->neededbytes = 16;
2382 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2385 demux->todrop = stream->samples[idx].offset - offset;
2386 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2388 /* set up for EOS */
2389 demux->neededbytes = -1;
2394 gst_event_unref (event);
2398 case GST_EVENT_FLUSH_START:
2400 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2401 gst_event_unref (event);
2406 case GST_EVENT_FLUSH_STOP:
2410 dur = demux->segment.duration;
2411 gst_qtdemux_reset (demux, FALSE);
2412 demux->segment.duration = dur;
2414 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2415 gst_event_unref (event);
2421 /* If we are in push mode, and get an EOS before we've seen any streams,
2422 * then error out - we have nowhere to send the EOS */
2423 if (!demux->pullbased) {
2425 gboolean has_valid_stream = FALSE;
2426 for (i = 0; i < demux->n_streams; i++) {
2427 if (demux->streams[i]->pad != NULL) {
2428 has_valid_stream = TRUE;
2432 if (!has_valid_stream)
2433 gst_qtdemux_post_no_playable_stream_error (demux);
2435 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2436 (guint) gst_adapter_available (demux->adapter));
2437 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2443 case GST_EVENT_CAPS:{
2444 GstCaps *caps = NULL;
2446 gst_event_parse_caps (event, &caps);
2447 gst_qtdemux_setcaps (demux, caps);
2449 gst_event_unref (event);
2452 case GST_EVENT_PROTECTION:
2454 const gchar *system_id = NULL;
2456 gst_event_parse_protection (event, &system_id, NULL, NULL);
2457 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2459 gst_qtdemux_append_protection_system_id (demux, system_id);
2460 /* save the event for later, for source pads that have not been created */
2461 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2462 /* send it to all pads that already exist */
2463 gst_qtdemux_push_event (demux, event);
2471 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2479 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2481 GstQTDemux *demux = GST_QTDEMUX (element);
2483 GST_OBJECT_LOCK (demux);
2484 if (demux->element_index)
2485 gst_object_unref (demux->element_index);
2487 demux->element_index = gst_object_ref (index);
2489 demux->element_index = NULL;
2491 GST_OBJECT_UNLOCK (demux);
2492 /* object lock might be taken again */
2494 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2495 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2496 demux->element_index, demux->index_id);
2500 gst_qtdemux_get_index (GstElement * element)
2502 GstIndex *result = NULL;
2503 GstQTDemux *demux = GST_QTDEMUX (element);
2505 GST_OBJECT_LOCK (demux);
2506 if (demux->element_index)
2507 result = gst_object_ref (demux->element_index);
2508 GST_OBJECT_UNLOCK (demux);
2510 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2517 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2519 g_free ((gpointer) stream->stco.data);
2520 stream->stco.data = NULL;
2521 g_free ((gpointer) stream->stsz.data);
2522 stream->stsz.data = NULL;
2523 g_free ((gpointer) stream->stsc.data);
2524 stream->stsc.data = NULL;
2525 g_free ((gpointer) stream->stts.data);
2526 stream->stts.data = NULL;
2527 g_free ((gpointer) stream->stss.data);
2528 stream->stss.data = NULL;
2529 g_free ((gpointer) stream->stps.data);
2530 stream->stps.data = NULL;
2531 g_free ((gpointer) stream->ctts.data);
2532 stream->ctts.data = NULL;
2536 gst_qtdemux_stream_flush_segments_data (GstQTDemux * qtdemux,
2537 QtDemuxStream * stream)
2539 g_free (stream->segments);
2540 stream->segments = NULL;
2541 stream->segment_index = -1;
2542 stream->accumulated_base = 0;
2546 gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux,
2547 QtDemuxStream * stream)
2549 g_free (stream->samples);
2550 stream->samples = NULL;
2551 gst_qtdemux_stbl_free (stream);
2554 g_free (stream->ra_entries);
2555 stream->ra_entries = NULL;
2556 stream->n_ra_entries = 0;
2558 stream->sample_index = -1;
2559 stream->stbl_index = -1;
2560 stream->n_samples = 0;
2561 stream->time_position = 0;
2563 stream->n_samples_moof = 0;
2564 stream->duration_moof = 0;
2565 stream->duration_last_moof = 0;
2569 gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream)
2572 if (stream->allocator)
2573 gst_object_unref (stream->allocator);
2574 while (stream->buffers) {
2575 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2576 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2578 for (i = 0; i < stream->stsd_entries_length; i++) {
2579 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2580 if (entry->rgb8_palette) {
2581 gst_memory_unref (entry->rgb8_palette);
2582 entry->rgb8_palette = NULL;
2584 entry->sparse = FALSE;
2587 gst_tag_list_unref (stream->stream_tags);
2588 stream->stream_tags = gst_tag_list_new_empty ();
2589 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2590 g_free (stream->redirect_uri);
2591 stream->redirect_uri = NULL;
2592 stream->sent_eos = FALSE;
2593 stream->protected = FALSE;
2594 if (stream->protection_scheme_info) {
2595 if (stream->protection_scheme_type == FOURCC_cenc) {
2596 QtDemuxCencSampleSetInfo *info =
2597 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2598 if (info->default_properties)
2599 gst_structure_free (info->default_properties);
2600 if (info->crypto_info)
2601 g_ptr_array_free (info->crypto_info, TRUE);
2603 g_free (stream->protection_scheme_info);
2604 stream->protection_scheme_info = NULL;
2606 stream->protection_scheme_type = 0;
2607 stream->protection_scheme_version = 0;
2608 g_queue_foreach (&stream->protection_scheme_event_queue,
2609 (GFunc) gst_event_unref, NULL);
2610 g_queue_clear (&stream->protection_scheme_event_queue);
2611 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
2612 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
2616 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
2619 gst_qtdemux_stream_clear (qtdemux, stream);
2620 for (i = 0; i < stream->stsd_entries_length; i++) {
2621 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2623 gst_caps_unref (entry->caps);
2627 gst_tag_list_unref (stream->stream_tags);
2629 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
2630 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
2632 g_free (stream->stsd_entries);
2637 gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int i)
2639 g_assert (i >= 0 && i < qtdemux->n_streams && qtdemux->streams[i] != NULL);
2641 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[i]);
2642 qtdemux->streams[i] = qtdemux->streams[qtdemux->n_streams - 1];
2643 qtdemux->streams[qtdemux->n_streams - 1] = NULL;
2644 qtdemux->n_streams--;
2647 static GstStateChangeReturn
2648 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2650 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2651 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2653 switch (transition) {
2654 case GST_STATE_CHANGE_PAUSED_TO_READY:
2660 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2662 switch (transition) {
2663 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2664 gst_qtdemux_reset (qtdemux, TRUE);
2675 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2677 /* counts as header data */
2678 qtdemux->header_size += length;
2680 /* only consider at least a sufficiently complete ftyp atom */
2684 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2685 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2686 GST_FOURCC_ARGS (qtdemux->major_brand));
2687 if (qtdemux->comp_brands)
2688 gst_buffer_unref (qtdemux->comp_brands);
2689 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2690 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2695 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2696 GstTagList * xmptaglist)
2698 /* Strip out bogus fields */
2700 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2701 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2702 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2704 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2707 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2709 /* prioritize native tags using _KEEP mode */
2710 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2711 gst_tag_list_unref (xmptaglist);
2715 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2717 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2718 const char *xml_str, const char *param_name, int *value)
2720 char *value_start, *value_end, *endptr;
2721 const short value_length_max = 12;
2722 char init_view_ret[12];
2723 int value_length = 0;
2726 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2729 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2734 value_start += strlen (param_name);
2735 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2738 value_end = strchr (value_start, '<');
2740 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2744 value_length = value_end - value_start;
2745 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2746 || (value_start[value_length - 1] == '\t')))
2749 if (value_start[i] == '+' || value_start[i] == '-')
2751 while (i < value_length) {
2752 if (value_start[i] < '0' || value_start[i] > '9') {
2753 GST_ERROR_OBJECT (qtdemux,
2754 "error: incorrect value, integer was expected\n");
2760 if (value_length >= value_length_max || value_length < 1) {
2761 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2765 strncpy (init_view_ret, value_start, value_length_max);
2766 init_view_ret[value_length] = '\0';
2768 *value = strtol (init_view_ret, &endptr, 10);
2769 if (endptr == init_view_ret) {
2770 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2778 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2779 const char *xml_str, const char *param_name, char **value)
2781 char *value_start, *value_end;
2782 const short value_length_max = 256;
2783 int value_length = 0;
2785 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2788 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2793 value_start += strlen (param_name);
2794 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2797 value_end = strchr (value_start, '<');
2799 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2803 value_length = value_end - value_start;
2804 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2805 || (value_start[value_length - 1] == '\t')))
2808 if (value_length >= value_length_max || value_length < 1) {
2809 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2813 *value = (char *) malloc (value_length);
2815 if (*value == NULL) {
2816 GST_ERROR_OBJECT (qtdemux, "error: malloc failed\n");
2820 strncpy (*value, value_start, value_length);
2826 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2827 const char *xml_str, const char *param_name, gboolean * value)
2829 char *value_start, *value_end;
2830 int value_length = 0;
2832 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2835 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2840 value_start += strlen (param_name);
2841 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2844 value_end = strchr (value_start, '<');
2846 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2850 value_length = value_end - value_start;
2851 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2852 || (value_start[value_length - 1] == '\t')))
2855 if (value_length < 1) {
2856 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2860 *value = strstr (value_start, "true") ? TRUE : FALSE;
2866 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux,
2867 const char *xmlStr, QtDemuxSphericalMetadata * spherical_metadata)
2869 const char is_spherical_str[] = "<GSpherical:Spherical>";
2870 const char is_stitched_str[] = "<GSpherical:Stitched>";
2871 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2872 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2873 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2874 const char source_count_str[] = "<GSpherical:SourceCount>";
2875 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2876 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2877 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2878 const char timestamp_str[] = "<GSpherical:Timestamp>";
2879 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2880 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2881 const char cropped_area_image_width_str[] =
2882 "<GSpherical:CroppedAreaImageWidthPixels>";
2883 const char cropped_area_image_height_str[] =
2884 "<GSpherical:CroppedAreaImageHeightPixels>";
2885 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
2886 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
2888 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
2889 (gboolean *) & spherical_metadata->is_spherical);
2890 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
2891 (gboolean *) & spherical_metadata->is_stitched);
2893 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
2894 _get_string_value_from_xml_string (qtdemux, xmlStr,
2895 stitching_software_str, &spherical_metadata->stitching_software);
2896 _get_string_value_from_xml_string (qtdemux, xmlStr,
2897 projection_type_str, &spherical_metadata->projection_type);
2898 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
2899 &spherical_metadata->stereo_mode);
2900 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
2901 &spherical_metadata->source_count);
2902 _get_int_value_from_xml_string (qtdemux, xmlStr,
2903 init_view_heading_str, &spherical_metadata->init_view_heading);
2904 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
2905 &spherical_metadata->init_view_pitch);
2906 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
2907 &spherical_metadata->init_view_roll);
2908 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
2909 &spherical_metadata->timestamp);
2910 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
2911 &spherical_metadata->full_pano_width_pixels);
2912 _get_int_value_from_xml_string (qtdemux, xmlStr,
2913 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
2914 _get_int_value_from_xml_string (qtdemux, xmlStr,
2915 cropped_area_image_width_str,
2916 &spherical_metadata->cropped_area_image_width);
2917 _get_int_value_from_xml_string (qtdemux, xmlStr,
2918 cropped_area_image_height_str,
2919 &spherical_metadata->cropped_area_image_height);
2920 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
2921 &spherical_metadata->cropped_area_left);
2922 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
2923 &spherical_metadata->cropped_area_top);
2930 gst_tag_register_spherical_tags (void) {
2931 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
2934 _("Flag indicating if the video is a spherical video"),
2936 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
2939 _("Flag indicating if the video is stitched"),
2941 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
2943 _("tag-stitching-software"),
2944 _("Software used to stitch the spherical video"),
2946 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
2948 _("tag-projection-type"),
2949 _("Projection type used in the video frames"),
2951 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
2953 _("tag-stereo-mode"),
2954 _("Description of stereoscopic 3D layout"),
2956 gst_tag_register ("source_count", GST_TAG_FLAG_META,
2958 _("tag-source-count"),
2959 _("Number of cameras used to create the spherical video"),
2961 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
2963 _("tag-init-view-heading"),
2964 _("The heading angle of the initial view in degrees"),
2966 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
2968 _("tag-init-view-pitch"),
2969 _("The pitch angle of the initial view in degrees"),
2971 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
2973 _("tag-init-view-roll"),
2974 _("The roll angle of the initial view in degrees"),
2976 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
2979 _("Epoch timestamp of when the first frame in the video was recorded"),
2981 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
2983 _("tag-full-pano-width"),
2984 _("Width of the encoded video frame in pixels"),
2986 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
2988 _("tag-full-pano-height"),
2989 _("Height of the encoded video frame in pixels"),
2991 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
2993 _("tag-cropped-area-image-width"),
2994 _("Width of the video frame to display (e.g. cropping)"),
2996 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
2998 _("tag-cropped-area-image-height"),
2999 _("Height of the video frame to display (e.g. cropping)"),
3001 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3003 _("tag-cropped-area-left"),
3004 _("Column where the left edge of the image was cropped from the"
3005 " full sized panorama"),
3007 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3009 _("tag-cropped-area-top"),
3010 _("Row where the top edge of the image was cropped from the"
3011 " full sized panorama"),
3013 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3015 _("tag-ambisonic-type"),
3016 _("Specifies the type of ambisonic audio represented"),
3018 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3020 _("tag-ambisonic-format"),
3021 _("Specifies the ambisonic audio format"),
3023 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3025 _("tag-ambisonic-order"),
3026 _("Specifies the ambisonic audio channel order"),
3033 _send_message_to_bus (GstQTDemux * qtdemux,
3034 QtDemuxSphericalMetadata * spherical_metadata)
3036 GstTagList *taglist;
3038 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3039 spherical_metadata->is_spherical);
3040 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3041 spherical_metadata->is_stitched);
3042 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3043 spherical_metadata->stitching_software);
3044 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3045 spherical_metadata->projection_type);
3046 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3047 spherical_metadata->stereo_mode);
3048 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3049 spherical_metadata->source_count);
3050 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3051 spherical_metadata->init_view_heading);
3052 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3053 spherical_metadata->init_view_pitch);
3054 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3055 spherical_metadata->init_view_roll);
3056 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3057 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3058 spherical_metadata->full_pano_width_pixels);
3059 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3060 spherical_metadata->full_pano_height_pixels);
3061 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3062 spherical_metadata->cropped_area_image_width);
3063 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3064 spherical_metadata->cropped_area_image_height);
3065 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3066 spherical_metadata->cropped_area_left);
3067 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3068 spherical_metadata->cropped_area_top);
3069 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3070 spherical_metadata->ambisonic_type);
3071 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3072 spherical_metadata->ambisonic_order);
3073 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3074 spherical_metadata->ambisonic_format);
3076 taglist = gst_tag_list_new_empty ();
3077 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3078 "is_spherical", spherical_metadata->is_spherical,
3079 "is_stitched", spherical_metadata->is_stitched,
3080 "source_count", spherical_metadata->source_count,
3081 "init_view_heading", spherical_metadata->init_view_heading,
3082 "init_view_pitch", spherical_metadata->init_view_pitch,
3083 "init_view_roll", spherical_metadata->init_view_roll,
3084 "timestamp", spherical_metadata->timestamp,
3085 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3086 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3087 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3088 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3089 "cropped_area_left", spherical_metadata->cropped_area_left,
3090 "cropped_area_top", spherical_metadata->cropped_area_top,
3091 "ambisonic_type", spherical_metadata->ambisonic_type,
3092 "ambisonic_format", spherical_metadata->ambisonic_format,
3093 "ambisonic_order", spherical_metadata->ambisonic_order,
3096 if (spherical_metadata->stitching_software)
3097 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3098 "stitching_software", spherical_metadata->stitching_software,
3100 if (spherical_metadata->projection_type)
3101 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3102 "projection_type", spherical_metadata->projection_type,
3104 if (spherical_metadata->stereo_mode)
3105 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3106 "stereo_mode", spherical_metadata->stereo_mode,
3109 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3110 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3111 gst_tag_list_copy (taglist)));
3113 gst_tag_list_unref(taglist);
3119 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3124 guint8 ambisonic_type = 0;
3125 guint32 ambisonic_order = 0;
3126 guint8 ambisonic_channel_ordering = 0;
3127 guint8 ambisonic_normalization = 0;
3128 guint32 num_channels = 0;
3129 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3133 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3135 qtdemux->header_size += length;
3136 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3138 if (length <= offset + 16) {
3139 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3143 version = QT_UINT8 (buffer + offset);
3144 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3145 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3146 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3147 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3148 num_channels = QT_UINT32 (buffer + offset + 8);
3149 for (i = 0; i < num_channels; ++i)
3150 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3152 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3153 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3154 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3155 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3156 ambisonic_channel_ordering);
3157 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3158 ambisonic_normalization);
3159 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3160 for (i = 0; i < num_channels; ++i)
3161 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3163 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3164 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3165 spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3167 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3168 if (num_channels == 4) {
3169 spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3171 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3172 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3173 && (channel_map[0] == 0) && (channel_map[1] == 1)
3174 && (channel_map[2] == 2) && (channel_map[3] == 3))
3175 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3177 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3178 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3179 && (channel_map[0] == 0) && (channel_map[1] == 3)
3180 && (channel_map[2] == 1) && (channel_map[3] == 2))
3181 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3188 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3191 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3199 QtDemuxStream *stream;
3200 GstStructure *structure;
3201 QtDemuxCencSampleSetInfo *ss_info = NULL;
3202 const gchar *system_id;
3203 gboolean uses_sub_sample_encryption = FALSE;
3205 if (qtdemux->n_streams == 0)
3208 stream = qtdemux->streams[0];
3210 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3211 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3212 GST_WARNING_OBJECT (qtdemux,
3213 "Attempting PIFF box parsing on an unencrypted stream.");
3217 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3218 G_TYPE_STRING, &system_id, NULL);
3219 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3221 stream->protected = TRUE;
3222 stream->protection_scheme_type = FOURCC_cenc;
3224 if (!stream->protection_scheme_info)
3225 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3227 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3229 if (ss_info->default_properties)
3230 gst_structure_free (ss_info->default_properties);
3232 ss_info->default_properties =
3233 gst_structure_new ("application/x-cenc",
3234 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE, NULL);
3236 if (ss_info->crypto_info) {
3237 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3238 g_ptr_array_free (ss_info->crypto_info, TRUE);
3239 ss_info->crypto_info = NULL;
3243 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3245 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3246 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3250 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3251 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3255 if ((flags & 0x000001)) {
3256 guint32 algorithm_id = 0;
3259 gboolean is_encrypted = TRUE;
3261 if (!gst_byte_reader_get_uint24_le (&br, &algorithm_id)) {
3262 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3267 if (algorithm_id == 0) {
3268 is_encrypted = FALSE;
3269 } else if (algorithm_id == 1) {
3270 /* FIXME: maybe store this in properties? */
3271 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3272 } else if (algorithm_id == 2) {
3273 /* FIXME: maybe store this in properties? */
3274 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3277 if (!gst_byte_reader_get_uint8 (&br, &iv_size))
3280 if (!gst_byte_reader_get_data (&br, 16, &kid))
3283 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3284 gst_buffer_fill (kid_buf, 0, kid, 16);
3285 if (ss_info->default_properties)
3286 gst_structure_free (ss_info->default_properties);
3287 ss_info->default_properties =
3288 gst_structure_new ("application/x-cenc",
3289 "iv_size", G_TYPE_UINT, iv_size,
3290 "encrypted", G_TYPE_BOOLEAN, is_encrypted,
3291 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3292 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3293 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3294 gst_buffer_unref (kid_buf);
3295 } else if ((flags & 0x000002)) {
3296 uses_sub_sample_encryption = TRUE;
3299 if (!gst_byte_reader_get_uint32_be (&br, &qtdemux->cenc_aux_sample_count)) {
3300 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3304 ss_info->crypto_info =
3305 g_ptr_array_new_full (qtdemux->cenc_aux_sample_count,
3306 (GDestroyNotify) qtdemux_gst_structure_free);
3308 for (i = 0; i < qtdemux->cenc_aux_sample_count; ++i) {
3309 GstStructure *properties;
3313 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3314 if (properties == NULL) {
3315 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3319 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3320 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3321 gst_structure_free (properties);
3324 buf = gst_buffer_new_wrapped (data, iv_size);
3325 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3326 gst_buffer_unref (buf);
3328 if (uses_sub_sample_encryption) {
3329 guint16 n_subsamples;
3331 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3332 || n_subsamples == 0) {
3333 GST_ERROR_OBJECT (qtdemux,
3334 "failed to get subsample count for sample %u", i);
3335 gst_structure_free (properties);
3338 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3339 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3340 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3342 gst_structure_free (properties);
3345 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3346 gst_structure_set (properties,
3347 "subsample_count", G_TYPE_UINT, n_subsamples,
3348 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3349 gst_buffer_unref (buf);
3351 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3354 g_ptr_array_add (ss_info->crypto_info, properties);
3359 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3361 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3362 0x97, 0xA9, 0x42, 0xE8,
3363 0x9C, 0x71, 0x99, 0x94,
3364 0x91, 0xE3, 0xAF, 0xAC
3366 static const guint8 playready_uuid[] = {
3367 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3368 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3371 static const guint8 piff_sample_encryption_uuid[] = {
3372 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3373 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3378 /* counts as header data */
3379 qtdemux->header_size += length;
3381 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3383 if (length <= offset + 16) {
3384 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3388 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3390 GstTagList *taglist;
3392 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3393 length - offset - 16, NULL);
3394 taglist = gst_tag_list_from_xmp_buffer (buf);
3395 gst_buffer_unref (buf);
3397 /* make sure we have a usable taglist */
3398 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3400 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3402 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3404 const gunichar2 *s_utf16;
3407 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3408 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3409 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3410 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3414 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3415 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3417 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3418 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3420 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3421 GST_READ_UINT32_LE (buffer + offset),
3422 GST_READ_UINT32_LE (buffer + offset + 4),
3423 GST_READ_UINT32_LE (buffer + offset + 8),
3424 GST_READ_UINT32_LE (buffer + offset + 12));
3429 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3431 GstSidxParser sidx_parser;
3432 GstIsoffParserResult res;
3435 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3438 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3440 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3441 if (res == GST_ISOFF_QT_PARSER_DONE) {
3442 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3444 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3447 /* caller verifies at least 8 bytes in buf */
3449 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3450 guint64 * plength, guint32 * pfourcc)
3455 length = QT_UINT32 (data);
3456 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3457 fourcc = QT_FOURCC (data + 4);
3458 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3461 length = G_MAXUINT64;
3462 } else if (length == 1 && size >= 16) {
3463 /* this means we have an extended size, which is the 64 bit value of
3464 * the next 8 bytes */
3465 length = QT_UINT64 (data + 8);
3466 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3476 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3478 guint32 version = 0;
3479 GstClockTime duration = 0;
3481 if (!gst_byte_reader_get_uint32_be (br, &version))
3486 if (!gst_byte_reader_get_uint64_be (br, &duration))
3491 if (!gst_byte_reader_get_uint32_be (br, &dur))
3496 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3497 qtdemux->duration = duration;
3503 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3509 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3510 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3512 if (!stream->parsed_trex && qtdemux->moov_node) {
3514 GstByteReader trex_data;
3516 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3518 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3521 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3523 /* skip version/flags */
3524 if (!gst_byte_reader_skip (&trex_data, 4))
3526 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3528 if (id != stream->track_id)
3530 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3532 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3534 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3536 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3539 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3540 "duration %d, size %d, flags 0x%x", stream->track_id,
3543 stream->parsed_trex = TRUE;
3544 stream->def_sample_description_index = sdi;
3545 stream->def_sample_duration = dur;
3546 stream->def_sample_size = size;
3547 stream->def_sample_flags = flags;
3550 /* iterate all siblings */
3551 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3557 *ds_duration = stream->def_sample_duration;
3558 *ds_size = stream->def_sample_size;
3559 *ds_flags = stream->def_sample_flags;
3561 /* even then, above values are better than random ... */
3562 if (G_UNLIKELY (!stream->parsed_trex)) {
3563 GST_WARNING_OBJECT (qtdemux,
3564 "failed to find fragment defaults for stream %d", stream->track_id);
3571 /* This method should be called whenever a more accurate duration might
3572 * have been found. It will update all relevant variables if/where needed
3575 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3579 GstClockTime prevdur;
3581 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3583 if (movdur > qtdemux->duration) {
3584 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3585 GST_DEBUG_OBJECT (qtdemux,
3586 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3587 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3588 qtdemux->duration = movdur;
3589 GST_DEBUG_OBJECT (qtdemux,
3590 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3591 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3592 GST_TIME_ARGS (qtdemux->segment.stop));
3593 if (qtdemux->segment.duration == prevdur) {
3594 /* If the current segment has duration/stop identical to previous duration
3595 * update them also (because they were set at that point in time with
3596 * the wrong duration */
3597 /* We convert the value *from* the timescale version to avoid rounding errors */
3598 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3599 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3600 qtdemux->segment.duration = fixeddur;
3601 qtdemux->segment.stop = fixeddur;
3604 for (i = 0; i < qtdemux->n_streams; i++) {
3605 QtDemuxStream *stream = qtdemux->streams[i];
3607 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3608 if (movdur > stream->duration) {
3609 GST_DEBUG_OBJECT (qtdemux,
3610 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3611 GST_TIME_ARGS (duration));
3612 stream->duration = movdur;
3613 if (stream->dummy_segment) {
3614 /* Update all dummy values to new duration */
3615 stream->segments[0].stop_time = duration;
3616 stream->segments[0].duration = duration;
3617 stream->segments[0].media_stop = duration;
3619 /* let downstream know we possibly have a new stop time */
3620 if (stream->segment_index != -1) {
3623 if (qtdemux->segment.rate >= 0) {
3624 pos = stream->segment.start;
3626 pos = stream->segment.stop;
3629 gst_qtdemux_stream_update_segment (qtdemux, stream,
3630 stream->segment_index, pos, NULL, NULL);
3639 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3640 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3641 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3642 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3645 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3647 gint32 data_offset = 0;
3648 guint32 flags = 0, first_flags = 0, samples_count = 0;
3651 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3652 QtDemuxSample *sample;
3653 gboolean ismv = FALSE;
3654 gint64 initial_offset;
3656 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
3657 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3658 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3659 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3661 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3662 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3666 /* presence of stss or not can't really tell us much,
3667 * and flags and so on tend to be marginally reliable in these files */
3668 if (stream->subtype == FOURCC_soun) {
3669 GST_DEBUG_OBJECT (qtdemux,
3670 "sound track in fragmented file; marking all keyframes");
3671 stream->all_keyframe = TRUE;
3674 if (!gst_byte_reader_skip (trun, 1) ||
3675 !gst_byte_reader_get_uint24_be (trun, &flags))
3678 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3681 if (flags & TR_DATA_OFFSET) {
3682 /* note this is really signed */
3683 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3685 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3686 /* default base offset = first byte of moof */
3687 if (*base_offset == -1) {
3688 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3689 *base_offset = moof_offset;
3691 *running_offset = *base_offset + data_offset;
3693 /* if no offset at all, that would mean data starts at moof start,
3694 * which is a bit wrong and is ismv crappy way, so compensate
3695 * assuming data is in mdat following moof */
3696 if (*base_offset == -1) {
3697 *base_offset = moof_offset + moof_length + 8;
3698 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3701 if (*running_offset == -1)
3702 *running_offset = *base_offset;
3705 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3707 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3708 data_offset, flags, samples_count);
3710 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3711 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3712 GST_DEBUG_OBJECT (qtdemux,
3713 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3714 flags ^= TR_FIRST_SAMPLE_FLAGS;
3716 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3718 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3722 /* FIXME ? spec says other bits should also be checked to determine
3723 * entry size (and prefix size for that matter) */
3725 dur_offset = size_offset = 0;
3726 if (flags & TR_SAMPLE_DURATION) {
3727 GST_LOG_OBJECT (qtdemux, "entry duration present");
3728 dur_offset = entry_size;
3731 if (flags & TR_SAMPLE_SIZE) {
3732 GST_LOG_OBJECT (qtdemux, "entry size present");
3733 size_offset = entry_size;
3736 if (flags & TR_SAMPLE_FLAGS) {
3737 GST_LOG_OBJECT (qtdemux, "entry flags present");
3738 flags_offset = entry_size;
3741 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3742 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3743 ct_offset = entry_size;
3747 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3749 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3751 if (stream->n_samples + samples_count >=
3752 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3755 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3756 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3757 (stream->n_samples + samples_count) *
3758 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3760 /* create a new array of samples if it's the first sample parsed */
3761 if (stream->n_samples == 0) {
3762 g_assert (stream->samples == NULL);
3763 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3764 /* or try to reallocate it with space enough to insert the new samples */
3766 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3767 stream->n_samples + samples_count);
3768 if (stream->samples == NULL)
3771 if (qtdemux->fragment_start != -1) {
3772 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3773 qtdemux->fragment_start = -1;
3775 if (stream->n_samples == 0) {
3776 if (decode_ts > 0) {
3777 timestamp = decode_ts;
3778 } else if (stream->pending_seek != NULL) {
3779 /* if we don't have a timestamp from a tfdt box, we'll use the one
3780 * from the mfra seek table */
3781 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3782 GST_TIME_ARGS (stream->pending_seek->ts));
3784 /* FIXME: this is not fully correct, the timestamp refers to the random
3785 * access sample refered to in the tfra entry, which may not necessarily
3786 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3787 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3792 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3793 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3794 GST_TIME_ARGS (gst_ts));
3796 /* subsequent fragments extend stream */
3798 stream->samples[stream->n_samples - 1].timestamp +
3799 stream->samples[stream->n_samples - 1].duration;
3801 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3802 * difference (1 sec.) between decode_ts and timestamp, prefer the
3804 if (has_tfdt && !qtdemux->upstream_format_is_time
3805 && ABSDIFF (decode_ts, timestamp) >
3806 MAX (stream->duration_last_moof / 2,
3807 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3808 GST_INFO_OBJECT (qtdemux,
3809 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3810 ") are significantly different (more than %" GST_TIME_FORMAT
3811 "), using decode_ts",
3812 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3813 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3814 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3815 MAX (stream->duration_last_moof / 2,
3816 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3817 timestamp = decode_ts;
3820 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3821 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3822 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3826 initial_offset = *running_offset;
3828 sample = stream->samples + stream->n_samples;
3829 for (i = 0; i < samples_count; i++) {
3830 guint32 dur, size, sflags, ct;
3832 /* first read sample data */
3833 if (flags & TR_SAMPLE_DURATION) {
3834 dur = QT_UINT32 (data + dur_offset);
3836 dur = d_sample_duration;
3838 if (flags & TR_SAMPLE_SIZE) {
3839 size = QT_UINT32 (data + size_offset);
3841 size = d_sample_size;
3843 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3845 sflags = first_flags;
3847 sflags = d_sample_flags;
3849 } else if (flags & TR_SAMPLE_FLAGS) {
3850 sflags = QT_UINT32 (data + flags_offset);
3852 sflags = d_sample_flags;
3854 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3855 ct = QT_UINT32 (data + ct_offset);
3861 /* fill the sample information */
3862 sample->offset = *running_offset;
3863 sample->pts_offset = ct;
3864 sample->size = size;
3865 sample->timestamp = timestamp;
3866 sample->duration = dur;
3867 /* sample-is-difference-sample */
3868 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3869 * now idea how it relates to bitfield other than massive LE/BE confusion */
3870 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3871 *running_offset += size;
3873 stream->duration_moof += dur;
3877 /* Update total duration if needed */
3878 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3880 /* Pre-emptively figure out size of mdat based on trun information.
3881 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3882 * size, else we will still be able to use this when dealing with gap'ed
3884 qtdemux->mdatleft = *running_offset - initial_offset;
3885 qtdemux->mdatoffset = initial_offset;
3886 qtdemux->mdatsize = qtdemux->mdatleft;
3888 stream->n_samples += samples_count;
3889 stream->n_samples_moof += samples_count;
3891 if (stream->pending_seek != NULL)
3892 stream->pending_seek = NULL;
3898 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3903 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3909 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3910 "be larger than %uMB (broken file?)", stream->n_samples,
3911 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3916 /* find stream with @id */
3917 static inline QtDemuxStream *
3918 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3920 QtDemuxStream *stream;
3924 if (G_UNLIKELY (!id)) {
3925 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3929 /* try to get it fast and simple */
3930 if (G_LIKELY (id <= qtdemux->n_streams)) {
3931 stream = qtdemux->streams[id - 1];
3932 if (G_LIKELY (stream->track_id == id))
3936 /* linear search otherwise */
3937 for (i = 0; i < qtdemux->n_streams; i++) {
3938 stream = qtdemux->streams[i];
3939 if (stream->track_id == id)
3942 if (qtdemux->mss_mode) {
3943 /* mss should have only 1 stream anyway */
3944 return qtdemux->streams[0];
3951 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3952 guint32 * fragment_number)
3954 if (!gst_byte_reader_skip (mfhd, 4))
3956 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3961 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3967 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3968 QtDemuxStream ** stream, guint32 * default_sample_duration,
3969 guint32 * default_sample_size, guint32 * default_sample_flags,
3970 gint64 * base_offset)
3973 guint32 track_id = 0;
3975 if (!gst_byte_reader_skip (tfhd, 1) ||
3976 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3979 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3982 *stream = qtdemux_find_stream (qtdemux, track_id);
3983 if (G_UNLIKELY (!*stream))
3984 goto unknown_stream;
3986 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3987 *base_offset = qtdemux->moof_offset;
3989 if (flags & TF_BASE_DATA_OFFSET)
3990 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3993 /* obtain stream defaults */
3994 qtdemux_parse_trex (qtdemux, *stream,
3995 default_sample_duration, default_sample_size, default_sample_flags);
3997 (*stream)->stsd_sample_description_id =
3998 (*stream)->def_sample_description_index - 1;
4000 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4001 guint32 sample_description_index;
4002 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4004 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4007 if (qtdemux->mss_mode) {
4008 /* mss has no stsd entry */
4009 (*stream)->stsd_sample_description_id = 0;
4012 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4013 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4016 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4017 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4020 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4021 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4028 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4033 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
4039 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4040 guint64 * decode_time)
4042 guint32 version = 0;
4044 if (!gst_byte_reader_get_uint32_be (br, &version))
4049 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4052 guint32 dec_time = 0;
4053 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4055 *decode_time = dec_time;
4058 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4065 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4070 /* Returns a pointer to a GstStructure containing the properties of
4071 * the stream sample identified by @sample_index. The caller must unref
4072 * the returned object after use. Returns NULL if unsuccessful. */
4073 static GstStructure *
4074 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4075 QtDemuxStream * stream, guint sample_index)
4077 QtDemuxCencSampleSetInfo *info = NULL;
4079 g_return_val_if_fail (stream != NULL, NULL);
4080 g_return_val_if_fail (stream->protected, NULL);
4081 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4083 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4085 /* Currently, cenc properties for groups of samples are not supported, so
4086 * simply return a copy of the default sample properties */
4087 return gst_structure_copy (info->default_properties);
4090 /* Parses the sizes of sample auxiliary information contained within a stream,
4091 * as given in a saiz box. Returns array of sample_count guint8 size values,
4092 * or NULL on failure */
4094 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4095 GstByteReader * br, guint32 * sample_count)
4099 guint8 default_info_size;
4101 g_return_val_if_fail (qtdemux != NULL, NULL);
4102 g_return_val_if_fail (stream != NULL, NULL);
4103 g_return_val_if_fail (br != NULL, NULL);
4104 g_return_val_if_fail (sample_count != NULL, NULL);
4106 if (!gst_byte_reader_get_uint32_be (br, &flags))
4110 /* aux_info_type and aux_info_type_parameter are ignored */
4111 if (!gst_byte_reader_skip (br, 8))
4115 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4117 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4119 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4121 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4124 if (default_info_size == 0) {
4125 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4129 info_sizes = g_new (guint8, *sample_count);
4130 memset (info_sizes, default_info_size, *sample_count);
4136 /* Parses the offset of sample auxiliary information contained within a stream,
4137 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4139 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4140 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4145 guint32 aux_info_type = 0;
4146 guint32 aux_info_type_parameter = 0;
4147 guint32 entry_count;
4150 const guint8 *aux_info_type_data = NULL;
4152 g_return_val_if_fail (qtdemux != NULL, FALSE);
4153 g_return_val_if_fail (stream != NULL, FALSE);
4154 g_return_val_if_fail (br != NULL, FALSE);
4155 g_return_val_if_fail (offset != NULL, FALSE);
4157 if (!gst_byte_reader_get_uint8 (br, &version))
4160 if (!gst_byte_reader_get_uint24_be (br, &flags))
4165 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4167 aux_info_type = QT_FOURCC (aux_info_type_data);
4169 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4171 } else if (stream->protected) {
4172 aux_info_type = stream->protection_scheme_type;
4174 aux_info_type = CUR_STREAM (stream)->fourcc;
4178 *info_type = aux_info_type;
4179 if (info_type_parameter)
4180 *info_type_parameter = aux_info_type_parameter;
4182 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4183 "aux_info_type_parameter: %#06x",
4184 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4186 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4189 if (entry_count != 1) {
4190 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4195 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4197 *offset = (guint64) off_32;
4199 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4204 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4209 qtdemux_gst_structure_free (GstStructure * gststructure)
4212 gst_structure_free (gststructure);
4216 /* Parses auxiliary information relating to samples protected using Common
4217 * Encryption (cenc); the format of this information is defined in
4218 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
4220 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4221 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4223 QtDemuxCencSampleSetInfo *ss_info = NULL;
4226 GPtrArray *old_crypto_info = NULL;
4227 guint old_entries = 0;
4229 g_return_val_if_fail (qtdemux != NULL, FALSE);
4230 g_return_val_if_fail (stream != NULL, FALSE);
4231 g_return_val_if_fail (br != NULL, FALSE);
4232 g_return_val_if_fail (stream->protected, FALSE);
4233 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4235 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4237 if (ss_info->crypto_info) {
4238 old_crypto_info = ss_info->crypto_info;
4239 /* Count number of non-null entries remaining at the tail end */
4240 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4241 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4247 ss_info->crypto_info =
4248 g_ptr_array_new_full (sample_count + old_entries,
4249 (GDestroyNotify) qtdemux_gst_structure_free);
4251 /* We preserve old entries because we parse the next moof in advance
4252 * of consuming all samples from the previous moof, and otherwise
4253 * we'd discard the corresponding crypto info for the samples
4254 * from the previous fragment. */
4256 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4258 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4259 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4261 g_ptr_array_index (old_crypto_info, i) = NULL;
4265 if (old_crypto_info) {
4266 /* Everything now belongs to the new array */
4267 g_ptr_array_free (old_crypto_info, TRUE);
4270 for (i = 0; i < sample_count; ++i) {
4271 GstStructure *properties;
4272 guint16 n_subsamples = 0;
4277 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4278 if (properties == NULL) {
4279 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4282 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4283 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4284 gst_structure_free (properties);
4287 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
4288 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4289 gst_structure_free (properties);
4292 buf = gst_buffer_new_wrapped (data, iv_size);
4293 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4294 gst_buffer_unref (buf);
4295 size = info_sizes[i];
4296 if (size > iv_size) {
4297 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4298 || !(n_subsamples > 0)) {
4299 gst_structure_free (properties);
4300 GST_ERROR_OBJECT (qtdemux,
4301 "failed to get subsample count for sample %u", i);
4304 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4305 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4306 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4308 gst_structure_free (properties);
4311 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4313 gst_structure_free (properties);
4316 gst_structure_set (properties,
4317 "subsample_count", G_TYPE_UINT, n_subsamples,
4318 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4319 gst_buffer_unref (buf);
4321 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4323 g_ptr_array_add (ss_info->crypto_info, properties);
4328 /* Converts a UUID in raw byte form to a string representation, as defined in
4329 * RFC 4122. The caller takes ownership of the returned string and is
4330 * responsible for freeing it after use. */
4332 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4334 const guint8 *uuid = (const guint8 *) uuid_bytes;
4336 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4337 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4338 uuid[0], uuid[1], uuid[2], uuid[3],
4339 uuid[4], uuid[5], uuid[6], uuid[7],
4340 uuid[8], uuid[9], uuid[10], uuid[11],
4341 uuid[12], uuid[13], uuid[14], uuid[15]);
4344 /* Parses a Protection System Specific Header box (pssh), as defined in the
4345 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4346 * information needed by a specific content protection system in order to
4347 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4350 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4352 gchar *sysid_string;
4353 guint32 pssh_size = QT_UINT32 (node->data);
4354 GstBuffer *pssh = NULL;
4355 GstEvent *event = NULL;
4356 guint32 parent_box_type;
4359 if (G_UNLIKELY (pssh_size < 32U)) {
4360 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4365 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4367 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4369 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4370 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4371 gst_buffer_get_size (pssh));
4373 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4375 /* Push an event containing the pssh box onto the queues of all streams. */
4376 event = gst_event_new_protection (sysid_string, pssh,
4377 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4378 for (i = 0; i < qtdemux->n_streams; ++i) {
4379 g_queue_push_tail (&qtdemux->streams[i]->protection_scheme_event_queue,
4380 gst_event_ref (event));
4382 g_free (sysid_string);
4383 gst_event_unref (event);
4384 gst_buffer_unref (pssh);
4389 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4390 guint64 moof_offset, QtDemuxStream * stream)
4392 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4394 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4395 GNode *saiz_node, *saio_node, *pssh_node;
4396 GstByteReader saiz_data, saio_data;
4397 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4398 gint64 base_offset, running_offset;
4401 /* NOTE @stream ignored */
4403 moof_node = g_node_new ((guint8 *) buffer);
4404 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4405 qtdemux_node_dump (qtdemux, moof_node);
4407 /* Get fragment number from mfhd and check it's valid */
4409 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4410 if (mfhd_node == NULL)
4412 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4414 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4416 /* unknown base_offset to start with */
4417 base_offset = running_offset = -1;
4418 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4420 guint64 decode_time = 0;
4422 /* Fragment Header node */
4424 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4428 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4429 &ds_size, &ds_flags, &base_offset))
4432 /* The following code assumes at most a single set of sample auxiliary
4433 * data in the fragment (consisting of a saiz box and a corresponding saio
4434 * box); in theory, however, there could be multiple sets of sample
4435 * auxiliary data in a fragment. */
4437 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4440 guint32 info_type = 0;
4442 guint32 info_type_parameter = 0;
4444 g_free (qtdemux->cenc_aux_info_sizes);
4446 qtdemux->cenc_aux_info_sizes =
4447 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4448 &qtdemux->cenc_aux_sample_count);
4449 if (qtdemux->cenc_aux_info_sizes == NULL) {
4450 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4454 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4457 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4458 g_free (qtdemux->cenc_aux_info_sizes);
4459 qtdemux->cenc_aux_info_sizes = NULL;
4463 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4464 &info_type, &info_type_parameter, &offset))) {
4465 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4466 g_free (qtdemux->cenc_aux_info_sizes);
4467 qtdemux->cenc_aux_info_sizes = NULL;
4470 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4471 offset += (guint64) (base_offset - qtdemux->moof_offset);
4472 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4474 if (offset > length) {
4475 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4476 qtdemux->cenc_aux_info_offset = offset;
4478 gst_byte_reader_init (&br, buffer + offset, length - offset);
4479 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4480 qtdemux->cenc_aux_info_sizes,
4481 qtdemux->cenc_aux_sample_count)) {
4482 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4483 g_free (qtdemux->cenc_aux_info_sizes);
4484 qtdemux->cenc_aux_info_sizes = NULL;
4492 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4495 /* We'll use decode_time to interpolate timestamps
4496 * in case the input timestamps are missing */
4497 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4499 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4500 " (%" GST_TIME_FORMAT ")", decode_time,
4501 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4502 decode_time) : GST_CLOCK_TIME_NONE));
4504 /* Discard the fragment buffer timestamp info to avoid using it.
4505 * Rely on tfdt instead as it is more accurate than the timestamp
4506 * that is fetched from a manifest/playlist and is usually
4508 qtdemux->fragment_start = -1;
4511 if (G_UNLIKELY (!stream)) {
4512 /* we lost track of offset, we'll need to regain it,
4513 * but can delay complaining until later or avoid doing so altogether */
4517 if (G_UNLIKELY (base_offset < -1))
4520 if (qtdemux->upstream_format_is_time)
4521 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
4523 /* initialise moof sample data */
4524 stream->n_samples_moof = 0;
4525 stream->duration_last_moof = stream->duration_moof;
4526 stream->duration_moof = 0;
4528 /* Track Run node */
4530 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4533 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4534 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4535 &running_offset, decode_time, (tfdt_node != NULL));
4536 /* iterate all siblings */
4537 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4541 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4543 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4544 guint32 box_length = QT_UINT32 (uuid_buffer);
4546 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4549 /* if no new base_offset provided for next traf,
4550 * base is end of current traf */
4551 base_offset = running_offset;
4552 running_offset = -1;
4554 if (stream->n_samples_moof && stream->duration_moof)
4555 stream->new_caps = TRUE;
4558 /* iterate all siblings */
4559 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4562 /* parse any protection system info */
4563 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4565 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4566 qtdemux_parse_pssh (qtdemux, pssh_node);
4567 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4570 g_node_destroy (moof_node);
4575 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4580 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4585 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4590 g_node_destroy (moof_node);
4591 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4592 (_("This file is corrupt and cannot be played.")), (NULL));
4598 /* might be used if some day we actually use mfra & co
4599 * for random access to fragments,
4600 * but that will require quite some modifications and much less relying
4601 * on a sample array */
4605 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4607 QtDemuxStream *stream;
4608 guint32 ver_flags, track_id, len, num_entries, i;
4609 guint value_size, traf_size, trun_size, sample_size;
4610 guint64 time = 0, moof_offset = 0;
4612 GstBuffer *buf = NULL;
4617 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4619 if (!gst_byte_reader_skip (&tfra, 8))
4622 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4625 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4626 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4627 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4630 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4632 stream = qtdemux_find_stream (qtdemux, track_id);
4634 goto unknown_trackid;
4636 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4637 sample_size = (len & 3) + 1;
4638 trun_size = ((len & 12) >> 2) + 1;
4639 traf_size = ((len & 48) >> 4) + 1;
4641 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4642 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4644 if (num_entries == 0)
4647 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4648 value_size + value_size + traf_size + trun_size + sample_size))
4651 g_free (stream->ra_entries);
4652 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4653 stream->n_ra_entries = num_entries;
4655 for (i = 0; i < num_entries; i++) {
4656 qt_atom_parser_get_offset (&tfra, value_size, &time);
4657 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4658 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4659 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4660 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4662 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4664 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4665 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4667 stream->ra_entries[i].ts = time;
4668 stream->ra_entries[i].moof_offset = moof_offset;
4670 /* don't want to go through the entire file and read all moofs at startup */
4672 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4673 if (ret != GST_FLOW_OK)
4675 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4676 moof_offset, stream);
4677 gst_buffer_unref (buf);
4681 check_update_duration (qtdemux, time);
4688 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4693 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4698 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4704 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4706 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4707 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4708 GstBuffer *mfro = NULL, *mfra = NULL;
4710 gboolean ret = FALSE;
4711 GNode *mfra_node, *tfra_node;
4712 guint64 mfra_offset = 0;
4713 guint32 fourcc, mfra_size;
4716 /* query upstream size in bytes */
4717 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4718 goto size_query_failed;
4720 /* mfro box should be at the very end of the file */
4721 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4722 if (flow != GST_FLOW_OK)
4725 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4727 fourcc = QT_FOURCC (mfro_map.data + 4);
4728 if (fourcc != FOURCC_mfro)
4731 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4732 if (mfro_map.size < 16)
4733 goto invalid_mfro_size;
4735 mfra_size = QT_UINT32 (mfro_map.data + 12);
4736 if (mfra_size >= len)
4737 goto invalid_mfra_size;
4739 mfra_offset = len - mfra_size;
4741 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4742 mfra_offset, mfra_size);
4744 /* now get and parse mfra box */
4745 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4746 if (flow != GST_FLOW_OK)
4749 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4751 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4752 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4754 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4757 qtdemux_parse_tfra (qtdemux, tfra_node);
4758 /* iterate all siblings */
4759 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4761 g_node_destroy (mfra_node);
4763 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4769 if (mfro_map.memory != NULL)
4770 gst_buffer_unmap (mfro, &mfro_map);
4771 gst_buffer_unref (mfro);
4774 if (mfra_map.memory != NULL)
4775 gst_buffer_unmap (mfra, &mfra_map);
4776 gst_buffer_unref (mfra);
4783 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4788 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4793 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4798 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4804 add_offset (guint64 offset, guint64 advance)
4806 /* Avoid 64-bit overflow by clamping */
4807 if (offset > G_MAXUINT64 - advance)
4809 return offset + advance;
4812 static GstFlowReturn
4813 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4817 GstBuffer *buf = NULL;
4818 GstFlowReturn ret = GST_FLOW_OK;
4819 guint64 cur_offset = qtdemux->offset;
4822 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4823 if (G_UNLIKELY (ret != GST_FLOW_OK))
4825 gst_buffer_map (buf, &map, GST_MAP_READ);
4826 if (G_LIKELY (map.size >= 8))
4827 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4828 gst_buffer_unmap (buf, &map);
4829 gst_buffer_unref (buf);
4831 /* maybe we already got most we needed, so only consider this eof */
4832 if (G_UNLIKELY (length == 0)) {
4833 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4834 (_("Invalid atom size.")),
4835 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4836 GST_FOURCC_ARGS (fourcc)));
4843 /* record for later parsing when needed */
4844 if (!qtdemux->moof_offset) {
4845 qtdemux->moof_offset = qtdemux->offset;
4847 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4850 qtdemux->offset += length; /* skip moof and keep going */
4852 if (qtdemux->got_moov) {
4853 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4864 GST_LOG_OBJECT (qtdemux,
4865 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4866 GST_FOURCC_ARGS (fourcc), cur_offset);
4867 qtdemux->offset = add_offset (qtdemux->offset, length);
4872 GstBuffer *moov = NULL;
4874 if (qtdemux->got_moov) {
4875 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4876 qtdemux->offset = add_offset (qtdemux->offset, length);
4880 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4881 if (ret != GST_FLOW_OK)
4883 gst_buffer_map (moov, &map, GST_MAP_READ);
4885 if (length != map.size) {
4886 /* Some files have a 'moov' atom at the end of the file which contains
4887 * a terminal 'free' atom where the body of the atom is missing.
4888 * Check for, and permit, this special case.
4890 if (map.size >= 8) {
4891 guint8 *final_data = map.data + (map.size - 8);
4892 guint32 final_length = QT_UINT32 (final_data);
4893 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4895 if (final_fourcc == FOURCC_free
4896 && map.size + final_length - 8 == length) {
4897 /* Ok, we've found that special case. Allocate a new buffer with
4898 * that free atom actually present. */
4899 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4900 gst_buffer_fill (newmoov, 0, map.data, map.size);
4901 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4902 gst_buffer_unmap (moov, &map);
4903 gst_buffer_unref (moov);
4905 gst_buffer_map (moov, &map, GST_MAP_READ);
4910 if (length != map.size) {
4911 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4912 (_("This file is incomplete and cannot be played.")),
4913 ("We got less than expected (received %" G_GSIZE_FORMAT
4914 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4915 (guint) length, cur_offset));
4916 gst_buffer_unmap (moov, &map);
4917 gst_buffer_unref (moov);
4918 ret = GST_FLOW_ERROR;
4921 qtdemux->offset += length;
4923 qtdemux_parse_moov (qtdemux, map.data, length);
4924 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4926 qtdemux_parse_tree (qtdemux);
4927 if (qtdemux->moov_node_compressed) {
4928 g_node_destroy (qtdemux->moov_node_compressed);
4929 g_free (qtdemux->moov_node->data);
4931 qtdemux->moov_node_compressed = NULL;
4932 g_node_destroy (qtdemux->moov_node);
4933 qtdemux->moov_node = NULL;
4934 gst_buffer_unmap (moov, &map);
4935 gst_buffer_unref (moov);
4936 qtdemux->got_moov = TRUE;
4942 GstBuffer *ftyp = NULL;
4944 /* extract major brand; might come in handy for ISO vs QT issues */
4945 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4946 if (ret != GST_FLOW_OK)
4948 qtdemux->offset += length;
4949 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4950 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4951 gst_buffer_unmap (ftyp, &map);
4952 gst_buffer_unref (ftyp);
4957 GstBuffer *uuid = NULL;
4959 /* uuid are extension atoms */
4960 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4961 if (ret != GST_FLOW_OK)
4963 qtdemux->offset += length;
4964 gst_buffer_map (uuid, &map, GST_MAP_READ);
4965 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4966 gst_buffer_unmap (uuid, &map);
4967 gst_buffer_unref (uuid);
4972 GstBuffer *sidx = NULL;
4973 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4974 if (ret != GST_FLOW_OK)
4976 qtdemux->offset += length;
4977 gst_buffer_map (sidx, &map, GST_MAP_READ);
4978 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4979 gst_buffer_unmap (sidx, &map);
4980 gst_buffer_unref (sidx);
4985 GstBuffer *unknown = NULL;
4987 GST_LOG_OBJECT (qtdemux,
4988 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4989 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4991 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4992 if (ret != GST_FLOW_OK)
4994 gst_buffer_map (unknown, &map, GST_MAP_READ);
4995 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4996 gst_buffer_unmap (unknown, &map);
4997 gst_buffer_unref (unknown);
4998 qtdemux->offset += length;
5004 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5005 /* digested all data, show what we have */
5006 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5007 if (spherical_metadata)
5008 _send_message_to_bus (qtdemux, spherical_metadata);
5009 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5010 qtdemux_prepare_streams (qtdemux);
5011 ret = qtdemux_expose_streams (qtdemux);
5013 qtdemux->state = QTDEMUX_STATE_MOVIE;
5014 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5021 /* Seeks to the previous keyframe of the indexed stream and
5022 * aligns other streams with respect to the keyframe timestamp
5023 * of indexed stream. Only called in case of Reverse Playback
5025 static GstFlowReturn
5026 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5029 guint32 seg_idx = 0, k_index = 0;
5030 guint32 ref_seg_idx, ref_k_index;
5031 GstClockTime k_pos = 0, last_stop = 0;
5032 QtDemuxSegment *seg = NULL;
5033 QtDemuxStream *ref_str = NULL;
5034 guint64 seg_media_start_mov; /* segment media start time in mov format */
5037 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5038 * and finally align all the other streams on that timestamp with their
5039 * respective keyframes */
5040 for (n = 0; n < qtdemux->n_streams; n++) {
5041 QtDemuxStream *str = qtdemux->streams[n];
5043 /* No candidate yet, take the first stream */
5049 /* So that stream has a segment, we prefer video streams */
5050 if (str->subtype == FOURCC_vide) {
5056 if (G_UNLIKELY (!ref_str)) {
5057 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5061 if (G_UNLIKELY (!ref_str->from_sample)) {
5062 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5066 /* So that stream has been playing from from_sample to to_sample. We will
5067 * get the timestamp of the previous sample and search for a keyframe before
5068 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5069 if (ref_str->subtype == FOURCC_vide) {
5070 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5071 ref_str->from_sample - 1, FALSE);
5073 if (ref_str->from_sample >= 10)
5074 k_index = ref_str->from_sample - 10;
5080 ref_str->samples[k_index].timestamp +
5081 ref_str->samples[k_index].pts_offset;
5083 /* get current segment for that stream */
5084 seg = &ref_str->segments[ref_str->segment_index];
5085 /* Use segment start in original timescale for comparisons */
5086 seg_media_start_mov = seg->trak_media_start;
5088 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5089 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT "\n",
5090 k_index, target_ts, seg_media_start_mov,
5091 GST_TIME_ARGS (seg->media_start));
5093 /* Crawl back through segments to find the one containing this I frame */
5094 while (target_ts < seg_media_start_mov) {
5095 GST_DEBUG_OBJECT (qtdemux,
5096 "keyframe position (sample %u) is out of segment %u " " target %"
5097 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5098 ref_str->segment_index, target_ts, seg_media_start_mov);
5100 if (G_UNLIKELY (!ref_str->segment_index)) {
5101 /* Reached first segment, let's consider it's EOS */
5104 ref_str->segment_index--;
5105 seg = &ref_str->segments[ref_str->segment_index];
5106 /* Use segment start in original timescale for comparisons */
5107 seg_media_start_mov = seg->trak_media_start;
5109 /* Calculate time position of the keyframe and where we should stop */
5111 QTSTREAMTIME_TO_GSTTIME (ref_str,
5112 target_ts - seg->trak_media_start) + seg->time;
5114 QTSTREAMTIME_TO_GSTTIME (ref_str,
5115 ref_str->samples[ref_str->from_sample].timestamp -
5116 seg->trak_media_start) + seg->time;
5118 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5119 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5120 k_index, GST_TIME_ARGS (k_pos));
5122 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5123 qtdemux->segment.position = last_stop;
5124 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5125 GST_TIME_ARGS (last_stop));
5127 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5128 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5132 ref_seg_idx = ref_str->segment_index;
5133 ref_k_index = k_index;
5135 /* Align them all on this */
5136 for (n = 0; n < qtdemux->n_streams; n++) {
5138 GstClockTime seg_time = 0;
5139 QtDemuxStream *str = qtdemux->streams[n];
5141 /* aligning reference stream again might lead to backing up to yet another
5142 * keyframe (due to timestamp rounding issues),
5143 * potentially putting more load on downstream; so let's try to avoid */
5144 if (str == ref_str) {
5145 seg_idx = ref_seg_idx;
5146 seg = &str->segments[seg_idx];
5147 k_index = ref_k_index;
5148 GST_DEBUG_OBJECT (qtdemux, "reference stream %d segment %d, "
5149 "sample at index %d", n, ref_str->segment_index, k_index);
5151 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5152 GST_DEBUG_OBJECT (qtdemux,
5153 "stream %d align segment %d for keyframe pos %" GST_TIME_FORMAT, n,
5154 seg_idx, GST_TIME_ARGS (k_pos));
5156 /* get segment and time in the segment */
5157 seg = &str->segments[seg_idx];
5158 seg_time = k_pos - seg->time;
5160 /* get the media time in the segment.
5161 * No adjustment for empty "filler" segments */
5162 if (seg->media_start != GST_CLOCK_TIME_NONE)
5163 seg_time += seg->media_start;
5165 /* get the index of the sample with media time */
5166 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5167 GST_DEBUG_OBJECT (qtdemux,
5168 "stream %d sample for %" GST_TIME_FORMAT " at %u", n,
5169 GST_TIME_ARGS (seg_time), index);
5171 /* find previous keyframe */
5172 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5175 /* Remember until where we want to go */
5176 str->to_sample = str->from_sample - 1;
5177 /* Define our time position */
5179 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5180 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5181 if (seg->media_start != GST_CLOCK_TIME_NONE)
5182 str->time_position -= seg->media_start;
5184 /* Now seek back in time */
5185 gst_qtdemux_move_stream (qtdemux, str, k_index);
5186 GST_DEBUG_OBJECT (qtdemux, "stream %d keyframe at %u, time position %"
5187 GST_TIME_FORMAT " playing from sample %u to %u", n, k_index,
5188 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5194 return GST_FLOW_EOS;
5198 * Gets the current qt segment start, stop and position for the
5199 * given time offset. This is used in update_segment()
5202 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5203 QtDemuxStream * stream, GstClockTime offset,
5204 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5206 GstClockTime seg_time;
5207 GstClockTime start, stop, time;
5208 QtDemuxSegment *segment;
5210 segment = &stream->segments[stream->segment_index];
5212 /* get time in this segment */
5213 seg_time = (offset - segment->time) * segment->rate;
5215 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5216 GST_TIME_ARGS (seg_time));
5218 if (G_UNLIKELY (seg_time > segment->duration)) {
5219 GST_LOG_OBJECT (stream->pad,
5220 "seg_time > segment->duration %" GST_TIME_FORMAT,
5221 GST_TIME_ARGS (segment->duration));
5222 seg_time = segment->duration;
5225 /* qtdemux->segment.stop is in outside-time-realm, whereas
5226 * segment->media_stop is in track-time-realm.
5228 * In order to compare the two, we need to bring segment.stop
5229 * into the track-time-realm
5231 * FIXME - does this comment still hold? Don't see any conversion here */
5233 stop = qtdemux->segment.stop;
5234 if (stop == GST_CLOCK_TIME_NONE)
5235 stop = qtdemux->segment.duration;
5236 if (stop == GST_CLOCK_TIME_NONE)
5237 stop = segment->media_stop;
5240 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5242 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5243 start = segment->time + seg_time;
5245 stop = start - seg_time + segment->duration;
5246 } else if (qtdemux->segment.rate >= 0) {
5247 start = MIN (segment->media_start + seg_time, stop);
5250 if (segment->media_start >= qtdemux->segment.start) {
5251 time = segment->time;
5253 time = segment->time + (qtdemux->segment.start - segment->media_start);
5256 start = MAX (segment->media_start, qtdemux->segment.start);
5257 stop = MIN (segment->media_start + seg_time, stop);
5266 * Updates the qt segment used for the stream and pushes a new segment event
5267 * downstream on this stream's pad.
5270 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5271 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5272 GstClockTime * _stop)
5274 QtDemuxSegment *segment;
5275 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5279 /* update the current segment */
5280 stream->segment_index = seg_idx;
5282 /* get the segment */
5283 segment = &stream->segments[seg_idx];
5285 if (G_UNLIKELY (offset < segment->time)) {
5286 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5287 GST_TIME_ARGS (segment->time));
5291 /* segment lies beyond total indicated duration */
5292 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5293 segment->time > qtdemux->segment.duration)) {
5294 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5295 " < segment->time %" GST_TIME_FORMAT,
5296 GST_TIME_ARGS (qtdemux->segment.duration),
5297 GST_TIME_ARGS (segment->time));
5301 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5302 &start, &stop, &time);
5304 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5305 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5306 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5308 /* combine global rate with that of the segment */
5309 rate = segment->rate * qtdemux->segment.rate;
5311 /* Copy flags from main segment */
5312 stream->segment.flags = qtdemux->segment.flags;
5314 /* update the segment values used for clipping */
5315 stream->segment.offset = qtdemux->segment.offset;
5316 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5317 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5318 stream->segment.rate = rate;
5319 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5320 stream->cslg_shift);
5321 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5322 stream->cslg_shift);
5323 stream->segment.time = time;
5324 stream->segment.position = stream->segment.start;
5326 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5329 /* now prepare and send the segment */
5331 event = gst_event_new_segment (&stream->segment);
5332 if (qtdemux->segment_seqnum) {
5333 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5335 gst_pad_push_event (stream->pad, event);
5336 /* assume we can send more data now */
5337 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5338 /* clear to send tags on this pad now */
5339 gst_qtdemux_push_tags (qtdemux, stream);
5350 /* activate the given segment number @seg_idx of @stream at time @offset.
5351 * @offset is an absolute global position over all the segments.
5353 * This will push out a NEWSEGMENT event with the right values and
5354 * position the stream index to the first decodable sample before
5358 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5359 guint32 seg_idx, GstClockTime offset)
5361 QtDemuxSegment *segment;
5362 guint32 index, kf_index;
5363 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5365 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5366 seg_idx, GST_TIME_ARGS (offset));
5368 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5372 segment = &stream->segments[stream->segment_index];
5374 /* in the fragmented case, we pick a fragment that starts before our
5375 * desired position and rely on downstream to wait for a keyframe
5376 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5377 * tfra entries tells us which trun/sample the key unit is in, but we don't
5378 * make use of this additional information at the moment) */
5379 if (qtdemux->fragmented) {
5380 stream->to_sample = G_MAXUINT32;
5384 /* We don't need to look for a sample in push-based */
5385 if (!qtdemux->pullbased)
5388 /* and move to the keyframe before the indicated media time of the
5390 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5391 if (qtdemux->segment.rate >= 0) {
5392 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5393 stream->to_sample = G_MAXUINT32;
5394 GST_DEBUG_OBJECT (stream->pad,
5395 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5396 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5397 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5399 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5400 stream->to_sample = index;
5401 GST_DEBUG_OBJECT (stream->pad,
5402 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5403 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5404 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5407 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5408 "this is an empty segment");
5412 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5413 * encountered an error and printed a message so we return appropriately */
5417 /* we're at the right spot */
5418 if (index == stream->sample_index) {
5419 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5423 /* find keyframe of the target index */
5424 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5427 /* indent does stupid stuff with stream->samples[].timestamp */
5429 /* if we move forwards, we don't have to go back to the previous
5430 * keyframe since we already sent that. We can also just jump to
5431 * the keyframe right before the target index if there is one. */
5432 if (index > stream->sample_index) {
5433 /* moving forwards check if we move past a keyframe */
5434 if (kf_index > stream->sample_index) {
5435 GST_DEBUG_OBJECT (stream->pad,
5436 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5437 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5438 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5439 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5441 GST_DEBUG_OBJECT (stream->pad,
5442 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5443 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5444 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5447 GST_DEBUG_OBJECT (stream->pad,
5448 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5449 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5450 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5451 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5459 /* prepare to get the current sample of @stream, getting essential values.
5461 * This function will also prepare and send the segment when needed.
5463 * Return FALSE if the stream is EOS.
5468 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5469 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5470 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5471 gboolean * keyframe)
5473 QtDemuxSample *sample;
5474 GstClockTime time_position;
5477 g_return_val_if_fail (stream != NULL, FALSE);
5479 time_position = stream->time_position;
5480 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5483 seg_idx = stream->segment_index;
5484 if (G_UNLIKELY (seg_idx == -1)) {
5485 /* find segment corresponding to time_position if we are looking
5487 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5490 /* different segment, activate it, sample_index will be set. */
5491 if (G_UNLIKELY (stream->segment_index != seg_idx))
5492 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5494 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream->
5496 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5498 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5499 " prepare empty sample");
5502 *pts = *dts = time_position;
5503 *duration = seg->duration - (time_position - seg->time);
5510 if (stream->sample_index == -1)
5511 stream->sample_index = 0;
5513 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5514 stream->sample_index, stream->n_samples);
5516 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5517 if (!qtdemux->fragmented)
5520 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5524 GST_OBJECT_LOCK (qtdemux);
5525 flow = qtdemux_add_fragmented_samples (qtdemux);
5526 GST_OBJECT_UNLOCK (qtdemux);
5528 if (flow != GST_FLOW_OK)
5531 while (stream->sample_index >= stream->n_samples);
5534 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5535 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5536 stream->sample_index);
5540 /* now get the info for the sample we're at */
5541 sample = &stream->samples[stream->sample_index];
5543 *dts = QTSAMPLE_DTS (stream, sample);
5544 *pts = QTSAMPLE_PTS (stream, sample);
5545 *offset = sample->offset;
5546 *size = sample->size;
5547 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5548 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5555 stream->time_position = GST_CLOCK_TIME_NONE;
5560 /* move to the next sample in @stream.
5562 * Moves to the next segment when needed.
5565 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5567 QtDemuxSample *sample;
5568 QtDemuxSegment *segment;
5570 /* get current segment */
5571 segment = &stream->segments[stream->segment_index];
5573 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5574 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5578 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5579 /* Mark the stream as EOS */
5580 GST_DEBUG_OBJECT (qtdemux,
5581 "reached max allowed sample %u, mark EOS", stream->to_sample);
5582 stream->time_position = GST_CLOCK_TIME_NONE;
5586 /* move to next sample */
5587 stream->sample_index++;
5588 stream->offset_in_sample = 0;
5590 /* reached the last sample, we need the next segment */
5591 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5594 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5595 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5596 stream->sample_index);
5600 /* get next sample */
5601 sample = &stream->samples[stream->sample_index];
5603 /* see if we are past the segment */
5604 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5607 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5608 /* inside the segment, update time_position, looks very familiar to
5609 * GStreamer segments, doesn't it? */
5610 stream->time_position =
5611 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5613 /* not yet in segment, time does not yet increment. This means
5614 * that we are still prerolling keyframes to the decoder so it can
5615 * decode the first sample of the segment. */
5616 stream->time_position = segment->time;
5620 /* move to the next segment */
5623 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5625 if (stream->segment_index == stream->n_segments - 1) {
5626 /* are we at the end of the last segment, we're EOS */
5627 stream->time_position = GST_CLOCK_TIME_NONE;
5629 /* else we're only at the end of the current segment */
5630 stream->time_position = segment->stop_time;
5632 /* make sure we select a new segment */
5634 /* accumulate previous segments */
5635 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5636 stream->accumulated_base +=
5637 (stream->segment.stop -
5638 stream->segment.start) / ABS (stream->segment.rate);
5640 stream->segment_index = -1;
5645 gst_qtdemux_sync_streams (GstQTDemux * demux)
5649 if (demux->n_streams <= 1)
5652 for (i = 0; i < demux->n_streams; i++) {
5653 QtDemuxStream *stream;
5654 GstClockTime end_time;
5656 stream = demux->streams[i];
5661 /* TODO advance time on subtitle streams here, if any some day */
5663 /* some clips/trailers may have unbalanced streams at the end,
5664 * so send EOS on shorter stream to prevent stalling others */
5666 /* do not mess with EOS if SEGMENT seeking */
5667 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5670 if (demux->pullbased) {
5671 /* loop mode is sample time based */
5672 if (!STREAM_IS_EOS (stream))
5675 /* push mode is byte position based */
5676 if (stream->n_samples &&
5677 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5681 if (stream->sent_eos)
5684 /* only act if some gap */
5685 end_time = stream->segments[stream->n_segments - 1].stop_time;
5686 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5687 ", stream end: %" GST_TIME_FORMAT,
5688 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5689 if (GST_CLOCK_TIME_IS_VALID (end_time)
5690 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5693 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5694 GST_PAD_NAME (stream->pad));
5695 stream->sent_eos = TRUE;
5696 event = gst_event_new_eos ();
5697 if (demux->segment_seqnum)
5698 gst_event_set_seqnum (event, demux->segment_seqnum);
5699 gst_pad_push_event (stream->pad, event);
5704 /* EOS and NOT_LINKED need to be combined. This means that we return:
5706 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5707 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5709 static GstFlowReturn
5710 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5713 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5716 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5719 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5721 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5725 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5726 * completely clipped
5728 * Should be used only with raw buffers */
5730 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5733 guint64 start, stop, cstart, cstop, diff;
5734 GstClockTime pts, duration;
5736 gint num_rate, denom_rate;
5741 osize = size = gst_buffer_get_size (buf);
5744 /* depending on the type, setup the clip parameters */
5745 if (stream->subtype == FOURCC_soun) {
5746 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5747 num_rate = GST_SECOND;
5748 denom_rate = (gint) CUR_STREAM (stream)->rate;
5750 } else if (stream->subtype == FOURCC_vide) {
5752 num_rate = CUR_STREAM (stream)->fps_n;
5753 denom_rate = CUR_STREAM (stream)->fps_d;
5758 if (frame_size <= 0)
5759 goto bad_frame_size;
5761 /* we can only clip if we have a valid pts */
5762 pts = GST_BUFFER_PTS (buf);
5763 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5766 duration = GST_BUFFER_DURATION (buf);
5768 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5770 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5774 stop = start + duration;
5776 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5777 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5780 /* see if some clipping happened */
5781 diff = cstart - start;
5787 /* bring clipped time to samples and to bytes */
5788 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5791 GST_DEBUG_OBJECT (qtdemux,
5792 "clipping start to %" GST_TIME_FORMAT " %"
5793 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5799 diff = stop - cstop;
5804 /* bring clipped time to samples and then to bytes */
5805 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5807 GST_DEBUG_OBJECT (qtdemux,
5808 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5809 " bytes", GST_TIME_ARGS (cstop), diff);
5814 if (offset != 0 || size != osize)
5815 gst_buffer_resize (buf, offset, size);
5817 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5818 GST_BUFFER_PTS (buf) = pts;
5819 GST_BUFFER_DURATION (buf) = duration;
5823 /* dropped buffer */
5826 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5831 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5836 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5841 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5842 gst_buffer_unref (buf);
5848 gst_qtdemux_align_buffer (GstQTDemux * demux,
5849 GstBuffer * buffer, gsize alignment)
5853 gst_buffer_map (buffer, &map, GST_MAP_READ);
5855 if (map.size < sizeof (guintptr)) {
5856 gst_buffer_unmap (buffer, &map);
5860 if (((guintptr) map.data) & (alignment - 1)) {
5861 GstBuffer *new_buffer;
5862 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5864 new_buffer = gst_buffer_new_allocate (NULL,
5865 gst_buffer_get_size (buffer), ¶ms);
5867 /* Copy data "by hand", so ensure alignment is kept: */
5868 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5870 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5871 GST_DEBUG_OBJECT (demux,
5872 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5875 gst_buffer_unmap (buffer, &map);
5876 gst_buffer_unref (buffer);
5881 gst_buffer_unmap (buffer, &map);
5885 /* the input buffer metadata must be writable,
5886 * but time/duration etc not yet set and need not be preserved */
5888 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5895 /* not many cases for now */
5896 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
5897 /* send a one time dvd clut event */
5898 if (stream->pending_event && stream->pad)
5899 gst_pad_push_event (stream->pad, stream->pending_event);
5900 stream->pending_event = NULL;
5903 if (G_UNLIKELY (stream->subtype != FOURCC_text
5904 && stream->subtype != FOURCC_sbtl &&
5905 stream->subtype != FOURCC_subp)) {
5909 gst_buffer_map (buf, &map, GST_MAP_READ);
5911 /* empty buffer is sent to terminate previous subtitle */
5912 if (map.size <= 2) {
5913 gst_buffer_unmap (buf, &map);
5914 gst_buffer_unref (buf);
5917 if (stream->subtype == FOURCC_subp) {
5918 /* That's all the processing needed for subpictures */
5919 gst_buffer_unmap (buf, &map);
5923 nsize = GST_READ_UINT16_BE (map.data);
5924 nsize = MIN (nsize, map.size - 2);
5926 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5929 /* takes care of UTF-8 validation or UTF-16 recognition,
5930 * no other encoding expected */
5931 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5932 gst_buffer_unmap (buf, &map);
5934 gst_buffer_unref (buf);
5935 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5937 /* this should not really happen unless the subtitle is corrupted */
5938 gst_buffer_unref (buf);
5942 /* FIXME ? convert optional subsequent style info to markup */
5947 /* Sets a buffer's attributes properly and pushes it downstream.
5948 * Also checks for additional actions and custom processing that may
5949 * need to be done first.
5951 static GstFlowReturn
5952 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
5953 QtDemuxStream * stream, GstBuffer * buf,
5954 GstClockTime dts, GstClockTime pts, GstClockTime duration,
5955 gboolean keyframe, GstClockTime position, guint64 byte_position)
5957 GstFlowReturn ret = GST_FLOW_OK;
5959 /* offset the timestamps according to the edit list */
5961 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
5965 gst_buffer_map (buf, &map, GST_MAP_READ);
5966 url = g_strndup ((gchar *) map.data, map.size);
5967 gst_buffer_unmap (buf, &map);
5968 if (url != NULL && strlen (url) != 0) {
5969 /* we have RTSP redirect now */
5970 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5971 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
5972 gst_structure_new ("redirect",
5973 "new-location", G_TYPE_STRING, url, NULL)));
5974 qtdemux->posted_redirect = TRUE;
5976 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
5982 /* position reporting */
5983 if (qtdemux->segment.rate >= 0) {
5984 qtdemux->segment.position = position;
5985 gst_qtdemux_sync_streams (qtdemux);
5988 if (G_UNLIKELY (!stream->pad)) {
5989 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
5990 gst_buffer_unref (buf);
5994 /* send out pending buffers */
5995 while (stream->buffers) {
5996 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
5998 if (G_UNLIKELY (stream->discont)) {
5999 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6000 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6001 stream->discont = FALSE;
6003 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6006 if (stream->alignment > 1)
6007 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6008 gst_pad_push (stream->pad, buffer);
6010 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6013 /* we're going to modify the metadata */
6014 buf = gst_buffer_make_writable (buf);
6016 if (G_UNLIKELY (stream->need_process))
6017 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6023 GST_BUFFER_DTS (buf) = dts;
6024 GST_BUFFER_PTS (buf) = pts;
6025 GST_BUFFER_DURATION (buf) = duration;
6026 GST_BUFFER_OFFSET (buf) = -1;
6027 GST_BUFFER_OFFSET_END (buf) = -1;
6029 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6030 gst_buffer_append_memory (buf,
6031 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6033 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6034 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6037 if (G_UNLIKELY (qtdemux->element_index)) {
6038 GstClockTime stream_time;
6041 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6043 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6044 GST_LOG_OBJECT (qtdemux,
6045 "adding association %" GST_TIME_FORMAT "-> %"
6046 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6047 gst_index_add_association (qtdemux->element_index,
6049 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6050 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6051 GST_FORMAT_BYTES, byte_position, NULL);
6056 if (stream->need_clip)
6057 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6059 if (G_UNLIKELY (buf == NULL))
6062 if (G_UNLIKELY (stream->discont)) {
6063 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6064 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6065 stream->discont = FALSE;
6067 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6071 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6072 stream->on_keyframe = FALSE;
6074 stream->on_keyframe = TRUE;
6078 GST_LOG_OBJECT (qtdemux,
6079 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6080 ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
6081 GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
6082 GST_PAD_NAME (stream->pad));
6084 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
6085 GstStructure *crypto_info;
6086 QtDemuxCencSampleSetInfo *info =
6087 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6091 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6092 gst_pad_push_event (stream->pad, event);
6095 if (info->crypto_info == NULL) {
6096 GST_DEBUG_OBJECT (qtdemux, "cenc metadata hasn't been parsed yet");
6097 gst_buffer_unref (buf);
6101 /* The end of the crypto_info array matches our n_samples position,
6102 * so count backward from there */
6103 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6104 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6105 /* steal structure from array */
6106 crypto_info = g_ptr_array_index (info->crypto_info, index);
6107 g_ptr_array_index (info->crypto_info, index) = NULL;
6108 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6109 info->crypto_info->len);
6110 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6111 GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
6113 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6114 index, stream->sample_index);
6118 if (stream->alignment > 1)
6119 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6121 ret = gst_pad_push (stream->pad, buf);
6123 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6124 /* mark position in stream, we'll need this to know when to send GAP event */
6125 stream->segment.position = pts + duration;
6132 static const QtDemuxRandomAccessEntry *
6133 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6134 GstClockTime pos, gboolean after)
6136 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6137 guint n_entries = stream->n_ra_entries;
6140 /* we assume the table is sorted */
6141 for (i = 0; i < n_entries; ++i) {
6142 if (entries[i].ts > pos)
6146 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6147 * probably okay to assume that the index lists the very first fragment */
6154 return &entries[i - 1];
6158 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6160 const QtDemuxRandomAccessEntry *best_entry = NULL;
6163 GST_OBJECT_LOCK (qtdemux);
6165 g_assert (qtdemux->n_streams > 0);
6167 for (i = 0; i < qtdemux->n_streams; i++) {
6168 const QtDemuxRandomAccessEntry *entry;
6169 QtDemuxStream *stream;
6170 gboolean is_audio_or_video;
6172 stream = qtdemux->streams[i];
6174 g_free (stream->samples);
6175 stream->samples = NULL;
6176 stream->n_samples = 0;
6177 stream->stbl_index = -1; /* no samples have yet been parsed */
6178 stream->sample_index = -1;
6180 if (stream->protection_scheme_info) {
6181 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6182 if (stream->protection_scheme_type == FOURCC_cenc) {
6183 QtDemuxCencSampleSetInfo *info =
6184 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6185 if (info->crypto_info) {
6186 g_ptr_array_free (info->crypto_info, TRUE);
6187 info->crypto_info = NULL;
6192 if (stream->ra_entries == NULL)
6195 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6196 is_audio_or_video = TRUE;
6198 is_audio_or_video = FALSE;
6201 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6202 stream->time_position, !is_audio_or_video);
6204 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6205 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6207 stream->pending_seek = entry;
6209 /* decide position to jump to just based on audio/video tracks, not subs */
6210 if (!is_audio_or_video)
6213 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6217 if (best_entry == NULL) {
6218 GST_OBJECT_UNLOCK (qtdemux);
6222 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6223 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6224 GST_TIME_ARGS (qtdemux->streams[0]->time_position),
6225 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6227 qtdemux->moof_offset = best_entry->moof_offset;
6229 qtdemux_add_fragmented_samples (qtdemux);
6231 GST_OBJECT_UNLOCK (qtdemux);
6235 static GstFlowReturn
6236 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6238 GstFlowReturn ret = GST_FLOW_OK;
6239 GstBuffer *buf = NULL;
6240 QtDemuxStream *stream;
6241 GstClockTime min_time;
6243 GstClockTime dts = GST_CLOCK_TIME_NONE;
6244 GstClockTime pts = GST_CLOCK_TIME_NONE;
6245 GstClockTime duration = 0;
6246 gboolean keyframe = FALSE;
6247 guint sample_size = 0;
6253 gst_qtdemux_push_pending_newsegment (qtdemux);
6255 if (qtdemux->fragmented_seek_pending) {
6256 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6257 gst_qtdemux_do_fragmented_seek (qtdemux);
6258 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6259 qtdemux->fragmented_seek_pending = FALSE;
6262 /* Figure out the next stream sample to output, min_time is expressed in
6263 * global time and runs over the edit list segments. */
6264 min_time = G_MAXUINT64;
6266 for (i = 0; i < qtdemux->n_streams; i++) {
6267 GstClockTime position;
6269 stream = qtdemux->streams[i];
6270 position = stream->time_position;
6272 /* position of -1 is EOS */
6273 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6274 min_time = position;
6279 if (G_UNLIKELY (index == -1)) {
6280 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6284 /* check for segment end */
6285 if (G_UNLIKELY (qtdemux->segment.stop != -1
6286 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6287 || (qtdemux->segment.rate < 0
6288 && qtdemux->segment.start > min_time))
6289 && qtdemux->streams[index]->on_keyframe)) {
6290 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6291 qtdemux->streams[index]->time_position = GST_CLOCK_TIME_NONE;
6295 /* gap events for subtitle streams */
6296 for (i = 0; i < qtdemux->n_streams; i++) {
6297 stream = qtdemux->streams[i];
6298 if (stream->pad && (stream->subtype == FOURCC_subp
6299 || stream->subtype == FOURCC_text
6300 || stream->subtype == FOURCC_sbtl)) {
6301 /* send one second gap events until the stream catches up */
6302 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6303 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6304 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6305 stream->segment.position + GST_SECOND < min_time) {
6307 gst_event_new_gap (stream->segment.position, GST_SECOND);
6308 gst_pad_push_event (stream->pad, gap);
6309 stream->segment.position += GST_SECOND;
6314 stream = qtdemux->streams[index];
6315 /* fetch info for the current sample of this stream */
6316 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6317 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6320 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6321 if (stream->new_caps) {
6322 gst_qtdemux_configure_stream (qtdemux, stream);
6323 qtdemux_do_allocation (qtdemux, stream);
6326 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6327 if (G_UNLIKELY (qtdemux->
6328 segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6329 if (stream->subtype == FOURCC_vide && !keyframe) {
6330 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on stream %d", index);
6335 GST_DEBUG_OBJECT (qtdemux,
6336 "pushing from stream %d, empty %d offset %" G_GUINT64_FORMAT
6337 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6338 ", duration %" GST_TIME_FORMAT, index, empty, offset, sample_size,
6339 GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
6341 if (G_UNLIKELY (empty)) {
6342 /* empty segment, push a gap and move to the next one */
6343 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6344 stream->segment.position = pts + duration;
6348 /* hmm, empty sample, skip and move to next sample */
6349 if (G_UNLIKELY (sample_size <= 0))
6352 /* last pushed sample was out of boundary, goto next sample */
6353 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6356 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6359 GST_DEBUG_OBJECT (qtdemux,
6360 "size %d larger than stream max_buffer_size %d, trimming",
6361 sample_size, stream->max_buffer_size);
6363 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6366 if (qtdemux->cenc_aux_info_offset > 0) {
6369 GstBuffer *aux_info = NULL;
6371 /* pull the data stored before the sample */
6373 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6374 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6375 if (G_UNLIKELY (ret != GST_FLOW_OK))
6377 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6378 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6379 gst_byte_reader_init (&br, map.data + 8, map.size);
6380 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6381 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6382 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6383 gst_buffer_unmap (aux_info, &map);
6384 gst_buffer_unref (aux_info);
6385 ret = GST_FLOW_ERROR;
6388 gst_buffer_unmap (aux_info, &map);
6389 gst_buffer_unref (aux_info);
6392 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6395 if (stream->use_allocator) {
6396 /* if we have a per-stream allocator, use it */
6397 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6400 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6402 if (G_UNLIKELY (ret != GST_FLOW_OK))
6405 if (size != sample_size) {
6406 pts += gst_util_uint64_scale_int (GST_SECOND,
6407 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6410 gst_util_uint64_scale_int (GST_SECOND,
6411 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6414 gst_util_uint64_scale_int (GST_SECOND,
6415 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6418 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6419 dts, pts, duration, keyframe, min_time, offset);
6421 if (size != sample_size) {
6422 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6423 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6425 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6427 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6428 if (time_position >= segment->media_start) {
6429 /* inside the segment, update time_position, looks very familiar to
6430 * GStreamer segments, doesn't it? */
6431 stream->time_position = (time_position - segment->media_start) +
6434 /* not yet in segment, time does not yet increment. This means
6435 * that we are still prerolling keyframes to the decoder so it can
6436 * decode the first sample of the segment. */
6437 stream->time_position = segment->time;
6442 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6443 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6444 * we have no more data for the pad to push */
6445 if (ret == GST_FLOW_EOS)
6448 stream->offset_in_sample += size;
6449 if (stream->offset_in_sample >= sample_size) {
6450 gst_qtdemux_advance_sample (qtdemux, stream);
6455 gst_qtdemux_advance_sample (qtdemux, stream);
6463 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6469 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6470 /* EOS will be raised if all are EOS */
6477 gst_qtdemux_loop (GstPad * pad)
6479 GstQTDemux *qtdemux;
6483 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6485 cur_offset = qtdemux->offset;
6486 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6487 cur_offset, qt_demux_state_string (qtdemux->state));
6489 switch (qtdemux->state) {
6490 case QTDEMUX_STATE_INITIAL:
6491 case QTDEMUX_STATE_HEADER:
6492 ret = gst_qtdemux_loop_state_header (qtdemux);
6494 case QTDEMUX_STATE_MOVIE:
6495 ret = gst_qtdemux_loop_state_movie (qtdemux);
6496 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6497 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6505 /* if something went wrong, pause */
6506 if (ret != GST_FLOW_OK)
6510 gst_object_unref (qtdemux);
6516 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6517 (NULL), ("streaming stopped, invalid state"));
6518 gst_pad_pause_task (pad);
6519 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6524 const gchar *reason = gst_flow_get_name (ret);
6526 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6528 gst_pad_pause_task (pad);
6530 /* fatal errors need special actions */
6532 if (ret == GST_FLOW_EOS) {
6533 if (qtdemux->n_streams == 0) {
6534 /* we have no streams, post an error */
6535 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6537 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6540 if ((stop = qtdemux->segment.stop) == -1)
6541 stop = qtdemux->segment.duration;
6543 if (qtdemux->segment.rate >= 0) {
6544 GstMessage *message;
6547 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6548 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6549 GST_FORMAT_TIME, stop);
6550 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6551 if (qtdemux->segment_seqnum) {
6552 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6553 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6555 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6556 gst_qtdemux_push_event (qtdemux, event);
6558 GstMessage *message;
6561 /* For Reverse Playback */
6562 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6563 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6564 GST_FORMAT_TIME, qtdemux->segment.start);
6565 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6566 qtdemux->segment.start);
6567 if (qtdemux->segment_seqnum) {
6568 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6569 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6571 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6572 gst_qtdemux_push_event (qtdemux, event);
6577 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6578 event = gst_event_new_eos ();
6579 if (qtdemux->segment_seqnum)
6580 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6581 gst_qtdemux_push_event (qtdemux, event);
6583 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6584 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6585 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6594 * Returns if there are samples to be played.
6597 has_next_entry (GstQTDemux * demux)
6599 QtDemuxStream *stream;
6602 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6604 for (i = 0; i < demux->n_streams; i++) {
6605 stream = demux->streams[i];
6607 if (stream->sample_index == -1) {
6608 stream->sample_index = 0;
6609 stream->offset_in_sample = 0;
6612 if (stream->sample_index >= stream->n_samples) {
6613 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
6616 GST_DEBUG_OBJECT (demux, "Found a sample");
6620 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6627 * Returns the size of the first entry at the current offset.
6628 * If -1, there are none (which means EOS or empty file).
6631 next_entry_size (GstQTDemux * demux)
6633 QtDemuxStream *stream;
6636 guint64 smalloffs = (guint64) - 1;
6637 QtDemuxSample *sample;
6639 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6642 for (i = 0; i < demux->n_streams; i++) {
6643 stream = demux->streams[i];
6645 if (stream->sample_index == -1) {
6646 stream->sample_index = 0;
6647 stream->offset_in_sample = 0;
6650 if (stream->sample_index >= stream->n_samples) {
6651 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
6655 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6656 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6657 stream->sample_index);
6661 sample = &stream->samples[stream->sample_index];
6663 GST_LOG_OBJECT (demux,
6664 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
6665 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
6666 sample->offset, sample->size);
6668 if (((smalloffs == -1)
6669 || (sample->offset < smalloffs)) && (sample->size)) {
6671 smalloffs = sample->offset;
6675 GST_LOG_OBJECT (demux,
6676 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
6677 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
6682 stream = demux->streams[smallidx];
6683 sample = &stream->samples[stream->sample_index];
6685 if (sample->offset >= demux->offset) {
6686 demux->todrop = sample->offset - demux->offset;
6687 return sample->size + demux->todrop;
6690 GST_DEBUG_OBJECT (demux,
6691 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6696 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6698 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6700 gst_element_post_message (GST_ELEMENT_CAST (demux),
6701 gst_message_new_element (GST_OBJECT_CAST (demux),
6702 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6706 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6711 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6714 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6715 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6716 GST_SEEK_TYPE_NONE, -1);
6718 /* store seqnum to drop flush events, they don't need to reach downstream */
6719 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6720 res = gst_pad_push_event (demux->sinkpad, event);
6721 demux->offset_seek_seqnum = 0;
6726 /* check for seekable upstream, above and beyond a mere query */
6728 gst_qtdemux_check_seekability (GstQTDemux * demux)
6731 gboolean seekable = FALSE;
6732 gint64 start = -1, stop = -1;
6734 if (demux->upstream_size)
6737 if (demux->upstream_format_is_time)
6740 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6741 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6742 GST_DEBUG_OBJECT (demux, "seeking query failed");
6746 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6748 /* try harder to query upstream size if we didn't get it the first time */
6749 if (seekable && stop == -1) {
6750 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6751 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6754 /* if upstream doesn't know the size, it's likely that it's not seekable in
6755 * practice even if it technically may be seekable */
6756 if (seekable && (start != 0 || stop <= start)) {
6757 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6762 gst_query_unref (query);
6764 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6765 G_GUINT64_FORMAT ")", seekable, start, stop);
6766 demux->upstream_seekable = seekable;
6767 demux->upstream_size = seekable ? stop : -1;
6771 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6773 g_return_if_fail (bytes <= demux->todrop);
6775 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6776 gst_adapter_flush (demux->adapter, bytes);
6777 demux->neededbytes -= bytes;
6778 demux->offset += bytes;
6779 demux->todrop -= bytes;
6783 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
6785 if (G_UNLIKELY (demux->pending_newsegment)) {
6788 gst_qtdemux_push_pending_newsegment (demux);
6789 /* clear to send tags on all streams */
6790 for (i = 0; i < demux->n_streams; i++) {
6791 QtDemuxStream *stream;
6792 stream = demux->streams[i];
6793 gst_qtdemux_push_tags (demux, stream);
6794 if (CUR_STREAM (stream)->sparse) {
6795 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6796 gst_pad_push_event (stream->pad,
6797 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
6804 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
6805 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
6807 GstClockTime ts, dur;
6812 stream->segments[segment_index].duration - (pos -
6813 stream->segments[segment_index].time);
6814 gap = gst_event_new_gap (ts, dur);
6815 stream->time_position += dur;
6817 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
6818 "segment: %" GST_PTR_FORMAT, gap);
6819 gst_pad_push_event (stream->pad, gap);
6823 gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
6824 QtDemuxStream * stream)
6828 /* Push any initial gap segments before proceeding to the
6830 for (i = 0; i < stream->n_segments; i++) {
6831 gst_qtdemux_activate_segment (demux, stream, i, stream->time_position);
6833 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
6834 gst_qtdemux_send_gap_for_segment (demux, stream, i,
6835 stream->time_position);
6837 /* Only support empty segment at the beginning followed by
6838 * one non-empty segment, this was checked when parsing the
6839 * edts atom, arriving here is unexpected */
6840 g_assert (i + 1 == stream->n_segments);
6846 static GstFlowReturn
6847 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6851 demux = GST_QTDEMUX (parent);
6853 GST_DEBUG_OBJECT (demux,
6854 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
6855 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
6856 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
6857 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
6858 gst_buffer_get_size (inbuf), demux->offset);
6860 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6861 gboolean is_gap_input = FALSE;
6864 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6866 for (i = 0; i < demux->n_streams; i++) {
6867 demux->streams[i]->discont = TRUE;
6870 /* Check if we can land back on our feet in the case where upstream is
6871 * handling the seeking/pushing of samples with gaps in between (like
6872 * in the case of trick-mode DASH for example) */
6873 if (demux->upstream_format_is_time
6874 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
6876 for (i = 0; i < demux->n_streams; i++) {
6878 GST_LOG_OBJECT (demux,
6879 "Stream #%d , checking if offset %" G_GUINT64_FORMAT
6880 " is a sample start", i, GST_BUFFER_OFFSET (inbuf));
6882 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
6883 demux->streams[i], GST_BUFFER_OFFSET (inbuf));
6885 QtDemuxSample *sample = &demux->streams[i]->samples[res];
6886 GST_LOG_OBJECT (demux,
6887 "Checking if sample %d from stream %d is valid (offset:%"
6888 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res, i,
6889 sample->offset, sample->size);
6890 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
6891 GST_LOG_OBJECT (demux,
6892 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
6894 is_gap_input = TRUE;
6895 /* We can go back to standard playback mode */
6896 demux->state = QTDEMUX_STATE_MOVIE;
6897 /* Remember which sample this stream is at */
6898 demux->streams[i]->sample_index = res;
6899 /* Finally update all push-based values to the expected values */
6900 demux->neededbytes = demux->streams[i]->samples[res].size;
6901 demux->offset = GST_BUFFER_OFFSET (inbuf);
6903 demux->mdatsize - demux->offset + demux->mdatoffset;
6908 if (!is_gap_input) {
6909 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
6910 /* Reset state if it's a real discont */
6911 demux->neededbytes = 16;
6912 demux->state = QTDEMUX_STATE_INITIAL;
6913 demux->offset = GST_BUFFER_OFFSET (inbuf);
6914 gst_adapter_clear (demux->adapter);
6917 /* Reverse fragmented playback, need to flush all we have before
6918 * consuming a new fragment.
6919 * The samples array have the timestamps calculated by accumulating the
6920 * durations but this won't work for reverse playback of fragments as
6921 * the timestamps of a subsequent fragment should be smaller than the
6922 * previously received one. */
6923 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
6924 gst_qtdemux_process_adapter (demux, TRUE);
6925 for (i = 0; i < demux->n_streams; i++)
6926 gst_qtdemux_stream_flush_samples_data (demux, demux->streams[i]);
6930 gst_adapter_push (demux->adapter, inbuf);
6932 GST_DEBUG_OBJECT (demux,
6933 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6934 demux->neededbytes, gst_adapter_available (demux->adapter));
6936 return gst_qtdemux_process_adapter (demux, FALSE);
6939 static GstFlowReturn
6940 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6942 GstFlowReturn ret = GST_FLOW_OK;
6944 /* we never really mean to buffer that much */
6945 if (demux->neededbytes == -1) {
6949 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6950 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6952 #ifndef GST_DISABLE_GST_DEBUG
6954 guint64 discont_offset, distance_from_discont;
6956 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
6957 distance_from_discont =
6958 gst_adapter_distance_from_discont (demux->adapter);
6960 GST_DEBUG_OBJECT (demux,
6961 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
6962 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
6963 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
6964 demux->offset, discont_offset, distance_from_discont);
6968 switch (demux->state) {
6969 case QTDEMUX_STATE_INITIAL:{
6974 gst_qtdemux_check_seekability (demux);
6976 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6978 /* get fourcc/length, set neededbytes */
6979 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6981 gst_adapter_unmap (demux->adapter);
6983 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
6984 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
6986 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6987 (_("This file is invalid and cannot be played.")),
6988 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
6989 GST_FOURCC_ARGS (fourcc)));
6990 ret = GST_FLOW_ERROR;
6993 if (fourcc == FOURCC_mdat) {
6994 gint next_entry = next_entry_size (demux);
6995 if (demux->n_streams > 0 && (next_entry != -1 || !demux->fragmented)) {
6996 /* we have the headers, start playback */
6997 demux->state = QTDEMUX_STATE_MOVIE;
6998 demux->neededbytes = next_entry;
6999 demux->mdatleft = size;
7000 demux->mdatsize = demux->mdatleft;
7002 /* no headers yet, try to get them */
7005 guint64 old, target;
7008 old = demux->offset;
7009 target = old + size;
7011 /* try to jump over the atom with a seek */
7012 /* only bother if it seems worth doing so,
7013 * and avoids possible upstream/server problems */
7014 if (demux->upstream_seekable &&
7015 demux->upstream_size > 4 * (1 << 20)) {
7016 res = qtdemux_seek_offset (demux, target);
7018 GST_DEBUG_OBJECT (demux, "skipping seek");
7023 GST_DEBUG_OBJECT (demux, "seek success");
7024 /* remember the offset fo the first mdat so we can seek back to it
7025 * after we have the headers */
7026 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7027 demux->first_mdat = old;
7028 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7031 /* seek worked, continue reading */
7032 demux->offset = target;
7033 demux->neededbytes = 16;
7034 demux->state = QTDEMUX_STATE_INITIAL;
7036 /* seek failed, need to buffer */
7037 demux->offset = old;
7038 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7039 /* there may be multiple mdat (or alike) buffers */
7041 if (demux->mdatbuffer)
7042 bs = gst_buffer_get_size (demux->mdatbuffer);
7045 if (size + bs > 10 * (1 << 20))
7047 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7048 demux->neededbytes = size;
7049 if (!demux->mdatbuffer)
7050 demux->mdatoffset = demux->offset;
7053 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7054 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7055 (_("This file is invalid and cannot be played.")),
7056 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7057 GST_FOURCC_ARGS (fourcc), size));
7058 ret = GST_FLOW_ERROR;
7061 /* this means we already started buffering and still no moov header,
7062 * let's continue buffering everything till we get moov */
7063 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7064 || fourcc == FOURCC_moof))
7066 demux->neededbytes = size;
7067 demux->state = QTDEMUX_STATE_HEADER;
7071 case QTDEMUX_STATE_HEADER:{
7075 GST_DEBUG_OBJECT (demux, "In header");
7077 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7079 /* parse the header */
7080 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7082 if (fourcc == FOURCC_moov) {
7085 /* in usual fragmented setup we could try to scan for more
7086 * and end up at the the moov (after mdat) again */
7087 if (demux->got_moov && demux->n_streams > 0 &&
7089 || demux->last_moov_offset == demux->offset)) {
7090 GST_DEBUG_OBJECT (demux,
7091 "Skipping moov atom as we have (this) one already");
7093 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7095 if (demux->got_moov && demux->fragmented) {
7096 GST_DEBUG_OBJECT (demux,
7097 "Got a second moov, clean up data from old one");
7098 if (demux->moov_node_compressed) {
7099 g_node_destroy (demux->moov_node_compressed);
7100 if (demux->moov_node)
7101 g_free (demux->moov_node->data);
7103 demux->moov_node_compressed = NULL;
7104 if (demux->moov_node)
7105 g_node_destroy (demux->moov_node);
7106 demux->moov_node = NULL;
7108 /* prepare newsegment to send when streaming actually starts */
7109 if (!demux->pending_newsegment) {
7110 demux->pending_newsegment =
7111 gst_event_new_segment (&demux->segment);
7112 if (demux->segment_seqnum)
7113 gst_event_set_seqnum (demux->pending_newsegment,
7114 demux->segment_seqnum);
7118 demux->last_moov_offset = demux->offset;
7120 qtdemux_parse_moov (demux, data, demux->neededbytes);
7121 qtdemux_node_dump (demux, demux->moov_node);
7122 qtdemux_parse_tree (demux);
7123 qtdemux_prepare_streams (demux);
7124 if (!demux->got_moov)
7125 qtdemux_expose_streams (demux);
7128 for (n = 0; n < demux->n_streams; n++) {
7129 QtDemuxStream *stream = demux->streams[n];
7131 gst_qtdemux_configure_stream (demux, stream);
7135 demux->got_moov = TRUE;
7136 gst_qtdemux_check_send_pending_segment (demux);
7138 /* fragmented streams headers shouldn't contain edts atoms */
7139 if (!demux->fragmented) {
7140 for (n = 0; n < demux->n_streams; n++) {
7141 gst_qtdemux_stream_send_initial_gap_segments (demux,
7146 if (demux->moov_node_compressed) {
7147 g_node_destroy (demux->moov_node_compressed);
7148 g_free (demux->moov_node->data);
7150 demux->moov_node_compressed = NULL;
7151 g_node_destroy (demux->moov_node);
7152 demux->moov_node = NULL;
7153 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7155 } else if (fourcc == FOURCC_moof) {
7156 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7158 GstClockTime prev_pts;
7159 guint64 prev_offset;
7160 guint64 adapter_discont_offset, adapter_discont_dist;
7162 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7165 * The timestamp of the moof buffer is relevant as some scenarios
7166 * won't have the initial timestamp in the atoms. Whenever a new
7167 * buffer has started, we get that buffer's PTS and use it as a base
7168 * timestamp for the trun entries.
7170 * To keep track of the current buffer timestamp and starting point
7171 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7172 * from the beggining of the buffer, with the distance and demux->offset
7173 * we know if it is still the same buffer or not.
7175 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7176 prev_offset = demux->offset - dist;
7177 if (demux->fragment_start_offset == -1
7178 || prev_offset > demux->fragment_start_offset) {
7179 demux->fragment_start_offset = prev_offset;
7180 demux->fragment_start = prev_pts;
7181 GST_DEBUG_OBJECT (demux,
7182 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7183 GST_TIME_FORMAT, demux->fragment_start_offset,
7184 GST_TIME_ARGS (demux->fragment_start));
7187 /* We can't use prev_offset() here because this would require
7188 * upstream to set consistent and correct offsets on all buffers
7189 * since the discont. Nothing ever did that in the past and we
7190 * would break backwards compatibility here then.
7191 * Instead take the offset we had at the last discont and count
7192 * the bytes from there. This works with old code as there would
7193 * be no discont between moov and moof, and also works with
7194 * adaptivedemux which correctly sets offset and will set the
7195 * DISCONT flag accordingly when needed.
7197 * We also only do this for upstream TIME segments as otherwise
7198 * there are potential backwards compatibility problems with
7199 * seeking in PUSH mode and upstream providing inconsistent
7201 adapter_discont_offset =
7202 gst_adapter_offset_at_discont (demux->adapter);
7203 adapter_discont_dist =
7204 gst_adapter_distance_from_discont (demux->adapter);
7206 GST_DEBUG_OBJECT (demux,
7207 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7208 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7209 demux->offset, adapter_discont_offset, adapter_discont_dist);
7211 if (demux->upstream_format_is_time) {
7212 demux->moof_offset = adapter_discont_offset;
7213 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7214 demux->moof_offset += adapter_discont_dist;
7215 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7216 demux->moof_offset = demux->offset;
7218 demux->moof_offset = demux->offset;
7221 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7222 demux->moof_offset, NULL)) {
7223 gst_adapter_unmap (demux->adapter);
7224 ret = GST_FLOW_ERROR;
7227 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7228 if (demux->mss_mode && !demux->exposed) {
7229 if (!demux->pending_newsegment) {
7230 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
7231 demux->pending_newsegment =
7232 gst_event_new_segment (&demux->segment);
7233 if (demux->segment_seqnum)
7234 gst_event_set_seqnum (demux->pending_newsegment,
7235 demux->segment_seqnum);
7237 qtdemux_expose_streams (demux);
7240 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7242 } else if (fourcc == FOURCC_ftyp) {
7243 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7244 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7245 } else if (fourcc == FOURCC_uuid) {
7246 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7247 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7248 } else if (fourcc == FOURCC_sidx) {
7249 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7250 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7254 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7257 /* [free] is a padding atom */
7258 GST_DEBUG_OBJECT (demux,
7259 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7260 GST_FOURCC_ARGS (fourcc));
7263 GST_WARNING_OBJECT (demux,
7264 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7265 GST_FOURCC_ARGS (fourcc));
7266 /* Let's jump that one and go back to initial state */
7270 gst_adapter_unmap (demux->adapter);
7273 if (demux->mdatbuffer && demux->n_streams) {
7274 gsize remaining_data_size = 0;
7276 /* the mdat was before the header */
7277 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7278 demux->n_streams, demux->mdatbuffer);
7279 /* restore our adapter/offset view of things with upstream;
7280 * put preceding buffered data ahead of current moov data.
7281 * This should also handle evil mdat, moov, mdat cases and alike */
7282 gst_adapter_flush (demux->adapter, demux->neededbytes);
7284 /* Store any remaining data after the mdat for later usage */
7285 remaining_data_size = gst_adapter_available (demux->adapter);
7286 if (remaining_data_size > 0) {
7287 g_assert (demux->restoredata_buffer == NULL);
7288 demux->restoredata_buffer =
7289 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7290 demux->restoredata_offset = demux->offset + demux->neededbytes;
7291 GST_DEBUG_OBJECT (demux,
7292 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7293 G_GUINT64_FORMAT, remaining_data_size,
7294 demux->restoredata_offset);
7297 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7298 demux->mdatbuffer = NULL;
7299 demux->offset = demux->mdatoffset;
7300 demux->neededbytes = next_entry_size (demux);
7301 demux->state = QTDEMUX_STATE_MOVIE;
7302 demux->mdatleft = gst_adapter_available (demux->adapter);
7303 demux->mdatsize = demux->mdatleft;
7305 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7306 gst_adapter_flush (demux->adapter, demux->neededbytes);
7308 /* only go back to the mdat if there are samples to play */
7309 if (demux->got_moov && demux->first_mdat != -1
7310 && has_next_entry (demux)) {
7313 /* we need to seek back */
7314 res = qtdemux_seek_offset (demux, demux->first_mdat);
7316 demux->offset = demux->first_mdat;
7318 GST_DEBUG_OBJECT (demux, "Seek back failed");
7321 demux->offset += demux->neededbytes;
7323 demux->neededbytes = 16;
7324 demux->state = QTDEMUX_STATE_INITIAL;
7329 case QTDEMUX_STATE_BUFFER_MDAT:{
7333 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7335 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7336 gst_buffer_extract (buf, 0, fourcc, 4);
7337 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7338 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7339 if (demux->mdatbuffer)
7340 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7342 demux->mdatbuffer = buf;
7343 demux->offset += demux->neededbytes;
7344 demux->neededbytes = 16;
7345 demux->state = QTDEMUX_STATE_INITIAL;
7346 gst_qtdemux_post_progress (demux, 1, 1);
7350 case QTDEMUX_STATE_MOVIE:{
7351 QtDemuxStream *stream = NULL;
7352 QtDemuxSample *sample;
7354 GstClockTime dts, pts, duration;
7357 GST_DEBUG_OBJECT (demux,
7358 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7360 if (demux->fragmented) {
7361 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7363 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7364 /* if needed data starts within this atom,
7365 * then it should not exceed this atom */
7366 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7367 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7368 (_("This file is invalid and cannot be played.")),
7369 ("sample data crosses atom boundary"));
7370 ret = GST_FLOW_ERROR;
7373 demux->mdatleft -= demux->neededbytes;
7375 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7376 /* so we are dropping more than left in this atom */
7377 gst_qtdemux_drop_data (demux, demux->mdatleft);
7378 demux->mdatleft = 0;
7380 /* need to resume atom parsing so we do not miss any other pieces */
7381 demux->state = QTDEMUX_STATE_INITIAL;
7382 demux->neededbytes = 16;
7384 /* check if there was any stored post mdat data from previous buffers */
7385 if (demux->restoredata_buffer) {
7386 g_assert (gst_adapter_available (demux->adapter) == 0);
7388 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7389 demux->restoredata_buffer = NULL;
7390 demux->offset = demux->restoredata_offset;
7397 if (demux->todrop) {
7398 if (demux->cenc_aux_info_offset > 0) {
7402 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7403 data = gst_adapter_map (demux->adapter, demux->todrop);
7404 gst_byte_reader_init (&br, data + 8, demux->todrop);
7405 if (!qtdemux_parse_cenc_aux_info (demux, demux->streams[0], &br,
7406 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7407 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7408 ret = GST_FLOW_ERROR;
7409 gst_adapter_unmap (demux->adapter);
7410 g_free (demux->cenc_aux_info_sizes);
7411 demux->cenc_aux_info_sizes = NULL;
7414 demux->cenc_aux_info_offset = 0;
7415 g_free (demux->cenc_aux_info_sizes);
7416 demux->cenc_aux_info_sizes = NULL;
7417 gst_adapter_unmap (demux->adapter);
7419 gst_qtdemux_drop_data (demux, demux->todrop);
7423 /* initial newsegment sent here after having added pads,
7424 * possible others in sink_event */
7425 gst_qtdemux_check_send_pending_segment (demux);
7427 /* Figure out which stream this packet belongs to */
7428 for (i = 0; i < demux->n_streams; i++) {
7429 stream = demux->streams[i];
7430 if (stream->sample_index >= stream->n_samples)
7432 GST_LOG_OBJECT (demux,
7433 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
7434 " / size:%d)", i, stream->sample_index,
7435 stream->samples[stream->sample_index].offset,
7436 stream->samples[stream->sample_index].size);
7438 if (stream->samples[stream->sample_index].offset == demux->offset)
7442 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
7443 goto unknown_stream;
7445 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7447 if (stream->new_caps) {
7448 gst_qtdemux_configure_stream (demux, stream);
7451 /* Put data in a buffer, set timestamps, caps, ... */
7452 sample = &stream->samples[stream->sample_index];
7454 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7455 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7456 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7458 dts = QTSAMPLE_DTS (stream, sample);
7459 pts = QTSAMPLE_PTS (stream, sample);
7460 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7461 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7463 /* check for segment end */
7464 if (G_UNLIKELY (demux->segment.stop != -1
7465 && demux->segment.stop <= pts && stream->on_keyframe)) {
7466 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7467 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7469 /* skip this data, stream is EOS */
7470 gst_adapter_flush (demux->adapter, demux->neededbytes);
7471 demux->offset += demux->neededbytes;
7473 /* check if all streams are eos */
7475 for (i = 0; i < demux->n_streams; i++) {
7476 if (!STREAM_IS_EOS (demux->streams[i])) {
7485 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7487 /* FIXME: should either be an assert or a plain check */
7488 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7490 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7491 dts, pts, duration, keyframe, dts, demux->offset);
7495 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7497 /* skip this data, stream is EOS */
7498 gst_adapter_flush (demux->adapter, demux->neededbytes);
7501 stream->sample_index++;
7502 stream->offset_in_sample = 0;
7504 /* update current offset and figure out size of next buffer */
7505 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7506 demux->offset, demux->neededbytes);
7507 demux->offset += demux->neededbytes;
7508 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7512 if (ret == GST_FLOW_EOS) {
7513 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7514 demux->neededbytes = -1;
7518 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7519 if (demux->fragmented) {
7520 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7521 /* there may be more to follow, only finish this atom */
7522 demux->todrop = demux->mdatleft;
7523 demux->neededbytes = demux->todrop;
7528 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7529 goto non_ok_unlinked_flow;
7538 /* when buffering movie data, at least show user something is happening */
7539 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7540 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7541 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7542 demux->neededbytes);
7549 non_ok_unlinked_flow:
7551 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7552 gst_flow_get_name (ret));
7557 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7558 ret = GST_FLOW_ERROR;
7563 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7569 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7570 (NULL), ("qtdemuxer invalid state %d", demux->state));
7571 ret = GST_FLOW_ERROR;
7576 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7577 (NULL), ("no 'moov' atom within the first 10 MB"));
7578 ret = GST_FLOW_ERROR;
7584 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7589 query = gst_query_new_scheduling ();
7591 if (!gst_pad_peer_query (sinkpad, query)) {
7592 gst_query_unref (query);
7596 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7597 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7598 gst_query_unref (query);
7603 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7604 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7608 GST_DEBUG_OBJECT (sinkpad, "activating push");
7609 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7614 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7615 GstPadMode mode, gboolean active)
7618 GstQTDemux *demux = GST_QTDEMUX (parent);
7621 case GST_PAD_MODE_PUSH:
7622 demux->pullbased = FALSE;
7625 case GST_PAD_MODE_PULL:
7627 demux->pullbased = TRUE;
7628 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7631 res = gst_pad_stop_task (sinkpad);
7643 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7649 memset (&z, 0, sizeof (z));
7654 if ((ret = inflateInit (&z)) != Z_OK) {
7655 GST_ERROR ("inflateInit() returned %d", ret);
7659 z.next_in = z_buffer;
7660 z.avail_in = z_length;
7662 buffer = (guint8 *) g_malloc (*length);
7663 z.avail_out = *length;
7664 z.next_out = (Bytef *) buffer;
7666 ret = inflate (&z, Z_NO_FLUSH);
7667 if (ret == Z_STREAM_END) {
7669 } else if (ret != Z_OK) {
7670 GST_WARNING ("inflate() returned %d", ret);
7675 buffer = (guint8 *) g_realloc (buffer, *length);
7676 z.next_out = (Bytef *) (buffer + z.total_out);
7677 z.avail_out += 4096;
7678 } while (z.avail_in > 0);
7680 if (ret != Z_STREAM_END) {
7685 *length = z.total_out;
7692 #endif /* HAVE_ZLIB */
7695 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7699 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7701 /* counts as header data */
7702 qtdemux->header_size += length;
7704 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7705 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7707 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7714 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7715 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7716 if (dcom == NULL || cmvd == NULL)
7717 goto invalid_compression;
7719 dcom_len = QT_UINT32 (dcom->data);
7721 goto invalid_compression;
7723 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7727 guint uncompressed_length;
7728 guint compressed_length;
7732 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7734 goto invalid_compression;
7736 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7737 compressed_length = cmvd_len - 12;
7738 GST_LOG ("length = %u", uncompressed_length);
7741 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7742 compressed_length, &uncompressed_length);
7745 qtdemux->moov_node_compressed = qtdemux->moov_node;
7746 qtdemux->moov_node = g_node_new (buf);
7748 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7749 uncompressed_length);
7753 #endif /* HAVE_ZLIB */
7755 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7756 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7763 invalid_compression:
7765 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
7771 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
7774 while (G_UNLIKELY (buf < end)) {
7778 if (G_UNLIKELY (buf + 4 > end)) {
7779 GST_LOG_OBJECT (qtdemux, "buffer overrun");
7782 len = QT_UINT32 (buf);
7783 if (G_UNLIKELY (len == 0)) {
7784 GST_LOG_OBJECT (qtdemux, "empty container");
7787 if (G_UNLIKELY (len < 8)) {
7788 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
7791 if (G_UNLIKELY (len > (end - buf))) {
7792 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
7793 (gint) (end - buf));
7797 child = g_node_new ((guint8 *) buf);
7798 g_node_append (node, child);
7799 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
7800 qtdemux_parse_node (qtdemux, child, buf, len);
7808 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
7811 int len = QT_UINT32 (xdxt->data);
7812 guint8 *buf = xdxt->data;
7813 guint8 *end = buf + len;
7816 /* skip size and type */
7824 size = QT_UINT32 (buf);
7825 type = QT_FOURCC (buf + 4);
7827 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
7829 if (buf + size > end || size <= 0)
7835 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
7836 GST_FOURCC_ARGS (type));
7840 buffer = gst_buffer_new_and_alloc (size);
7841 gst_buffer_fill (buffer, 0, buf, size);
7842 stream->buffers = g_slist_append (stream->buffers, buffer);
7843 GST_LOG_OBJECT (qtdemux, "parsing theora header");
7846 buffer = gst_buffer_new_and_alloc (size);
7847 gst_buffer_fill (buffer, 0, buf, size);
7848 stream->buffers = g_slist_append (stream->buffers, buffer);
7849 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
7852 buffer = gst_buffer_new_and_alloc (size);
7853 gst_buffer_fill (buffer, 0, buf, size);
7854 stream->buffers = g_slist_append (stream->buffers, buffer);
7855 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
7858 GST_WARNING_OBJECT (qtdemux,
7859 "unknown theora cookie %" GST_FOURCC_FORMAT,
7860 GST_FOURCC_ARGS (type));
7869 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
7873 guint32 node_length = 0;
7874 const QtNodeType *type;
7877 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
7879 if (G_UNLIKELY (length < 8))
7880 goto not_enough_data;
7882 node_length = QT_UINT32 (buffer);
7883 fourcc = QT_FOURCC (buffer + 4);
7885 /* ignore empty nodes */
7886 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
7889 type = qtdemux_type_get (fourcc);
7891 end = buffer + length;
7893 GST_LOG_OBJECT (qtdemux,
7894 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
7895 GST_FOURCC_ARGS (fourcc), node_length, type->name);
7897 if (node_length > length)
7898 goto broken_atom_size;
7900 if (type->flags & QT_FLAG_CONTAINER) {
7901 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7906 if (node_length < 20) {
7907 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
7910 GST_DEBUG_OBJECT (qtdemux,
7911 "parsing stsd (sample table, sample description) atom");
7912 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
7913 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7924 /* also read alac (or whatever) in stead of mp4a in the following,
7925 * since a similar layout is used in other cases as well */
7926 if (fourcc == FOURCC_mp4a)
7928 else if (fourcc == FOURCC_fLaC)
7933 /* There are two things we might encounter here: a true mp4a atom, and
7934 an mp4a entry in an stsd atom. The latter is what we're interested
7935 in, and it looks like an atom, but isn't really one. The true mp4a
7936 atom is short, so we detect it based on length here. */
7937 if (length < min_size) {
7938 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7939 GST_FOURCC_ARGS (fourcc));
7943 /* 'version' here is the sound sample description version. Types 0 and
7944 1 are documented in the QTFF reference, but type 2 is not: it's
7945 described in Apple header files instead (struct SoundDescriptionV2
7947 version = QT_UINT16 (buffer + 16);
7949 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7950 GST_FOURCC_ARGS (fourcc), version);
7952 /* parse any esds descriptors */
7964 GST_WARNING_OBJECT (qtdemux,
7965 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7966 GST_FOURCC_ARGS (fourcc), version);
7971 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7997 /* codec_data is contained inside these atoms, which all have
7998 * the same format. */
7999 /* video sample description size is 86 bytes without extension.
8000 * node_length have to be bigger than 86 bytes because video sample
8001 * description can include extenstions such as esds, fiel, glbl, etc. */
8002 if (node_length < 86) {
8003 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8004 " sample description length too short (%u < 86)",
8005 GST_FOURCC_ARGS (fourcc), node_length);
8009 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8010 GST_FOURCC_ARGS (fourcc));
8012 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8014 * revision level (2 bytes) : must be set to 0. */
8015 version = QT_UINT32 (buffer + 16);
8016 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8018 /* compressor name : PASCAL string and informative purposes
8019 * first byte : the number of bytes to be displayed.
8020 * it has to be less than 32 because it is reserved
8021 * space of 32 bytes total including itself. */
8022 str_len = QT_UINT8 (buffer + 50);
8024 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8025 (char *) buffer + 51);
8027 GST_WARNING_OBJECT (qtdemux,
8028 "compressorname length too big (%u > 31)", str_len);
8030 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8032 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8037 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8038 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8043 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8044 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8045 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8054 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8055 GST_FOURCC_ARGS (fourcc));
8059 version = QT_UINT32 (buffer + 12);
8060 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8067 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8072 if (length < offset) {
8073 GST_WARNING_OBJECT (qtdemux,
8074 "skipping too small %" GST_FOURCC_FORMAT " box",
8075 GST_FOURCC_ARGS (fourcc));
8078 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8084 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8089 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8094 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8097 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
8100 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
8103 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
8105 if (!strcmp (type->name, "unknown"))
8106 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8110 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8111 GST_FOURCC_ARGS (fourcc));
8117 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8118 (_("This file is corrupt and cannot be played.")),
8119 ("Not enough data for an atom header, got only %u bytes", length));
8124 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8125 (_("This file is corrupt and cannot be played.")),
8126 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8127 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8134 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8138 guint32 child_fourcc;
8140 for (child = g_node_first_child (node); child;
8141 child = g_node_next_sibling (child)) {
8142 buffer = (guint8 *) child->data;
8144 child_fourcc = QT_FOURCC (buffer + 4);
8146 if (G_UNLIKELY (child_fourcc == fourcc)) {
8154 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8155 GstByteReader * parser)
8159 guint32 child_fourcc, child_len;
8161 for (child = g_node_first_child (node); child;
8162 child = g_node_next_sibling (child)) {
8163 buffer = (guint8 *) child->data;
8165 child_len = QT_UINT32 (buffer);
8166 child_fourcc = QT_FOURCC (buffer + 4);
8168 if (G_UNLIKELY (child_fourcc == fourcc)) {
8169 if (G_UNLIKELY (child_len < (4 + 4)))
8171 /* FIXME: must verify if atom length < parent atom length */
8172 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8180 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8182 return g_node_nth_child (node, index);
8186 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8187 GstByteReader * parser)
8191 guint32 child_fourcc, child_len;
8193 for (child = g_node_next_sibling (node); child;
8194 child = g_node_next_sibling (child)) {
8195 buffer = (guint8 *) child->data;
8197 child_fourcc = QT_FOURCC (buffer + 4);
8199 if (child_fourcc == fourcc) {
8201 child_len = QT_UINT32 (buffer);
8202 if (G_UNLIKELY (child_len < (4 + 4)))
8204 /* FIXME: must verify if atom length < parent atom length */
8205 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8214 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8216 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8220 qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
8222 /* FIXME: This can only reliably work if demuxers have a
8223 * separate streaming thread per srcpad. This should be
8224 * done in a demuxer base class, which integrates parts
8227 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8232 query = gst_query_new_allocation (stream->caps, FALSE);
8234 if (!gst_pad_peer_query (stream->pad, query)) {
8235 /* not a problem, just debug a little */
8236 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8239 if (stream->allocator)
8240 gst_object_unref (stream->allocator);
8242 if (gst_query_get_n_allocation_params (query) > 0) {
8243 /* try the allocator */
8244 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8246 stream->use_allocator = TRUE;
8248 stream->allocator = NULL;
8249 gst_allocation_params_init (&stream->params);
8250 stream->use_allocator = FALSE;
8252 gst_query_unref (query);
8257 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8258 QtDemuxStream * stream)
8261 const gchar *selected_system;
8263 g_return_val_if_fail (qtdemux != NULL, FALSE);
8264 g_return_val_if_fail (stream != NULL, FALSE);
8265 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8268 if (stream->protection_scheme_type != FOURCC_cenc) {
8269 GST_ERROR_OBJECT (qtdemux, "unsupported protection scheme");
8272 if (qtdemux->protection_system_ids == NULL) {
8273 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
8274 "cenc protection system information has been found");
8277 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8278 selected_system = gst_protection_select_system ((const gchar **)
8279 qtdemux->protection_system_ids->pdata);
8280 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8281 qtdemux->protection_system_ids->len - 1);
8282 if (!selected_system) {
8283 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8284 "suitable decryptor element has been found");
8288 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8289 if (!gst_structure_has_name (s, "application/x-cenc")) {
8290 gst_structure_set (s,
8291 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8292 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8294 gst_structure_set_name (s, "application/x-cenc");
8300 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8302 if (stream->subtype == FOURCC_vide) {
8303 /* fps is calculated base on the duration of the average framerate since
8304 * qt does not have a fixed framerate. */
8305 gboolean fps_available = TRUE;
8307 if ((stream->n_samples == 1) && (stream->first_duration == 0)) {
8309 CUR_STREAM (stream)->fps_n = 0;
8310 CUR_STREAM (stream)->fps_d = 1;
8312 if (stream->duration == 0 || stream->n_samples < 2) {
8313 CUR_STREAM (stream)->fps_n = stream->timescale;
8314 CUR_STREAM (stream)->fps_d = 1;
8315 fps_available = FALSE;
8317 GstClockTime avg_duration;
8321 /* duration and n_samples can be updated for fragmented format
8322 * so, framerate of fragmented format is calculated using data in a moof */
8323 if (qtdemux->fragmented && stream->n_samples_moof > 0
8324 && stream->duration_moof > 0) {
8325 n_samples = stream->n_samples_moof;
8326 duration = stream->duration_moof;
8328 n_samples = stream->n_samples;
8329 duration = stream->duration;
8332 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8333 /* stream->duration is guint64, timescale, n_samples are guint32 */
8335 gst_util_uint64_scale_round (duration -
8336 stream->first_duration, GST_SECOND,
8337 (guint64) (stream->timescale) * (n_samples - 1));
8339 GST_LOG_OBJECT (qtdemux,
8340 "Calculating avg sample duration based on stream (or moof) duration %"
8342 " minus first sample %u, leaving %d samples gives %"
8343 GST_TIME_FORMAT, duration, stream->first_duration,
8344 n_samples - 1, GST_TIME_ARGS (avg_duration));
8346 gst_video_guess_framerate (avg_duration, &CUR_STREAM (stream)->fps_n,
8347 &CUR_STREAM (stream)->fps_d);
8349 GST_DEBUG_OBJECT (qtdemux,
8350 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8351 stream->timescale, CUR_STREAM (stream)->fps_n,
8352 CUR_STREAM (stream)->fps_d);
8356 if (CUR_STREAM (stream)->caps) {
8357 CUR_STREAM (stream)->caps =
8358 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8360 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8361 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8362 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8364 /* set framerate if calculated framerate is reliable */
8365 if (fps_available) {
8366 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8367 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8368 CUR_STREAM (stream)->fps_d, NULL);
8371 /* calculate pixel-aspect-ratio using display width and height */
8372 GST_DEBUG_OBJECT (qtdemux,
8373 "video size %dx%d, target display size %dx%d",
8374 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8375 stream->display_width, stream->display_height);
8376 /* qt file might have pasp atom */
8377 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8378 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8379 CUR_STREAM (stream)->par_h);
8380 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8381 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8382 CUR_STREAM (stream)->par_h, NULL);
8383 } else if (stream->display_width > 0 && stream->display_height > 0
8384 && CUR_STREAM (stream)->width > 0
8385 && CUR_STREAM (stream)->height > 0) {
8388 /* calculate the pixel aspect ratio using the display and pixel w/h */
8389 n = stream->display_width * CUR_STREAM (stream)->height;
8390 d = stream->display_height * CUR_STREAM (stream)->width;
8393 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8394 CUR_STREAM (stream)->par_w = n;
8395 CUR_STREAM (stream)->par_h = d;
8396 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8397 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8398 CUR_STREAM (stream)->par_h, NULL);
8401 if (CUR_STREAM (stream)->interlace_mode > 0) {
8402 if (CUR_STREAM (stream)->interlace_mode == 1) {
8403 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8404 G_TYPE_STRING, "progressive", NULL);
8405 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8406 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8407 G_TYPE_STRING, "interleaved", NULL);
8408 if (CUR_STREAM (stream)->field_order == 9) {
8409 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8410 G_TYPE_STRING, "top-field-first", NULL);
8411 } else if (CUR_STREAM (stream)->field_order == 14) {
8412 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8413 G_TYPE_STRING, "bottom-field-first", NULL);
8418 /* Create incomplete colorimetry here if needed */
8419 if (CUR_STREAM (stream)->colorimetry.range ||
8420 CUR_STREAM (stream)->colorimetry.matrix ||
8421 CUR_STREAM (stream)->colorimetry.transfer
8422 || CUR_STREAM (stream)->colorimetry.primaries) {
8423 gchar *colorimetry =
8424 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8425 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8426 G_TYPE_STRING, colorimetry, NULL);
8427 g_free (colorimetry);
8430 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8431 guint par_w = 1, par_h = 1;
8433 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8434 par_w = CUR_STREAM (stream)->par_w;
8435 par_h = CUR_STREAM (stream)->par_h;
8438 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8439 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8441 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8444 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8445 "multiview-mode", G_TYPE_STRING,
8446 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8447 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8448 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8453 else if (stream->subtype == FOURCC_soun) {
8454 if (CUR_STREAM (stream)->caps) {
8455 CUR_STREAM (stream)->caps =
8456 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8457 if (CUR_STREAM (stream)->rate > 0)
8458 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8459 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8460 if (CUR_STREAM (stream)->n_channels > 0)
8461 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8462 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8463 if (CUR_STREAM (stream)->n_channels > 2) {
8464 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8465 * correctly; this is just the minimum we can do - assume
8466 * we don't actually have any channel positions. */
8467 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8468 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8474 GstCaps *prev_caps = NULL;
8476 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8477 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8478 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8479 gst_pad_set_active (stream->pad, TRUE);
8481 gst_pad_use_fixed_caps (stream->pad);
8483 if (stream->protected) {
8484 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8485 GST_ERROR_OBJECT (qtdemux,
8486 "Failed to configure protected stream caps.");
8491 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8492 CUR_STREAM (stream)->caps);
8493 if (stream->new_stream) {
8496 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8499 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8502 gst_event_parse_stream_flags (event, &stream_flags);
8503 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8504 qtdemux->have_group_id = TRUE;
8506 qtdemux->have_group_id = FALSE;
8507 gst_event_unref (event);
8508 } else if (!qtdemux->have_group_id) {
8509 qtdemux->have_group_id = TRUE;
8510 qtdemux->group_id = gst_util_group_id_next ();
8513 stream->new_stream = FALSE;
8515 gst_pad_create_stream_id_printf (stream->pad,
8516 GST_ELEMENT_CAST (qtdemux), "%03u", stream->track_id);
8517 event = gst_event_new_stream_start (stream_id);
8518 if (qtdemux->have_group_id)
8519 gst_event_set_group_id (event, qtdemux->group_id);
8520 if (stream->disabled)
8521 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8522 if (CUR_STREAM (stream)->sparse) {
8523 stream_flags |= GST_STREAM_FLAG_SPARSE;
8525 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8527 gst_event_set_stream_flags (event, stream_flags);
8528 gst_pad_push_event (stream->pad, event);
8532 prev_caps = gst_pad_get_current_caps (stream->pad);
8534 if (CUR_STREAM (stream)->caps) {
8536 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8537 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8538 CUR_STREAM (stream)->caps);
8539 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8541 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8544 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8548 gst_caps_unref (prev_caps);
8549 stream->new_caps = FALSE;
8555 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8556 QtDemuxStream * stream)
8558 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8561 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8562 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8563 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8564 stream->stsd_entries_length)) {
8565 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8566 (_("This file is invalid and cannot be played.")),
8567 ("New sample description id is out of bounds (%d >= %d)",
8568 stream->stsd_sample_description_id, stream->stsd_entries_length));
8570 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8571 stream->new_caps = TRUE;
8576 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8577 QtDemuxStream * stream, GstTagList * list)
8579 gboolean ret = TRUE;
8580 /* consistent default for push based mode */
8581 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
8583 if (stream->subtype == FOURCC_vide) {
8584 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8587 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8590 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8591 gst_object_unref (stream->pad);
8597 qtdemux->n_video_streams++;
8598 } else if (stream->subtype == FOURCC_soun) {
8599 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
8602 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
8604 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8605 gst_object_unref (stream->pad);
8610 qtdemux->n_audio_streams++;
8611 } else if (stream->subtype == FOURCC_strm) {
8612 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
8613 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
8614 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
8615 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
8618 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
8620 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8621 gst_object_unref (stream->pad);
8626 qtdemux->n_sub_streams++;
8627 } else if (CUR_STREAM (stream)->caps) {
8628 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8631 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8633 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8634 gst_object_unref (stream->pad);
8639 qtdemux->n_video_streams++;
8641 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
8648 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
8649 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
8650 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
8651 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
8653 if (stream->stream_tags)
8654 gst_tag_list_unref (stream->stream_tags);
8655 stream->stream_tags = list;
8657 /* global tags go on each pad anyway */
8658 stream->send_global_tags = TRUE;
8659 /* send upstream GST_EVENT_PROTECTION events that were received before
8660 this source pad was created */
8661 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
8662 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
8666 gst_tag_list_unref (list);
8670 /* find next atom with @fourcc starting at @offset */
8671 static GstFlowReturn
8672 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
8673 guint64 * length, guint32 fourcc)
8679 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
8680 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
8686 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
8687 if (G_UNLIKELY (ret != GST_FLOW_OK))
8689 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
8692 gst_buffer_unref (buf);
8695 gst_buffer_map (buf, &map, GST_MAP_READ);
8696 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
8697 gst_buffer_unmap (buf, &map);
8698 gst_buffer_unref (buf);
8700 if (G_UNLIKELY (*length == 0)) {
8701 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
8702 ret = GST_FLOW_ERROR;
8706 if (lfourcc == fourcc) {
8707 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
8711 GST_LOG_OBJECT (qtdemux,
8712 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
8713 GST_FOURCC_ARGS (fourcc), *offset);
8722 /* might simply have had last one */
8723 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
8728 /* should only do something in pull mode */
8729 /* call with OBJECT lock */
8730 static GstFlowReturn
8731 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
8733 guint64 length, offset;
8734 GstBuffer *buf = NULL;
8735 GstFlowReturn ret = GST_FLOW_OK;
8736 GstFlowReturn res = GST_FLOW_OK;
8739 offset = qtdemux->moof_offset;
8740 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
8743 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8744 return GST_FLOW_EOS;
8747 /* best not do pull etc with lock held */
8748 GST_OBJECT_UNLOCK (qtdemux);
8750 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8751 if (ret != GST_FLOW_OK)
8754 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
8755 if (G_UNLIKELY (ret != GST_FLOW_OK))
8757 gst_buffer_map (buf, &map, GST_MAP_READ);
8758 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
8759 gst_buffer_unmap (buf, &map);
8760 gst_buffer_unref (buf);
8765 gst_buffer_unmap (buf, &map);
8766 gst_buffer_unref (buf);
8770 /* look for next moof */
8771 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8772 if (G_UNLIKELY (ret != GST_FLOW_OK))
8776 GST_OBJECT_LOCK (qtdemux);
8778 qtdemux->moof_offset = offset;
8784 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
8786 res = GST_FLOW_ERROR;
8791 /* maybe upstream temporarily flushing */
8792 if (ret != GST_FLOW_FLUSHING) {
8793 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8796 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
8797 /* resume at current position next time */
8804 /* initialise bytereaders for stbl sub-atoms */
8806 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
8808 stream->stbl_index = -1; /* no samples have yet been parsed */
8809 stream->sample_index = -1;
8811 /* time-to-sample atom */
8812 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
8815 /* copy atom data into a new buffer for later use */
8816 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
8818 /* skip version + flags */
8819 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
8820 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
8822 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
8824 /* make sure there's enough data */
8825 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
8826 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
8827 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
8828 stream->n_sample_times);
8829 if (!stream->n_sample_times)
8833 /* sync sample atom */
8834 stream->stps_present = FALSE;
8835 if ((stream->stss_present =
8836 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
8837 &stream->stss) ? TRUE : FALSE) == TRUE) {
8838 /* copy atom data into a new buffer for later use */
8839 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
8841 /* skip version + flags */
8842 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
8843 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
8846 if (stream->n_sample_syncs) {
8847 /* make sure there's enough data */
8848 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
8852 /* partial sync sample atom */
8853 if ((stream->stps_present =
8854 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
8855 &stream->stps) ? TRUE : FALSE) == TRUE) {
8856 /* copy atom data into a new buffer for later use */
8857 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
8859 /* skip version + flags */
8860 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
8861 !gst_byte_reader_get_uint32_be (&stream->stps,
8862 &stream->n_sample_partial_syncs))
8865 /* if there are no entries, the stss table contains the real
8867 if (stream->n_sample_partial_syncs) {
8868 /* make sure there's enough data */
8869 if (!qt_atom_parser_has_chunks (&stream->stps,
8870 stream->n_sample_partial_syncs, 4))
8877 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
8880 /* copy atom data into a new buffer for later use */
8881 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
8883 /* skip version + flags */
8884 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
8885 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
8888 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
8891 if (!stream->n_samples)
8894 /* sample-to-chunk atom */
8895 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
8898 /* copy atom data into a new buffer for later use */
8899 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
8901 /* skip version + flags */
8902 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
8903 !gst_byte_reader_get_uint32_be (&stream->stsc,
8904 &stream->n_samples_per_chunk))
8907 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
8908 stream->n_samples_per_chunk);
8910 /* make sure there's enough data */
8911 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
8917 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
8918 stream->co_size = sizeof (guint32);
8919 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
8921 stream->co_size = sizeof (guint64);
8925 /* copy atom data into a new buffer for later use */
8926 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
8928 /* skip version + flags */
8929 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
8932 /* chunks_are_samples == TRUE means treat chunks as samples */
8933 stream->chunks_are_samples = stream->sample_size
8934 && !CUR_STREAM (stream)->sampled;
8935 if (stream->chunks_are_samples) {
8936 /* treat chunks as samples */
8937 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
8940 /* skip number of entries */
8941 if (!gst_byte_reader_skip (&stream->stco, 4))
8944 /* make sure there are enough data in the stsz atom */
8945 if (!stream->sample_size) {
8946 /* different sizes for each sample */
8947 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
8952 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
8953 stream->n_samples, (guint) sizeof (QtDemuxSample),
8954 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
8956 if (stream->n_samples >=
8957 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
8958 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
8959 "be larger than %uMB (broken file?)", stream->n_samples,
8960 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
8964 g_assert (stream->samples == NULL);
8965 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
8966 if (!stream->samples) {
8967 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
8972 /* composition time-to-sample */
8973 if ((stream->ctts_present =
8974 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
8975 &stream->ctts) ? TRUE : FALSE) == TRUE) {
8976 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
8978 /* copy atom data into a new buffer for later use */
8979 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
8981 /* skip version + flags */
8982 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
8983 || !gst_byte_reader_get_uint32_be (&stream->ctts,
8984 &stream->n_composition_times))
8987 /* make sure there's enough data */
8988 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
8992 /* This is optional, if missing we iterate the ctts */
8993 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
8994 if (!gst_byte_reader_skip (&cslg, 1 + 3)
8995 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
8996 g_free ((gpointer) cslg.data);
9000 gint32 cslg_least = 0;
9001 guint num_entries, pos;
9004 pos = gst_byte_reader_get_pos (&stream->ctts);
9005 num_entries = stream->n_composition_times;
9007 stream->cslg_shift = 0;
9009 for (i = 0; i < num_entries; i++) {
9012 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9013 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9015 if (offset < cslg_least)
9016 cslg_least = offset;
9020 stream->cslg_shift = ABS (cslg_least);
9022 stream->cslg_shift = 0;
9024 /* reset the reader so we can generate sample table */
9025 gst_byte_reader_set_pos (&stream->ctts, pos);
9028 /* Ensure the cslg_shift value is consistent so we can use it
9029 * unconditionnally to produce TS and Segment */
9030 stream->cslg_shift = 0;
9037 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9038 (_("This file is corrupt and cannot be played.")), (NULL));
9043 gst_qtdemux_stbl_free (stream);
9044 if (!qtdemux->fragmented) {
9045 /* not quite good */
9046 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9049 /* may pick up samples elsewhere */
9055 /* collect samples from the next sample to be parsed up to sample @n for @stream
9056 * by reading the info from @stbl
9058 * This code can be executed from both the streaming thread and the seeking
9059 * thread so it takes the object lock to protect itself
9062 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9065 QtDemuxSample *samples, *first, *cur, *last;
9066 guint32 n_samples_per_chunk;
9069 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9070 GST_FOURCC_FORMAT ", pad %s",
9071 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9072 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9074 n_samples = stream->n_samples;
9077 goto out_of_samples;
9079 GST_OBJECT_LOCK (qtdemux);
9080 if (n <= stream->stbl_index)
9081 goto already_parsed;
9083 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9085 if (!stream->stsz.data) {
9086 /* so we already parsed and passed all the moov samples;
9087 * onto fragmented ones */
9088 g_assert (qtdemux->fragmented);
9092 /* pointer to the sample table */
9093 samples = stream->samples;
9095 /* starts from -1, moves to the next sample index to parse */
9096 stream->stbl_index++;
9098 /* keep track of the first and last sample to fill */
9099 first = &samples[stream->stbl_index];
9102 if (!stream->chunks_are_samples) {
9103 /* set the sample sizes */
9104 if (stream->sample_size == 0) {
9105 /* different sizes for each sample */
9106 for (cur = first; cur <= last; cur++) {
9107 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9108 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9109 (guint) (cur - samples), cur->size);
9112 /* samples have the same size */
9113 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9114 for (cur = first; cur <= last; cur++)
9115 cur->size = stream->sample_size;
9119 n_samples_per_chunk = stream->n_samples_per_chunk;
9122 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9125 if (stream->stsc_chunk_index >= stream->last_chunk
9126 || stream->stsc_chunk_index < stream->first_chunk) {
9127 stream->first_chunk =
9128 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9129 stream->samples_per_chunk =
9130 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9132 stream->stsd_sample_description_id =
9133 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9135 /* chunk numbers are counted from 1 it seems */
9136 if (G_UNLIKELY (stream->first_chunk == 0))
9139 --stream->first_chunk;
9141 /* the last chunk of each entry is calculated by taking the first chunk
9142 * of the next entry; except if there is no next, where we fake it with
9144 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9145 stream->last_chunk = G_MAXUINT32;
9147 stream->last_chunk =
9148 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9149 if (G_UNLIKELY (stream->last_chunk == 0))
9152 --stream->last_chunk;
9155 GST_LOG_OBJECT (qtdemux,
9156 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9157 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9158 stream->samples_per_chunk, stream->stsd_sample_description_id);
9160 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9163 if (stream->last_chunk != G_MAXUINT32) {
9164 if (!qt_atom_parser_peek_sub (&stream->stco,
9165 stream->first_chunk * stream->co_size,
9166 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9171 stream->co_chunk = stream->stco;
9172 if (!gst_byte_reader_skip (&stream->co_chunk,
9173 stream->first_chunk * stream->co_size))
9177 stream->stsc_chunk_index = stream->first_chunk;
9180 last_chunk = stream->last_chunk;
9182 if (stream->chunks_are_samples) {
9183 cur = &samples[stream->stsc_chunk_index];
9185 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9188 stream->stsc_chunk_index = j;
9193 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9196 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9197 "%" G_GUINT64_FORMAT, j, cur->offset);
9199 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9200 CUR_STREAM (stream)->bytes_per_frame > 0) {
9202 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9203 CUR_STREAM (stream)->samples_per_frame *
9204 CUR_STREAM (stream)->bytes_per_frame;
9206 cur->size = stream->samples_per_chunk;
9209 GST_DEBUG_OBJECT (qtdemux,
9210 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9211 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9212 stream->stco_sample_index)), cur->size);
9214 cur->timestamp = stream->stco_sample_index;
9215 cur->duration = stream->samples_per_chunk;
9216 cur->keyframe = TRUE;
9219 stream->stco_sample_index += stream->samples_per_chunk;
9221 stream->stsc_chunk_index = j;
9223 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9224 guint32 samples_per_chunk;
9225 guint64 chunk_offset;
9227 if (!stream->stsc_sample_index
9228 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9229 &stream->chunk_offset))
9232 samples_per_chunk = stream->samples_per_chunk;
9233 chunk_offset = stream->chunk_offset;
9235 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9236 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9237 G_GUINT64_FORMAT " and size %d",
9238 (guint) (cur - samples), chunk_offset, cur->size);
9240 cur->offset = chunk_offset;
9241 chunk_offset += cur->size;
9244 if (G_UNLIKELY (cur > last)) {
9246 stream->stsc_sample_index = k + 1;
9247 stream->chunk_offset = chunk_offset;
9248 stream->stsc_chunk_index = j;
9252 stream->stsc_sample_index = 0;
9254 stream->stsc_chunk_index = j;
9256 stream->stsc_index++;
9259 if (stream->chunks_are_samples)
9263 guint32 n_sample_times;
9265 n_sample_times = stream->n_sample_times;
9268 for (i = stream->stts_index; i < n_sample_times; i++) {
9269 guint32 stts_samples;
9270 gint32 stts_duration;
9273 if (stream->stts_sample_index >= stream->stts_samples
9274 || !stream->stts_sample_index) {
9276 stream->stts_samples =
9277 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9278 stream->stts_duration =
9279 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9281 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9282 i, stream->stts_samples, stream->stts_duration);
9284 stream->stts_sample_index = 0;
9287 stts_samples = stream->stts_samples;
9288 stts_duration = stream->stts_duration;
9289 stts_time = stream->stts_time;
9291 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9292 GST_DEBUG_OBJECT (qtdemux,
9293 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9294 (guint) (cur - samples), j,
9295 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9297 cur->timestamp = stts_time;
9298 cur->duration = stts_duration;
9300 /* avoid 32-bit wrap-around,
9301 * but still mind possible 'negative' duration */
9302 stts_time += (gint64) stts_duration;
9305 if (G_UNLIKELY (cur > last)) {
9307 stream->stts_time = stts_time;
9308 stream->stts_sample_index = j + 1;
9309 if (stream->stts_sample_index >= stream->stts_samples)
9310 stream->stts_index++;
9314 stream->stts_sample_index = 0;
9315 stream->stts_time = stts_time;
9316 stream->stts_index++;
9318 /* fill up empty timestamps with the last timestamp, this can happen when
9319 * the last samples do not decode and so we don't have timestamps for them.
9320 * We however look at the last timestamp to estimate the track length so we
9321 * need something in here. */
9322 for (; cur < last; cur++) {
9323 GST_DEBUG_OBJECT (qtdemux,
9324 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9325 (guint) (cur - samples),
9326 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9327 cur->timestamp = stream->stts_time;
9333 /* sample sync, can be NULL */
9334 if (stream->stss_present == TRUE) {
9335 guint32 n_sample_syncs;
9337 n_sample_syncs = stream->n_sample_syncs;
9339 if (!n_sample_syncs) {
9340 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9341 stream->all_keyframe = TRUE;
9343 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9344 /* note that the first sample is index 1, not 0 */
9347 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9349 if (G_LIKELY (index > 0 && index <= n_samples)) {
9351 samples[index].keyframe = TRUE;
9352 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9353 /* and exit if we have enough samples */
9354 if (G_UNLIKELY (index >= n)) {
9361 stream->stss_index = i;
9364 /* stps marks partial sync frames like open GOP I-Frames */
9365 if (stream->stps_present == TRUE) {
9366 guint32 n_sample_partial_syncs;
9368 n_sample_partial_syncs = stream->n_sample_partial_syncs;
9370 /* if there are no entries, the stss table contains the real
9372 if (n_sample_partial_syncs) {
9373 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
9374 /* note that the first sample is index 1, not 0 */
9377 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
9379 if (G_LIKELY (index > 0 && index <= n_samples)) {
9381 samples[index].keyframe = TRUE;
9382 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9383 /* and exit if we have enough samples */
9384 if (G_UNLIKELY (index >= n)) {
9391 stream->stps_index = i;
9395 /* no stss, all samples are keyframes */
9396 stream->all_keyframe = TRUE;
9397 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
9402 /* composition time to sample */
9403 if (stream->ctts_present == TRUE) {
9404 guint32 n_composition_times;
9406 gint32 ctts_soffset;
9408 /* Fill in the pts_offsets */
9410 n_composition_times = stream->n_composition_times;
9412 for (i = stream->ctts_index; i < n_composition_times; i++) {
9413 if (stream->ctts_sample_index >= stream->ctts_count
9414 || !stream->ctts_sample_index) {
9415 stream->ctts_count =
9416 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
9417 stream->ctts_soffset =
9418 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9419 stream->ctts_sample_index = 0;
9422 ctts_count = stream->ctts_count;
9423 ctts_soffset = stream->ctts_soffset;
9425 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
9426 cur->pts_offset = ctts_soffset;
9429 if (G_UNLIKELY (cur > last)) {
9431 stream->ctts_sample_index = j + 1;
9435 stream->ctts_sample_index = 0;
9436 stream->ctts_index++;
9440 stream->stbl_index = n;
9441 /* if index has been completely parsed, free data that is no-longer needed */
9442 if (n + 1 == stream->n_samples) {
9443 gst_qtdemux_stbl_free (stream);
9444 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
9445 if (qtdemux->pullbased) {
9446 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
9447 while (n + 1 == stream->n_samples)
9448 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
9452 GST_OBJECT_UNLOCK (qtdemux);
9459 GST_LOG_OBJECT (qtdemux,
9460 "Tried to parse up to sample %u but this sample has already been parsed",
9462 /* if fragmented, there may be more */
9463 if (qtdemux->fragmented && n == stream->stbl_index)
9465 GST_OBJECT_UNLOCK (qtdemux);
9471 GST_LOG_OBJECT (qtdemux,
9472 "Tried to parse up to sample %u but there are only %u samples", n + 1,
9474 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9475 (_("This file is corrupt and cannot be played.")), (NULL));
9480 GST_OBJECT_UNLOCK (qtdemux);
9481 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9482 (_("This file is corrupt and cannot be played.")), (NULL));
9487 /* collect all segment info for @stream.
9490 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
9494 /* accept edts if they contain gaps at start and there is only
9495 * one media segment */
9496 gboolean allow_pushbased_edts = TRUE;
9497 gint media_segments_count = 0;
9499 /* parse and prepare segment info from the edit list */
9500 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
9501 stream->n_segments = 0;
9502 stream->segments = NULL;
9503 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
9506 gint i, count, entry_size;
9509 const guint8 *buffer;
9513 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
9514 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
9517 buffer = elst->data;
9519 size = QT_UINT32 (buffer);
9520 /* version, flags, n_segments */
9522 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9525 version = QT_UINT8 (buffer + 8);
9526 entry_size = (version == 1) ? 20 : 12;
9528 n_segments = QT_UINT32 (buffer + 12);
9530 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
9531 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9535 /* we might allocate a bit too much, at least allocate 1 segment */
9536 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
9538 /* segments always start from 0 */
9543 for (i = 0; i < n_segments; i++) {
9546 gboolean time_valid = TRUE;
9547 QtDemuxSegment *segment;
9549 GstClockTime media_start = GST_CLOCK_TIME_NONE;
9552 media_time = QT_UINT64 (buffer + 8);
9553 duration = QT_UINT64 (buffer);
9554 if (media_time == G_MAXUINT64)
9557 media_time = QT_UINT32 (buffer + 4);
9558 duration = QT_UINT32 (buffer);
9559 if (media_time == G_MAXUINT32)
9564 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
9566 segment = &stream->segments[count++];
9568 /* time and duration expressed in global timescale */
9569 segment->time = stime;
9570 /* add non scaled values so we don't cause roundoff errors */
9571 if (duration || media_start == GST_CLOCK_TIME_NONE) {
9573 stime = QTTIME_TO_GSTTIME (qtdemux, time);
9574 segment->duration = stime - segment->time;
9576 /* zero duration does not imply media_start == media_stop
9577 * but, only specify media_start.*/
9578 stime = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
9579 if (GST_CLOCK_TIME_IS_VALID (stime) && time_valid
9580 && stime >= media_start) {
9581 segment->duration = stime - media_start;
9583 segment->duration = GST_CLOCK_TIME_NONE;
9586 segment->stop_time = stime;
9588 segment->trak_media_start = media_time;
9589 /* media_time expressed in stream timescale */
9591 segment->media_start = media_start;
9592 segment->media_stop = segment->media_start + segment->duration;
9593 media_segments_count++;
9595 segment->media_start = GST_CLOCK_TIME_NONE;
9596 segment->media_stop = GST_CLOCK_TIME_NONE;
9598 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
9600 if (rate_int <= 1) {
9601 /* 0 is not allowed, some programs write 1 instead of the floating point
9603 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
9607 segment->rate = rate_int / 65536.0;
9610 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
9611 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
9612 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
9613 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
9614 i, GST_TIME_ARGS (segment->time),
9615 GST_TIME_ARGS (segment->duration),
9616 GST_TIME_ARGS (segment->media_start), media_time,
9617 GST_TIME_ARGS (segment->media_stop),
9618 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
9620 if (segment->stop_time > qtdemux->segment.stop) {
9621 GST_WARNING_OBJECT (qtdemux, "Segment %d "
9622 " extends to %" GST_TIME_FORMAT
9623 " past the end of the file duration %" GST_TIME_FORMAT
9624 " it will be truncated", i, GST_TIME_ARGS (segment->stop_time),
9625 GST_TIME_ARGS (qtdemux->segment.stop));
9626 qtdemux->segment.stop = segment->stop_time;
9629 buffer += entry_size;
9631 GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
9632 stream->n_segments = count;
9633 if (media_segments_count != 1)
9634 allow_pushbased_edts = FALSE;
9638 /* push based does not handle segments, so act accordingly here,
9639 * and warn if applicable */
9640 if (!qtdemux->pullbased && !allow_pushbased_edts) {
9641 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
9642 /* remove and use default one below, we stream like it anyway */
9643 g_free (stream->segments);
9644 stream->segments = NULL;
9645 stream->n_segments = 0;
9648 /* no segments, create one to play the complete trak */
9649 if (stream->n_segments == 0) {
9650 GstClockTime stream_duration =
9651 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
9653 if (stream->segments == NULL)
9654 stream->segments = g_new (QtDemuxSegment, 1);
9656 /* represent unknown our way */
9657 if (stream_duration == 0)
9658 stream_duration = GST_CLOCK_TIME_NONE;
9660 stream->segments[0].time = 0;
9661 stream->segments[0].stop_time = stream_duration;
9662 stream->segments[0].duration = stream_duration;
9663 stream->segments[0].media_start = 0;
9664 stream->segments[0].media_stop = stream_duration;
9665 stream->segments[0].rate = 1.0;
9666 stream->segments[0].trak_media_start = 0;
9668 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
9669 GST_TIME_ARGS (stream_duration));
9670 stream->n_segments = 1;
9671 stream->dummy_segment = TRUE;
9673 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
9679 * Parses the stsd atom of a svq3 trak looking for
9680 * the SMI and gama atoms.
9683 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
9684 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
9686 const guint8 *_gamma = NULL;
9687 GstBuffer *_seqh = NULL;
9688 const guint8 *stsd_data = stsd_entry_data;
9689 guint32 length = QT_UINT32 (stsd_data);
9693 GST_WARNING_OBJECT (qtdemux, "stsd too short");
9699 version = QT_UINT16 (stsd_data);
9704 while (length > 8) {
9705 guint32 fourcc, size;
9707 size = QT_UINT32 (stsd_data);
9708 fourcc = QT_FOURCC (stsd_data + 4);
9709 data = stsd_data + 8;
9712 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
9713 "svq3 atom parsing");
9722 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
9723 " for gama atom, expected 12", size);
9728 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
9730 if (_seqh != NULL) {
9731 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
9732 " found, ignoring");
9734 seqh_size = QT_UINT32 (data + 4);
9735 if (seqh_size > 0) {
9736 _seqh = gst_buffer_new_and_alloc (seqh_size);
9737 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
9744 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
9745 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
9749 if (size <= length) {
9755 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
9758 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
9759 G_GUINT16_FORMAT, version);
9770 gst_buffer_unref (_seqh);
9775 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
9782 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
9783 * atom that might contain a 'data' atom with the rtsp uri.
9784 * This case was reported in bug #597497, some info about
9785 * the hndl atom can be found in TN1195
9787 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
9788 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
9791 guint32 dref_num_entries = 0;
9792 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
9793 gst_byte_reader_skip (&dref, 4) &&
9794 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
9797 /* search dref entries for hndl atom */
9798 for (i = 0; i < dref_num_entries; i++) {
9799 guint32 size = 0, type;
9800 guint8 string_len = 0;
9801 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
9802 qt_atom_parser_get_fourcc (&dref, &type)) {
9803 if (type == FOURCC_hndl) {
9804 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
9806 /* skip data reference handle bytes and the
9807 * following pascal string and some extra 4
9808 * bytes I have no idea what are */
9809 if (!gst_byte_reader_skip (&dref, 4) ||
9810 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
9811 !gst_byte_reader_skip (&dref, string_len + 4)) {
9812 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
9816 /* iterate over the atoms to find the data atom */
9817 while (gst_byte_reader_get_remaining (&dref) >= 8) {
9821 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
9822 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
9823 if (atom_type == FOURCC_data) {
9824 const guint8 *uri_aux = NULL;
9826 /* found the data atom that might contain the rtsp uri */
9827 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
9828 "hndl atom, interpreting it as an URI");
9829 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
9831 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
9832 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
9834 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
9835 "didn't contain a rtsp address");
9837 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
9842 /* skipping to the next entry */
9843 if (!gst_byte_reader_skip (&dref, atom_size - 8))
9846 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
9853 /* skip to the next entry */
9854 if (!gst_byte_reader_skip (&dref, size - 8))
9857 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
9860 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
9866 #define AMR_NB_ALL_MODES 0x81ff
9867 #define AMR_WB_ALL_MODES 0x83ff
9869 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
9871 /* The 'damr' atom is of the form:
9873 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
9874 * 32 b 8 b 16 b 8 b 8 b
9876 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
9877 * represents the highest mode used in the stream (and thus the maximum
9878 * bitrate), with a couple of special cases as seen below.
9881 /* Map of frame type ID -> bitrate */
9882 static const guint nb_bitrates[] = {
9883 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
9885 static const guint wb_bitrates[] = {
9886 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
9892 gst_buffer_map (buf, &map, GST_MAP_READ);
9894 if (map.size != 0x11) {
9895 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
9899 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
9900 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
9901 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
9905 mode_set = QT_UINT16 (map.data + 13);
9907 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
9908 max_mode = 7 + (wb ? 1 : 0);
9910 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
9911 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
9913 if (max_mode == -1) {
9914 GST_DEBUG ("No mode indication was found (mode set) = %x",
9919 gst_buffer_unmap (buf, &map);
9920 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
9923 gst_buffer_unmap (buf, &map);
9928 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
9929 GstByteReader * reader, guint32 * matrix, const gchar * atom)
9932 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
9938 if (gst_byte_reader_get_remaining (reader) < 36)
9941 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
9942 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
9943 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
9944 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
9945 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
9946 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
9947 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
9948 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
9949 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
9951 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
9952 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
9953 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
9955 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
9956 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
9958 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
9959 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
9966 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
9967 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
9974 * This macro will only compare value abdegh, it expects cfi to have already
9977 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
9978 (m)[3] == (d << 16) && (m)[4] == (e << 16))
9980 /* only handle the cases where the last column has standard values */
9981 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
9982 const gchar *rotation_tag = NULL;
9984 /* no rotation needed */
9985 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
9987 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
9988 rotation_tag = "rotate-90";
9989 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
9990 rotation_tag = "rotate-180";
9991 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
9992 rotation_tag = "rotate-270";
9994 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
9997 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
9999 if (rotation_tag != NULL) {
10000 if (*taglist == NULL)
10001 *taglist = gst_tag_list_new_empty ();
10002 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10003 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10006 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10010 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10011 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10012 * Common Encryption (cenc), the function will also parse the tenc box (defined
10013 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10014 * (typically an enc[v|a|t|s] sample entry); the function will set
10015 * @original_fmt to the fourcc of the original unencrypted stream format.
10016 * Returns TRUE if successful; FALSE otherwise. */
10018 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10019 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10026 g_return_val_if_fail (qtdemux != NULL, FALSE);
10027 g_return_val_if_fail (stream != NULL, FALSE);
10028 g_return_val_if_fail (container != NULL, FALSE);
10029 g_return_val_if_fail (original_fmt != NULL, FALSE);
10031 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10032 if (G_UNLIKELY (!sinf)) {
10033 if (stream->protection_scheme_type == FOURCC_cenc) {
10034 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10035 "mandatory for Common Encryption");
10041 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10042 if (G_UNLIKELY (!frma)) {
10043 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10047 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10048 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10049 GST_FOURCC_ARGS (*original_fmt));
10051 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10053 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10056 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10057 stream->protection_scheme_version =
10058 QT_UINT32 ((const guint8 *) schm->data + 16);
10060 GST_DEBUG_OBJECT (qtdemux,
10061 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10062 "protection_scheme_version: %#010x",
10063 GST_FOURCC_ARGS (stream->protection_scheme_type),
10064 stream->protection_scheme_version);
10066 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10068 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10071 if (stream->protection_scheme_type == FOURCC_cenc) {
10072 QtDemuxCencSampleSetInfo *info;
10074 const guint8 *tenc_data;
10075 guint32 isEncrypted;
10077 const guint8 *default_kid;
10078 GstBuffer *kid_buf;
10080 if (G_UNLIKELY (!stream->protection_scheme_info))
10081 stream->protection_scheme_info =
10082 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10084 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10086 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10088 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10089 "which is mandatory for Common Encryption");
10092 tenc_data = (const guint8 *) tenc->data + 12;
10093 isEncrypted = QT_UINT24 (tenc_data);
10094 iv_size = QT_UINT8 (tenc_data + 3);
10095 default_kid = (tenc_data + 4);
10096 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
10097 gst_buffer_fill (kid_buf, 0, default_kid, 16);
10098 if (info->default_properties)
10099 gst_structure_free (info->default_properties);
10100 info->default_properties =
10101 gst_structure_new ("application/x-cenc",
10102 "iv_size", G_TYPE_UINT, iv_size,
10103 "encrypted", G_TYPE_BOOLEAN, (isEncrypted == 1),
10104 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
10105 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
10106 "is_encrypted=%u, iv_size=%u", isEncrypted, iv_size);
10107 gst_buffer_unref (kid_buf);
10112 /* parse the traks.
10113 * With each track we associate a new QtDemuxStream that contains all the info
10115 * traks that do not decode to something (like strm traks) will not have a pad.
10118 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10120 GstByteReader tkhd;
10139 QtDemuxStream *stream = NULL;
10140 gboolean new_stream = FALSE;
10141 gchar *codec = NULL;
10142 const guint8 *stsd_data;
10143 const guint8 *stsd_entry_data;
10144 guint remaining_stsd_len;
10145 guint stsd_entry_count;
10147 guint16 lang_code; /* quicktime lang code or packed iso code */
10149 guint32 tkhd_flags = 0;
10150 guint8 tkhd_version = 0;
10151 guint32 w = 0, h = 0;
10153 guint value_size, stsd_len, len;
10157 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10159 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10160 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10161 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10164 /* pick between 64 or 32 bits */
10165 value_size = tkhd_version == 1 ? 8 : 4;
10166 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10167 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10170 if (!qtdemux->got_moov) {
10171 if (qtdemux_find_stream (qtdemux, track_id))
10172 goto existing_stream;
10173 stream = _create_stream ();
10174 stream->track_id = track_id;
10177 stream = qtdemux_find_stream (qtdemux, track_id);
10179 GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
10183 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10185 /* flush samples data from this track from previous moov */
10186 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
10187 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
10189 /* need defaults for fragments */
10190 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10192 if ((tkhd_flags & 1) == 0)
10193 stream->disabled = TRUE;
10195 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10196 tkhd_version, tkhd_flags, stream->track_id);
10198 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10201 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10202 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10203 if (qtdemux->major_brand != FOURCC_mjp2 ||
10204 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
10208 len = QT_UINT32 ((guint8 *) mdhd->data);
10209 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
10210 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
10211 if (version == 0x01000000) {
10214 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
10215 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
10216 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
10220 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
10221 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
10222 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
10225 if (lang_code < 0x400) {
10226 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
10227 } else if (lang_code == 0x7fff) {
10228 stream->lang_id[0] = 0; /* unspecified */
10230 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
10231 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
10232 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
10233 stream->lang_id[3] = 0;
10236 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
10237 stream->timescale);
10238 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
10240 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
10241 lang_code, stream->lang_id);
10243 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
10246 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
10247 /* chapters track reference */
10248 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
10250 gsize length = GST_READ_UINT32_BE (chap->data);
10251 if (qtdemux->chapters_track_id)
10252 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
10254 if (length >= 12) {
10255 qtdemux->chapters_track_id =
10256 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
10261 /* fragmented files may have bogus duration in moov */
10262 if (!qtdemux->fragmented &&
10263 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
10264 guint64 tdur1, tdur2;
10266 /* don't overflow */
10267 tdur1 = stream->timescale * (guint64) qtdemux->duration;
10268 tdur2 = qtdemux->timescale * (guint64) stream->duration;
10271 * some of those trailers, nowadays, have prologue images that are
10272 * themselves video tracks as well. I haven't really found a way to
10273 * identify those yet, except for just looking at their duration. */
10274 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
10275 GST_WARNING_OBJECT (qtdemux,
10276 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
10277 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
10278 "found, assuming preview image or something; skipping track",
10279 stream->duration, stream->timescale, qtdemux->duration,
10280 qtdemux->timescale);
10282 gst_qtdemux_stream_free (qtdemux, stream);
10287 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
10290 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
10291 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
10293 len = QT_UINT32 ((guint8 *) hdlr->data);
10295 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
10296 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
10297 GST_FOURCC_ARGS (stream->subtype));
10299 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
10302 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
10305 /*parse svmi header if existing */
10306 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10308 len = QT_UINT32 ((guint8 *) svmi->data);
10309 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10311 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10312 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10313 guint8 frame_type, frame_layout;
10315 /* MPEG-A stereo video */
10316 if (qtdemux->major_brand == FOURCC_ss02)
10317 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10319 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10320 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10321 switch (frame_type) {
10323 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10326 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10329 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10332 /* mode 3 is primary/secondary view sequence, ie
10333 * left/right views in separate tracks. See section 7.2
10334 * of ISO/IEC 23000-11:2009 */
10335 GST_FIXME_OBJECT (qtdemux,
10336 "Implement stereo video in separate streams");
10339 if ((frame_layout & 0x1) == 0)
10340 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10342 GST_LOG_OBJECT (qtdemux,
10343 "StereoVideo: composition type: %u, is_left_first: %u",
10344 frame_type, frame_layout);
10345 stream->multiview_mode = mode;
10346 stream->multiview_flags = flags;
10350 /* parse rest of tkhd */
10351 if (stream->subtype == FOURCC_vide) {
10354 /* version 1 uses some 64-bit ints */
10355 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
10358 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
10361 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
10362 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
10365 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
10366 &stream->stream_tags);
10370 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
10372 stsd_data = (const guint8 *) stsd->data;
10374 /* stsd should at least have one entry */
10375 stsd_len = QT_UINT32 (stsd_data);
10376 if (stsd_len < 24) {
10377 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
10378 if (stream->subtype == FOURCC_vivo) {
10380 gst_qtdemux_stream_free (qtdemux, stream);
10387 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
10388 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10389 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10390 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10392 stsd_entry_data = stsd_data + 16;
10393 remaining_stsd_len = stsd_len - 16;
10394 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10395 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10397 /* and that entry should fit within stsd */
10398 len = QT_UINT32 (stsd_entry_data);
10399 if (len > remaining_stsd_len)
10402 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10403 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10404 GST_FOURCC_ARGS (entry->fourcc));
10405 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10407 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10408 goto error_encrypted;
10410 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10411 /* FIXME this looks wrong, there might be multiple children
10412 * with the same type */
10413 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10414 stream->protected = TRUE;
10415 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10416 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10419 if (stream->subtype == FOURCC_vide) {
10421 gint depth, palette_size, palette_count;
10422 guint32 *palette_data = NULL;
10424 entry->sampled = TRUE;
10426 stream->display_width = w >> 16;
10427 stream->display_height = h >> 16;
10430 if (len < 86) /* TODO verify */
10433 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10434 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10435 entry->fps_n = 0; /* this is filled in later */
10436 entry->fps_d = 0; /* this is filled in later */
10437 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10438 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10440 /* if color_table_id is 0, ctab atom must follow; however some files
10441 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10442 * if color table is not present we'll correct the value */
10443 if (entry->color_table_id == 0 &&
10445 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10446 entry->color_table_id = -1;
10449 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10450 entry->width, entry->height, entry->bits_per_sample,
10451 entry->color_table_id);
10453 depth = entry->bits_per_sample;
10455 /* more than 32 bits means grayscale */
10456 gray = (depth > 32);
10457 /* low 32 bits specify the depth */
10460 /* different number of palette entries is determined by depth. */
10462 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10463 palette_count = (1 << depth);
10464 palette_size = palette_count * 4;
10466 if (entry->color_table_id) {
10467 switch (palette_count) {
10471 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10474 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10479 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10481 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10486 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10488 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10491 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10492 (_("The video in this file might not play correctly.")),
10493 ("unsupported palette depth %d", depth));
10497 gint i, j, start, end;
10503 start = QT_UINT32 (stsd_entry_data + offset + 70);
10504 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10505 end = QT_UINT16 (stsd_entry_data + offset + 76);
10507 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10508 start, end, palette_count);
10515 if (len < 94 + (end - start) * 8)
10518 /* palette is always the same size */
10519 palette_data = g_malloc0 (256 * 4);
10520 palette_size = 256 * 4;
10522 for (j = 0, i = start; i <= end; j++, i++) {
10523 guint32 a, r, g, b;
10525 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10526 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10527 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10528 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10530 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10531 (g & 0xff00) | (b >> 8);
10536 gst_caps_unref (entry->caps);
10539 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10541 if (G_UNLIKELY (!entry->caps)) {
10542 g_free (palette_data);
10543 goto unknown_stream;
10547 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10548 GST_TAG_VIDEO_CODEC, codec, NULL);
10553 if (palette_data) {
10556 if (entry->rgb8_palette)
10557 gst_memory_unref (entry->rgb8_palette);
10558 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10559 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10561 s = gst_caps_get_structure (entry->caps, 0);
10563 /* non-raw video has a palette_data property. raw video has the palette as
10564 * an extra plane that we append to the output buffers before we push
10566 if (!gst_structure_has_name (s, "video/x-raw")) {
10567 GstBuffer *palette;
10569 palette = gst_buffer_new ();
10570 gst_buffer_append_memory (palette, entry->rgb8_palette);
10571 entry->rgb8_palette = NULL;
10573 gst_caps_set_simple (entry->caps, "palette_data",
10574 GST_TYPE_BUFFER, palette, NULL);
10575 gst_buffer_unref (palette);
10577 } else if (palette_count != 0) {
10578 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10579 (NULL), ("Unsupported palette depth %d", depth));
10582 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10583 QT_UINT16 (stsd_entry_data + offset + 32));
10589 /* pick 'the' stsd child */
10590 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10591 if (!stream->protected) {
10592 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10596 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) {
10602 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10603 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10604 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10605 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10609 const guint8 *pasp_data = (const guint8 *) pasp->data;
10610 gint len = QT_UINT32 (pasp_data);
10613 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10614 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10616 CUR_STREAM (stream)->par_w = 0;
10617 CUR_STREAM (stream)->par_h = 0;
10620 CUR_STREAM (stream)->par_w = 0;
10621 CUR_STREAM (stream)->par_h = 0;
10625 const guint8 *fiel_data = (const guint8 *) fiel->data;
10626 gint len = QT_UINT32 (fiel_data);
10629 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10630 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10635 const guint8 *colr_data = (const guint8 *) colr->data;
10636 gint len = QT_UINT32 (colr_data);
10638 if (len == 19 || len == 18) {
10639 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10641 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10642 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10643 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10644 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10645 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10647 switch (primaries) {
10649 CUR_STREAM (stream)->colorimetry.primaries =
10650 GST_VIDEO_COLOR_PRIMARIES_BT709;
10653 CUR_STREAM (stream)->colorimetry.primaries =
10654 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
10657 CUR_STREAM (stream)->colorimetry.primaries =
10658 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
10661 CUR_STREAM (stream)->colorimetry.primaries =
10662 GST_VIDEO_COLOR_PRIMARIES_BT2020;
10668 switch (transfer_function) {
10670 CUR_STREAM (stream)->colorimetry.transfer =
10671 GST_VIDEO_TRANSFER_BT709;
10674 CUR_STREAM (stream)->colorimetry.transfer =
10675 GST_VIDEO_TRANSFER_SMPTE240M;
10683 CUR_STREAM (stream)->colorimetry.matrix =
10684 GST_VIDEO_COLOR_MATRIX_BT709;
10687 CUR_STREAM (stream)->colorimetry.matrix =
10688 GST_VIDEO_COLOR_MATRIX_BT601;
10691 CUR_STREAM (stream)->colorimetry.matrix =
10692 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
10695 CUR_STREAM (stream)->colorimetry.matrix =
10696 GST_VIDEO_COLOR_MATRIX_BT2020;
10702 CUR_STREAM (stream)->colorimetry.range =
10703 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10704 GST_VIDEO_COLOR_RANGE_16_235;
10706 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10709 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10714 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10715 stream->stream_tags);
10722 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10723 const guint8 *avc_data = stsd_entry_data + 0x56;
10726 while (len >= 0x8) {
10729 if (QT_UINT32 (avc_data) <= len)
10730 size = QT_UINT32 (avc_data) - 0x8;
10735 /* No real data, so break out */
10738 switch (QT_FOURCC (avc_data + 0x4)) {
10741 /* parse, if found */
10744 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10746 /* First 4 bytes are the length of the atom, the next 4 bytes
10747 * are the fourcc, the next 1 byte is the version, and the
10748 * subsequent bytes are profile_tier_level structure like data. */
10749 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
10750 avc_data + 8 + 1, size - 1);
10751 buf = gst_buffer_new_and_alloc (size);
10752 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
10753 gst_caps_set_simple (entry->caps,
10754 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10755 gst_buffer_unref (buf);
10763 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
10765 /* First 4 bytes are the length of the atom, the next 4 bytes
10766 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
10767 * next 1 byte is the version, and the
10768 * subsequent bytes are sequence parameter set like data. */
10770 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
10772 gst_codec_utils_h264_caps_set_level_and_profile
10773 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
10775 buf = gst_buffer_new_and_alloc (size);
10776 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
10777 gst_caps_set_simple (entry->caps,
10778 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10779 gst_buffer_unref (buf);
10785 guint avg_bitrate, max_bitrate;
10787 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
10791 max_bitrate = QT_UINT32 (avc_data + 0xc);
10792 avg_bitrate = QT_UINT32 (avc_data + 0x10);
10794 if (!max_bitrate && !avg_bitrate)
10797 /* Some muxers seem to swap the average and maximum bitrates
10798 * (I'm looking at you, YouTube), so we swap for sanity. */
10799 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
10800 guint temp = avg_bitrate;
10802 avg_bitrate = max_bitrate;
10803 max_bitrate = temp;
10806 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
10807 gst_tag_list_add (stream->stream_tags,
10808 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
10809 max_bitrate, NULL);
10811 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
10812 gst_tag_list_add (stream->stream_tags,
10813 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
10825 avc_data += size + 8;
10834 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10835 const guint8 *hevc_data = stsd_entry_data + 0x56;
10838 while (len >= 0x8) {
10841 if (QT_UINT32 (hevc_data) <= len)
10842 size = QT_UINT32 (hevc_data) - 0x8;
10847 /* No real data, so break out */
10850 switch (QT_FOURCC (hevc_data + 0x4)) {
10853 /* parse, if found */
10856 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10858 /* First 4 bytes are the length of the atom, the next 4 bytes
10859 * are the fourcc, the next 1 byte is the version, and the
10860 * subsequent bytes are sequence parameter set like data. */
10861 gst_codec_utils_h265_caps_set_level_tier_and_profile
10862 (entry->caps, hevc_data + 8 + 1, size - 1);
10864 buf = gst_buffer_new_and_alloc (size);
10865 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
10866 gst_caps_set_simple (entry->caps,
10867 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10868 gst_buffer_unref (buf);
10875 hevc_data += size + 8;
10888 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
10889 GST_FOURCC_ARGS (fourcc));
10891 /* codec data might be in glbl extension atom */
10893 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
10899 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
10901 len = QT_UINT32 (data);
10904 buf = gst_buffer_new_and_alloc (len);
10905 gst_buffer_fill (buf, 0, data + 8, len);
10906 gst_caps_set_simple (entry->caps,
10907 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10908 gst_buffer_unref (buf);
10915 /* see annex I of the jpeg2000 spec */
10916 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
10917 const guint8 *data;
10918 const gchar *colorspace = NULL;
10920 guint32 ncomp_map = 0;
10921 gint32 *comp_map = NULL;
10922 guint32 nchan_def = 0;
10923 gint32 *chan_def = NULL;
10925 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
10926 /* some required atoms */
10927 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10930 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
10934 /* number of components; redundant with info in codestream, but useful
10936 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
10937 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
10939 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
10941 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
10944 GST_DEBUG_OBJECT (qtdemux, "found colr");
10945 /* extract colour space info */
10946 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
10947 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
10949 colorspace = "sRGB";
10952 colorspace = "GRAY";
10955 colorspace = "sYUV";
10963 /* colr is required, and only values 16, 17, and 18 are specified,
10964 so error if we have no colorspace */
10967 /* extract component mapping */
10968 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
10970 guint32 cmap_len = 0;
10972 cmap_len = QT_UINT32 (cmap->data);
10973 if (cmap_len >= 8) {
10974 /* normal box, subtract off header */
10976 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
10977 if (cmap_len % 4 == 0) {
10978 ncomp_map = (cmap_len / 4);
10979 comp_map = g_new0 (gint32, ncomp_map);
10980 for (i = 0; i < ncomp_map; i++) {
10983 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
10984 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
10985 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
10986 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
10991 /* extract channel definitions */
10992 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
10994 guint32 cdef_len = 0;
10996 cdef_len = QT_UINT32 (cdef->data);
10997 if (cdef_len >= 10) {
10998 /* normal box, subtract off header and len */
11000 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11001 if (cdef_len % 6 == 0) {
11002 nchan_def = (cdef_len / 6);
11003 chan_def = g_new0 (gint32, nchan_def);
11004 for (i = 0; i < nchan_def; i++)
11006 for (i = 0; i < nchan_def; i++) {
11007 guint16 cn, typ, asoc;
11008 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11009 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11010 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11011 if (cn < nchan_def) {
11014 chan_def[cn] = asoc;
11017 chan_def[cn] = 0; /* alpha */
11020 chan_def[cn] = -typ;
11028 gst_caps_set_simple (entry->caps,
11029 "num-components", G_TYPE_INT, ncomp, NULL);
11030 gst_caps_set_simple (entry->caps,
11031 "colorspace", G_TYPE_STRING, colorspace, NULL);
11034 GValue arr = { 0, };
11035 GValue elt = { 0, };
11037 g_value_init (&arr, GST_TYPE_ARRAY);
11038 g_value_init (&elt, G_TYPE_INT);
11039 for (i = 0; i < ncomp_map; i++) {
11040 g_value_set_int (&elt, comp_map[i]);
11041 gst_value_array_append_value (&arr, &elt);
11043 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11044 "component-map", &arr);
11045 g_value_unset (&elt);
11046 g_value_unset (&arr);
11051 GValue arr = { 0, };
11052 GValue elt = { 0, };
11054 g_value_init (&arr, GST_TYPE_ARRAY);
11055 g_value_init (&elt, G_TYPE_INT);
11056 for (i = 0; i < nchan_def; i++) {
11057 g_value_set_int (&elt, chan_def[i]);
11058 gst_value_array_append_value (&arr, &elt);
11060 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11061 "channel-definitions", &arr);
11062 g_value_unset (&elt);
11063 g_value_unset (&arr);
11067 /* some optional atoms */
11068 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11069 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11071 /* indicate possible fields in caps */
11073 data = (guint8 *) field->data + 8;
11075 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11076 (gint) * data, NULL);
11078 /* add codec_data if provided */
11083 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11084 data = prefix->data;
11085 len = QT_UINT32 (data);
11088 buf = gst_buffer_new_and_alloc (len);
11089 gst_buffer_fill (buf, 0, data + 8, len);
11090 gst_caps_set_simple (entry->caps,
11091 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11092 gst_buffer_unref (buf);
11101 GstBuffer *seqh = NULL;
11102 const guint8 *gamma_data = NULL;
11103 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11105 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11108 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11109 QT_FP32 (gamma_data), NULL);
11112 /* sorry for the bad name, but we don't know what this is, other
11113 * than its own fourcc */
11114 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11116 gst_buffer_unref (seqh);
11119 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11120 buf = gst_buffer_new_and_alloc (len);
11121 gst_buffer_fill (buf, 0, stsd_data, len);
11122 gst_caps_set_simple (entry->caps,
11123 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11124 gst_buffer_unref (buf);
11129 /* https://developer.apple.com/standards/qtff-2001.pdf,
11130 * page 92, "Video Sample Description", under table 3.1 */
11133 const gint compressor_offset =
11134 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11135 const gint min_size = compressor_offset + 32 + 2 + 2;
11138 guint16 color_table_id = 0;
11141 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11143 /* recover information on interlaced/progressive */
11144 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11148 len = QT_UINT32 (jpeg->data);
11149 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11151 if (len >= min_size) {
11152 gst_byte_reader_init (&br, jpeg->data, len);
11154 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11155 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11156 if (color_table_id != 0) {
11157 /* the spec says there can be concatenated chunks in the data, and we want
11158 * to find one called field. Walk through them. */
11159 gint offset = min_size;
11160 while (offset + 8 < len) {
11161 guint32 size = 0, tag;
11162 ok = gst_byte_reader_get_uint32_le (&br, &size);
11163 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11164 if (!ok || size < 8) {
11165 GST_WARNING_OBJECT (qtdemux,
11166 "Failed to walk optional chunk list");
11169 GST_DEBUG_OBJECT (qtdemux,
11170 "Found optional %4.4s chunk, size %u",
11171 (const char *) &tag, size);
11172 if (tag == FOURCC_fiel) {
11173 guint8 n_fields = 0, ordering = 0;
11174 gst_byte_reader_get_uint8 (&br, &n_fields);
11175 gst_byte_reader_get_uint8 (&br, &ordering);
11176 if (n_fields == 1 || n_fields == 2) {
11177 GST_DEBUG_OBJECT (qtdemux,
11178 "Found fiel tag with %u fields, ordering %u",
11179 n_fields, ordering);
11181 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11182 "interlace-mode", G_TYPE_STRING, "interleaved",
11185 GST_WARNING_OBJECT (qtdemux,
11186 "Found fiel tag with invalid fields (%u)", n_fields);
11192 GST_DEBUG_OBJECT (qtdemux,
11193 "Color table ID is 0, not trying to get interlacedness");
11196 GST_WARNING_OBJECT (qtdemux,
11197 "Length of jpeg chunk is too small, not trying to get interlacedness");
11205 gst_caps_set_simple (entry->caps,
11206 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11212 GNode *xith, *xdxt;
11214 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11215 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11219 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11223 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11224 /* collect the headers and store them in a stream list so that we can
11225 * send them out first */
11226 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11236 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11237 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11240 ovc1_data = ovc1->data;
11241 ovc1_len = QT_UINT32 (ovc1_data);
11242 if (ovc1_len <= 198) {
11243 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11246 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11247 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11248 gst_caps_set_simple (entry->caps,
11249 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11250 gst_buffer_unref (buf);
11255 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11256 const guint8 *vc1_data = stsd_entry_data + 0x56;
11262 if (QT_UINT32 (vc1_data) <= len)
11263 size = QT_UINT32 (vc1_data) - 8;
11268 /* No real data, so break out */
11271 switch (QT_FOURCC (vc1_data + 0x4)) {
11272 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11276 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11277 buf = gst_buffer_new_and_alloc (size);
11278 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11279 gst_caps_set_simple (entry->caps,
11280 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11281 gst_buffer_unref (buf);
11288 vc1_data += size + 8;
11297 GST_INFO_OBJECT (qtdemux,
11298 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11299 GST_FOURCC_ARGS (fourcc), entry->caps);
11301 } else if (stream->subtype == FOURCC_soun) {
11302 int version, samplesize;
11303 guint16 compression_id;
11304 gboolean amrwb = FALSE;
11307 /* sample description entry (16) + sound sample description v0 (20) */
11311 version = QT_UINT32 (stsd_entry_data + offset);
11312 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11313 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11314 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11315 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11317 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11318 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11319 QT_UINT32 (stsd_entry_data + offset + 4));
11320 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11321 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11322 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11323 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11324 QT_UINT16 (stsd_entry_data + offset + 14));
11325 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11327 if (compression_id == 0xfffe)
11328 entry->sampled = TRUE;
11330 /* first assume uncompressed audio */
11331 entry->bytes_per_sample = samplesize / 8;
11332 entry->samples_per_frame = entry->n_channels;
11333 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11334 entry->samples_per_packet = entry->samples_per_frame;
11335 entry->bytes_per_packet = entry->bytes_per_sample;
11339 /* Yes, these have to be hard-coded */
11342 entry->samples_per_packet = 6;
11343 entry->bytes_per_packet = 1;
11344 entry->bytes_per_frame = 1 * entry->n_channels;
11345 entry->bytes_per_sample = 1;
11346 entry->samples_per_frame = 6 * entry->n_channels;
11351 entry->samples_per_packet = 3;
11352 entry->bytes_per_packet = 1;
11353 entry->bytes_per_frame = 1 * entry->n_channels;
11354 entry->bytes_per_sample = 1;
11355 entry->samples_per_frame = 3 * entry->n_channels;
11360 entry->samples_per_packet = 64;
11361 entry->bytes_per_packet = 34;
11362 entry->bytes_per_frame = 34 * entry->n_channels;
11363 entry->bytes_per_sample = 2;
11364 entry->samples_per_frame = 64 * entry->n_channels;
11370 entry->samples_per_packet = 1;
11371 entry->bytes_per_packet = 1;
11372 entry->bytes_per_frame = 1 * entry->n_channels;
11373 entry->bytes_per_sample = 1;
11374 entry->samples_per_frame = 1 * entry->n_channels;
11379 entry->samples_per_packet = 160;
11380 entry->bytes_per_packet = 33;
11381 entry->bytes_per_frame = 33 * entry->n_channels;
11382 entry->bytes_per_sample = 2;
11383 entry->samples_per_frame = 160 * entry->n_channels;
11390 if (version == 0x00010000) {
11391 /* sample description entry (16) + sound sample description v1 (20+16) */
11402 /* only parse extra decoding config for non-pcm audio */
11403 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11404 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11405 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11406 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11408 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11409 entry->samples_per_packet);
11410 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11411 entry->bytes_per_packet);
11412 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11413 entry->bytes_per_frame);
11414 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11415 entry->bytes_per_sample);
11417 if (!entry->sampled && entry->bytes_per_packet) {
11418 entry->samples_per_frame = (entry->bytes_per_frame /
11419 entry->bytes_per_packet) * entry->samples_per_packet;
11420 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11421 entry->samples_per_frame);
11426 } else if (version == 0x00020000) {
11433 /* sample description entry (16) + sound sample description v2 (56) */
11437 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
11438 entry->rate = qtfp.fp;
11439 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11441 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11442 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11443 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11444 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11445 QT_UINT32 (stsd_entry_data + offset + 20));
11446 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11447 QT_UINT32 (stsd_entry_data + offset + 24));
11448 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11449 QT_UINT32 (stsd_entry_data + offset + 28));
11450 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11451 QT_UINT32 (stsd_entry_data + offset + 32));
11452 } else if (version != 0x00000) {
11453 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11458 gst_caps_unref (entry->caps);
11460 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11461 stsd_entry_data + 32, len - 16, &codec);
11469 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
11471 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
11473 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
11475 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11478 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11479 gst_caps_set_simple (entry->caps,
11480 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
11487 const guint8 *owma_data;
11488 const gchar *codec_name = NULL;
11492 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11493 /* FIXME this should also be gst_riff_strf_auds,
11494 * but the latter one is actually missing bits-per-sample :( */
11499 gint32 nSamplesPerSec;
11500 gint32 nAvgBytesPerSec;
11501 gint16 nBlockAlign;
11502 gint16 wBitsPerSample;
11505 WAVEFORMATEX *wfex;
11507 GST_DEBUG_OBJECT (qtdemux, "parse owma");
11508 owma_data = stsd_entry_data;
11509 owma_len = QT_UINT32 (owma_data);
11510 if (owma_len <= 54) {
11511 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
11514 wfex = (WAVEFORMATEX *) (owma_data + 36);
11515 buf = gst_buffer_new_and_alloc (owma_len - 54);
11516 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
11517 if (wfex->wFormatTag == 0x0161) {
11518 codec_name = "Windows Media Audio";
11520 } else if (wfex->wFormatTag == 0x0162) {
11521 codec_name = "Windows Media Audio 9 Pro";
11523 } else if (wfex->wFormatTag == 0x0163) {
11524 codec_name = "Windows Media Audio 9 Lossless";
11525 /* is that correct? gstffmpegcodecmap.c is missing it, but
11526 * fluendo codec seems to support it */
11530 gst_caps_set_simple (entry->caps,
11531 "codec_data", GST_TYPE_BUFFER, buf,
11532 "wmaversion", G_TYPE_INT, version,
11533 "block_align", G_TYPE_INT,
11534 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
11535 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
11536 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
11537 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
11538 gst_buffer_unref (buf);
11542 codec = g_strdup (codec_name);
11548 gint len = QT_UINT32 (stsd_entry_data) - offset;
11549 const guint8 *wfex_data = stsd_entry_data + offset;
11550 const gchar *codec_name = NULL;
11552 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11553 /* FIXME this should also be gst_riff_strf_auds,
11554 * but the latter one is actually missing bits-per-sample :( */
11559 gint32 nSamplesPerSec;
11560 gint32 nAvgBytesPerSec;
11561 gint16 nBlockAlign;
11562 gint16 wBitsPerSample;
11567 /* FIXME: unify with similar wavformatex parsing code above */
11568 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
11574 if (QT_UINT32 (wfex_data) <= len)
11575 size = QT_UINT32 (wfex_data) - 8;
11580 /* No real data, so break out */
11583 switch (QT_FOURCC (wfex_data + 4)) {
11584 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
11586 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
11591 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
11592 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
11593 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
11594 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
11595 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
11596 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
11597 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
11599 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
11600 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
11601 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
11602 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
11603 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
11604 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
11606 if (wfex.wFormatTag == 0x0161) {
11607 codec_name = "Windows Media Audio";
11609 } else if (wfex.wFormatTag == 0x0162) {
11610 codec_name = "Windows Media Audio 9 Pro";
11612 } else if (wfex.wFormatTag == 0x0163) {
11613 codec_name = "Windows Media Audio 9 Lossless";
11614 /* is that correct? gstffmpegcodecmap.c is missing it, but
11615 * fluendo codec seems to support it */
11619 gst_caps_set_simple (entry->caps,
11620 "wmaversion", G_TYPE_INT, version,
11621 "block_align", G_TYPE_INT, wfex.nBlockAlign,
11622 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
11623 "width", G_TYPE_INT, wfex.wBitsPerSample,
11624 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
11626 if (size > wfex.cbSize) {
11629 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
11630 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
11631 size - wfex.cbSize);
11632 gst_caps_set_simple (entry->caps,
11633 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11634 gst_buffer_unref (buf);
11636 GST_WARNING_OBJECT (qtdemux, "no codec data");
11641 codec = g_strdup (codec_name);
11649 wfex_data += size + 8;
11655 const guint8 *opus_data;
11656 guint8 *channel_mapping = NULL;
11659 guint8 channel_mapping_family;
11660 guint8 stream_count;
11661 guint8 coupled_count;
11664 opus_data = stsd_entry_data;
11666 channels = GST_READ_UINT8 (opus_data + 45);
11667 rate = GST_READ_UINT32_LE (opus_data + 48);
11668 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
11669 stream_count = GST_READ_UINT8 (opus_data + 55);
11670 coupled_count = GST_READ_UINT8 (opus_data + 56);
11672 if (channels > 0) {
11673 channel_mapping = g_malloc (channels * sizeof (guint8));
11674 for (i = 0; i < channels; i++)
11675 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
11678 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
11679 channel_mapping_family, stream_count, coupled_count,
11691 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11692 GST_TAG_AUDIO_CODEC, codec, NULL);
11696 /* some bitrate info may have ended up in caps */
11697 s = gst_caps_get_structure (entry->caps, 0);
11698 gst_structure_get_int (s, "bitrate", &bitrate);
11700 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11701 GST_TAG_BITRATE, bitrate, NULL);
11704 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11705 if (!stream->protected) {
11707 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) {
11711 if (stream->protected && fourcc == FOURCC_mp4a) {
11712 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
11716 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_mp4a) {
11724 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
11726 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
11728 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
11732 /* If the fourcc's bottom 16 bits gives 'sm', then the top
11733 16 bits is a byte-swapped wave-style codec identifier,
11734 and we can find a WAVE header internally to a 'wave' atom here.
11735 This can more clearly be thought of as 'ms' as the top 16 bits, and a
11736 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
11739 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
11740 if (len < offset + 20) {
11741 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
11743 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
11744 const guint8 *data = stsd_entry_data + offset + 16;
11746 GNode *waveheadernode;
11748 wavenode = g_node_new ((guint8 *) data);
11749 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
11750 const guint8 *waveheader;
11753 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
11754 if (waveheadernode) {
11755 waveheader = (const guint8 *) waveheadernode->data;
11756 headerlen = QT_UINT32 (waveheader);
11758 if (headerlen > 8) {
11759 gst_riff_strf_auds *header = NULL;
11760 GstBuffer *headerbuf;
11766 headerbuf = gst_buffer_new_and_alloc (headerlen);
11767 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
11769 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
11770 headerbuf, &header, &extra)) {
11771 gst_caps_unref (entry->caps);
11772 /* FIXME: Need to do something with the channel reorder map */
11774 gst_riff_create_audio_caps (header->format, NULL, header,
11775 extra, NULL, NULL, NULL);
11778 gst_buffer_unref (extra);
11783 GST_DEBUG ("Didn't find waveheadernode for this codec");
11785 g_node_destroy (wavenode);
11788 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11789 stream->stream_tags);
11793 /* FIXME: what is in the chunk? */
11796 gint len = QT_UINT32 (stsd_data);
11798 /* seems to be always = 116 = 0x74 */
11804 gint len = QT_UINT32 (stsd_entry_data);
11807 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
11809 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
11810 gst_caps_set_simple (entry->caps,
11811 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11812 gst_buffer_unref (buf);
11814 gst_caps_set_simple (entry->caps,
11815 "samplesize", G_TYPE_INT, samplesize, NULL);
11820 GNode *alac, *wave = NULL;
11822 /* apparently, m4a has this atom appended directly in the stsd entry,
11823 * while mov has it in a wave atom */
11824 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
11826 /* alac now refers to stsd entry atom */
11827 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
11829 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
11831 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
11834 const guint8 *alac_data = alac->data;
11835 gint len = QT_UINT32 (alac->data);
11839 GST_DEBUG_OBJECT (qtdemux,
11840 "discarding alac atom with unexpected len %d", len);
11842 /* codec-data contains alac atom size and prefix,
11843 * ffmpeg likes it that way, not quite gst-ish though ...*/
11844 buf = gst_buffer_new_and_alloc (len);
11845 gst_buffer_fill (buf, 0, alac->data, len);
11846 gst_caps_set_simple (entry->caps,
11847 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11848 gst_buffer_unref (buf);
11850 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
11851 entry->n_channels = QT_UINT8 (alac_data + 21);
11852 entry->rate = QT_UINT32 (alac_data + 32);
11855 gst_caps_set_simple (entry->caps,
11856 "samplesize", G_TYPE_INT, samplesize, NULL);
11861 /* The codingname of the sample entry is 'fLaC' */
11862 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
11865 /* The 'dfLa' box is added to the sample entry to convey
11866 initializing information for the decoder. */
11867 const GNode *dfla =
11868 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
11871 const guint32 len = QT_UINT32 (dfla->data);
11873 /* Must contain at least dfLa box header (12),
11874 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
11876 GST_DEBUG_OBJECT (qtdemux,
11877 "discarding dfla atom with unexpected len %d", len);
11879 /* skip dfLa header to get the METADATA_BLOCKs */
11880 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
11881 const guint32 metadata_blocks_len = len - 12;
11883 gchar *stream_marker = g_strdup ("fLaC");
11884 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
11885 strlen (stream_marker));
11888 guint32 remainder = 0;
11889 guint32 block_size = 0;
11890 gboolean is_last = FALSE;
11892 GValue array = G_VALUE_INIT;
11893 GValue value = G_VALUE_INIT;
11895 g_value_init (&array, GST_TYPE_ARRAY);
11896 g_value_init (&value, GST_TYPE_BUFFER);
11898 gst_value_set_buffer (&value, block);
11899 gst_value_array_append_value (&array, &value);
11900 g_value_reset (&value);
11902 gst_buffer_unref (block);
11904 /* check there's at least one METADATA_BLOCK_HEADER's worth
11905 * of data, and we haven't already finished parsing */
11906 while (!is_last && ((index + 3) < metadata_blocks_len)) {
11907 remainder = metadata_blocks_len - index;
11909 /* add the METADATA_BLOCK_HEADER size to the signalled size */
11911 (metadata_blocks[index + 1] << 16) +
11912 (metadata_blocks[index + 2] << 8) +
11913 metadata_blocks[index + 3];
11915 /* be careful not to read off end of box */
11916 if (block_size > remainder) {
11920 is_last = metadata_blocks[index] >> 7;
11922 block = gst_buffer_new_and_alloc (block_size);
11924 gst_buffer_fill (block, 0, &metadata_blocks[index],
11927 gst_value_set_buffer (&value, block);
11928 gst_value_array_append_value (&array, &value);
11929 g_value_reset (&value);
11931 gst_buffer_unref (block);
11933 index += block_size;
11936 /* only append the metadata if we successfully read all of it */
11938 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
11939 (stream)->caps, 0), "streamheader", &array);
11941 GST_WARNING_OBJECT (qtdemux,
11942 "discarding all METADATA_BLOCKs due to invalid "
11943 "block_size %d at idx %d, rem %d", block_size, index,
11947 g_value_unset (&value);
11948 g_value_unset (&array);
11950 /* The sample rate obtained from the stsd may not be accurate
11951 * since it cannot represent rates greater than 65535Hz, so
11952 * override that value with the sample rate from the
11953 * METADATA_BLOCK_STREAMINFO block */
11954 CUR_STREAM (stream)->rate =
11955 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
11966 gint len = QT_UINT32 (stsd_entry_data);
11969 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
11972 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
11974 /* If we have enough data, let's try to get the 'damr' atom. See
11975 * the 3GPP container spec (26.244) for more details. */
11976 if ((len - 0x34) > 8 &&
11977 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
11978 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11979 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
11982 gst_caps_set_simple (entry->caps,
11983 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11984 gst_buffer_unref (buf);
11990 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
11991 gint len = QT_UINT32 (stsd_entry_data);
11994 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
11996 if (sound_version == 1) {
11997 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
11998 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
11999 guint8 codec_data[2];
12001 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12003 gint sample_rate_index =
12004 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12006 /* build AAC codec data */
12007 codec_data[0] = profile << 3;
12008 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12009 codec_data[1] = (sample_rate_index & 0x01) << 7;
12010 codec_data[1] |= (channels & 0xF) << 3;
12012 buf = gst_buffer_new_and_alloc (2);
12013 gst_buffer_fill (buf, 0, codec_data, 2);
12014 gst_caps_set_simple (entry->caps,
12015 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12016 gst_buffer_unref (buf);
12022 GST_INFO_OBJECT (qtdemux,
12023 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12027 GST_INFO_OBJECT (qtdemux,
12028 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12029 GST_FOURCC_ARGS (fourcc), entry->caps);
12031 } else if (stream->subtype == FOURCC_strm) {
12032 if (fourcc == FOURCC_rtsp) {
12033 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12035 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12036 GST_FOURCC_ARGS (fourcc));
12037 goto unknown_stream;
12039 entry->sampled = TRUE;
12040 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12041 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
12043 entry->sampled = TRUE;
12044 entry->sparse = TRUE;
12047 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12050 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12051 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12056 /* hunt for sort-of codec data */
12060 GNode *mp4s = NULL;
12061 GNode *esds = NULL;
12063 /* look for palette in a stsd->mp4s->esds sub-atom */
12064 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12066 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12067 if (esds == NULL) {
12069 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12073 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12074 stream->stream_tags);
12078 GST_INFO_OBJECT (qtdemux,
12079 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12082 GST_INFO_OBJECT (qtdemux,
12083 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12084 GST_FOURCC_ARGS (fourcc), entry->caps);
12086 /* everything in 1 sample */
12087 entry->sampled = TRUE;
12090 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12093 if (entry->caps == NULL)
12094 goto unknown_stream;
12097 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12098 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12104 /* promote to sampled format */
12105 if (entry->fourcc == FOURCC_samr) {
12106 /* force mono 8000 Hz for AMR */
12107 entry->sampled = TRUE;
12108 entry->n_channels = 1;
12109 entry->rate = 8000;
12110 } else if (entry->fourcc == FOURCC_sawb) {
12111 /* force mono 16000 Hz for AMR-WB */
12112 entry->sampled = TRUE;
12113 entry->n_channels = 1;
12114 entry->rate = 16000;
12115 } else if (entry->fourcc == FOURCC_mp4a) {
12116 entry->sampled = TRUE;
12120 stsd_entry_data += len;
12121 remaining_stsd_len -= len;
12125 /* collect sample information */
12126 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12127 goto samples_failed;
12129 if (qtdemux->fragmented) {
12132 /* need all moov samples as basis; probably not many if any at all */
12133 /* prevent moof parsing taking of at this time */
12134 offset = qtdemux->moof_offset;
12135 qtdemux->moof_offset = 0;
12136 if (stream->n_samples &&
12137 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12138 qtdemux->moof_offset = offset;
12139 goto samples_failed;
12141 qtdemux->moof_offset = 0;
12142 /* movie duration more reliable in this case (e.g. mehd) */
12143 if (qtdemux->segment.duration &&
12144 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12146 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12149 /* configure segments */
12150 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12151 goto segments_failed;
12153 /* add some language tag, if useful */
12154 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12155 strcmp (stream->lang_id, "und")) {
12156 const gchar *lang_code;
12158 /* convert ISO 639-2 code to ISO 639-1 */
12159 lang_code = gst_tag_get_language_code (stream->lang_id);
12160 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12161 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12164 /* Check for UDTA tags */
12165 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12166 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12169 /* now we are ready to add the stream */
12170 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
12171 goto too_many_streams;
12173 if (!qtdemux->got_moov) {
12174 qtdemux->streams[qtdemux->n_streams] = stream;
12175 qtdemux->n_streams++;
12176 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
12184 GST_INFO_OBJECT (qtdemux, "skip disabled track");
12186 gst_qtdemux_stream_free (qtdemux, stream);
12191 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12192 (_("This file is corrupt and cannot be played.")), (NULL));
12194 gst_qtdemux_stream_free (qtdemux, stream);
12199 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12201 gst_qtdemux_stream_free (qtdemux, stream);
12207 /* we posted an error already */
12208 /* free stbl sub-atoms */
12209 gst_qtdemux_stbl_free (stream);
12211 gst_qtdemux_stream_free (qtdemux, stream);
12216 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12219 gst_qtdemux_stream_free (qtdemux, stream);
12224 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12225 GST_FOURCC_ARGS (stream->subtype));
12227 gst_qtdemux_stream_free (qtdemux, stream);
12232 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
12233 (_("This file contains too many streams. Only playing first %d"),
12234 GST_QTDEMUX_MAX_STREAMS), (NULL));
12239 /* If we can estimate the overall bitrate, and don't have information about the
12240 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12241 * the overall bitrate minus the sum of the bitrates of all other streams. This
12242 * should be useful for the common case where we have one audio and one video
12243 * stream and can estimate the bitrate of one, but not the other. */
12245 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12247 QtDemuxStream *stream = NULL;
12248 gint64 size, sys_bitrate, sum_bitrate = 0;
12249 GstClockTime duration;
12253 if (qtdemux->fragmented)
12256 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12258 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12260 GST_DEBUG_OBJECT (qtdemux,
12261 "Size in bytes of the stream not known - bailing");
12265 /* Subtract the header size */
12266 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12267 size, qtdemux->header_size);
12269 if (size < qtdemux->header_size)
12272 size = size - qtdemux->header_size;
12274 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12275 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12279 for (i = 0; i < qtdemux->n_streams; i++) {
12280 switch (qtdemux->streams[i]->subtype) {
12283 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12284 CUR_STREAM (qtdemux->streams[i])->caps);
12285 /* retrieve bitrate, prefer avg then max */
12287 if (qtdemux->streams[i]->stream_tags) {
12288 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12289 GST_TAG_MAXIMUM_BITRATE, &bitrate);
12290 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12291 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12292 GST_TAG_NOMINAL_BITRATE, &bitrate);
12293 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12294 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12295 GST_TAG_BITRATE, &bitrate);
12296 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12299 sum_bitrate += bitrate;
12302 GST_DEBUG_OBJECT (qtdemux,
12303 ">1 stream with unknown bitrate - bailing");
12306 stream = qtdemux->streams[i];
12310 /* For other subtypes, we assume no significant impact on bitrate */
12316 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12320 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12322 if (sys_bitrate < sum_bitrate) {
12323 /* This can happen, since sum_bitrate might be derived from maximum
12324 * bitrates and not average bitrates */
12325 GST_DEBUG_OBJECT (qtdemux,
12326 "System bitrate less than sum bitrate - bailing");
12330 bitrate = sys_bitrate - sum_bitrate;
12331 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12332 ", Stream bitrate = %u", sys_bitrate, bitrate);
12334 if (!stream->stream_tags)
12335 stream->stream_tags = gst_tag_list_new_empty ();
12337 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12339 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12340 GST_TAG_BITRATE, bitrate, NULL);
12343 static GstFlowReturn
12344 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12347 GstFlowReturn ret = GST_FLOW_OK;
12349 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12351 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
12352 QtDemuxStream *stream = qtdemux->streams[i];
12353 guint32 sample_num = 0;
12355 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
12356 i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12358 if (qtdemux->fragmented) {
12359 /* need all moov samples first */
12360 GST_OBJECT_LOCK (qtdemux);
12361 while (stream->n_samples == 0)
12362 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12364 GST_OBJECT_UNLOCK (qtdemux);
12366 /* discard any stray moof */
12367 qtdemux->moof_offset = 0;
12370 /* prepare braking */
12371 if (ret != GST_FLOW_ERROR)
12374 /* in pull mode, we should have parsed some sample info by now;
12375 * and quite some code will not handle no samples.
12376 * in push mode, we'll just have to deal with it */
12377 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12378 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12379 gst_qtdemux_remove_stream (qtdemux, i);
12384 /* parse the initial sample for use in setting the frame rate cap */
12385 while (sample_num == 0 && sample_num < stream->n_samples) {
12386 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12390 if (stream->n_samples > 0 && stream->stbl_index >= 0) {
12391 stream->first_duration = stream->samples[0].duration;
12392 GST_LOG_OBJECT (qtdemux, "stream %d first duration %u",
12393 stream->track_id, stream->first_duration);
12400 static GstFlowReturn
12401 qtdemux_expose_streams (GstQTDemux * qtdemux)
12404 GSList *oldpads = NULL;
12407 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
12409 for (i = 0; i < qtdemux->n_streams; i++) {
12410 QtDemuxStream *stream = qtdemux->streams[i];
12411 GstPad *oldpad = stream->pad;
12414 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
12415 i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12417 if ((stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) &&
12418 stream->track_id == qtdemux->chapters_track_id) {
12419 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12420 so that it doesn't look like a subtitle track */
12421 gst_qtdemux_remove_stream (qtdemux, i);
12426 /* now we have all info and can expose */
12427 list = stream->stream_tags;
12428 stream->stream_tags = NULL;
12430 oldpads = g_slist_prepend (oldpads, oldpad);
12431 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12432 return GST_FLOW_ERROR;
12435 gst_qtdemux_guess_bitrate (qtdemux);
12437 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
12439 for (iter = oldpads; iter; iter = g_slist_next (iter)) {
12440 GstPad *oldpad = iter->data;
12443 event = gst_event_new_eos ();
12444 if (qtdemux->segment_seqnum)
12445 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
12447 gst_pad_push_event (oldpad, event);
12448 gst_pad_set_active (oldpad, FALSE);
12449 gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad);
12450 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, oldpad);
12451 gst_object_unref (oldpad);
12454 /* check if we should post a redirect in case there is a single trak
12455 * and it is a redirecting trak */
12456 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
12459 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
12460 "an external content");
12461 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
12462 gst_structure_new ("redirect",
12463 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
12465 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
12466 qtdemux->posted_redirect = TRUE;
12469 for (i = 0; i < qtdemux->n_streams; i++) {
12470 QtDemuxStream *stream = qtdemux->streams[i];
12472 qtdemux_do_allocation (qtdemux, stream);
12475 qtdemux->exposed = TRUE;
12476 return GST_FLOW_OK;
12479 /* check if major or compatible brand is 3GP */
12480 static inline gboolean
12481 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
12484 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12486 } else if (qtdemux->comp_brands != NULL) {
12490 gboolean res = FALSE;
12492 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
12495 while (size >= 4) {
12496 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12501 gst_buffer_unmap (qtdemux->comp_brands, &map);
12508 /* check if tag is a spec'ed 3GP tag keyword storing a string */
12509 static inline gboolean
12510 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
12512 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
12513 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
12514 || fourcc == FOURCC_albm;
12518 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
12519 const char *tag, const char *dummy, GNode * node)
12521 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12525 gdouble longitude, latitude, altitude;
12528 len = QT_UINT32 (node->data);
12535 /* TODO: language code skipped */
12537 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
12540 /* do not alarm in trivial case, but bail out otherwise */
12541 if (*(data + offset) != 0) {
12542 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
12546 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12547 GST_TAG_GEO_LOCATION_NAME, name, NULL);
12548 offset += strlen (name);
12552 if (len < offset + 2 + 4 + 4 + 4)
12555 /* +1 +1 = skip null-terminator and location role byte */
12557 /* table in spec says unsigned, semantics say negative has meaning ... */
12558 longitude = QT_SFP32 (data + offset);
12561 latitude = QT_SFP32 (data + offset);
12564 altitude = QT_SFP32 (data + offset);
12566 /* one invalid means all are invalid */
12567 if (longitude >= -180.0 && longitude <= 180.0 &&
12568 latitude >= -90.0 && latitude <= 90.0) {
12569 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12570 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
12571 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
12572 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
12575 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
12582 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
12589 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
12590 const char *tag, const char *dummy, GNode * node)
12596 len = QT_UINT32 (node->data);
12600 y = QT_UINT16 ((guint8 *) node->data + 12);
12602 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
12605 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
12607 date = g_date_new_dmy (1, 1, y);
12608 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
12609 g_date_free (date);
12613 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
12614 const char *tag, const char *dummy, GNode * node)
12617 char *tag_str = NULL;
12622 len = QT_UINT32 (node->data);
12627 entity = (guint8 *) node->data + offset;
12628 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
12629 GST_DEBUG_OBJECT (qtdemux,
12630 "classification info: %c%c%c%c invalid classification entity",
12631 entity[0], entity[1], entity[2], entity[3]);
12636 table = QT_UINT16 ((guint8 *) node->data + offset);
12638 /* Language code skipped */
12642 /* Tag format: "XXXX://Y[YYYY]/classification info string"
12643 * XXXX: classification entity, fixed length 4 chars.
12644 * Y[YYYY]: classification table, max 5 chars.
12646 tag_str = g_strdup_printf ("----://%u/%s",
12647 table, (char *) node->data + offset);
12649 /* memcpy To be sure we're preserving byte order */
12650 memcpy (tag_str, entity, 4);
12651 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
12653 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
12662 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
12668 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
12669 const char *tag, const char *dummy, GNode * node)
12671 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12677 gboolean ret = TRUE;
12678 const gchar *charset = NULL;
12680 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12682 len = QT_UINT32 (data->data);
12683 type = QT_UINT32 ((guint8 *) data->data + 8);
12684 if (type == 0x00000001 && len > 16) {
12685 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
12688 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
12689 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
12692 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
12696 len = QT_UINT32 (node->data);
12697 type = QT_UINT32 ((guint8 *) node->data + 4);
12698 if ((type >> 24) == 0xa9 && len > 8 + 4) {
12702 /* Type starts with the (C) symbol, so the next data is a list
12703 * of (string size(16), language code(16), string) */
12705 str_len = QT_UINT16 ((guint8 *) node->data + 8);
12706 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
12708 /* the string + fourcc + size + 2 16bit fields,
12709 * means that there are more tags in this atom */
12710 if (len > str_len + 8 + 4) {
12711 /* TODO how to represent the same tag in different languages? */
12712 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
12713 "text alternatives, reading only first one");
12717 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
12718 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
12720 if (lang_code < 0x800) { /* MAC encoded string */
12723 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
12724 QT_FOURCC ((guint8 *) node->data + 4))) {
12725 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
12727 /* we go for 3GP style encoding if major brands claims so,
12728 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
12729 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
12730 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
12731 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
12733 /* 16-bit Language code is ignored here as well */
12734 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
12741 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
12742 ret = FALSE; /* may have to fallback */
12745 GError *err = NULL;
12747 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
12748 charset, NULL, NULL, &err);
12750 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
12751 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
12753 g_error_free (err);
12756 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
12757 len - offset, env_vars);
12760 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
12761 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
12765 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
12772 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
12773 const char *tag, const char *dummy, GNode * node)
12775 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
12779 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
12780 const char *tag, const char *dummy, GNode * node)
12782 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12784 char *s, *t, *k = NULL;
12789 /* first try normal string tag if major brand not 3GP */
12790 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
12791 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
12792 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
12793 * let's try it 3gpp way after minor safety check */
12795 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
12801 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
12805 len = QT_UINT32 (data);
12809 count = QT_UINT8 (data + 14);
12811 for (; count; count--) {
12814 if (offset + 1 > len)
12816 slen = QT_UINT8 (data + offset);
12818 if (offset + slen > len)
12820 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
12823 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
12825 t = g_strjoin (",", k, s, NULL);
12833 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
12840 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
12841 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
12850 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
12856 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
12857 const char *tag1, const char *tag2, GNode * node)
12864 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12866 len = QT_UINT32 (data->data);
12867 type = QT_UINT32 ((guint8 *) data->data + 8);
12868 if (type == 0x00000000 && len >= 22) {
12869 n1 = QT_UINT16 ((guint8 *) data->data + 18);
12870 n2 = QT_UINT16 ((guint8 *) data->data + 20);
12872 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
12873 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
12876 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
12877 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
12884 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
12885 const char *tag1, const char *dummy, GNode * node)
12892 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12894 len = QT_UINT32 (data->data);
12895 type = QT_UINT32 ((guint8 *) data->data + 8);
12896 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
12897 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
12898 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
12899 n1 = QT_UINT16 ((guint8 *) data->data + 16);
12901 /* do not add bpm=0 */
12902 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
12903 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
12911 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
12912 const char *tag1, const char *dummy, GNode * node)
12919 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12921 len = QT_UINT32 (data->data);
12922 type = QT_UINT32 ((guint8 *) data->data + 8);
12923 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
12924 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
12925 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
12926 num = QT_UINT32 ((guint8 *) data->data + 16);
12928 /* do not add num=0 */
12929 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
12930 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
12937 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
12938 const char *tag1, const char *dummy, GNode * node)
12945 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12947 len = QT_UINT32 (data->data);
12948 type = QT_UINT32 ((guint8 *) data->data + 8);
12949 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
12950 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
12951 GstTagImageType image_type;
12953 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
12954 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
12956 image_type = GST_TAG_IMAGE_TYPE_NONE;
12959 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
12960 len - 16, image_type))) {
12961 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
12962 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
12963 gst_sample_unref (sample);
12970 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
12971 const char *tag, const char *dummy, GNode * node)
12978 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12980 len = QT_UINT32 (data->data);
12981 type = QT_UINT32 ((guint8 *) data->data + 8);
12982 if (type == 0x00000001 && len > 16) {
12983 guint y, m = 1, d = 1;
12986 s = g_strndup ((char *) data->data + 16, len - 16);
12987 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
12988 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
12989 if (ret >= 1 && y > 1500 && y < 3000) {
12992 date = g_date_new_dmy (d, m, y);
12993 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
12994 g_date_free (date);
12996 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
13004 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
13005 const char *tag, const char *dummy, GNode * node)
13009 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13011 /* re-route to normal string tag if major brand says so
13012 * or no data atom and compatible brand suggests so */
13013 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13014 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
13015 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
13020 guint len, type, n;
13022 len = QT_UINT32 (data->data);
13023 type = QT_UINT32 ((guint8 *) data->data + 8);
13024 if (type == 0x00000000 && len >= 18) {
13025 n = QT_UINT16 ((guint8 *) data->data + 16);
13027 const gchar *genre;
13029 genre = gst_tag_id3_genre_get (n - 1);
13030 if (genre != NULL) {
13031 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
13032 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
13040 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
13041 const gchar * tag, guint8 * data, guint32 datasize)
13046 /* make a copy to have \0 at the end */
13047 datacopy = g_strndup ((gchar *) data, datasize);
13049 /* convert the str to double */
13050 if (sscanf (datacopy, "%lf", &value) == 1) {
13051 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
13052 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
13054 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
13062 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
13063 const char *tag, const char *tag_bis, GNode * node)
13072 const gchar *meanstr;
13073 const gchar *namestr;
13075 /* checking the whole ---- atom size for consistency */
13076 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
13077 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
13081 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
13083 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
13087 meansize = QT_UINT32 (mean->data);
13088 if (meansize <= 12) {
13089 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
13092 meanstr = ((gchar *) mean->data) + 12;
13095 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
13097 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
13101 namesize = QT_UINT32 (name->data);
13102 if (namesize <= 12) {
13103 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
13106 namestr = ((gchar *) name->data) + 12;
13114 * uint24 - data type
13118 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13120 GST_WARNING_OBJECT (demux, "No data atom in this tag");
13123 datasize = QT_UINT32 (data->data);
13124 if (datasize <= 16) {
13125 GST_WARNING_OBJECT (demux, "Data atom too small");
13128 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
13130 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
13131 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
13132 static const struct
13134 const gchar name[28];
13135 const gchar tag[28];
13138 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
13139 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
13140 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
13141 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
13142 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
13143 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
13144 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
13145 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
13149 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
13150 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
13151 switch (gst_tag_get_type (tags[i].tag)) {
13152 case G_TYPE_DOUBLE:
13153 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
13154 ((guint8 *) data->data) + 16, datasize - 16);
13156 case G_TYPE_STRING:
13157 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
13166 if (i == G_N_ELEMENTS (tags))
13176 #ifndef GST_DISABLE_GST_DEBUG
13178 gchar *namestr_dbg;
13179 gchar *meanstr_dbg;
13181 meanstr_dbg = g_strndup (meanstr, meansize);
13182 namestr_dbg = g_strndup (namestr, namesize);
13184 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
13185 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
13187 g_free (namestr_dbg);
13188 g_free (meanstr_dbg);
13195 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
13196 const char *tag_bis, GNode * node)
13201 GstTagList *id32_taglist = NULL;
13203 GST_LOG_OBJECT (demux, "parsing ID32");
13206 len = GST_READ_UINT32_BE (data);
13208 /* need at least full box and language tag */
13212 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
13213 gst_buffer_fill (buf, 0, data + 14, len - 14);
13215 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
13216 if (id32_taglist) {
13217 GST_LOG_OBJECT (demux, "parsing ok");
13218 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
13219 gst_tag_list_unref (id32_taglist);
13221 GST_LOG_OBJECT (demux, "parsing failed");
13224 gst_buffer_unref (buf);
13227 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
13228 const char *tag, const char *tag_bis, GNode * node);
13231 FOURCC_pcst -> if media is a podcast -> bool
13232 FOURCC_cpil -> if media is part of a compilation -> bool
13233 FOURCC_pgap -> if media is part of a gapless context -> bool
13234 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
13237 static const struct
13240 const gchar *gst_tag;
13241 const gchar *gst_tag_bis;
13242 const GstQTDemuxAddTagFunc func;
13245 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13246 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13247 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
13248 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13249 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13250 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
13251 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13252 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13253 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13254 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13255 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13256 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13257 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13258 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13259 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13260 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13261 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
13262 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
13263 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
13264 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13265 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13266 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
13267 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13268 qtdemux_tag_add_num}, {
13269 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13270 qtdemux_tag_add_num}, {
13271 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
13272 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
13273 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
13274 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
13275 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
13276 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
13277 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13278 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13279 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
13280 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
13281 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
13282 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13283 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13284 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
13285 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
13286 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13287 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
13288 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
13289 qtdemux_tag_add_classification}, {
13290 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
13291 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
13292 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
13294 /* This is a special case, some tags are stored in this
13295 * 'reverse dns naming', according to:
13296 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
13299 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
13300 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
13301 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
13304 struct _GstQtDemuxTagList
13307 GstTagList *taglist;
13309 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
13312 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
13318 const gchar *style;
13323 GstQTDemux *demux = qtdemuxtaglist->demux;
13324 GstTagList *taglist = qtdemuxtaglist->taglist;
13327 len = QT_UINT32 (data);
13328 buf = gst_buffer_new_and_alloc (len);
13329 gst_buffer_fill (buf, 0, data, len);
13331 /* heuristic to determine style of tag */
13332 if (QT_FOURCC (data + 4) == FOURCC_____ ||
13333 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
13335 else if (demux->major_brand == FOURCC_qt__)
13336 style = "quicktime";
13337 /* fall back to assuming iso/3gp tag style */
13341 /* santize the name for the caps. */
13342 for (i = 0; i < 4; i++) {
13343 guint8 d = data[4 + i];
13344 if (g_ascii_isalnum (d))
13345 ndata[i] = g_ascii_tolower (d);
13350 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
13351 ndata[0], ndata[1], ndata[2], ndata[3]);
13352 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
13354 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
13355 sample = gst_sample_new (buf, NULL, NULL, s);
13356 gst_buffer_unref (buf);
13357 g_free (media_type);
13359 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
13362 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
13363 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
13365 gst_sample_unref (sample);
13369 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
13376 GstQtDemuxTagList demuxtaglist;
13378 demuxtaglist.demux = qtdemux;
13379 demuxtaglist.taglist = taglist;
13381 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
13382 if (meta != NULL) {
13383 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
13384 if (ilst == NULL) {
13385 GST_LOG_OBJECT (qtdemux, "no ilst");
13390 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
13394 while (i < G_N_ELEMENTS (add_funcs)) {
13395 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
13399 len = QT_UINT32 (node->data);
13401 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
13402 GST_FOURCC_ARGS (add_funcs[i].fourcc));
13404 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
13405 add_funcs[i].gst_tag_bis, node);
13407 g_node_destroy (node);
13413 /* parsed nodes have been removed, pass along remainder as blob */
13414 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
13415 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
13417 /* parse up XMP_ node if existing */
13418 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
13419 if (xmp_ != NULL) {
13421 GstTagList *xmptaglist;
13423 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
13424 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
13425 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
13426 gst_buffer_unref (buf);
13428 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
13430 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
13436 GstStructure *structure; /* helper for sort function */
13438 guint min_req_bitrate;
13439 guint min_req_qt_version;
13443 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13445 GstQtReference *ref_a = (GstQtReference *) a;
13446 GstQtReference *ref_b = (GstQtReference *) b;
13448 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13449 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13451 /* known bitrates go before unknown; higher bitrates go first */
13452 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13455 /* sort the redirects and post a message for the application.
13458 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13460 GstQtReference *best;
13463 GValue list_val = { 0, };
13466 g_assert (references != NULL);
13468 references = g_list_sort (references, qtdemux_redirects_sort_func);
13470 best = (GstQtReference *) references->data;
13472 g_value_init (&list_val, GST_TYPE_LIST);
13474 for (l = references; l != NULL; l = l->next) {
13475 GstQtReference *ref = (GstQtReference *) l->data;
13476 GValue struct_val = { 0, };
13478 ref->structure = gst_structure_new ("redirect",
13479 "new-location", G_TYPE_STRING, ref->location, NULL);
13481 if (ref->min_req_bitrate > 0) {
13482 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13483 ref->min_req_bitrate, NULL);
13486 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13487 g_value_set_boxed (&struct_val, ref->structure);
13488 gst_value_list_append_value (&list_val, &struct_val);
13489 g_value_unset (&struct_val);
13490 /* don't free anything here yet, since we need best->structure below */
13493 g_assert (best != NULL);
13494 s = gst_structure_copy (best->structure);
13496 if (g_list_length (references) > 1) {
13497 gst_structure_set_value (s, "locations", &list_val);
13500 g_value_unset (&list_val);
13502 for (l = references; l != NULL; l = l->next) {
13503 GstQtReference *ref = (GstQtReference *) l->data;
13505 gst_structure_free (ref->structure);
13506 g_free (ref->location);
13509 g_list_free (references);
13511 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13512 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13513 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13514 qtdemux->posted_redirect = TRUE;
13517 /* look for redirect nodes, collect all redirect information and
13521 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13523 GNode *rmra, *rmda, *rdrf;
13525 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13527 GList *redirects = NULL;
13529 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13531 GstQtReference ref = { NULL, NULL, 0, 0 };
13532 GNode *rmdr, *rmvc;
13534 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13535 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13536 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13537 ref.min_req_bitrate);
13540 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13541 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13542 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
13544 #ifndef GST_DISABLE_GST_DEBUG
13545 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
13547 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
13549 GST_LOG_OBJECT (qtdemux,
13550 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
13551 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
13552 bitmask, check_type);
13553 if (package == FOURCC_qtim && check_type == 0) {
13554 ref.min_req_qt_version = version;
13558 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
13564 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
13565 if (ref_len > 20) {
13566 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
13567 ref_data = (guint8 *) rdrf->data + 20;
13568 if (ref_type == FOURCC_alis) {
13569 guint record_len, record_version, fn_len;
13571 if (ref_len > 70) {
13572 /* MacOSX alias record, google for alias-layout.txt */
13573 record_len = QT_UINT16 (ref_data + 4);
13574 record_version = QT_UINT16 (ref_data + 4 + 2);
13575 fn_len = QT_UINT8 (ref_data + 50);
13576 if (record_len > 50 && record_version == 2 && fn_len > 0) {
13577 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
13580 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
13583 } else if (ref_type == FOURCC_url_) {
13584 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
13586 GST_DEBUG_OBJECT (qtdemux,
13587 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
13588 GST_FOURCC_ARGS (ref_type));
13590 if (ref.location != NULL) {
13591 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
13593 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
13595 GST_WARNING_OBJECT (qtdemux,
13596 "Failed to extract redirect location from rdrf atom");
13599 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
13603 /* look for others */
13604 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
13607 if (redirects != NULL) {
13608 qtdemux_process_redirects (qtdemux, redirects);
13614 static GstTagList *
13615 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
13619 if (tags == NULL) {
13620 tags = gst_tag_list_new_empty ();
13621 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
13624 if (qtdemux->major_brand == FOURCC_mjp2)
13625 fmt = "Motion JPEG 2000";
13626 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
13628 else if (qtdemux->major_brand == FOURCC_qt__)
13630 else if (qtdemux->fragmented)
13633 fmt = "ISO MP4/M4A";
13635 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
13636 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
13638 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
13644 /* we have read the complete moov node now.
13645 * This function parses all of the relevant info, creates the traks and
13646 * prepares all data structures for playback
13649 qtdemux_parse_tree (GstQTDemux * qtdemux)
13655 GstClockTime duration;
13657 guint64 creation_time;
13658 GstDateTime *datetime = NULL;
13661 /* make sure we have a usable taglist */
13662 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13664 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
13665 if (mvhd == NULL) {
13666 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
13667 return qtdemux_parse_redirects (qtdemux);
13670 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
13671 if (version == 1) {
13672 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
13673 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
13674 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
13675 } else if (version == 0) {
13676 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
13677 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
13678 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
13680 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
13684 /* Moving qt creation time (secs since 1904) to unix time */
13685 if (creation_time != 0) {
13686 /* Try to use epoch first as it should be faster and more commonly found */
13687 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
13690 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
13691 /* some data cleansing sanity */
13692 g_get_current_time (&now);
13693 if (now.tv_sec + 24 * 3600 < creation_time) {
13694 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
13696 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
13699 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
13700 GDateTime *dt, *dt_local;
13702 dt = g_date_time_add_seconds (base_dt, creation_time);
13703 dt_local = g_date_time_to_local (dt);
13704 datetime = gst_date_time_new_from_g_date_time (dt_local);
13706 g_date_time_unref (base_dt);
13707 g_date_time_unref (dt);
13711 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
13712 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
13714 gst_date_time_unref (datetime);
13717 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
13718 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
13720 /* check for fragmented file and get some (default) data */
13721 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
13724 GstByteReader mehd_data;
13726 /* let track parsing or anyone know weird stuff might happen ... */
13727 qtdemux->fragmented = TRUE;
13729 /* compensate for total duration */
13730 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
13732 qtdemux_parse_mehd (qtdemux, &mehd_data);
13735 /* set duration in the segment info */
13736 gst_qtdemux_get_duration (qtdemux, &duration);
13738 qtdemux->segment.duration = duration;
13739 /* also do not exceed duration; stop is set that way post seek anyway,
13740 * and segment activation falls back to duration,
13741 * whereas loop only checks stop, so let's align this here as well */
13742 qtdemux->segment.stop = duration;
13745 /* parse all traks */
13746 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
13748 qtdemux_parse_trak (qtdemux, trak);
13749 /* iterate all siblings */
13750 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
13753 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13756 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
13758 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13760 GST_LOG_OBJECT (qtdemux, "No udta node found.");
13763 /* maybe also some tags in meta box */
13764 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
13766 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
13767 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13769 GST_LOG_OBJECT (qtdemux, "No meta node found.");
13772 /* parse any protection system info */
13773 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
13775 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
13776 qtdemux_parse_pssh (qtdemux, pssh);
13777 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
13780 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
13785 /* taken from ffmpeg */
13787 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
13799 len = (len << 7) | (c & 0x7f);
13807 /* this can change the codec originally present in @list */
13809 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
13810 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
13812 int len = QT_UINT32 (esds->data);
13813 guint8 *ptr = esds->data;
13814 guint8 *end = ptr + len;
13816 guint8 *data_ptr = NULL;
13818 guint8 object_type_id = 0;
13819 const char *codec_name = NULL;
13820 GstCaps *caps = NULL;
13822 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
13824 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
13826 while (ptr + 1 < end) {
13827 tag = QT_UINT8 (ptr);
13828 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
13830 len = read_descr_size (ptr, end, &ptr);
13831 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
13833 /* Check the stated amount of data is available for reading */
13834 if (len < 0 || ptr + len > end)
13838 case ES_DESCRIPTOR_TAG:
13839 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
13840 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
13843 case DECODER_CONFIG_DESC_TAG:{
13844 guint max_bitrate, avg_bitrate;
13846 object_type_id = QT_UINT8 (ptr);
13847 max_bitrate = QT_UINT32 (ptr + 5);
13848 avg_bitrate = QT_UINT32 (ptr + 9);
13849 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
13850 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
13851 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
13852 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
13853 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
13854 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
13855 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13856 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
13858 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
13859 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
13860 avg_bitrate, NULL);
13865 case DECODER_SPECIFIC_INFO_TAG:
13866 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
13867 if (object_type_id == 0xe0 && len == 0x40) {
13873 GST_DEBUG_OBJECT (qtdemux,
13874 "Have VOBSUB palette. Creating palette event");
13875 /* move to decConfigDescr data and read palette */
13877 for (i = 0; i < 16; i++) {
13878 clut[i] = QT_UINT32 (data);
13882 s = gst_structure_new ("application/x-gst-dvd", "event",
13883 G_TYPE_STRING, "dvd-spu-clut-change",
13884 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
13885 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
13886 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
13887 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
13888 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
13889 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
13890 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
13891 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
13894 /* store event and trigger custom processing */
13895 stream->pending_event =
13896 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
13898 /* Generic codec_data handler puts it on the caps */
13905 case SL_CONFIG_DESC_TAG:
13906 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
13910 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
13912 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
13918 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
13919 * in use, and should also be used to override some other parameters for some
13921 switch (object_type_id) {
13922 case 0x20: /* MPEG-4 */
13923 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
13924 * profile_and_level_indication */
13925 if (data_ptr != NULL && data_len >= 5 &&
13926 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
13927 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
13928 data_ptr + 4, data_len - 4);
13930 break; /* Nothing special needed here */
13931 case 0x21: /* H.264 */
13932 codec_name = "H.264 / AVC";
13933 caps = gst_caps_new_simple ("video/x-h264",
13934 "stream-format", G_TYPE_STRING, "avc",
13935 "alignment", G_TYPE_STRING, "au", NULL);
13937 case 0x40: /* AAC (any) */
13938 case 0x66: /* AAC Main */
13939 case 0x67: /* AAC LC */
13940 case 0x68: /* AAC SSR */
13941 /* Override channels and rate based on the codec_data, as it's often
13943 /* Only do so for basic setup without HE-AAC extension */
13944 if (data_ptr && data_len == 2) {
13945 guint channels, rate;
13947 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
13949 entry->n_channels = channels;
13951 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
13953 entry->rate = rate;
13956 /* Set level and profile if possible */
13957 if (data_ptr != NULL && data_len >= 2) {
13958 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
13959 data_ptr, data_len);
13961 const gchar *profile_str = NULL;
13964 guint8 *codec_data;
13965 gint rate_idx, profile;
13967 /* No codec_data, let's invent something.
13968 * FIXME: This is wrong for SBR! */
13970 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
13972 buffer = gst_buffer_new_and_alloc (2);
13973 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
13974 codec_data = map.data;
13977 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
13980 switch (object_type_id) {
13982 profile_str = "main";
13986 profile_str = "lc";
13990 profile_str = "ssr";
13998 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14000 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14002 gst_buffer_unmap (buffer, &map);
14003 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14004 GST_TYPE_BUFFER, buffer, NULL);
14005 gst_buffer_unref (buffer);
14008 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14009 G_TYPE_STRING, profile_str, NULL);
14013 case 0x60: /* MPEG-2, various profiles */
14019 codec_name = "MPEG-2 video";
14020 caps = gst_caps_new_simple ("video/mpeg",
14021 "mpegversion", G_TYPE_INT, 2,
14022 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14024 case 0x69: /* MPEG-2 BC audio */
14025 case 0x6B: /* MPEG-1 audio */
14026 caps = gst_caps_new_simple ("audio/mpeg",
14027 "mpegversion", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
14028 codec_name = "MPEG-1 audio";
14030 case 0x6A: /* MPEG-1 */
14031 codec_name = "MPEG-1 video";
14032 caps = gst_caps_new_simple ("video/mpeg",
14033 "mpegversion", G_TYPE_INT, 1,
14034 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14036 case 0x6C: /* MJPEG */
14038 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14040 codec_name = "Motion-JPEG";
14042 case 0x6D: /* PNG */
14044 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14046 codec_name = "PNG still images";
14048 case 0x6E: /* JPEG2000 */
14049 codec_name = "JPEG-2000";
14050 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14052 case 0xA4: /* Dirac */
14053 codec_name = "Dirac";
14054 caps = gst_caps_new_empty_simple ("video/x-dirac");
14056 case 0xA5: /* AC3 */
14057 codec_name = "AC-3 audio";
14058 caps = gst_caps_new_simple ("audio/x-ac3",
14059 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14061 case 0xA9: /* AC3 */
14062 codec_name = "DTS audio";
14063 caps = gst_caps_new_simple ("audio/x-dts",
14064 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14066 case 0xE1: /* QCELP */
14067 /* QCELP, the codec_data is a riff tag (little endian) with
14068 * more info (http://ftp.3gpp2.org/TSGC/Working/2003/2003-05-SanDiego/TSG-C-2003-05-San%20Diego/WG1/SWG12/C12-20030512-006%20=%20C12-20030217-015_Draft_Baseline%20Text%20of%20FFMS_R2.doc). */
14069 caps = gst_caps_new_empty_simple ("audio/qcelp");
14070 codec_name = "QCELP";
14076 /* If we have a replacement caps, then change our caps for this stream */
14078 gst_caps_unref (entry->caps);
14079 entry->caps = caps;
14082 if (codec_name && list)
14083 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14084 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14086 /* Add the codec_data attribute to caps, if we have it */
14090 buffer = gst_buffer_new_and_alloc (data_len);
14091 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14093 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14094 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14096 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14098 gst_buffer_unref (buffer);
14103 static inline GstCaps *
14104 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14108 char *s, fourstr[5];
14110 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14111 for (i = 0; i < 4; i++) {
14112 if (!g_ascii_isalnum (fourstr[i]))
14115 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14116 caps = gst_caps_new_empty_simple (s);
14121 #define _codec(name) \
14123 if (codec_name) { \
14124 *codec_name = g_strdup (name); \
14129 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14130 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14131 const guint8 * stsd_entry_data, gchar ** codec_name)
14133 GstCaps *caps = NULL;
14134 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14137 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
14138 _codec ("PNG still images");
14139 caps = gst_caps_new_empty_simple ("image/png");
14142 _codec ("JPEG still images");
14144 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14147 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14148 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14149 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14150 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14151 _codec ("Motion-JPEG");
14153 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14156 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14157 _codec ("Motion-JPEG format B");
14158 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14161 _codec ("JPEG-2000");
14162 /* override to what it should be according to spec, avoid palette_data */
14163 entry->bits_per_sample = 24;
14164 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14167 _codec ("Sorensen video v.3");
14168 caps = gst_caps_new_simple ("video/x-svq",
14169 "svqversion", G_TYPE_INT, 3, NULL);
14171 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14172 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14173 _codec ("Sorensen video v.1");
14174 caps = gst_caps_new_simple ("video/x-svq",
14175 "svqversion", G_TYPE_INT, 1, NULL);
14177 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14178 caps = gst_caps_new_empty_simple ("video/x-raw");
14179 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14180 _codec ("Windows Raw RGB");
14181 stream->alignment = 32;
14187 bps = QT_UINT16 (stsd_entry_data + 82);
14190 format = GST_VIDEO_FORMAT_RGB15;
14193 format = GST_VIDEO_FORMAT_RGB16;
14196 format = GST_VIDEO_FORMAT_RGB;
14199 format = GST_VIDEO_FORMAT_ARGB;
14207 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14208 format = GST_VIDEO_FORMAT_I420;
14210 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14211 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14212 format = GST_VIDEO_FORMAT_I420;
14215 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14216 format = GST_VIDEO_FORMAT_UYVY;
14218 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14219 format = GST_VIDEO_FORMAT_v308;
14221 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14222 format = GST_VIDEO_FORMAT_v216;
14225 format = GST_VIDEO_FORMAT_v210;
14227 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14228 format = GST_VIDEO_FORMAT_r210;
14230 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14231 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14232 format = GST_VIDEO_FORMAT_v410;
14235 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14236 * but different order than AYUV
14237 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14238 format = GST_VIDEO_FORMAT_v408;
14241 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14242 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14243 _codec ("MPEG-1 video");
14244 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14245 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14247 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14248 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14249 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14250 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14251 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14252 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14253 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14254 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14255 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14256 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14257 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14258 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14259 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14260 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14261 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14262 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14263 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14264 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14265 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14266 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14267 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14268 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14269 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14270 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14271 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14272 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14273 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14274 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14275 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14276 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14277 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14278 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14279 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14280 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14281 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14282 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14283 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14284 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14285 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14286 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14287 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14288 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14289 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14290 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14291 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14292 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14293 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14294 _codec ("MPEG-2 video");
14295 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14296 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14298 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14299 _codec ("GIF still images");
14300 caps = gst_caps_new_empty_simple ("image/gif");
14303 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14305 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14307 /* ffmpeg uses the height/width props, don't know why */
14308 caps = gst_caps_new_simple ("video/x-h263",
14309 "variant", G_TYPE_STRING, "itu", NULL);
14313 _codec ("MPEG-4 video");
14314 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14315 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14317 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14318 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14319 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14320 caps = gst_caps_new_simple ("video/x-msmpeg",
14321 "msmpegversion", G_TYPE_INT, 43, NULL);
14323 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14325 caps = gst_caps_new_simple ("video/x-divx",
14326 "divxversion", G_TYPE_INT, 3, NULL);
14328 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14329 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14331 caps = gst_caps_new_simple ("video/x-divx",
14332 "divxversion", G_TYPE_INT, 4, NULL);
14334 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14336 caps = gst_caps_new_simple ("video/x-divx",
14337 "divxversion", G_TYPE_INT, 5, NULL);
14340 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14342 caps = gst_caps_new_simple ("video/x-ffv",
14343 "ffvversion", G_TYPE_INT, 1, NULL);
14346 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14347 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14352 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14353 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14354 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14358 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14359 _codec ("Cinepak");
14360 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14362 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14363 _codec ("Apple QuickDraw");
14364 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14366 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14367 _codec ("Apple video");
14368 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14372 _codec ("H.264 / AVC");
14373 caps = gst_caps_new_simple ("video/x-h264",
14374 "stream-format", G_TYPE_STRING, "avc",
14375 "alignment", G_TYPE_STRING, "au", NULL);
14378 _codec ("H.264 / AVC");
14379 caps = gst_caps_new_simple ("video/x-h264",
14380 "stream-format", G_TYPE_STRING, "avc3",
14381 "alignment", G_TYPE_STRING, "au", NULL);
14385 _codec ("H.265 / HEVC");
14386 caps = gst_caps_new_simple ("video/x-h265",
14387 "stream-format", G_TYPE_STRING, "hvc1",
14388 "alignment", G_TYPE_STRING, "au", NULL);
14391 _codec ("H.265 / HEVC");
14392 caps = gst_caps_new_simple ("video/x-h265",
14393 "stream-format", G_TYPE_STRING, "hev1",
14394 "alignment", G_TYPE_STRING, "au", NULL);
14397 _codec ("Run-length encoding");
14398 caps = gst_caps_new_simple ("video/x-rle",
14399 "layout", G_TYPE_STRING, "quicktime", NULL);
14402 _codec ("Run-length encoding");
14403 caps = gst_caps_new_simple ("video/x-rle",
14404 "layout", G_TYPE_STRING, "microsoft", NULL);
14406 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14407 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14408 _codec ("Indeo Video 3");
14409 caps = gst_caps_new_simple ("video/x-indeo",
14410 "indeoversion", G_TYPE_INT, 3, NULL);
14412 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14413 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14414 _codec ("Intel Video 4");
14415 caps = gst_caps_new_simple ("video/x-indeo",
14416 "indeoversion", G_TYPE_INT, 4, NULL);
14420 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14421 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14422 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14423 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14424 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14425 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14426 _codec ("DV Video");
14427 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14428 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14430 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14431 case FOURCC_dv5p: /* DVCPRO50 PAL */
14432 _codec ("DVCPro50 Video");
14433 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14434 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14436 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
14437 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
14438 _codec ("DVCProHD Video");
14439 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
14440 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14442 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
14443 _codec ("Apple Graphics (SMC)");
14444 caps = gst_caps_new_empty_simple ("video/x-smc");
14446 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
14448 caps = gst_caps_new_empty_simple ("video/x-vp3");
14450 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
14451 _codec ("VP6 Flash");
14452 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
14456 caps = gst_caps_new_empty_simple ("video/x-theora");
14457 /* theora uses one byte of padding in the data stream because it does not
14458 * allow 0 sized packets while theora does */
14459 entry->padding = 1;
14463 caps = gst_caps_new_empty_simple ("video/x-dirac");
14465 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
14466 _codec ("TIFF still images");
14467 caps = gst_caps_new_empty_simple ("image/tiff");
14469 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
14470 _codec ("Apple Intermediate Codec");
14471 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
14473 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
14474 _codec ("AVID DNxHD");
14475 caps = gst_caps_from_string ("video/x-dnxhd");
14478 _codec ("On2 VP8");
14479 caps = gst_caps_from_string ("video/x-vp8");
14482 _codec ("Apple ProRes LT");
14484 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
14488 _codec ("Apple ProRes HQ");
14490 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
14494 _codec ("Apple ProRes");
14496 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14500 _codec ("Apple ProRes Proxy");
14502 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14506 _codec ("Apple ProRes 4444");
14508 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14512 _codec ("Apple ProRes 4444 XQ");
14514 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14518 _codec ("GoPro CineForm");
14519 caps = gst_caps_from_string ("video/x-cineform");
14524 caps = gst_caps_new_simple ("video/x-wmv",
14525 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
14527 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
14530 caps = _get_unknown_codec_name ("video", fourcc);
14535 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
14538 gst_video_info_init (&info);
14539 gst_video_info_set_format (&info, format, entry->width, entry->height);
14541 caps = gst_video_info_to_caps (&info);
14542 *codec_name = gst_pb_utils_get_codec_description (caps);
14544 /* enable clipping for raw video streams */
14545 stream->need_clip = TRUE;
14546 stream->alignment = 32;
14553 round_up_pow2 (guint n)
14565 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14566 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
14567 int len, gchar ** codec_name)
14570 const GstStructure *s;
14573 GstAudioFormat format = 0;
14576 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14578 depth = entry->bytes_per_packet * 8;
14581 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
14583 /* 8-bit audio is unsigned */
14585 format = GST_AUDIO_FORMAT_U8;
14586 /* otherwise it's signed and big-endian just like 'twos' */
14588 endian = G_BIG_ENDIAN;
14595 endian = G_LITTLE_ENDIAN;
14598 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
14600 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
14604 caps = gst_caps_new_simple ("audio/x-raw",
14605 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14606 "layout", G_TYPE_STRING, "interleaved", NULL);
14607 stream->alignment = GST_ROUND_UP_8 (depth);
14608 stream->alignment = round_up_pow2 (stream->alignment);
14611 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
14612 _codec ("Raw 64-bit floating-point audio");
14613 caps = gst_caps_new_simple ("audio/x-raw",
14614 "format", G_TYPE_STRING, "F64BE",
14615 "layout", G_TYPE_STRING, "interleaved", NULL);
14616 stream->alignment = 8;
14618 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
14619 _codec ("Raw 32-bit floating-point audio");
14620 caps = gst_caps_new_simple ("audio/x-raw",
14621 "format", G_TYPE_STRING, "F32BE",
14622 "layout", G_TYPE_STRING, "interleaved", NULL);
14623 stream->alignment = 4;
14626 _codec ("Raw 24-bit PCM audio");
14627 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14629 caps = gst_caps_new_simple ("audio/x-raw",
14630 "format", G_TYPE_STRING, "S24BE",
14631 "layout", G_TYPE_STRING, "interleaved", NULL);
14632 stream->alignment = 4;
14634 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
14635 _codec ("Raw 32-bit PCM audio");
14636 caps = gst_caps_new_simple ("audio/x-raw",
14637 "format", G_TYPE_STRING, "S32BE",
14638 "layout", G_TYPE_STRING, "interleaved", NULL);
14639 stream->alignment = 4;
14641 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
14642 _codec ("Raw 16-bit PCM audio");
14643 caps = gst_caps_new_simple ("audio/x-raw",
14644 "format", G_TYPE_STRING, "S16LE",
14645 "layout", G_TYPE_STRING, "interleaved", NULL);
14646 stream->alignment = 2;
14649 _codec ("Mu-law audio");
14650 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
14653 _codec ("A-law audio");
14654 caps = gst_caps_new_empty_simple ("audio/x-alaw");
14658 _codec ("Microsoft ADPCM");
14659 /* Microsoft ADPCM-ACM code 2 */
14660 caps = gst_caps_new_simple ("audio/x-adpcm",
14661 "layout", G_TYPE_STRING, "microsoft", NULL);
14665 _codec ("DVI/IMA ADPCM");
14666 caps = gst_caps_new_simple ("audio/x-adpcm",
14667 "layout", G_TYPE_STRING, "dvi", NULL);
14671 _codec ("DVI/Intel IMA ADPCM");
14672 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
14673 caps = gst_caps_new_simple ("audio/x-adpcm",
14674 "layout", G_TYPE_STRING, "quicktime", NULL);
14678 /* MPEG layer 3, CBR only (pre QT4.1) */
14680 _codec ("MPEG-1 layer 3");
14681 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
14682 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
14683 "mpegversion", G_TYPE_INT, 1, NULL);
14685 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
14686 _codec ("MPEG-1 layer 2");
14688 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
14689 "mpegversion", G_TYPE_INT, 1, NULL);
14692 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
14693 _codec ("EAC-3 audio");
14694 caps = gst_caps_new_simple ("audio/x-eac3",
14695 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14696 entry->sampled = TRUE;
14698 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
14700 _codec ("AC-3 audio");
14701 caps = gst_caps_new_simple ("audio/x-ac3",
14702 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14703 entry->sampled = TRUE;
14705 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
14706 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
14707 _codec ("DTS audio");
14708 caps = gst_caps_new_simple ("audio/x-dts",
14709 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14710 entry->sampled = TRUE;
14712 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
14713 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
14714 _codec ("DTS-HD audio");
14715 caps = gst_caps_new_simple ("audio/x-dts",
14716 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14717 entry->sampled = TRUE;
14721 caps = gst_caps_new_simple ("audio/x-mace",
14722 "maceversion", G_TYPE_INT, 3, NULL);
14726 caps = gst_caps_new_simple ("audio/x-mace",
14727 "maceversion", G_TYPE_INT, 6, NULL);
14729 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
14731 caps = gst_caps_new_empty_simple ("application/ogg");
14733 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
14734 _codec ("DV audio");
14735 caps = gst_caps_new_empty_simple ("audio/x-dv");
14738 _codec ("MPEG-4 AAC audio");
14739 caps = gst_caps_new_simple ("audio/mpeg",
14740 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
14741 "stream-format", G_TYPE_STRING, "raw", NULL);
14743 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
14744 _codec ("QDesign Music");
14745 caps = gst_caps_new_empty_simple ("audio/x-qdm");
14748 _codec ("QDesign Music v.2");
14749 /* FIXME: QDesign music version 2 (no constant) */
14750 if (FALSE && data) {
14751 caps = gst_caps_new_simple ("audio/x-qdm2",
14752 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
14753 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
14754 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
14756 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
14760 _codec ("GSM audio");
14761 caps = gst_caps_new_empty_simple ("audio/x-gsm");
14764 _codec ("AMR audio");
14765 caps = gst_caps_new_empty_simple ("audio/AMR");
14768 _codec ("AMR-WB audio");
14769 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
14772 _codec ("Quicktime IMA ADPCM");
14773 caps = gst_caps_new_simple ("audio/x-adpcm",
14774 "layout", G_TYPE_STRING, "quicktime", NULL);
14777 _codec ("Apple lossless audio");
14778 caps = gst_caps_new_empty_simple ("audio/x-alac");
14781 _codec ("Free Lossless Audio Codec");
14782 caps = gst_caps_new_simple ("audio/x-flac",
14783 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14785 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
14786 _codec ("QualComm PureVoice");
14787 caps = gst_caps_from_string ("audio/qcelp");
14792 caps = gst_caps_new_empty_simple ("audio/x-wma");
14796 caps = gst_caps_new_empty_simple ("audio/x-opus");
14798 case GST_MAKE_FOURCC ('l', 'p', 'c', 'm'):
14803 GstAudioFormat format;
14806 FLAG_IS_FLOAT = 0x1,
14807 FLAG_IS_BIG_ENDIAN = 0x2,
14808 FLAG_IS_SIGNED = 0x4,
14809 FLAG_IS_PACKED = 0x8,
14810 FLAG_IS_ALIGNED_HIGH = 0x10,
14811 FLAG_IS_NON_INTERLEAVED = 0x20
14813 _codec ("Raw LPCM audio");
14815 if (data && len >= 56) {
14816 depth = QT_UINT32 (data + 40);
14817 flags = QT_UINT32 (data + 44);
14818 width = QT_UINT32 (data + 48) * 8 / entry->n_channels;
14820 if ((flags & FLAG_IS_FLOAT) == 0) {
14825 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
14826 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
14827 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
14828 caps = gst_caps_new_simple ("audio/x-raw",
14829 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14830 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14831 "non-interleaved" : "interleaved", NULL);
14832 stream->alignment = GST_ROUND_UP_8 (depth);
14833 stream->alignment = round_up_pow2 (stream->alignment);
14838 if (flags & FLAG_IS_BIG_ENDIAN)
14839 format = GST_AUDIO_FORMAT_F64BE;
14841 format = GST_AUDIO_FORMAT_F64LE;
14843 if (flags & FLAG_IS_BIG_ENDIAN)
14844 format = GST_AUDIO_FORMAT_F32BE;
14846 format = GST_AUDIO_FORMAT_F32LE;
14848 caps = gst_caps_new_simple ("audio/x-raw",
14849 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14850 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14851 "non-interleaved" : "interleaved", NULL);
14852 stream->alignment = width / 8;
14856 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
14860 caps = _get_unknown_codec_name ("audio", fourcc);
14866 GstCaps *templ_caps =
14867 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
14868 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
14869 gst_caps_unref (caps);
14870 gst_caps_unref (templ_caps);
14871 caps = intersection;
14874 /* enable clipping for raw audio streams */
14875 s = gst_caps_get_structure (caps, 0);
14876 name = gst_structure_get_name (s);
14877 if (g_str_has_prefix (name, "audio/x-raw")) {
14878 stream->need_clip = TRUE;
14879 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
14880 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
14886 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14887 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14888 const guint8 * stsd_entry_data, gchar ** codec_name)
14892 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14896 _codec ("DVD subtitle");
14897 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
14898 stream->need_process = TRUE;
14901 _codec ("Quicktime timed text");
14904 _codec ("3GPP timed text");
14906 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
14908 /* actual text piece needs to be extracted */
14909 stream->need_process = TRUE;
14912 _codec ("XML subtitles");
14913 caps = gst_caps_new_empty_simple ("application/ttml+xml");
14917 caps = _get_unknown_codec_name ("text", fourcc);
14925 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14926 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14927 const guint8 * stsd_entry_data, gchar ** codec_name)
14933 _codec ("MPEG 1 video");
14934 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14935 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14945 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
14946 const gchar * system_id)
14950 if (!qtdemux->protection_system_ids)
14951 qtdemux->protection_system_ids =
14952 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
14953 /* Check whether we already have an entry for this system ID. */
14954 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
14955 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
14956 if (g_ascii_strcasecmp (system_id, id) == 0) {
14960 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
14961 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,