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>
59 #include <gst/riff/riff.h>
60 #include <gst/pbutils/pbutils.h>
62 #include "qtatomparser.h"
63 #include "qtdemux_types.h"
64 #include "qtdemux_dump.h"
66 #include "descriptors.h"
67 #include "qtdemux_lang.h"
69 #include "qtpalette.h"
76 #include <gst/math-compat.h>
82 /* max. size considered 'sane' for non-mdat atoms */
83 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
85 /* if the sample index is larger than this, something is likely wrong */
86 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
88 /* For converting qt creation times to unix epoch times */
89 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
90 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
91 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
92 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
94 #define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))
96 #define STREAM_IS_EOS(s) ((s)->time_position == GST_CLOCK_TIME_NONE)
98 #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
100 #define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s))
101 #define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
102 #define QTDEMUX_NTH_STREAM(demux,idx) \
103 QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
104 #define QTDEMUX_NTH_OLD_STREAM(demux,idx) \
105 QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
107 GST_DEBUG_CATEGORY (qtdemux_debug);
108 #define GST_CAT_DEFAULT qtdemux_debug
110 typedef struct _QtDemuxSegment QtDemuxSegment;
111 typedef struct _QtDemuxSample QtDemuxSample;
113 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
115 struct _QtDemuxSample
118 gint32 pts_offset; /* Add this value to timestamp to get the pts */
120 guint64 timestamp; /* DTS In mov time */
121 guint32 duration; /* In mov time */
122 gboolean keyframe; /* TRUE when this packet is a keyframe */
125 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
126 typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
128 struct _QtDemuxSphericalMetadata
130 gboolean is_spherical;
131 gboolean is_stitched;
132 char *stitching_software;
133 char *projection_type;
136 int init_view_heading;
140 int full_pano_width_pixels;
141 int full_pano_height_pixels;
142 int cropped_area_image_width;
143 int cropped_area_image_height;
144 int cropped_area_left;
145 int cropped_area_top;
146 QTDEMUX_AMBISONIC_TYPE ambisonic_type;
147 QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
148 QTDEMUX_AMBISONIC_ORDER ambisonic_order;
151 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
153 /* Macros for converting to/from timescale */
154 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
155 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
157 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
158 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
160 /* timestamp is the DTS */
161 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
162 /* timestamp + offset + cslg_shift is the outgoing PTS */
163 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
164 /* timestamp + offset is the PTS used for internal seek calcuations */
165 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
166 /* timestamp + duration - dts is the duration */
167 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
169 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
171 #define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
172 #define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
173 GST_TRACE("Locking from thread %p", g_thread_self()); \
174 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
175 GST_TRACE("Locked from thread %p", g_thread_self()); \
178 #define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
179 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
180 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
184 * Quicktime has tracks and segments. A track is a continuous piece of
185 * multimedia content. The track is not always played from start to finish but
186 * instead, pieces of the track are 'cut out' and played in sequence. This is
187 * what the segments do.
189 * Inside the track we have keyframes (K) and delta frames. The track has its
190 * own timing, which starts from 0 and extends to end. The position in the track
191 * is called the media_time.
193 * The segments now describe the pieces that should be played from this track
194 * and are basically tuples of media_time/duration/rate entries. We can have
195 * multiple segments and they are all played after one another. An example:
197 * segment 1: media_time: 1 second, duration: 1 second, rate 1
198 * segment 2: media_time: 3 second, duration: 2 second, rate 2
200 * To correctly play back this track, one must play: 1 second of media starting
201 * from media_time 1 followed by 2 seconds of media starting from media_time 3
204 * Each of the segments will be played at a specific time, the first segment at
205 * time 0, the second one after the duration of the first one, etc.. Note that
206 * the time in resulting playback is not identical to the media_time of the
209 * Visually, assuming the track has 4 second of media_time:
212 * .-----------------------------------------------------------.
213 * track: | K.....K.........K........K.......K.......K...........K... |
214 * '-----------------------------------------------------------'
216 * .------------^ ^ .----------^ ^
217 * / .-------------' / .------------------'
219 * .--------------. .--------------.
220 * | segment 1 | | segment 2 |
221 * '--------------' '--------------'
223 * The challenge here is to cut out the right pieces of the track for each of
224 * the playback segments. This fortunately can easily be done with the SEGMENT
225 * events of GStreamer.
227 * For playback of segment 1, we need to provide the decoder with the keyframe
228 * (a), in the above figure, but we must instruct it only to output the decoded
229 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
230 * position set to the time of the segment: 0.
232 * We then proceed to push data from keyframe (a) to frame (b). The decoder
233 * decodes but clips all before media_time 1.
235 * After finishing a segment, we push out a new SEGMENT event with the clipping
236 * boundaries of the new data.
238 * This is a good usecase for the GStreamer accumulated SEGMENT events.
241 struct _QtDemuxSegment
243 /* global time and duration, all gst time */
245 GstClockTime stop_time;
246 GstClockTime duration;
247 /* media time of trak, all gst time */
248 GstClockTime media_start;
249 GstClockTime media_stop;
251 /* Media start time in trak timescale units */
252 guint32 trak_media_start;
255 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
257 /* Used with fragmented MP4 files (mfra atom) */
262 } QtDemuxRandomAccessEntry;
264 typedef struct _QtDemuxStreamStsdEntry
275 /* Numerator/denominator framerate */
278 GstVideoColorimetry colorimetry;
279 guint16 bits_per_sample;
280 guint16 color_table_id;
281 GstMemory *rgb8_palette;
282 guint interlace_mode;
288 guint samples_per_packet;
289 guint samples_per_frame;
290 guint bytes_per_packet;
291 guint bytes_per_sample;
292 guint bytes_per_frame;
295 /* if we use chunks or samples */
299 } QtDemuxStreamStsdEntry;
301 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
303 struct _QtDemuxStream
310 QtDemuxStreamStsdEntry *stsd_entries;
311 guint stsd_entries_length;
312 guint cur_stsd_entry_index;
317 gboolean new_caps; /* If TRUE, caps need to be generated (by
318 * calling _configure_stream()) This happens
319 * for MSS and fragmented streams */
321 gboolean new_stream; /* signals that a stream_start is required */
322 gboolean on_keyframe; /* if this stream last pushed buffer was a
323 * keyframe. This is important to identify
324 * where to stop pushing buffers after a
325 * segment stop time */
327 /* if the stream has a redirect URI in its headers, we store it here */
334 guint64 duration; /* in timescale units */
338 gchar lang_id[4]; /* ISO 639-2T language code */
342 QtDemuxSample *samples;
343 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
344 guint32 n_samples_moof; /* sample count in a moof */
345 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
346 * the framerate of fragmented format stream */
347 guint64 duration_last_moof;
349 guint32 offset_in_sample; /* Offset in the current sample, used for
350 * streams which have got exceedingly big
351 * sample size (such as 24s of raw audio).
352 * Only used when max_buffer_size is non-NULL */
353 guint32 max_buffer_size; /* Maximum allowed size for output buffers.
354 * Currently only set for raw audio streams*/
362 gboolean use_allocator;
363 GstAllocator *allocator;
364 GstAllocationParams params;
368 /* when a discontinuity is pending */
371 /* list of buffers to push first */
374 /* if we need to clip this buffer. This is only needed for uncompressed
378 /* buffer needs some custom processing, e.g. subtitles */
379 gboolean need_process;
380 /* buffer needs potentially be split, e.g. CEA608 subtitles */
383 /* current position */
384 guint32 segment_index;
385 guint32 sample_index;
386 GstClockTime time_position; /* in gst time */
387 guint64 accumulated_base;
389 /* the Gst segment we are processing out, used for clipping */
392 /* quicktime segments */
394 QtDemuxSegment *segments;
395 gboolean dummy_segment;
400 GstTagList *stream_tags;
401 gboolean send_global_tags;
403 GstEvent *pending_event;
413 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
417 GstByteReader co_chunk;
419 guint32 current_chunk;
421 guint32 samples_per_chunk;
422 guint32 stsd_sample_description_id;
423 guint32 stco_sample_index;
425 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
428 guint32 n_samples_per_chunk;
429 guint32 stsc_chunk_index;
430 guint32 stsc_sample_index;
431 guint64 chunk_offset;
434 guint32 stts_samples;
435 guint32 n_sample_times;
436 guint32 stts_sample_index;
438 guint32 stts_duration;
440 gboolean stss_present;
441 guint32 n_sample_syncs;
444 gboolean stps_present;
445 guint32 n_sample_partial_syncs;
447 QtDemuxRandomAccessEntry *ra_entries;
450 const QtDemuxRandomAccessEntry *pending_seek;
453 gboolean ctts_present;
454 guint32 n_composition_times;
456 guint32 ctts_sample_index;
464 gboolean parsed_trex;
465 guint32 def_sample_description_index; /* index is 1-based */
466 guint32 def_sample_duration;
467 guint32 def_sample_size;
468 guint32 def_sample_flags;
472 /* stereoscopic video streams */
473 GstVideoMultiviewMode multiview_mode;
474 GstVideoMultiviewFlags multiview_flags;
476 /* protected streams */
478 guint32 protection_scheme_type;
479 guint32 protection_scheme_version;
480 gpointer protection_scheme_info; /* specific to the protection scheme */
481 GQueue protection_scheme_event_queue;
483 gint ref_count; /* atomic */
486 /* Contains properties and cryptographic info for a set of samples from a
487 * track protected using Common Encryption (cenc) */
488 struct _QtDemuxCencSampleSetInfo
490 GstStructure *default_properties;
492 /* @crypto_info holds one GstStructure per sample */
493 GPtrArray *crypto_info;
497 qt_demux_state_string (enum QtDemuxState state)
500 case QTDEMUX_STATE_INITIAL:
502 case QTDEMUX_STATE_HEADER:
504 case QTDEMUX_STATE_MOVIE:
506 case QTDEMUX_STATE_BUFFER_MDAT:
507 return "<BUFFER_MDAT>";
513 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
514 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
515 guint32 fourcc, GstByteReader * parser);
516 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
517 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
518 guint32 fourcc, GstByteReader * parser);
520 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
522 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
524 static GstStaticPadTemplate gst_qtdemux_sink_template =
525 GST_STATIC_PAD_TEMPLATE ("sink",
528 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
532 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
533 GST_STATIC_PAD_TEMPLATE ("video_%u",
536 GST_STATIC_CAPS_ANY);
538 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
539 GST_STATIC_PAD_TEMPLATE ("audio_%u",
542 GST_STATIC_CAPS_ANY);
544 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
545 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
548 GST_STATIC_CAPS_ANY);
550 #define gst_qtdemux_parent_class parent_class
551 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
553 static void gst_qtdemux_dispose (GObject * object);
556 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
557 GstClockTime media_time);
559 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
560 QtDemuxStream * str, gint64 media_offset);
563 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
564 static GstIndex *gst_qtdemux_get_index (GstElement * element);
566 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
567 GstStateChange transition);
568 static void gst_qtdemux_set_context (GstElement * element,
569 GstContext * context);
570 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
571 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
572 GstObject * parent, GstPadMode mode, gboolean active);
574 static void gst_qtdemux_loop (GstPad * pad);
575 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
577 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
579 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
581 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
582 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
583 QtDemuxStream * stream);
584 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
585 QtDemuxStream * stream);
586 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
589 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
591 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
592 const guint8 * buffer, guint length);
593 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
594 const guint8 * buffer, guint length);
595 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
596 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
599 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
600 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
602 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
603 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
604 const guint8 * stsd_entry_data, gchar ** codec_name);
605 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
606 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
607 const guint8 * data, int len, gchar ** codec_name);
608 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
609 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
610 gchar ** codec_name);
611 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
612 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
613 const guint8 * stsd_entry_data, gchar ** codec_name);
615 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
616 QtDemuxStream * stream, guint32 n);
617 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
618 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
619 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
620 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
621 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
622 static void qtdemux_do_allocation (QtDemuxStream * stream,
623 GstQTDemux * qtdemux);
624 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
625 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
626 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
627 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
628 GstClockTime * _start, GstClockTime * _stop);
629 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
630 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
632 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
633 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
635 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
637 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
638 QtDemuxStream * stream, guint sample_index);
639 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
641 static void qtdemux_gst_structure_free (GstStructure * gststructure);
642 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
644 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
645 static void gst_tag_register_spherical_tags (void);
646 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
649 gst_qtdemux_class_init (GstQTDemuxClass * klass)
651 GObjectClass *gobject_class;
652 GstElementClass *gstelement_class;
654 gobject_class = (GObjectClass *) klass;
655 gstelement_class = (GstElementClass *) klass;
657 parent_class = g_type_class_peek_parent (klass);
659 gobject_class->dispose = gst_qtdemux_dispose;
661 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
663 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
664 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
666 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
668 gst_tag_register_musicbrainz_tags ();
670 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
671 gst_tag_register_spherical_tags ();
672 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
674 gst_element_class_add_static_pad_template (gstelement_class,
675 &gst_qtdemux_sink_template);
676 gst_element_class_add_static_pad_template (gstelement_class,
677 &gst_qtdemux_videosrc_template);
678 gst_element_class_add_static_pad_template (gstelement_class,
679 &gst_qtdemux_audiosrc_template);
680 gst_element_class_add_static_pad_template (gstelement_class,
681 &gst_qtdemux_subsrc_template);
682 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
684 "Demultiplex a QuickTime file into audio and video streams",
685 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
687 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
692 gst_qtdemux_init (GstQTDemux * qtdemux)
695 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
696 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
697 gst_pad_set_activatemode_function (qtdemux->sinkpad,
698 qtdemux_sink_activate_mode);
699 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
700 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
701 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
702 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
704 qtdemux->adapter = gst_adapter_new ();
705 g_queue_init (&qtdemux->protection_event_queue);
706 qtdemux->flowcombiner = gst_flow_combiner_new ();
707 g_mutex_init (&qtdemux->expose_lock);
709 qtdemux->active_streams = g_ptr_array_new_with_free_func
710 ((GDestroyNotify) gst_qtdemux_stream_unref);
711 qtdemux->old_streams = g_ptr_array_new_with_free_func
712 ((GDestroyNotify) gst_qtdemux_stream_unref);
714 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
715 qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
716 malloc (sizeof (QtDemuxSphericalMetadata));
718 if (qtdemux->spherical_metadata) {
719 qtdemux->spherical_metadata->is_spherical = FALSE;
720 qtdemux->spherical_metadata->is_stitched = FALSE;
721 qtdemux->spherical_metadata->stitching_software = NULL;
722 qtdemux->spherical_metadata->projection_type = NULL;
723 qtdemux->spherical_metadata->stereo_mode = NULL;
724 qtdemux->spherical_metadata->source_count = 0;
725 qtdemux->spherical_metadata->init_view_heading = 0;
726 qtdemux->spherical_metadata->init_view_pitch = 0;
727 qtdemux->spherical_metadata->init_view_roll = 0;
728 qtdemux->spherical_metadata->timestamp = 0;
729 qtdemux->spherical_metadata->full_pano_width_pixels = 0;
730 qtdemux->spherical_metadata->full_pano_height_pixels = 0;
731 qtdemux->spherical_metadata->cropped_area_image_width = 0;
732 qtdemux->spherical_metadata->cropped_area_image_height = 0;
733 qtdemux->spherical_metadata->cropped_area_left = 0;
734 qtdemux->spherical_metadata->cropped_area_top = 0;
735 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
736 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
737 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
739 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
741 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
743 gst_qtdemux_reset (qtdemux, TRUE);
747 gst_qtdemux_dispose (GObject * object)
749 GstQTDemux *qtdemux = GST_QTDEMUX (object);
751 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
752 if (qtdemux->spherical_metadata) {
753 if (qtdemux->spherical_metadata->stitching_software)
754 free(qtdemux->spherical_metadata->stitching_software);
755 if (qtdemux->spherical_metadata->projection_type)
756 free(qtdemux->spherical_metadata->projection_type);
757 if (qtdemux->spherical_metadata->stereo_mode)
758 free(qtdemux->spherical_metadata->stereo_mode);
760 free(qtdemux->spherical_metadata);
761 qtdemux->spherical_metadata = NULL;
763 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
765 if (qtdemux->adapter) {
766 g_object_unref (G_OBJECT (qtdemux->adapter));
767 qtdemux->adapter = NULL;
769 gst_tag_list_unref (qtdemux->tag_list);
770 gst_flow_combiner_free (qtdemux->flowcombiner);
771 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
773 g_queue_clear (&qtdemux->protection_event_queue);
775 g_free (qtdemux->cenc_aux_info_sizes);
776 qtdemux->cenc_aux_info_sizes = NULL;
777 g_mutex_clear (&qtdemux->expose_lock);
779 g_ptr_array_free (qtdemux->active_streams, TRUE);
780 g_ptr_array_free (qtdemux->old_streams, TRUE);
782 G_OBJECT_CLASS (parent_class)->dispose (object);
786 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
788 if (qtdemux->posted_redirect) {
789 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
790 (_("This file contains no playable streams.")),
791 ("no known streams found, a redirect message has been posted"));
793 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
794 (_("This file contains no playable streams.")),
795 ("no known streams found"));
800 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
802 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
803 mem, size, 0, size, mem, free_func);
807 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
814 if (G_UNLIKELY (size == 0)) {
816 GstBuffer *tmp = NULL;
818 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
819 if (ret != GST_FLOW_OK)
822 gst_buffer_map (tmp, &map, GST_MAP_READ);
823 size = QT_UINT32 (map.data);
824 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
826 gst_buffer_unmap (tmp, &map);
827 gst_buffer_unref (tmp);
830 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
831 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
832 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
833 /* we're pulling header but already got most interesting bits,
834 * so never mind the rest (e.g. tags) (that much) */
835 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
839 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
840 (_("This file is invalid and cannot be played.")),
841 ("atom has bogus size %" G_GUINT64_FORMAT, size));
842 return GST_FLOW_ERROR;
846 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
848 if (G_UNLIKELY (flow != GST_FLOW_OK))
851 bsize = gst_buffer_get_size (*buf);
852 /* Catch short reads - we don't want any partial atoms */
853 if (G_UNLIKELY (bsize < size)) {
854 GST_WARNING_OBJECT (qtdemux,
855 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
856 gst_buffer_unref (*buf);
866 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
867 GstFormat src_format, gint64 src_value, GstFormat dest_format,
871 QtDemuxStream *stream = gst_pad_get_element_private (pad);
874 if (stream->subtype != FOURCC_vide) {
879 switch (src_format) {
880 case GST_FORMAT_TIME:
881 switch (dest_format) {
882 case GST_FORMAT_BYTES:{
883 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
889 *dest_value = stream->samples[index].offset;
891 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
892 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
893 GST_TIME_ARGS (src_value), *dest_value);
901 case GST_FORMAT_BYTES:
902 switch (dest_format) {
903 case GST_FORMAT_TIME:{
905 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
914 QTSTREAMTIME_TO_GSTTIME (stream,
915 stream->samples[index].timestamp);
916 GST_DEBUG_OBJECT (qtdemux,
917 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
918 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
937 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
939 gboolean res = FALSE;
941 *duration = GST_CLOCK_TIME_NONE;
943 if (qtdemux->duration != 0 &&
944 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
945 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
948 *duration = GST_CLOCK_TIME_NONE;
955 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
958 gboolean res = FALSE;
959 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
961 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
963 switch (GST_QUERY_TYPE (query)) {
964 case GST_QUERY_POSITION:{
967 gst_query_parse_position (query, &fmt, NULL);
968 if (fmt == GST_FORMAT_TIME
969 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
970 gst_query_set_position (query, GST_FORMAT_TIME,
971 qtdemux->segment.position);
976 case GST_QUERY_DURATION:{
979 gst_query_parse_duration (query, &fmt, NULL);
980 if (fmt == GST_FORMAT_TIME) {
981 /* First try to query upstream */
982 res = gst_pad_query_default (pad, parent, query);
984 GstClockTime duration;
985 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
986 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
993 case GST_QUERY_CONVERT:{
994 GstFormat src_fmt, dest_fmt;
995 gint64 src_value, dest_value = 0;
997 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
999 res = gst_qtdemux_src_convert (qtdemux, pad,
1000 src_fmt, src_value, dest_fmt, &dest_value);
1002 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
1006 case GST_QUERY_FORMATS:
1007 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
1010 case GST_QUERY_SEEKING:{
1014 /* try upstream first */
1015 res = gst_pad_query_default (pad, parent, query);
1018 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
1019 if (fmt == GST_FORMAT_TIME) {
1020 GstClockTime duration;
1022 gst_qtdemux_get_duration (qtdemux, &duration);
1024 if (!qtdemux->pullbased) {
1027 /* we might be able with help from upstream */
1029 q = gst_query_new_seeking (GST_FORMAT_BYTES);
1030 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
1031 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
1032 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
1034 gst_query_unref (q);
1036 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
1042 case GST_QUERY_SEGMENT:
1047 format = qtdemux->segment.format;
1050 gst_segment_to_stream_time (&qtdemux->segment, format,
1051 qtdemux->segment.start);
1052 if ((stop = qtdemux->segment.stop) == -1)
1053 stop = qtdemux->segment.duration;
1055 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
1057 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
1062 res = gst_pad_query_default (pad, parent, query);
1070 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
1072 if (G_LIKELY (stream->pad)) {
1073 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
1074 GST_DEBUG_PAD_NAME (stream->pad));
1076 if (!gst_tag_list_is_empty (stream->stream_tags)) {
1077 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
1078 stream->stream_tags);
1079 gst_pad_push_event (stream->pad,
1080 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
1081 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
1082 /* post message qtdemux tag (for early recive application) */
1083 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1084 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
1085 gst_tag_list_copy (stream->stream_tags)));
1089 if (G_UNLIKELY (stream->send_global_tags)) {
1090 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1092 gst_pad_push_event (stream->pad,
1093 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1094 stream->send_global_tags = FALSE;
1099 /* push event on all source pads; takes ownership of the event */
1101 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1103 gboolean has_valid_stream = FALSE;
1104 GstEventType etype = GST_EVENT_TYPE (event);
1107 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1108 GST_EVENT_TYPE_NAME (event));
1110 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1112 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1113 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
1115 if ((pad = stream->pad)) {
1116 has_valid_stream = TRUE;
1118 if (etype == GST_EVENT_EOS) {
1119 /* let's not send twice */
1120 if (stream->sent_eos)
1122 stream->sent_eos = TRUE;
1125 gst_pad_push_event (pad, gst_event_ref (event));
1129 gst_event_unref (event);
1131 /* if it is EOS and there are no pads, post an error */
1132 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1133 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1143 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1145 if ((gint64) s1->timestamp > *media_time)
1147 if ((gint64) s1->timestamp == *media_time)
1153 /* find the index of the sample that includes the data for @media_time using a
1154 * binary search. Only to be called in optimized cases of linear search below.
1156 * Returns the index of the sample with the corresponding *DTS*.
1159 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1162 QtDemuxSample *result;
1165 /* convert media_time to mov format */
1167 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1169 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1170 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1171 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1173 if (G_LIKELY (result))
1174 index = result - str->samples;
1183 /* find the index of the sample that includes the data for @media_offset using a
1186 * Returns the index of the sample.
1189 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1190 QtDemuxStream * str, gint64 media_offset)
1192 QtDemuxSample *result = str->samples;
1195 if (result == NULL || str->n_samples == 0)
1198 if (media_offset == result->offset)
1202 while (index < str->n_samples - 1) {
1203 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1206 if (media_offset < result->offset)
1217 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1222 /* find the index of the sample that includes the data for @media_time using a
1223 * linear search, and keeping in mind that not all samples may have been parsed
1224 * yet. If possible, it will delegate to binary search.
1226 * Returns the index of the sample.
1229 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1230 GstClockTime media_time)
1234 QtDemuxSample *sample;
1236 /* convert media_time to mov format */
1238 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1240 sample = str->samples;
1241 if (mov_time == sample->timestamp + sample->pts_offset)
1244 /* use faster search if requested time in already parsed range */
1245 sample = str->samples + str->stbl_index;
1246 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
1247 index = gst_qtdemux_find_index (qtdemux, str, media_time);
1248 sample = str->samples + index;
1250 while (index < str->n_samples - 1) {
1251 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1254 sample = str->samples + index + 1;
1255 if (mov_time < sample->timestamp) {
1256 sample = str->samples + index;
1264 /* sample->timestamp is now <= media_time, need to find the corresponding
1265 * PTS now by looking backwards */
1266 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
1268 sample = str->samples + index;
1276 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1281 /* find the index of the keyframe needed to decode the sample at @index
1282 * of stream @str, or of a subsequent keyframe (depending on @next)
1284 * Returns the index of the keyframe.
1287 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1288 guint32 index, gboolean next)
1290 guint32 new_index = index;
1292 if (index >= str->n_samples) {
1293 new_index = str->n_samples;
1297 /* all keyframes, return index */
1298 if (str->all_keyframe) {
1303 /* else search until we have a keyframe */
1304 while (new_index < str->n_samples) {
1305 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1308 if (str->samples[new_index].keyframe)
1320 if (new_index == str->n_samples) {
1321 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1326 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1327 "gave %u", next ? "after" : "before", index, new_index);
1334 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1339 /* find the segment for @time_position for @stream
1341 * Returns the index of the segment containing @time_position.
1342 * Returns the last segment and sets the @eos variable to TRUE
1343 * if the time is beyond the end. @eos may be NULL
1346 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1347 GstClockTime time_position)
1352 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1353 GST_TIME_ARGS (time_position));
1356 for (i = 0; i < stream->n_segments; i++) {
1357 QtDemuxSegment *segment = &stream->segments[i];
1359 GST_LOG_OBJECT (stream->pad,
1360 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1361 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1363 /* For the last segment we include stop_time in the last segment */
1364 if (i < stream->n_segments - 1) {
1365 if (segment->time <= time_position && time_position < segment->stop_time) {
1366 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1371 /* Last segment always matches */
1379 /* move the stream @str to the sample position @index.
1381 * Updates @str->sample_index and marks discontinuity if needed.
1384 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1387 /* no change needed */
1388 if (index == str->sample_index)
1391 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1394 /* position changed, we have a discont */
1395 str->sample_index = index;
1396 str->offset_in_sample = 0;
1397 /* Each time we move in the stream we store the position where we are
1399 str->from_sample = index;
1400 str->discont = TRUE;
1404 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1405 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1408 gint64 min_byte_offset = -1;
1411 min_offset = desired_time;
1413 /* for each stream, find the index of the sample in the segment
1414 * and move back to the previous keyframe. */
1415 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1417 guint32 index, kindex;
1419 GstClockTime media_start;
1420 GstClockTime media_time;
1421 GstClockTime seg_time;
1422 QtDemuxSegment *seg;
1423 gboolean empty_segment = FALSE;
1425 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1427 if (CUR_STREAM (str)->sparse && !use_sparse)
1430 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1431 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1433 /* get segment and time in the segment */
1434 seg = &str->segments[seg_idx];
1435 seg_time = (desired_time - seg->time) * seg->rate;
1437 while (QTSEGMENT_IS_EMPTY (seg)) {
1439 empty_segment = TRUE;
1440 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1443 if (seg_idx == str->n_segments)
1445 seg = &str->segments[seg_idx];
1448 if (seg_idx == str->n_segments) {
1449 /* FIXME track shouldn't have the last segment as empty, but if it
1450 * happens we better handle it */
1454 /* get the media time in the segment */
1455 media_start = seg->media_start + seg_time;
1457 /* get the index of the sample with media time */
1458 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1459 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1460 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1461 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1464 /* shift to next frame if we are looking for next keyframe */
1465 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1466 && index < str->stbl_index)
1469 if (!empty_segment) {
1470 /* find previous keyframe */
1471 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1473 /* we will settle for one before if none found after */
1474 if (next && kindex == -1)
1475 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1477 /* if the keyframe is at a different position, we need to update the
1478 * requested seek time */
1479 if (index != kindex) {
1482 /* get timestamp of keyframe */
1483 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1484 GST_DEBUG_OBJECT (qtdemux,
1485 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1486 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1487 str->samples[kindex].offset);
1489 /* keyframes in the segment get a chance to change the
1490 * desired_offset. keyframes out of the segment are
1492 if (media_time >= seg->media_start) {
1493 GstClockTime seg_time;
1495 /* this keyframe is inside the segment, convert back to
1497 seg_time = (media_time - seg->media_start) + seg->time;
1498 if ((!next && (seg_time < min_offset)) ||
1499 (next && (seg_time > min_offset)))
1500 min_offset = seg_time;
1505 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1506 min_byte_offset = str->samples[index].offset;
1510 *key_time = min_offset;
1512 *key_offset = min_byte_offset;
1516 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1517 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1521 g_return_val_if_fail (format != NULL, FALSE);
1522 g_return_val_if_fail (cur != NULL, FALSE);
1523 g_return_val_if_fail (stop != NULL, FALSE);
1525 if (*format == GST_FORMAT_TIME)
1529 if (cur_type != GST_SEEK_TYPE_NONE)
1530 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1531 if (res && stop_type != GST_SEEK_TYPE_NONE)
1532 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1535 *format = GST_FORMAT_TIME;
1540 /* perform seek in push based mode:
1541 find BYTE position to move to based on time and delegate to upstream
1544 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1549 GstSeekType cur_type, stop_type;
1550 gint64 cur, stop, key_cur;
1553 gint64 original_stop;
1556 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1558 gst_event_parse_seek (event, &rate, &format, &flags,
1559 &cur_type, &cur, &stop_type, &stop);
1560 seqnum = gst_event_get_seqnum (event);
1562 /* only forward streaming and seeking is possible */
1564 goto unsupported_seek;
1566 /* convert to TIME if needed and possible */
1567 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1571 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1572 * the original stop position to use when upstream pushes the new segment
1574 original_stop = stop;
1577 /* find reasonable corresponding BYTE position,
1578 * also try to mind about keyframes, since we can not go back a bit for them
1580 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1581 * mostly just work, but let's not yet boldly go there ... */
1582 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1587 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1588 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1591 GST_OBJECT_LOCK (qtdemux);
1592 qtdemux->seek_offset = byte_cur;
1593 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1594 qtdemux->push_seek_start = cur;
1596 qtdemux->push_seek_start = key_cur;
1599 if (stop_type == GST_SEEK_TYPE_NONE) {
1600 qtdemux->push_seek_stop = qtdemux->segment.stop;
1602 qtdemux->push_seek_stop = original_stop;
1604 GST_OBJECT_UNLOCK (qtdemux);
1606 qtdemux->segment_seqnum = seqnum;
1607 /* BYTE seek event */
1608 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1610 gst_event_set_seqnum (event, seqnum);
1611 res = gst_pad_push_event (qtdemux->sinkpad, event);
1618 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1624 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1629 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1634 /* perform the seek.
1636 * We set all segment_indexes in the streams to unknown and
1637 * adjust the time_position to the desired position. this is enough
1638 * to trigger a segment switch in the streaming thread to start
1639 * streaming from the desired position.
1641 * Keyframe seeking is a little more complicated when dealing with
1642 * segments. Ideally we want to move to the previous keyframe in
1643 * the segment but there might not be a keyframe in the segment. In
1644 * fact, none of the segments could contain a keyframe. We take a
1645 * practical approach: seek to the previous keyframe in the segment,
1646 * if there is none, seek to the beginning of the segment.
1648 * Called with STREAM_LOCK
1651 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1652 guint32 seqnum, GstSeekFlags flags)
1654 gint64 desired_offset;
1657 desired_offset = segment->position;
1659 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1660 GST_TIME_ARGS (desired_offset));
1662 /* may not have enough fragmented info to do this adjustment,
1663 * and we can't scan (and probably should not) at this time with
1664 * possibly flushing upstream */
1665 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1667 gboolean next, before, after;
1669 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1670 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1671 next = after && !before;
1672 if (segment->rate < 0)
1675 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1677 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1678 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1679 desired_offset = min_offset;
1682 /* and set all streams to the final position */
1683 gst_flow_combiner_reset (qtdemux->flowcombiner);
1684 qtdemux->segment_seqnum = seqnum;
1685 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1686 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1688 stream->time_position = desired_offset;
1689 stream->accumulated_base = 0;
1690 stream->sample_index = -1;
1691 stream->offset_in_sample = 0;
1692 stream->segment_index = -1;
1693 stream->sent_eos = FALSE;
1695 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1696 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1698 segment->position = desired_offset;
1699 segment->time = desired_offset;
1700 if (segment->rate >= 0) {
1701 segment->start = desired_offset;
1703 /* we stop at the end */
1704 if (segment->stop == -1)
1705 segment->stop = segment->duration;
1707 segment->stop = desired_offset;
1710 if (qtdemux->fragmented)
1711 qtdemux->fragmented_seek_pending = TRUE;
1716 /* do a seek in pull based mode */
1718 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1723 GstSeekType cur_type, stop_type;
1727 GstSegment seeksegment;
1728 guint32 seqnum = GST_SEQNUM_INVALID;
1729 GstEvent *flush_event;
1733 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1735 gst_event_parse_seek (event, &rate, &format, &flags,
1736 &cur_type, &cur, &stop_type, &stop);
1737 seqnum = gst_event_get_seqnum (event);
1739 /* we have to have a format as the segment format. Try to convert
1741 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1745 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1747 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1751 flush = flags & GST_SEEK_FLAG_FLUSH;
1753 /* stop streaming, either by flushing or by pausing the task */
1755 flush_event = gst_event_new_flush_start ();
1756 if (seqnum != GST_SEQNUM_INVALID)
1757 gst_event_set_seqnum (flush_event, seqnum);
1758 /* unlock upstream pull_range */
1759 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1760 /* make sure out loop function exits */
1761 gst_qtdemux_push_event (qtdemux, flush_event);
1763 /* non flushing seek, pause the task */
1764 gst_pad_pause_task (qtdemux->sinkpad);
1767 /* wait for streaming to finish */
1768 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1770 /* copy segment, we need this because we still need the old
1771 * segment when we close the current segment. */
1772 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1775 /* configure the segment with the seek variables */
1776 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1777 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1778 cur_type, cur, stop_type, stop, &update)) {
1780 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1782 /* now do the seek */
1783 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1786 /* now do the seek */
1787 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1790 /* prepare for streaming again */
1792 flush_event = gst_event_new_flush_stop (TRUE);
1793 if (seqnum != GST_SEQNUM_INVALID)
1794 gst_event_set_seqnum (flush_event, seqnum);
1796 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1797 gst_qtdemux_push_event (qtdemux, flush_event);
1800 /* commit the new segment */
1801 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1803 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1804 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1805 qtdemux->segment.format, qtdemux->segment.position);
1806 if (seqnum != GST_SEQNUM_INVALID)
1807 gst_message_set_seqnum (msg, seqnum);
1808 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1811 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1812 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1813 qtdemux->sinkpad, NULL);
1815 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1822 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1828 qtdemux_ensure_index (GstQTDemux * qtdemux)
1832 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1834 /* Build complete index */
1835 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1836 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1838 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1839 GST_LOG_OBJECT (qtdemux,
1840 "Building complete index of track-id %u for seeking failed!",
1850 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1853 gboolean res = TRUE;
1854 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1856 switch (GST_EVENT_TYPE (event)) {
1857 case GST_EVENT_SEEK:
1859 #ifndef GST_DISABLE_GST_DEBUG
1860 GstClockTime ts = gst_util_get_timestamp ();
1862 guint32 seqnum = gst_event_get_seqnum (event);
1864 qtdemux->received_seek = TRUE;
1866 if (seqnum == qtdemux->segment_seqnum) {
1867 GST_LOG_OBJECT (pad,
1868 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1869 gst_event_unref (event);
1873 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1874 /* seek should be handled by upstream, we might need to re-download fragments */
1875 GST_DEBUG_OBJECT (qtdemux,
1876 "let upstream handle seek for fragmented playback");
1880 /* Build complete index for seeking;
1881 * if not a fragmented file at least */
1882 if (!qtdemux->fragmented)
1883 if (!qtdemux_ensure_index (qtdemux))
1885 #ifndef GST_DISABLE_GST_DEBUG
1886 ts = gst_util_get_timestamp () - ts;
1887 GST_INFO_OBJECT (qtdemux,
1888 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1891 if (qtdemux->pullbased) {
1892 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1893 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1894 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1896 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1897 && QTDEMUX_N_STREAMS (qtdemux)
1898 && !qtdemux->fragmented) {
1899 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1901 GST_DEBUG_OBJECT (qtdemux,
1902 "ignoring seek in push mode in current state");
1905 gst_event_unref (event);
1909 res = gst_pad_event_default (pad, parent, event);
1919 GST_ERROR_OBJECT (qtdemux, "Index failed");
1920 gst_event_unref (event);
1926 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1928 * If @fw is false, the coding order is explored backwards.
1930 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1931 * sample is found for that track.
1933 * The stream and sample index of the sample with the minimum offset in the direction explored
1934 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1936 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1937 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1938 * @_stream and @_index. */
1940 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1941 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1944 gint64 time, min_time;
1945 QtDemuxStream *stream;
1952 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1955 gboolean set_sample;
1957 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1964 i = str->n_samples - 1;
1968 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1969 if (str->samples[i].size == 0)
1972 if (fw && (str->samples[i].offset < byte_pos))
1975 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1978 /* move stream to first available sample */
1980 gst_qtdemux_move_stream (qtdemux, str, i);
1984 /* avoid index from sparse streams since they might be far away */
1985 if (!CUR_STREAM (str)->sparse) {
1986 /* determine min/max time */
1987 time = QTSAMPLE_PTS (str, &str->samples[i]);
1988 if (min_time == -1 || (!fw && time > min_time) ||
1989 (fw && time < min_time)) {
1993 /* determine stream with leading sample, to get its position */
1995 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1996 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
2004 /* no sample for this stream, mark eos */
2006 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
2017 /* Copied from mpegtsbase code */
2018 /* FIXME: replace this function when we add new util function for stream-id creation */
2020 _get_upstream_id (GstQTDemux * demux)
2022 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
2025 /* Try to create one from the upstream URI, else use a randome number */
2029 /* Try to generate one from the URI query and
2030 * if it fails take a random number instead */
2031 query = gst_query_new_uri ();
2032 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
2033 gst_query_parse_uri (query, &uri);
2039 /* And then generate an SHA256 sum of the URI */
2040 cs = g_checksum_new (G_CHECKSUM_SHA256);
2041 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
2043 upstream_id = g_strdup (g_checksum_get_string (cs));
2044 g_checksum_free (cs);
2046 /* Just get some random number if the URI query fails */
2047 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
2048 "implementing a deterministic way of creating a stream-id");
2050 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
2051 g_random_int (), g_random_int ());
2054 gst_query_unref (query);
2059 static QtDemuxStream *
2060 _create_stream (GstQTDemux * demux, guint32 track_id)
2062 QtDemuxStream *stream;
2065 stream = g_new0 (QtDemuxStream, 1);
2066 stream->demux = demux;
2067 stream->track_id = track_id;
2068 upstream_id = _get_upstream_id (demux);
2069 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
2070 g_free (upstream_id);
2071 /* new streams always need a discont */
2072 stream->discont = TRUE;
2073 /* we enable clipping for raw audio/video streams */
2074 stream->need_clip = FALSE;
2075 stream->need_process = FALSE;
2076 stream->segment_index = -1;
2077 stream->time_position = 0;
2078 stream->sample_index = -1;
2079 stream->offset_in_sample = 0;
2080 stream->new_stream = TRUE;
2081 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
2082 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2083 stream->protected = FALSE;
2084 stream->protection_scheme_type = 0;
2085 stream->protection_scheme_version = 0;
2086 stream->protection_scheme_info = NULL;
2087 stream->n_samples_moof = 0;
2088 stream->duration_moof = 0;
2089 stream->duration_last_moof = 0;
2090 stream->alignment = 1;
2091 stream->stream_tags = gst_tag_list_new_empty ();
2092 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2093 g_queue_init (&stream->protection_scheme_event_queue);
2094 stream->ref_count = 1;
2095 /* consistent default for push based mode */
2096 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
2101 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
2103 GstStructure *structure;
2104 const gchar *variant;
2105 const GstCaps *mediacaps = NULL;
2107 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
2109 structure = gst_caps_get_structure (caps, 0);
2110 variant = gst_structure_get_string (structure, "variant");
2112 if (variant && strcmp (variant, "mss-fragmented") == 0) {
2113 QtDemuxStream *stream;
2114 const GValue *value;
2116 demux->fragmented = TRUE;
2117 demux->mss_mode = TRUE;
2119 if (QTDEMUX_N_STREAMS (demux) > 1) {
2120 /* can't do this, we can only renegotiate for another mss format */
2124 value = gst_structure_get_value (structure, "media-caps");
2127 const GValue *timescale_v;
2129 /* TODO update when stream changes during playback */
2131 if (QTDEMUX_N_STREAMS (demux) == 0) {
2132 stream = _create_stream (demux, 1);
2133 g_ptr_array_add (demux->active_streams, stream);
2134 /* mss has no stsd/stsd entry, use id 0 as default */
2135 stream->stsd_entries_length = 1;
2136 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2137 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2139 stream = QTDEMUX_NTH_STREAM (demux, 0);
2142 timescale_v = gst_structure_get_value (structure, "timescale");
2144 stream->timescale = g_value_get_uint64 (timescale_v);
2146 /* default mss timescale */
2147 stream->timescale = 10000000;
2149 demux->timescale = stream->timescale;
2151 mediacaps = gst_value_get_caps (value);
2152 if (!CUR_STREAM (stream)->caps
2153 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2154 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2156 stream->new_caps = TRUE;
2158 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2159 structure = gst_caps_get_structure (mediacaps, 0);
2160 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2161 stream->subtype = FOURCC_vide;
2163 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2164 gst_structure_get_int (structure, "height",
2165 &CUR_STREAM (stream)->height);
2166 gst_structure_get_fraction (structure, "framerate",
2167 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2168 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2170 stream->subtype = FOURCC_soun;
2171 gst_structure_get_int (structure, "channels",
2172 &CUR_STREAM (stream)->n_channels);
2173 gst_structure_get_int (structure, "rate", &rate);
2174 CUR_STREAM (stream)->rate = rate;
2177 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2179 demux->mss_mode = FALSE;
2186 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2190 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2191 gst_pad_stop_task (qtdemux->sinkpad);
2193 if (hard || qtdemux->upstream_format_is_time) {
2194 qtdemux->state = QTDEMUX_STATE_INITIAL;
2195 qtdemux->neededbytes = 16;
2196 qtdemux->todrop = 0;
2197 qtdemux->pullbased = FALSE;
2198 qtdemux->posted_redirect = FALSE;
2199 qtdemux->first_mdat = -1;
2200 qtdemux->header_size = 0;
2201 qtdemux->mdatoffset = -1;
2202 qtdemux->restoredata_offset = -1;
2203 if (qtdemux->mdatbuffer)
2204 gst_buffer_unref (qtdemux->mdatbuffer);
2205 if (qtdemux->restoredata_buffer)
2206 gst_buffer_unref (qtdemux->restoredata_buffer);
2207 qtdemux->mdatbuffer = NULL;
2208 qtdemux->restoredata_buffer = NULL;
2209 qtdemux->mdatleft = 0;
2210 qtdemux->mdatsize = 0;
2211 if (qtdemux->comp_brands)
2212 gst_buffer_unref (qtdemux->comp_brands);
2213 qtdemux->comp_brands = NULL;
2214 qtdemux->last_moov_offset = -1;
2215 if (qtdemux->moov_node_compressed) {
2216 g_node_destroy (qtdemux->moov_node_compressed);
2217 if (qtdemux->moov_node)
2218 g_free (qtdemux->moov_node->data);
2220 qtdemux->moov_node_compressed = NULL;
2221 if (qtdemux->moov_node)
2222 g_node_destroy (qtdemux->moov_node);
2223 qtdemux->moov_node = NULL;
2224 if (qtdemux->tag_list)
2225 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2226 qtdemux->tag_list = gst_tag_list_new_empty ();
2227 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2229 if (qtdemux->element_index)
2230 gst_object_unref (qtdemux->element_index);
2231 qtdemux->element_index = NULL;
2233 qtdemux->major_brand = 0;
2234 qtdemux->upstream_format_is_time = FALSE;
2235 qtdemux->upstream_seekable = FALSE;
2236 qtdemux->upstream_size = 0;
2238 qtdemux->fragment_start = -1;
2239 qtdemux->fragment_start_offset = -1;
2240 qtdemux->duration = 0;
2241 qtdemux->moof_offset = 0;
2242 qtdemux->chapters_track_id = 0;
2243 qtdemux->have_group_id = FALSE;
2244 qtdemux->group_id = G_MAXUINT;
2246 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2248 g_queue_clear (&qtdemux->protection_event_queue);
2250 qtdemux->received_seek = FALSE;
2251 qtdemux->first_moof_already_parsed = FALSE;
2253 qtdemux->offset = 0;
2254 gst_adapter_clear (qtdemux->adapter);
2255 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2256 qtdemux->need_segment = TRUE;
2259 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2260 g_ptr_array_set_size (qtdemux->active_streams, 0);
2261 g_ptr_array_set_size (qtdemux->old_streams, 0);
2262 qtdemux->n_video_streams = 0;
2263 qtdemux->n_audio_streams = 0;
2264 qtdemux->n_sub_streams = 0;
2265 qtdemux->exposed = FALSE;
2266 qtdemux->fragmented = FALSE;
2267 qtdemux->mss_mode = FALSE;
2268 gst_caps_replace (&qtdemux->media_caps, NULL);
2269 qtdemux->timescale = 0;
2270 qtdemux->got_moov = FALSE;
2271 qtdemux->cenc_aux_info_offset = 0;
2272 qtdemux->cenc_aux_info_sizes = NULL;
2273 qtdemux->cenc_aux_sample_count = 0;
2274 if (qtdemux->protection_system_ids) {
2275 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2276 qtdemux->protection_system_ids = NULL;
2278 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2279 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2280 GST_BIN_FLAG_STREAMS_AWARE);
2282 if (qtdemux->preferred_protection_system_id) {
2283 g_free (qtdemux->preferred_protection_system_id);
2284 qtdemux->preferred_protection_system_id = NULL;
2286 } else if (qtdemux->mss_mode) {
2287 gst_flow_combiner_reset (qtdemux->flowcombiner);
2288 g_ptr_array_foreach (qtdemux->active_streams,
2289 (GFunc) gst_qtdemux_stream_clear, NULL);
2291 gst_flow_combiner_reset (qtdemux->flowcombiner);
2292 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2293 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2294 stream->sent_eos = FALSE;
2295 stream->time_position = 0;
2296 stream->accumulated_base = 0;
2302 /* Maps the @segment to the qt edts internal segments and pushes
2303 * the correspnding segment event.
2305 * If it ends up being at a empty segment, a gap will be pushed and the next
2306 * edts segment will be activated in sequence.
2308 * To be used in push-mode only */
2310 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2314 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2315 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2317 stream->time_position = segment->start;
2319 /* in push mode we should be guaranteed that we will have empty segments
2320 * at the beginning and then one segment after, other scenarios are not
2321 * supported and are discarded when parsing the edts */
2322 for (i = 0; i < stream->n_segments; i++) {
2323 if (stream->segments[i].stop_time > segment->start) {
2324 /* push the empty segment and move to the next one */
2325 gst_qtdemux_activate_segment (qtdemux, stream, i,
2326 stream->time_position);
2327 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2328 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2329 stream->time_position);
2331 /* accumulate previous segments */
2332 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2333 stream->accumulated_base +=
2334 (stream->segment.stop -
2335 stream->segment.start) / ABS (stream->segment.rate);
2339 g_assert (i == stream->n_segments - 1);
2346 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2357 for (i = 0; i < len; i++) {
2358 QtDemuxStream *stream = g_ptr_array_index (src, i);
2360 #ifndef GST_DISABLE_GST_DEBUG
2361 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2362 stream, GST_STR_NULL (stream->stream_id), dest);
2364 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2367 g_ptr_array_set_size (src, 0);
2371 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2374 GstQTDemux *demux = GST_QTDEMUX (parent);
2375 gboolean res = TRUE;
2377 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2379 switch (GST_EVENT_TYPE (event)) {
2380 case GST_EVENT_SEGMENT:
2383 QtDemuxStream *stream;
2387 /* some debug output */
2388 gst_event_copy_segment (event, &segment);
2389 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2392 if (segment.format == GST_FORMAT_TIME) {
2393 demux->upstream_format_is_time = TRUE;
2394 demux->segment_seqnum = gst_event_get_seqnum (event);
2396 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2397 "not in time format");
2399 /* chain will send initial newsegment after pads have been added */
2400 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2401 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2406 /* check if this matches a time seek we received previously
2407 * FIXME for backwards compatibility reasons we use the
2408 * seek_offset here to compare. In the future we might want to
2409 * change this to use the seqnum as it uniquely should identify
2410 * the segment that corresponds to the seek. */
2411 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2412 ", received segment offset %" G_GINT64_FORMAT,
2413 demux->seek_offset, segment.start);
2414 if (segment.format == GST_FORMAT_BYTES
2415 && demux->seek_offset == segment.start) {
2416 GST_OBJECT_LOCK (demux);
2417 offset = segment.start;
2419 segment.format = GST_FORMAT_TIME;
2420 segment.start = demux->push_seek_start;
2421 segment.stop = demux->push_seek_stop;
2422 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2423 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2424 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2425 GST_OBJECT_UNLOCK (demux);
2428 /* we only expect a BYTE segment, e.g. following a seek */
2429 if (segment.format == GST_FORMAT_BYTES) {
2430 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2431 offset = segment.start;
2433 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2434 NULL, (gint64 *) & segment.start);
2435 if ((gint64) segment.start < 0)
2438 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2439 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2440 NULL, (gint64 *) & segment.stop);
2441 /* keyframe seeking should already arrange for start >= stop,
2442 * but make sure in other rare cases */
2443 segment.stop = MAX (segment.stop, segment.start);
2445 } else if (segment.format == GST_FORMAT_TIME) {
2446 /* push all data on the adapter before starting this
2448 gst_qtdemux_process_adapter (demux, TRUE);
2450 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2454 /* We shouldn't modify upstream driven TIME FORMAT segment */
2455 if (!demux->upstream_format_is_time) {
2456 /* accept upstream's notion of segment and distribute along */
2457 segment.format = GST_FORMAT_TIME;
2458 segment.position = segment.time = segment.start;
2459 segment.duration = demux->segment.duration;
2460 segment.base = gst_segment_to_running_time (&demux->segment,
2461 GST_FORMAT_TIME, demux->segment.position);
2464 gst_segment_copy_into (&segment, &demux->segment);
2465 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2467 /* map segment to internal qt segments and push on each stream */
2468 if (QTDEMUX_N_STREAMS (demux)) {
2469 demux->need_segment = TRUE;
2470 gst_qtdemux_check_send_pending_segment (demux);
2473 /* clear leftover in current segment, if any */
2474 gst_adapter_clear (demux->adapter);
2476 /* set up streaming thread */
2477 demux->offset = offset;
2478 if (demux->upstream_format_is_time) {
2479 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2480 "set values to restart reading from a new atom");
2481 demux->neededbytes = 16;
2484 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2487 demux->todrop = stream->samples[idx].offset - offset;
2488 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2490 /* set up for EOS */
2491 demux->neededbytes = -1;
2496 gst_event_unref (event);
2500 case GST_EVENT_FLUSH_START:
2502 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2503 gst_event_unref (event);
2506 QTDEMUX_EXPOSE_LOCK (demux);
2507 res = gst_pad_event_default (demux->sinkpad, parent, event);
2508 QTDEMUX_EXPOSE_UNLOCK (demux);
2511 case GST_EVENT_FLUSH_STOP:
2515 dur = demux->segment.duration;
2516 gst_qtdemux_reset (demux, FALSE);
2517 demux->segment.duration = dur;
2519 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2520 gst_event_unref (event);
2526 /* If we are in push mode, and get an EOS before we've seen any streams,
2527 * then error out - we have nowhere to send the EOS */
2528 if (!demux->pullbased) {
2530 gboolean has_valid_stream = FALSE;
2531 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2532 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2533 has_valid_stream = TRUE;
2537 if (!has_valid_stream)
2538 gst_qtdemux_post_no_playable_stream_error (demux);
2540 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2541 (guint) gst_adapter_available (demux->adapter));
2542 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2548 case GST_EVENT_CAPS:{
2549 GstCaps *caps = NULL;
2551 gst_event_parse_caps (event, &caps);
2552 gst_qtdemux_setcaps (demux, caps);
2554 gst_event_unref (event);
2557 case GST_EVENT_PROTECTION:
2559 const gchar *system_id = NULL;
2561 gst_event_parse_protection (event, &system_id, NULL, NULL);
2562 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2564 gst_qtdemux_append_protection_system_id (demux, system_id);
2565 /* save the event for later, for source pads that have not been created */
2566 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2567 /* send it to all pads that already exist */
2568 gst_qtdemux_push_event (demux, event);
2572 case GST_EVENT_STREAM_START:
2575 gst_event_unref (event);
2577 /* Drain all the buffers */
2578 gst_qtdemux_process_adapter (demux, TRUE);
2579 gst_qtdemux_reset (demux, FALSE);
2580 /* We expect new moov box after new stream-start event */
2581 if (demux->exposed) {
2582 gst_qtdemux_stream_concat (demux,
2583 demux->old_streams, demux->active_streams);
2592 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2599 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2602 GstQTDemux *demux = GST_QTDEMUX (parent);
2603 gboolean res = FALSE;
2605 switch (GST_QUERY_TYPE (query)) {
2606 case GST_QUERY_BITRATE:
2608 GstClockTime duration;
2610 /* populate demux->upstream_size if not done yet */
2611 gst_qtdemux_check_seekability (demux);
2613 if (demux->upstream_size != -1
2614 && gst_qtdemux_get_duration (demux, &duration)) {
2616 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2619 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2620 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2621 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2623 /* TODO: better results based on ranges/index tables */
2624 gst_query_set_bitrate (query, bitrate);
2630 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2640 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2642 GstQTDemux *demux = GST_QTDEMUX (element);
2644 GST_OBJECT_LOCK (demux);
2645 if (demux->element_index)
2646 gst_object_unref (demux->element_index);
2648 demux->element_index = gst_object_ref (index);
2650 demux->element_index = NULL;
2652 GST_OBJECT_UNLOCK (demux);
2653 /* object lock might be taken again */
2655 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2656 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2657 demux->element_index, demux->index_id);
2661 gst_qtdemux_get_index (GstElement * element)
2663 GstIndex *result = NULL;
2664 GstQTDemux *demux = GST_QTDEMUX (element);
2666 GST_OBJECT_LOCK (demux);
2667 if (demux->element_index)
2668 result = gst_object_ref (demux->element_index);
2669 GST_OBJECT_UNLOCK (demux);
2671 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2678 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2680 g_free ((gpointer) stream->stco.data);
2681 stream->stco.data = NULL;
2682 g_free ((gpointer) stream->stsz.data);
2683 stream->stsz.data = NULL;
2684 g_free ((gpointer) stream->stsc.data);
2685 stream->stsc.data = NULL;
2686 g_free ((gpointer) stream->stts.data);
2687 stream->stts.data = NULL;
2688 g_free ((gpointer) stream->stss.data);
2689 stream->stss.data = NULL;
2690 g_free ((gpointer) stream->stps.data);
2691 stream->stps.data = NULL;
2692 g_free ((gpointer) stream->ctts.data);
2693 stream->ctts.data = NULL;
2697 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2699 g_free (stream->segments);
2700 stream->segments = NULL;
2701 stream->segment_index = -1;
2702 stream->accumulated_base = 0;
2706 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2708 g_free (stream->samples);
2709 stream->samples = NULL;
2710 gst_qtdemux_stbl_free (stream);
2713 g_free (stream->ra_entries);
2714 stream->ra_entries = NULL;
2715 stream->n_ra_entries = 0;
2717 stream->sample_index = -1;
2718 stream->stbl_index = -1;
2719 stream->n_samples = 0;
2720 stream->time_position = 0;
2722 stream->n_samples_moof = 0;
2723 stream->duration_moof = 0;
2724 stream->duration_last_moof = 0;
2728 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2731 if (stream->allocator)
2732 gst_object_unref (stream->allocator);
2733 while (stream->buffers) {
2734 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2735 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2737 for (i = 0; i < stream->stsd_entries_length; i++) {
2738 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2739 if (entry->rgb8_palette) {
2740 gst_memory_unref (entry->rgb8_palette);
2741 entry->rgb8_palette = NULL;
2743 entry->sparse = FALSE;
2746 if (stream->stream_tags)
2747 gst_tag_list_unref (stream->stream_tags);
2749 stream->stream_tags = gst_tag_list_new_empty ();
2750 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2751 g_free (stream->redirect_uri);
2752 stream->redirect_uri = NULL;
2753 stream->sent_eos = FALSE;
2754 stream->protected = FALSE;
2755 if (stream->protection_scheme_info) {
2756 if (stream->protection_scheme_type == FOURCC_cenc) {
2757 QtDemuxCencSampleSetInfo *info =
2758 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2759 if (info->default_properties)
2760 gst_structure_free (info->default_properties);
2761 if (info->crypto_info)
2762 g_ptr_array_free (info->crypto_info, TRUE);
2764 g_free (stream->protection_scheme_info);
2765 stream->protection_scheme_info = NULL;
2767 stream->protection_scheme_type = 0;
2768 stream->protection_scheme_version = 0;
2769 g_queue_foreach (&stream->protection_scheme_event_queue,
2770 (GFunc) gst_event_unref, NULL);
2771 g_queue_clear (&stream->protection_scheme_event_queue);
2772 gst_qtdemux_stream_flush_segments_data (stream);
2773 gst_qtdemux_stream_flush_samples_data (stream);
2777 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2780 gst_qtdemux_stream_clear (stream);
2781 for (i = 0; i < stream->stsd_entries_length; i++) {
2782 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2784 gst_caps_unref (entry->caps);
2788 g_free (stream->stsd_entries);
2789 stream->stsd_entries = NULL;
2790 stream->stsd_entries_length = 0;
2793 static QtDemuxStream *
2794 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2796 g_atomic_int_add (&stream->ref_count, 1);
2802 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2804 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2805 gst_qtdemux_stream_reset (stream);
2806 gst_tag_list_unref (stream->stream_tags);
2808 GstQTDemux *demux = stream->demux;
2809 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2810 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2812 g_free (stream->stream_id);
2817 static GstStateChangeReturn
2818 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2820 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2821 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2823 switch (transition) {
2824 case GST_STATE_CHANGE_READY_TO_PAUSED:
2825 gst_qtdemux_reset (qtdemux, TRUE);
2831 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2833 switch (transition) {
2834 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2835 gst_qtdemux_reset (qtdemux, TRUE);
2846 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2848 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2850 g_return_if_fail (GST_IS_CONTEXT (context));
2852 if (gst_context_has_context_type (context,
2853 "drm-preferred-decryption-system-id")) {
2854 const GstStructure *s;
2856 s = gst_context_get_structure (context);
2857 g_free (qtdemux->preferred_protection_system_id);
2858 qtdemux->preferred_protection_system_id =
2859 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2860 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2861 qtdemux->preferred_protection_system_id);
2864 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2868 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2870 /* counts as header data */
2871 qtdemux->header_size += length;
2873 /* only consider at least a sufficiently complete ftyp atom */
2877 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2878 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2879 GST_FOURCC_ARGS (qtdemux->major_brand));
2880 if (qtdemux->comp_brands)
2881 gst_buffer_unref (qtdemux->comp_brands);
2882 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2883 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2888 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2889 GstTagList * xmptaglist)
2891 /* Strip out bogus fields */
2893 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2894 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2895 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2897 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2900 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2902 /* prioritize native tags using _KEEP mode */
2903 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2904 gst_tag_list_unref (xmptaglist);
2908 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2910 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2911 const char *xml_str, const char *param_name, int *value)
2913 char *value_start, *value_end, *endptr;
2914 const short value_length_max = 12;
2915 char init_view_ret[12];
2916 int value_length = 0;
2919 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2922 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2927 value_start += strlen (param_name);
2928 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2931 value_end = strchr (value_start, '<');
2933 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2937 value_length = value_end - value_start;
2938 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2939 || (value_start[value_length - 1] == '\t')))
2942 if (value_start[i] == '+' || value_start[i] == '-')
2944 while (i < value_length) {
2945 if (value_start[i] < '0' || value_start[i] > '9') {
2946 GST_ERROR_OBJECT (qtdemux,
2947 "error: incorrect value, integer was expected\n");
2953 if (value_length >= value_length_max || value_length < 1) {
2954 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2958 strncpy (init_view_ret, value_start, value_length_max);
2959 init_view_ret[value_length] = '\0';
2961 *value = strtol (init_view_ret, &endptr, 10);
2962 if (endptr == init_view_ret) {
2963 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2971 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2972 const char *xml_str, const char *param_name, char **value)
2974 char *value_start, *value_end;
2975 const short value_length_max = 256;
2976 int value_length = 0;
2978 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2981 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2986 value_start += strlen (param_name);
2987 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2990 value_end = strchr (value_start, '<');
2992 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2996 value_length = value_end - value_start;
2997 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2998 || (value_start[value_length - 1] == '\t')))
3001 if (value_length >= value_length_max || value_length < 1) {
3002 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
3006 *value = strndup(value_start, value_length);
3012 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
3013 const char *xml_str, const char *param_name, gboolean * value)
3015 char *value_start, *value_end;
3016 int value_length = 0;
3018 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
3021 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
3026 value_start += strlen (param_name);
3027 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
3030 value_end = strchr (value_start, '<');
3032 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
3036 value_length = value_end - value_start;
3037 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
3038 || (value_start[value_length - 1] == '\t')))
3041 if (value_length < 1) {
3042 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
3046 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
3052 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
3054 const char is_spherical_str[] = "<GSpherical:Spherical>";
3055 const char is_stitched_str[] = "<GSpherical:Stitched>";
3056 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
3057 const char projection_type_str[] = "<GSpherical:ProjectionType>";
3058 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
3059 const char source_count_str[] = "<GSpherical:SourceCount>";
3060 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
3061 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
3062 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
3063 const char timestamp_str[] = "<GSpherical:Timestamp>";
3064 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
3065 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
3066 const char cropped_area_image_width_str[] =
3067 "<GSpherical:CroppedAreaImageWidthPixels>";
3068 const char cropped_area_image_height_str[] =
3069 "<GSpherical:CroppedAreaImageHeightPixels>";
3070 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
3071 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
3073 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
3075 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
3076 (gboolean *) & spherical_metadata->is_spherical);
3077 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
3078 (gboolean *) & spherical_metadata->is_stitched);
3080 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
3081 _get_string_value_from_xml_string (qtdemux, xmlStr,
3082 stitching_software_str, &spherical_metadata->stitching_software);
3083 _get_string_value_from_xml_string (qtdemux, xmlStr,
3084 projection_type_str, &spherical_metadata->projection_type);
3085 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
3086 &spherical_metadata->stereo_mode);
3087 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
3088 &spherical_metadata->source_count);
3089 _get_int_value_from_xml_string (qtdemux, xmlStr,
3090 init_view_heading_str, &spherical_metadata->init_view_heading);
3091 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
3092 &spherical_metadata->init_view_pitch);
3093 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
3094 &spherical_metadata->init_view_roll);
3095 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
3096 &spherical_metadata->timestamp);
3097 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
3098 &spherical_metadata->full_pano_width_pixels);
3099 _get_int_value_from_xml_string (qtdemux, xmlStr,
3100 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
3101 _get_int_value_from_xml_string (qtdemux, xmlStr,
3102 cropped_area_image_width_str,
3103 &spherical_metadata->cropped_area_image_width);
3104 _get_int_value_from_xml_string (qtdemux, xmlStr,
3105 cropped_area_image_height_str,
3106 &spherical_metadata->cropped_area_image_height);
3107 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
3108 &spherical_metadata->cropped_area_left);
3109 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
3110 &spherical_metadata->cropped_area_top);
3117 gst_tag_register_spherical_tags (void) {
3118 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
3121 _("Flag indicating if the video is a spherical video"),
3123 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
3126 _("Flag indicating if the video is stitched"),
3128 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
3130 _("tag-stitching-software"),
3131 _("Software used to stitch the spherical video"),
3133 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
3135 _("tag-projection-type"),
3136 _("Projection type used in the video frames"),
3138 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
3140 _("tag-stereo-mode"),
3141 _("Description of stereoscopic 3D layout"),
3143 gst_tag_register ("source_count", GST_TAG_FLAG_META,
3145 _("tag-source-count"),
3146 _("Number of cameras used to create the spherical video"),
3148 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
3150 _("tag-init-view-heading"),
3151 _("The heading angle of the initial view in degrees"),
3153 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
3155 _("tag-init-view-pitch"),
3156 _("The pitch angle of the initial view in degrees"),
3158 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
3160 _("tag-init-view-roll"),
3161 _("The roll angle of the initial view in degrees"),
3163 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
3166 _("Epoch timestamp of when the first frame in the video was recorded"),
3168 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
3170 _("tag-full-pano-width"),
3171 _("Width of the encoded video frame in pixels"),
3173 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
3175 _("tag-full-pano-height"),
3176 _("Height of the encoded video frame in pixels"),
3178 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
3180 _("tag-cropped-area-image-width"),
3181 _("Width of the video frame to display (e.g. cropping)"),
3183 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
3185 _("tag-cropped-area-image-height"),
3186 _("Height of the video frame to display (e.g. cropping)"),
3188 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3190 _("tag-cropped-area-left"),
3191 _("Column where the left edge of the image was cropped from the"
3192 " full sized panorama"),
3194 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3196 _("tag-cropped-area-top"),
3197 _("Row where the top edge of the image was cropped from the"
3198 " full sized panorama"),
3200 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3202 _("tag-ambisonic-type"),
3203 _("Specifies the type of ambisonic audio represented"),
3205 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3207 _("tag-ambisonic-format"),
3208 _("Specifies the ambisonic audio format"),
3210 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3212 _("tag-ambisonic-order"),
3213 _("Specifies the ambisonic audio channel order"),
3220 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3222 GstTagList *taglist;
3223 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3225 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3226 spherical_metadata->is_spherical);
3227 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3228 spherical_metadata->is_stitched);
3229 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3230 spherical_metadata->stitching_software);
3231 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3232 spherical_metadata->projection_type);
3233 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3234 spherical_metadata->stereo_mode);
3235 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3236 spherical_metadata->source_count);
3237 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3238 spherical_metadata->init_view_heading);
3239 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3240 spherical_metadata->init_view_pitch);
3241 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3242 spherical_metadata->init_view_roll);
3243 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3244 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3245 spherical_metadata->full_pano_width_pixels);
3246 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3247 spherical_metadata->full_pano_height_pixels);
3248 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3249 spherical_metadata->cropped_area_image_width);
3250 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3251 spherical_metadata->cropped_area_image_height);
3252 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3253 spherical_metadata->cropped_area_left);
3254 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3255 spherical_metadata->cropped_area_top);
3256 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3257 spherical_metadata->ambisonic_type);
3258 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3259 spherical_metadata->ambisonic_order);
3260 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3261 spherical_metadata->ambisonic_format);
3263 taglist = gst_tag_list_new_empty ();
3264 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3265 "is_spherical", spherical_metadata->is_spherical,
3266 "is_stitched", spherical_metadata->is_stitched,
3267 "source_count", spherical_metadata->source_count,
3268 "init_view_heading", spherical_metadata->init_view_heading,
3269 "init_view_pitch", spherical_metadata->init_view_pitch,
3270 "init_view_roll", spherical_metadata->init_view_roll,
3271 "timestamp", spherical_metadata->timestamp,
3272 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3273 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3274 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3275 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3276 "cropped_area_left", spherical_metadata->cropped_area_left,
3277 "cropped_area_top", spherical_metadata->cropped_area_top,
3278 "ambisonic_type", spherical_metadata->ambisonic_type,
3279 "ambisonic_format", spherical_metadata->ambisonic_format,
3280 "ambisonic_order", spherical_metadata->ambisonic_order,
3283 if (spherical_metadata->stitching_software)
3284 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3285 "stitching_software", spherical_metadata->stitching_software,
3287 if (spherical_metadata->projection_type)
3288 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3289 "projection_type", spherical_metadata->projection_type,
3291 if (spherical_metadata->stereo_mode)
3292 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3293 "stereo_mode", spherical_metadata->stereo_mode,
3296 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3297 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3298 gst_tag_list_copy (taglist)));
3300 gst_tag_list_unref(taglist);
3306 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3311 guint8 ambisonic_type = 0;
3312 guint32 ambisonic_order = 0;
3313 guint8 ambisonic_channel_ordering = 0;
3314 guint8 ambisonic_normalization = 0;
3315 guint32 num_channels = 0;
3316 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3320 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3322 qtdemux->header_size += length;
3323 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3325 if (length <= offset + 16) {
3326 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3330 version = QT_UINT8 (buffer + offset);
3331 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3332 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3333 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3334 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3335 num_channels = QT_UINT32 (buffer + offset + 8);
3336 for (i = 0; i < num_channels; ++i)
3337 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3339 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3340 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3341 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3342 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3343 ambisonic_channel_ordering);
3344 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3345 ambisonic_normalization);
3346 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3347 for (i = 0; i < num_channels; ++i)
3348 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3350 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3351 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3352 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3354 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3355 if (num_channels == 4) {
3356 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3358 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3359 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3360 && (channel_map[0] == 0) && (channel_map[1] == 1)
3361 && (channel_map[2] == 2) && (channel_map[3] == 3))
3362 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3364 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3365 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3366 && (channel_map[0] == 0) && (channel_map[1] == 3)
3367 && (channel_map[2] == 1) && (channel_map[3] == 2))
3368 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3375 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3378 qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
3379 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
3382 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3383 gst_buffer_fill (kid_buf, 0, kid, 16);
3384 if (info->default_properties)
3385 gst_structure_free (info->default_properties);
3386 info->default_properties =
3387 gst_structure_new ("application/x-cenc",
3388 "iv_size", G_TYPE_UINT, iv_size,
3389 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
3390 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3391 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3392 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3393 gst_buffer_unref (kid_buf);
3397 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
3398 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
3400 guint32 algorithm_id = 0;
3402 gboolean is_encrypted = TRUE;
3405 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
3406 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3411 if (algorithm_id == 0) {
3412 is_encrypted = FALSE;
3413 } else if (algorithm_id == 1) {
3414 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3415 } else if (algorithm_id == 2) {
3416 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3419 if (!gst_byte_reader_get_uint8 (br, &iv_size))
3422 if (!gst_byte_reader_get_data (br, 16, &kid))
3425 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
3426 is_encrypted, iv_size, kid);
3427 gst_structure_set (info->default_properties, "piff_algorithm_id",
3428 G_TYPE_UINT, algorithm_id, NULL);
3434 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3442 QtDemuxStream *stream;
3443 GstStructure *structure;
3444 QtDemuxCencSampleSetInfo *ss_info = NULL;
3445 const gchar *system_id;
3446 gboolean uses_sub_sample_encryption = FALSE;
3447 guint32 sample_count;
3449 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
3452 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
3454 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3455 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3456 GST_WARNING_OBJECT (qtdemux,
3457 "Attempting PIFF box parsing on an unencrypted stream.");
3461 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3462 G_TYPE_STRING, &system_id, NULL);
3463 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3465 stream->protected = TRUE;
3466 stream->protection_scheme_type = FOURCC_cenc;
3468 if (!stream->protection_scheme_info)
3469 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3471 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3472 if (!ss_info->default_properties) {
3473 ss_info->default_properties =
3474 gst_structure_new ("application/x-cenc",
3475 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
3480 if (ss_info->crypto_info) {
3481 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3482 g_ptr_array_free (ss_info->crypto_info, TRUE);
3483 ss_info->crypto_info = NULL;
3487 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3489 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3490 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3494 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3495 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3499 if ((flags & 0x000001)) {
3500 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
3503 } else if ((flags & 0x000002)) {
3504 uses_sub_sample_encryption = TRUE;
3507 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
3509 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
3513 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
3514 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3518 ss_info->crypto_info =
3519 g_ptr_array_new_full (sample_count,
3520 (GDestroyNotify) qtdemux_gst_structure_free);
3522 for (i = 0; i < sample_count; ++i) {
3523 GstStructure *properties;
3527 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3528 if (properties == NULL) {
3529 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3530 qtdemux->cenc_aux_sample_count = i;
3534 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3535 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3536 gst_structure_free (properties);
3537 qtdemux->cenc_aux_sample_count = i;
3540 buf = gst_buffer_new_wrapped (data, iv_size);
3541 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3542 gst_buffer_unref (buf);
3544 if (uses_sub_sample_encryption) {
3545 guint16 n_subsamples;
3546 const GValue *kid_buf_value;
3548 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3549 || n_subsamples == 0) {
3550 GST_ERROR_OBJECT (qtdemux,
3551 "failed to get subsample count for sample %u", i);
3552 gst_structure_free (properties);
3553 qtdemux->cenc_aux_sample_count = i;
3556 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3557 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3558 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3560 gst_structure_free (properties);
3561 qtdemux->cenc_aux_sample_count = i;
3564 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3567 gst_structure_get_value (ss_info->default_properties, "kid");
3569 gst_structure_set (properties,
3570 "subsample_count", G_TYPE_UINT, n_subsamples,
3571 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3572 gst_structure_set_value (properties, "kid", kid_buf_value);
3573 gst_buffer_unref (buf);
3575 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3578 g_ptr_array_add (ss_info->crypto_info, properties);
3581 qtdemux->cenc_aux_sample_count = sample_count;
3585 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3587 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3588 0x97, 0xA9, 0x42, 0xE8,
3589 0x9C, 0x71, 0x99, 0x94,
3590 0x91, 0xE3, 0xAF, 0xAC
3592 static const guint8 playready_uuid[] = {
3593 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3594 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3597 static const guint8 piff_sample_encryption_uuid[] = {
3598 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3599 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3602 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3603 static const guint8 spherical_uuid[] = {
3604 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3605 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3607 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3611 /* counts as header data */
3612 qtdemux->header_size += length;
3614 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3616 if (length <= offset + 16) {
3617 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3621 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3622 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3623 const char *contents;
3625 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3626 contents = (char *) (buffer + offset + 16);
3627 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3629 if (qtdemux->spherical_metadata)
3630 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3634 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3636 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3638 GstTagList *taglist;
3640 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3641 length - offset - 16, NULL);
3642 taglist = gst_tag_list_from_xmp_buffer (buf);
3643 gst_buffer_unref (buf);
3645 /* make sure we have a usable taglist */
3646 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3648 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3650 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3652 const gunichar2 *s_utf16;
3655 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3656 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3657 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3658 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3662 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3663 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3665 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3666 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3668 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3669 GST_READ_UINT32_LE (buffer + offset),
3670 GST_READ_UINT32_LE (buffer + offset + 4),
3671 GST_READ_UINT32_LE (buffer + offset + 8),
3672 GST_READ_UINT32_LE (buffer + offset + 12));
3677 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3679 GstSidxParser sidx_parser;
3680 GstIsoffParserResult res;
3683 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3686 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3688 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3689 if (res == GST_ISOFF_QT_PARSER_DONE) {
3690 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3692 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3695 /* caller verifies at least 8 bytes in buf */
3697 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3698 guint64 * plength, guint32 * pfourcc)
3703 length = QT_UINT32 (data);
3704 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3705 fourcc = QT_FOURCC (data + 4);
3706 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3709 length = G_MAXUINT64;
3710 } else if (length == 1 && size >= 16) {
3711 /* this means we have an extended size, which is the 64 bit value of
3712 * the next 8 bytes */
3713 length = QT_UINT64 (data + 8);
3714 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3724 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3726 guint32 version = 0;
3727 GstClockTime duration = 0;
3729 if (!gst_byte_reader_get_uint32_be (br, &version))
3734 if (!gst_byte_reader_get_uint64_be (br, &duration))
3739 if (!gst_byte_reader_get_uint32_be (br, &dur))
3744 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3745 qtdemux->duration = duration;
3751 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3757 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3758 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3760 if (!stream->parsed_trex && qtdemux->moov_node) {
3762 GstByteReader trex_data;
3764 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3766 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3769 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3771 /* skip version/flags */
3772 if (!gst_byte_reader_skip (&trex_data, 4))
3774 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3776 if (id != stream->track_id)
3778 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3780 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3782 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3784 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3787 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3788 "duration %d, size %d, flags 0x%x", stream->track_id,
3791 stream->parsed_trex = TRUE;
3792 stream->def_sample_description_index = sdi;
3793 stream->def_sample_duration = dur;
3794 stream->def_sample_size = size;
3795 stream->def_sample_flags = flags;
3798 /* iterate all siblings */
3799 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3805 *ds_duration = stream->def_sample_duration;
3806 *ds_size = stream->def_sample_size;
3807 *ds_flags = stream->def_sample_flags;
3809 /* even then, above values are better than random ... */
3810 if (G_UNLIKELY (!stream->parsed_trex)) {
3811 GST_WARNING_OBJECT (qtdemux,
3812 "failed to find fragment defaults for stream %d", stream->track_id);
3819 /* This method should be called whenever a more accurate duration might
3820 * have been found. It will update all relevant variables if/where needed
3823 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3827 GstClockTime prevdur;
3829 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3831 if (movdur > qtdemux->duration) {
3832 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3833 GST_DEBUG_OBJECT (qtdemux,
3834 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3835 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3836 qtdemux->duration = movdur;
3837 GST_DEBUG_OBJECT (qtdemux,
3838 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3839 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3840 GST_TIME_ARGS (qtdemux->segment.stop));
3841 if (qtdemux->segment.duration == prevdur) {
3842 /* If the current segment has duration/stop identical to previous duration
3843 * update them also (because they were set at that point in time with
3844 * the wrong duration */
3845 /* We convert the value *from* the timescale version to avoid rounding errors */
3846 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3847 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3848 qtdemux->segment.duration = fixeddur;
3849 qtdemux->segment.stop = fixeddur;
3853 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3854 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3856 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3857 if (movdur > stream->duration) {
3858 GST_DEBUG_OBJECT (qtdemux,
3859 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3860 GST_TIME_ARGS (duration));
3861 stream->duration = movdur;
3862 /* internal duration tracking state has been updated above, so */
3863 /* preserve an open-ended dummy segment rather than repeatedly updating
3864 * it and spamming downstream accordingly with segment events */
3865 if (stream->dummy_segment &&
3866 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3867 /* Update all dummy values to new duration */
3868 stream->segments[0].stop_time = duration;
3869 stream->segments[0].duration = duration;
3870 stream->segments[0].media_stop = duration;
3872 /* let downstream know we possibly have a new stop time */
3873 if (stream->segment_index != -1) {
3876 if (qtdemux->segment.rate >= 0) {
3877 pos = stream->segment.start;
3879 pos = stream->segment.stop;
3882 gst_qtdemux_stream_update_segment (qtdemux, stream,
3883 stream->segment_index, pos, NULL, NULL);
3891 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3892 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3893 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3894 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3897 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3899 gint32 data_offset = 0;
3900 guint32 flags = 0, first_flags = 0, samples_count = 0;
3903 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3904 QtDemuxSample *sample;
3905 gboolean ismv = FALSE;
3906 gint64 initial_offset;
3908 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3909 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3910 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3911 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3913 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3914 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3918 /* presence of stss or not can't really tell us much,
3919 * and flags and so on tend to be marginally reliable in these files */
3920 if (stream->subtype == FOURCC_soun) {
3921 GST_DEBUG_OBJECT (qtdemux,
3922 "sound track in fragmented file; marking all keyframes");
3923 stream->all_keyframe = TRUE;
3926 if (!gst_byte_reader_skip (trun, 1) ||
3927 !gst_byte_reader_get_uint24_be (trun, &flags))
3930 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3933 if (flags & TR_DATA_OFFSET) {
3934 /* note this is really signed */
3935 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3937 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3938 /* default base offset = first byte of moof */
3939 if (*base_offset == -1) {
3940 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3941 *base_offset = moof_offset;
3943 *running_offset = *base_offset + data_offset;
3945 /* if no offset at all, that would mean data starts at moof start,
3946 * which is a bit wrong and is ismv crappy way, so compensate
3947 * assuming data is in mdat following moof */
3948 if (*base_offset == -1) {
3949 *base_offset = moof_offset + moof_length + 8;
3950 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3953 if (*running_offset == -1)
3954 *running_offset = *base_offset;
3957 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3959 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3960 data_offset, flags, samples_count);
3962 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3963 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3964 GST_DEBUG_OBJECT (qtdemux,
3965 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3966 flags ^= TR_FIRST_SAMPLE_FLAGS;
3968 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3970 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3974 /* FIXME ? spec says other bits should also be checked to determine
3975 * entry size (and prefix size for that matter) */
3977 dur_offset = size_offset = 0;
3978 if (flags & TR_SAMPLE_DURATION) {
3979 GST_LOG_OBJECT (qtdemux, "entry duration present");
3980 dur_offset = entry_size;
3983 if (flags & TR_SAMPLE_SIZE) {
3984 GST_LOG_OBJECT (qtdemux, "entry size present");
3985 size_offset = entry_size;
3988 if (flags & TR_SAMPLE_FLAGS) {
3989 GST_LOG_OBJECT (qtdemux, "entry flags present");
3990 flags_offset = entry_size;
3993 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3994 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3995 ct_offset = entry_size;
3999 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
4001 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
4003 if (stream->n_samples + samples_count >=
4004 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
4007 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
4008 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
4009 (stream->n_samples + samples_count) *
4010 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
4012 /* create a new array of samples if it's the first sample parsed */
4013 if (stream->n_samples == 0) {
4014 g_assert (stream->samples == NULL);
4015 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
4016 /* or try to reallocate it with space enough to insert the new samples */
4018 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
4019 stream->n_samples + samples_count);
4020 if (stream->samples == NULL)
4023 if (qtdemux->fragment_start != -1) {
4024 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
4025 qtdemux->fragment_start = -1;
4027 if (stream->n_samples == 0) {
4028 if (decode_ts > 0) {
4029 timestamp = decode_ts;
4030 } else if (stream->pending_seek != NULL) {
4031 /* if we don't have a timestamp from a tfdt box, we'll use the one
4032 * from the mfra seek table */
4033 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
4034 GST_TIME_ARGS (stream->pending_seek->ts));
4036 /* FIXME: this is not fully correct, the timestamp refers to the random
4037 * access sample refered to in the tfra entry, which may not necessarily
4038 * be the first sample in the tfrag/trun (but hopefully/usually is) */
4039 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
4044 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4045 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
4046 GST_TIME_ARGS (gst_ts));
4048 /* subsequent fragments extend stream */
4050 stream->samples[stream->n_samples - 1].timestamp +
4051 stream->samples[stream->n_samples - 1].duration;
4053 /* If this is a GST_FORMAT_BYTES stream and there's a significant
4054 * difference (1 sec.) between decode_ts and timestamp, prefer the
4056 if (has_tfdt && !qtdemux->upstream_format_is_time
4057 && ABSDIFF (decode_ts, timestamp) >
4058 MAX (stream->duration_last_moof / 2,
4059 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
4060 GST_INFO_OBJECT (qtdemux,
4061 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
4062 ") are significantly different (more than %" GST_TIME_FORMAT
4063 "), using decode_ts",
4064 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
4065 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
4066 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
4067 MAX (stream->duration_last_moof / 2,
4068 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
4069 timestamp = decode_ts;
4072 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4073 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4074 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
4078 initial_offset = *running_offset;
4080 sample = stream->samples + stream->n_samples;
4081 for (i = 0; i < samples_count; i++) {
4082 guint32 dur, size, sflags, ct;
4084 /* first read sample data */
4085 if (flags & TR_SAMPLE_DURATION) {
4086 dur = QT_UINT32 (data + dur_offset);
4088 dur = d_sample_duration;
4090 if (flags & TR_SAMPLE_SIZE) {
4091 size = QT_UINT32 (data + size_offset);
4093 size = d_sample_size;
4095 if (flags & TR_FIRST_SAMPLE_FLAGS) {
4097 sflags = first_flags;
4099 sflags = d_sample_flags;
4101 } else if (flags & TR_SAMPLE_FLAGS) {
4102 sflags = QT_UINT32 (data + flags_offset);
4104 sflags = d_sample_flags;
4106 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4107 ct = QT_UINT32 (data + ct_offset);
4113 /* fill the sample information */
4114 sample->offset = *running_offset;
4115 sample->pts_offset = ct;
4116 sample->size = size;
4117 sample->timestamp = timestamp;
4118 sample->duration = dur;
4119 /* sample-is-difference-sample */
4120 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
4121 * now idea how it relates to bitfield other than massive LE/BE confusion */
4122 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
4123 *running_offset += size;
4125 stream->duration_moof += dur;
4129 /* Update total duration if needed */
4130 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
4132 /* Pre-emptively figure out size of mdat based on trun information.
4133 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
4134 * size, else we will still be able to use this when dealing with gap'ed
4136 qtdemux->mdatleft = *running_offset - initial_offset;
4137 qtdemux->mdatoffset = initial_offset;
4138 qtdemux->mdatsize = qtdemux->mdatleft;
4140 stream->n_samples += samples_count;
4141 stream->n_samples_moof += samples_count;
4143 if (stream->pending_seek != NULL)
4144 stream->pending_seek = NULL;
4150 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
4155 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
4161 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
4162 "be larger than %uMB (broken file?)", stream->n_samples,
4163 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
4168 /* find stream with @id */
4169 static inline QtDemuxStream *
4170 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
4172 QtDemuxStream *stream;
4176 if (G_UNLIKELY (!id)) {
4177 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
4181 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4182 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4183 if (stream->track_id == id)
4186 if (qtdemux->mss_mode) {
4187 /* mss should have only 1 stream anyway */
4188 return QTDEMUX_NTH_STREAM (qtdemux, 0);
4195 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
4196 guint32 * fragment_number)
4198 if (!gst_byte_reader_skip (mfhd, 4))
4200 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
4205 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
4211 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
4212 QtDemuxStream ** stream, guint32 * default_sample_duration,
4213 guint32 * default_sample_size, guint32 * default_sample_flags,
4214 gint64 * base_offset)
4217 guint32 track_id = 0;
4219 if (!gst_byte_reader_skip (tfhd, 1) ||
4220 !gst_byte_reader_get_uint24_be (tfhd, &flags))
4223 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4226 *stream = qtdemux_find_stream (qtdemux, track_id);
4227 if (G_UNLIKELY (!*stream))
4228 goto unknown_stream;
4230 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4231 *base_offset = qtdemux->moof_offset;
4233 if (flags & TF_BASE_DATA_OFFSET)
4234 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4237 /* obtain stream defaults */
4238 qtdemux_parse_trex (qtdemux, *stream,
4239 default_sample_duration, default_sample_size, default_sample_flags);
4241 (*stream)->stsd_sample_description_id =
4242 (*stream)->def_sample_description_index - 1;
4244 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4245 guint32 sample_description_index;
4246 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4248 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4251 if (qtdemux->mss_mode) {
4252 /* mss has no stsd entry */
4253 (*stream)->stsd_sample_description_id = 0;
4256 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4257 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4260 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4261 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4264 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4265 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4272 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4277 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
4283 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4284 guint64 * decode_time)
4286 guint32 version = 0;
4288 if (!gst_byte_reader_get_uint32_be (br, &version))
4293 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4296 guint32 dec_time = 0;
4297 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4299 *decode_time = dec_time;
4302 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4309 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4314 /* Returns a pointer to a GstStructure containing the properties of
4315 * the stream sample identified by @sample_index. The caller must unref
4316 * the returned object after use. Returns NULL if unsuccessful. */
4317 static GstStructure *
4318 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4319 QtDemuxStream * stream, guint sample_index)
4321 QtDemuxCencSampleSetInfo *info = NULL;
4323 g_return_val_if_fail (stream != NULL, NULL);
4324 g_return_val_if_fail (stream->protected, NULL);
4325 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4327 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4329 /* Currently, cenc properties for groups of samples are not supported, so
4330 * simply return a copy of the default sample properties */
4331 return gst_structure_copy (info->default_properties);
4334 /* Parses the sizes of sample auxiliary information contained within a stream,
4335 * as given in a saiz box. Returns array of sample_count guint8 size values,
4336 * or NULL on failure */
4338 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4339 GstByteReader * br, guint32 * sample_count)
4343 guint8 default_info_size;
4345 g_return_val_if_fail (qtdemux != NULL, NULL);
4346 g_return_val_if_fail (stream != NULL, NULL);
4347 g_return_val_if_fail (br != NULL, NULL);
4348 g_return_val_if_fail (sample_count != NULL, NULL);
4350 if (!gst_byte_reader_get_uint32_be (br, &flags))
4354 /* aux_info_type and aux_info_type_parameter are ignored */
4355 if (!gst_byte_reader_skip (br, 8))
4359 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4361 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4363 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4365 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4368 if (default_info_size == 0) {
4369 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4373 info_sizes = g_new (guint8, *sample_count);
4374 memset (info_sizes, default_info_size, *sample_count);
4380 /* Parses the offset of sample auxiliary information contained within a stream,
4381 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4383 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4384 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4389 guint32 aux_info_type = 0;
4390 guint32 aux_info_type_parameter = 0;
4391 guint32 entry_count;
4394 const guint8 *aux_info_type_data = NULL;
4396 g_return_val_if_fail (qtdemux != NULL, FALSE);
4397 g_return_val_if_fail (stream != NULL, FALSE);
4398 g_return_val_if_fail (br != NULL, FALSE);
4399 g_return_val_if_fail (offset != NULL, FALSE);
4401 if (!gst_byte_reader_get_uint8 (br, &version))
4404 if (!gst_byte_reader_get_uint24_be (br, &flags))
4409 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4411 aux_info_type = QT_FOURCC (aux_info_type_data);
4413 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4415 } else if (stream->protected) {
4416 aux_info_type = stream->protection_scheme_type;
4418 aux_info_type = CUR_STREAM (stream)->fourcc;
4422 *info_type = aux_info_type;
4423 if (info_type_parameter)
4424 *info_type_parameter = aux_info_type_parameter;
4426 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4427 "aux_info_type_parameter: %#06x",
4428 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4430 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4433 if (entry_count != 1) {
4434 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4439 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4441 *offset = (guint64) off_32;
4443 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4448 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4453 qtdemux_gst_structure_free (GstStructure * gststructure)
4456 gst_structure_free (gststructure);
4460 /* Parses auxiliary information relating to samples protected using Common
4461 * Encryption (cenc); the format of this information is defined in
4462 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
4464 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4465 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4467 QtDemuxCencSampleSetInfo *ss_info = NULL;
4470 GPtrArray *old_crypto_info = NULL;
4471 guint old_entries = 0;
4473 g_return_val_if_fail (qtdemux != NULL, FALSE);
4474 g_return_val_if_fail (stream != NULL, FALSE);
4475 g_return_val_if_fail (br != NULL, FALSE);
4476 g_return_val_if_fail (stream->protected, FALSE);
4477 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4479 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4481 if (ss_info->crypto_info) {
4482 old_crypto_info = ss_info->crypto_info;
4483 /* Count number of non-null entries remaining at the tail end */
4484 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4485 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4491 ss_info->crypto_info =
4492 g_ptr_array_new_full (sample_count + old_entries,
4493 (GDestroyNotify) qtdemux_gst_structure_free);
4495 /* We preserve old entries because we parse the next moof in advance
4496 * of consuming all samples from the previous moof, and otherwise
4497 * we'd discard the corresponding crypto info for the samples
4498 * from the previous fragment. */
4500 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4502 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4503 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4505 g_ptr_array_index (old_crypto_info, i) = NULL;
4509 if (old_crypto_info) {
4510 /* Everything now belongs to the new array */
4511 g_ptr_array_free (old_crypto_info, TRUE);
4514 for (i = 0; i < sample_count; ++i) {
4515 GstStructure *properties;
4516 guint16 n_subsamples = 0;
4521 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4522 if (properties == NULL) {
4523 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4526 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4527 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4528 gst_structure_free (properties);
4531 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
4532 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4533 gst_structure_free (properties);
4536 buf = gst_buffer_new_wrapped (data, iv_size);
4537 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4538 gst_buffer_unref (buf);
4539 size = info_sizes[i];
4540 if (size > iv_size) {
4541 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4542 || !(n_subsamples > 0)) {
4543 gst_structure_free (properties);
4544 GST_ERROR_OBJECT (qtdemux,
4545 "failed to get subsample count for sample %u", i);
4548 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4549 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4550 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4552 gst_structure_free (properties);
4555 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4557 gst_structure_free (properties);
4560 gst_structure_set (properties,
4561 "subsample_count", G_TYPE_UINT, n_subsamples,
4562 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4563 gst_buffer_unref (buf);
4565 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4567 g_ptr_array_add (ss_info->crypto_info, properties);
4572 /* Converts a UUID in raw byte form to a string representation, as defined in
4573 * RFC 4122. The caller takes ownership of the returned string and is
4574 * responsible for freeing it after use. */
4576 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4578 const guint8 *uuid = (const guint8 *) uuid_bytes;
4580 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4581 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4582 uuid[0], uuid[1], uuid[2], uuid[3],
4583 uuid[4], uuid[5], uuid[6], uuid[7],
4584 uuid[8], uuid[9], uuid[10], uuid[11],
4585 uuid[12], uuid[13], uuid[14], uuid[15]);
4588 /* Parses a Protection System Specific Header box (pssh), as defined in the
4589 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4590 * information needed by a specific content protection system in order to
4591 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4594 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4596 gchar *sysid_string;
4597 guint32 pssh_size = QT_UINT32 (node->data);
4598 GstBuffer *pssh = NULL;
4599 GstEvent *event = NULL;
4600 guint32 parent_box_type;
4603 if (G_UNLIKELY (pssh_size < 32U)) {
4604 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4609 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4611 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4613 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4614 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4615 gst_buffer_get_size (pssh));
4617 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4619 /* Push an event containing the pssh box onto the queues of all streams. */
4620 event = gst_event_new_protection (sysid_string, pssh,
4621 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4622 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4623 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4624 GST_TRACE_OBJECT (qtdemux,
4625 "adding protection event for stream %s and system %s",
4626 stream->stream_id, sysid_string);
4627 g_queue_push_tail (&stream->protection_scheme_event_queue,
4628 gst_event_ref (event));
4630 g_free (sysid_string);
4631 gst_event_unref (event);
4632 gst_buffer_unref (pssh);
4637 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4638 guint64 moof_offset, QtDemuxStream * stream)
4640 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4642 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4643 GNode *saiz_node, *saio_node, *pssh_node;
4644 GstByteReader saiz_data, saio_data;
4645 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4646 gint64 base_offset, running_offset;
4648 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4650 /* NOTE @stream ignored */
4652 moof_node = g_node_new ((guint8 *) buffer);
4653 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4654 qtdemux_node_dump (qtdemux, moof_node);
4656 /* Get fragment number from mfhd and check it's valid */
4658 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4659 if (mfhd_node == NULL)
4661 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4663 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4665 /* unknown base_offset to start with */
4666 base_offset = running_offset = -1;
4667 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4669 guint64 decode_time = 0;
4671 /* Fragment Header node */
4673 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4677 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4678 &ds_size, &ds_flags, &base_offset))
4681 /* The following code assumes at most a single set of sample auxiliary
4682 * data in the fragment (consisting of a saiz box and a corresponding saio
4683 * box); in theory, however, there could be multiple sets of sample
4684 * auxiliary data in a fragment. */
4686 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4689 guint32 info_type = 0;
4691 guint32 info_type_parameter = 0;
4693 g_free (qtdemux->cenc_aux_info_sizes);
4695 qtdemux->cenc_aux_info_sizes =
4696 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4697 &qtdemux->cenc_aux_sample_count);
4698 if (qtdemux->cenc_aux_info_sizes == NULL) {
4699 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4703 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4706 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4707 g_free (qtdemux->cenc_aux_info_sizes);
4708 qtdemux->cenc_aux_info_sizes = NULL;
4712 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4713 &info_type, &info_type_parameter, &offset))) {
4714 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4715 g_free (qtdemux->cenc_aux_info_sizes);
4716 qtdemux->cenc_aux_info_sizes = NULL;
4719 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4720 offset += (guint64) (base_offset - qtdemux->moof_offset);
4721 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4723 if (offset > length) {
4724 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4725 qtdemux->cenc_aux_info_offset = offset;
4727 gst_byte_reader_init (&br, buffer + offset, length - offset);
4728 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4729 qtdemux->cenc_aux_info_sizes,
4730 qtdemux->cenc_aux_sample_count)) {
4731 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4732 g_free (qtdemux->cenc_aux_info_sizes);
4733 qtdemux->cenc_aux_info_sizes = NULL;
4741 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4744 /* We'll use decode_time to interpolate timestamps
4745 * in case the input timestamps are missing */
4746 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4748 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4749 " (%" GST_TIME_FORMAT ")", decode_time,
4750 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4751 decode_time) : GST_CLOCK_TIME_NONE));
4753 /* Discard the fragment buffer timestamp info to avoid using it.
4754 * Rely on tfdt instead as it is more accurate than the timestamp
4755 * that is fetched from a manifest/playlist and is usually
4757 qtdemux->fragment_start = -1;
4760 if (G_UNLIKELY (!stream)) {
4761 /* we lost track of offset, we'll need to regain it,
4762 * but can delay complaining until later or avoid doing so altogether */
4766 if (G_UNLIKELY (base_offset < -1))
4769 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4771 if (!qtdemux->pullbased) {
4772 /* Sample tables can grow enough to be problematic if the system memory
4773 * is very low (e.g. embedded devices) and the videos very long
4774 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4775 * Fortunately, we can easily discard them for each new fragment when
4776 * we know qtdemux will not receive seeks outside of the current fragment.
4777 * adaptivedemux honors this assumption.
4778 * This optimization is also useful for applications that use qtdemux as
4779 * a push-based simple demuxer, like Media Source Extensions. */
4780 gst_qtdemux_stream_flush_samples_data (stream);
4783 /* initialise moof sample data */
4784 stream->n_samples_moof = 0;
4785 stream->duration_last_moof = stream->duration_moof;
4786 stream->duration_moof = 0;
4788 /* Track Run node */
4790 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4793 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4794 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4795 &running_offset, decode_time, (tfdt_node != NULL));
4796 /* iterate all siblings */
4797 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4801 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4803 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4804 guint32 box_length = QT_UINT32 (uuid_buffer);
4806 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4809 /* if no new base_offset provided for next traf,
4810 * base is end of current traf */
4811 base_offset = running_offset;
4812 running_offset = -1;
4814 if (stream->n_samples_moof && stream->duration_moof)
4815 stream->new_caps = TRUE;
4818 /* iterate all siblings */
4819 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4822 /* parse any protection system info */
4823 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4825 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4826 qtdemux_parse_pssh (qtdemux, pssh_node);
4827 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4830 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4831 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4833 /* Unless the user has explictly requested another seek, perform an
4834 * internal seek to the time specified in the tfdt.
4836 * This way if the user opens a file where the first tfdt is 1 hour
4837 * into the presentation, they will not have to wait 1 hour for run
4838 * time to catch up and actual playback to start. */
4841 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4842 "performing an internal seek to %" GST_TIME_FORMAT,
4843 GST_TIME_ARGS (min_dts));
4845 qtdemux->segment.start = min_dts;
4846 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4848 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4849 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4850 stream->time_position = min_dts;
4853 /* Before this code was run a segment was already sent when the moov was
4854 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4855 * be emitted after a moov, and we can emit a second segment anyway for
4856 * special cases like this. */
4857 qtdemux->need_segment = TRUE;
4860 qtdemux->first_moof_already_parsed = TRUE;
4862 g_node_destroy (moof_node);
4867 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4872 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4877 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4882 g_node_destroy (moof_node);
4883 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4884 (_("This file is corrupt and cannot be played.")), (NULL));
4890 /* might be used if some day we actually use mfra & co
4891 * for random access to fragments,
4892 * but that will require quite some modifications and much less relying
4893 * on a sample array */
4897 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4899 QtDemuxStream *stream;
4900 guint32 ver_flags, track_id, len, num_entries, i;
4901 guint value_size, traf_size, trun_size, sample_size;
4902 guint64 time = 0, moof_offset = 0;
4904 GstBuffer *buf = NULL;
4909 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4911 if (!gst_byte_reader_skip (&tfra, 8))
4914 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4917 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4918 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4919 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4922 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4924 stream = qtdemux_find_stream (qtdemux, track_id);
4926 goto unknown_trackid;
4928 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4929 sample_size = (len & 3) + 1;
4930 trun_size = ((len & 12) >> 2) + 1;
4931 traf_size = ((len & 48) >> 4) + 1;
4933 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4934 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4936 if (num_entries == 0)
4939 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4940 value_size + value_size + traf_size + trun_size + sample_size))
4943 g_free (stream->ra_entries);
4944 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4945 stream->n_ra_entries = num_entries;
4947 for (i = 0; i < num_entries; i++) {
4948 qt_atom_parser_get_offset (&tfra, value_size, &time);
4949 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4950 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4951 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4952 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4954 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4956 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4957 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4959 stream->ra_entries[i].ts = time;
4960 stream->ra_entries[i].moof_offset = moof_offset;
4962 /* don't want to go through the entire file and read all moofs at startup */
4964 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4965 if (ret != GST_FLOW_OK)
4967 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4968 moof_offset, stream);
4969 gst_buffer_unref (buf);
4973 check_update_duration (qtdemux, time);
4980 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4985 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4990 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4996 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4998 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4999 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
5000 GstBuffer *mfro = NULL, *mfra = NULL;
5002 gboolean ret = FALSE;
5003 GNode *mfra_node, *tfra_node;
5004 guint64 mfra_offset = 0;
5005 guint32 fourcc, mfra_size;
5008 /* query upstream size in bytes */
5009 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
5010 goto size_query_failed;
5012 /* mfro box should be at the very end of the file */
5013 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
5014 if (flow != GST_FLOW_OK)
5017 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
5019 fourcc = QT_FOURCC (mfro_map.data + 4);
5020 if (fourcc != FOURCC_mfro)
5023 GST_INFO_OBJECT (qtdemux, "Found mfro box");
5024 if (mfro_map.size < 16)
5025 goto invalid_mfro_size;
5027 mfra_size = QT_UINT32 (mfro_map.data + 12);
5028 if (mfra_size >= len)
5029 goto invalid_mfra_size;
5031 mfra_offset = len - mfra_size;
5033 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
5034 mfra_offset, mfra_size);
5036 /* now get and parse mfra box */
5037 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
5038 if (flow != GST_FLOW_OK)
5041 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
5043 mfra_node = g_node_new ((guint8 *) mfra_map.data);
5044 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
5046 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
5049 qtdemux_parse_tfra (qtdemux, tfra_node);
5050 /* iterate all siblings */
5051 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
5053 g_node_destroy (mfra_node);
5055 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
5061 if (mfro_map.memory != NULL)
5062 gst_buffer_unmap (mfro, &mfro_map);
5063 gst_buffer_unref (mfro);
5066 if (mfra_map.memory != NULL)
5067 gst_buffer_unmap (mfra, &mfra_map);
5068 gst_buffer_unref (mfra);
5075 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
5080 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
5085 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
5090 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
5096 add_offset (guint64 offset, guint64 advance)
5098 /* Avoid 64-bit overflow by clamping */
5099 if (offset > G_MAXUINT64 - advance)
5101 return offset + advance;
5104 static GstFlowReturn
5105 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
5109 GstBuffer *buf = NULL;
5110 GstFlowReturn ret = GST_FLOW_OK;
5111 guint64 cur_offset = qtdemux->offset;
5114 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
5115 if (G_UNLIKELY (ret != GST_FLOW_OK))
5117 gst_buffer_map (buf, &map, GST_MAP_READ);
5118 if (G_LIKELY (map.size >= 8))
5119 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
5120 gst_buffer_unmap (buf, &map);
5121 gst_buffer_unref (buf);
5123 /* maybe we already got most we needed, so only consider this eof */
5124 if (G_UNLIKELY (length == 0)) {
5125 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5126 (_("Invalid atom size.")),
5127 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
5128 GST_FOURCC_ARGS (fourcc)));
5135 /* record for later parsing when needed */
5136 if (!qtdemux->moof_offset) {
5137 qtdemux->moof_offset = qtdemux->offset;
5139 if (qtdemux_pull_mfro_mfra (qtdemux)) {
5142 qtdemux->offset += length; /* skip moof and keep going */
5144 if (qtdemux->got_moov) {
5145 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
5157 GST_LOG_OBJECT (qtdemux,
5158 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5159 GST_FOURCC_ARGS (fourcc), cur_offset);
5160 qtdemux->offset = add_offset (qtdemux->offset, length);
5165 GstBuffer *moov = NULL;
5167 if (qtdemux->got_moov) {
5168 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
5169 qtdemux->offset = add_offset (qtdemux->offset, length);
5173 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
5174 if (ret != GST_FLOW_OK)
5176 gst_buffer_map (moov, &map, GST_MAP_READ);
5178 if (length != map.size) {
5179 /* Some files have a 'moov' atom at the end of the file which contains
5180 * a terminal 'free' atom where the body of the atom is missing.
5181 * Check for, and permit, this special case.
5183 if (map.size >= 8) {
5184 guint8 *final_data = map.data + (map.size - 8);
5185 guint32 final_length = QT_UINT32 (final_data);
5186 guint32 final_fourcc = QT_FOURCC (final_data + 4);
5188 if (final_fourcc == FOURCC_free
5189 && map.size + final_length - 8 == length) {
5190 /* Ok, we've found that special case. Allocate a new buffer with
5191 * that free atom actually present. */
5192 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
5193 gst_buffer_fill (newmoov, 0, map.data, map.size);
5194 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
5195 gst_buffer_unmap (moov, &map);
5196 gst_buffer_unref (moov);
5198 gst_buffer_map (moov, &map, GST_MAP_READ);
5203 if (length != map.size) {
5204 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5205 (_("This file is incomplete and cannot be played.")),
5206 ("We got less than expected (received %" G_GSIZE_FORMAT
5207 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
5208 (guint) length, cur_offset));
5209 gst_buffer_unmap (moov, &map);
5210 gst_buffer_unref (moov);
5211 ret = GST_FLOW_ERROR;
5214 qtdemux->offset += length;
5216 qtdemux_parse_moov (qtdemux, map.data, length);
5217 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
5219 qtdemux_parse_tree (qtdemux);
5220 if (qtdemux->moov_node_compressed) {
5221 g_node_destroy (qtdemux->moov_node_compressed);
5222 g_free (qtdemux->moov_node->data);
5224 qtdemux->moov_node_compressed = NULL;
5225 g_node_destroy (qtdemux->moov_node);
5226 qtdemux->moov_node = NULL;
5227 gst_buffer_unmap (moov, &map);
5228 gst_buffer_unref (moov);
5229 qtdemux->got_moov = TRUE;
5235 GstBuffer *ftyp = NULL;
5237 /* extract major brand; might come in handy for ISO vs QT issues */
5238 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
5239 if (ret != GST_FLOW_OK)
5241 qtdemux->offset += length;
5242 gst_buffer_map (ftyp, &map, GST_MAP_READ);
5243 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
5244 gst_buffer_unmap (ftyp, &map);
5245 gst_buffer_unref (ftyp);
5250 GstBuffer *uuid = NULL;
5252 /* uuid are extension atoms */
5253 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
5254 if (ret != GST_FLOW_OK)
5256 qtdemux->offset += length;
5257 gst_buffer_map (uuid, &map, GST_MAP_READ);
5258 qtdemux_parse_uuid (qtdemux, map.data, map.size);
5259 gst_buffer_unmap (uuid, &map);
5260 gst_buffer_unref (uuid);
5265 GstBuffer *sidx = NULL;
5266 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
5267 if (ret != GST_FLOW_OK)
5269 qtdemux->offset += length;
5270 gst_buffer_map (sidx, &map, GST_MAP_READ);
5271 qtdemux_parse_sidx (qtdemux, map.data, map.size);
5272 gst_buffer_unmap (sidx, &map);
5273 gst_buffer_unref (sidx);
5278 GstBuffer *unknown = NULL;
5280 GST_LOG_OBJECT (qtdemux,
5281 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5282 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5284 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5285 if (ret != GST_FLOW_OK)
5287 gst_buffer_map (unknown, &map, GST_MAP_READ);
5288 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5289 gst_buffer_unmap (unknown, &map);
5290 gst_buffer_unref (unknown);
5291 qtdemux->offset += length;
5297 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5298 /* digested all data, show what we have */
5299 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5300 if (qtdemux->spherical_metadata)
5301 _send_spherical_metadata_msg_to_bus (qtdemux);
5302 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5303 qtdemux_prepare_streams (qtdemux);
5304 QTDEMUX_EXPOSE_LOCK (qtdemux);
5305 ret = qtdemux_expose_streams (qtdemux);
5306 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
5308 qtdemux->state = QTDEMUX_STATE_MOVIE;
5309 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5316 /* Seeks to the previous keyframe of the indexed stream and
5317 * aligns other streams with respect to the keyframe timestamp
5318 * of indexed stream. Only called in case of Reverse Playback
5320 static GstFlowReturn
5321 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5323 guint32 seg_idx = 0, k_index = 0;
5324 guint32 ref_seg_idx, ref_k_index;
5325 GstClockTime k_pos = 0, last_stop = 0;
5326 QtDemuxSegment *seg = NULL;
5327 QtDemuxStream *ref_str = NULL;
5328 guint64 seg_media_start_mov; /* segment media start time in mov format */
5332 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5333 * and finally align all the other streams on that timestamp with their
5334 * respective keyframes */
5335 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5336 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5338 /* No candidate yet, take the first stream */
5344 /* So that stream has a segment, we prefer video streams */
5345 if (str->subtype == FOURCC_vide) {
5351 if (G_UNLIKELY (!ref_str)) {
5352 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5356 if (G_UNLIKELY (!ref_str->from_sample)) {
5357 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5361 /* So that stream has been playing from from_sample to to_sample. We will
5362 * get the timestamp of the previous sample and search for a keyframe before
5363 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5364 if (ref_str->subtype == FOURCC_vide) {
5365 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5366 ref_str->from_sample - 1, FALSE);
5368 if (ref_str->from_sample >= 10)
5369 k_index = ref_str->from_sample - 10;
5375 ref_str->samples[k_index].timestamp +
5376 ref_str->samples[k_index].pts_offset;
5378 /* get current segment for that stream */
5379 seg = &ref_str->segments[ref_str->segment_index];
5380 /* Use segment start in original timescale for comparisons */
5381 seg_media_start_mov = seg->trak_media_start;
5383 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5384 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
5385 k_index, target_ts, seg_media_start_mov,
5386 GST_TIME_ARGS (seg->media_start));
5388 /* Crawl back through segments to find the one containing this I frame */
5389 while (target_ts < seg_media_start_mov) {
5390 GST_DEBUG_OBJECT (qtdemux,
5391 "keyframe position (sample %u) is out of segment %u " " target %"
5392 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5393 ref_str->segment_index, target_ts, seg_media_start_mov);
5395 if (G_UNLIKELY (!ref_str->segment_index)) {
5396 /* Reached first segment, let's consider it's EOS */
5399 ref_str->segment_index--;
5400 seg = &ref_str->segments[ref_str->segment_index];
5401 /* Use segment start in original timescale for comparisons */
5402 seg_media_start_mov = seg->trak_media_start;
5404 /* Calculate time position of the keyframe and where we should stop */
5406 QTSTREAMTIME_TO_GSTTIME (ref_str,
5407 target_ts - seg->trak_media_start) + seg->time;
5409 QTSTREAMTIME_TO_GSTTIME (ref_str,
5410 ref_str->samples[ref_str->from_sample].timestamp -
5411 seg->trak_media_start) + seg->time;
5413 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5414 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5415 k_index, GST_TIME_ARGS (k_pos));
5417 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5418 qtdemux->segment.position = last_stop;
5419 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5420 GST_TIME_ARGS (last_stop));
5422 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5423 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5427 ref_seg_idx = ref_str->segment_index;
5428 ref_k_index = k_index;
5430 /* Align them all on this */
5431 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5433 GstClockTime seg_time = 0;
5434 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5436 /* aligning reference stream again might lead to backing up to yet another
5437 * keyframe (due to timestamp rounding issues),
5438 * potentially putting more load on downstream; so let's try to avoid */
5439 if (str == ref_str) {
5440 seg_idx = ref_seg_idx;
5441 seg = &str->segments[seg_idx];
5442 k_index = ref_k_index;
5443 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5444 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5446 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5447 GST_DEBUG_OBJECT (qtdemux,
5448 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5449 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5451 /* get segment and time in the segment */
5452 seg = &str->segments[seg_idx];
5453 seg_time = k_pos - seg->time;
5455 /* get the media time in the segment.
5456 * No adjustment for empty "filler" segments */
5457 if (seg->media_start != GST_CLOCK_TIME_NONE)
5458 seg_time += seg->media_start;
5460 /* get the index of the sample with media time */
5461 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5462 GST_DEBUG_OBJECT (qtdemux,
5463 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5464 GST_TIME_ARGS (seg_time), index);
5466 /* find previous keyframe */
5467 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5470 /* Remember until where we want to go */
5471 str->to_sample = str->from_sample - 1;
5472 /* Define our time position */
5474 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5475 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5476 if (seg->media_start != GST_CLOCK_TIME_NONE)
5477 str->time_position -= seg->media_start;
5479 /* Now seek back in time */
5480 gst_qtdemux_move_stream (qtdemux, str, k_index);
5481 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5482 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5483 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5489 return GST_FLOW_EOS;
5493 * Gets the current qt segment start, stop and position for the
5494 * given time offset. This is used in update_segment()
5497 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5498 QtDemuxStream * stream, GstClockTime offset,
5499 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5501 GstClockTime seg_time;
5502 GstClockTime start, stop, time;
5503 QtDemuxSegment *segment;
5505 segment = &stream->segments[stream->segment_index];
5507 /* get time in this segment */
5508 seg_time = (offset - segment->time) * segment->rate;
5510 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5511 GST_TIME_ARGS (seg_time));
5513 if (G_UNLIKELY (seg_time > segment->duration)) {
5514 GST_LOG_OBJECT (stream->pad,
5515 "seg_time > segment->duration %" GST_TIME_FORMAT,
5516 GST_TIME_ARGS (segment->duration));
5517 seg_time = segment->duration;
5520 /* qtdemux->segment.stop is in outside-time-realm, whereas
5521 * segment->media_stop is in track-time-realm.
5523 * In order to compare the two, we need to bring segment.stop
5524 * into the track-time-realm
5526 * FIXME - does this comment still hold? Don't see any conversion here */
5528 stop = qtdemux->segment.stop;
5529 if (stop == GST_CLOCK_TIME_NONE)
5530 stop = qtdemux->segment.duration;
5531 if (stop == GST_CLOCK_TIME_NONE)
5532 stop = segment->media_stop;
5535 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5537 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5538 start = segment->time + seg_time;
5540 stop = start - seg_time + segment->duration;
5541 } else if (qtdemux->segment.rate >= 0) {
5542 start = MIN (segment->media_start + seg_time, stop);
5545 if (segment->media_start >= qtdemux->segment.start) {
5546 time = segment->time;
5548 time = segment->time + (qtdemux->segment.start - segment->media_start);
5551 start = MAX (segment->media_start, qtdemux->segment.start);
5552 stop = MIN (segment->media_start + seg_time, stop);
5561 * Updates the qt segment used for the stream and pushes a new segment event
5562 * downstream on this stream's pad.
5565 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5566 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5567 GstClockTime * _stop)
5569 QtDemuxSegment *segment;
5570 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5574 /* update the current segment */
5575 stream->segment_index = seg_idx;
5577 /* get the segment */
5578 segment = &stream->segments[seg_idx];
5580 if (G_UNLIKELY (offset < segment->time)) {
5581 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5582 GST_TIME_ARGS (segment->time));
5586 /* segment lies beyond total indicated duration */
5587 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5588 segment->time > qtdemux->segment.duration)) {
5589 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5590 " < segment->time %" GST_TIME_FORMAT,
5591 GST_TIME_ARGS (qtdemux->segment.duration),
5592 GST_TIME_ARGS (segment->time));
5596 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5597 &start, &stop, &time);
5599 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5600 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5601 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5603 /* combine global rate with that of the segment */
5604 rate = segment->rate * qtdemux->segment.rate;
5606 /* Copy flags from main segment */
5607 stream->segment.flags = qtdemux->segment.flags;
5609 /* update the segment values used for clipping */
5610 stream->segment.offset = qtdemux->segment.offset;
5611 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5612 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5613 stream->segment.rate = rate;
5614 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5615 stream->cslg_shift);
5616 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5617 stream->cslg_shift);
5618 stream->segment.time = time;
5619 stream->segment.position = stream->segment.start;
5621 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5624 /* now prepare and send the segment */
5626 event = gst_event_new_segment (&stream->segment);
5627 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5628 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5630 gst_pad_push_event (stream->pad, event);
5631 /* assume we can send more data now */
5632 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5633 /* clear to send tags on this pad now */
5634 gst_qtdemux_push_tags (qtdemux, stream);
5645 /* activate the given segment number @seg_idx of @stream at time @offset.
5646 * @offset is an absolute global position over all the segments.
5648 * This will push out a NEWSEGMENT event with the right values and
5649 * position the stream index to the first decodable sample before
5653 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5654 guint32 seg_idx, GstClockTime offset)
5656 QtDemuxSegment *segment;
5657 guint32 index, kf_index;
5658 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5660 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5661 seg_idx, GST_TIME_ARGS (offset));
5663 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5667 segment = &stream->segments[stream->segment_index];
5669 /* in the fragmented case, we pick a fragment that starts before our
5670 * desired position and rely on downstream to wait for a keyframe
5671 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5672 * tfra entries tells us which trun/sample the key unit is in, but we don't
5673 * make use of this additional information at the moment) */
5674 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5675 stream->to_sample = G_MAXUINT32;
5678 /* well, it will be taken care of below */
5679 qtdemux->fragmented_seek_pending = FALSE;
5680 /* FIXME ideally the do_fragmented_seek can be done right here,
5681 * rather than at loop level
5682 * (which might even allow handling edit lists in a fragmented file) */
5685 /* We don't need to look for a sample in push-based */
5686 if (!qtdemux->pullbased)
5689 /* and move to the keyframe before the indicated media time of the
5691 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5692 if (qtdemux->segment.rate >= 0) {
5693 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5694 stream->to_sample = G_MAXUINT32;
5695 GST_DEBUG_OBJECT (stream->pad,
5696 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5697 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5698 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5700 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5701 stream->to_sample = index;
5702 GST_DEBUG_OBJECT (stream->pad,
5703 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5704 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5705 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5708 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5709 "this is an empty segment");
5713 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5714 * encountered an error and printed a message so we return appropriately */
5718 /* we're at the right spot */
5719 if (index == stream->sample_index) {
5720 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5724 /* find keyframe of the target index */
5725 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5728 /* indent does stupid stuff with stream->samples[].timestamp */
5730 /* if we move forwards, we don't have to go back to the previous
5731 * keyframe since we already sent that. We can also just jump to
5732 * the keyframe right before the target index if there is one. */
5733 if (index > stream->sample_index) {
5734 /* moving forwards check if we move past a keyframe */
5735 if (kf_index > stream->sample_index) {
5736 GST_DEBUG_OBJECT (stream->pad,
5737 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5738 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5739 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5740 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5742 GST_DEBUG_OBJECT (stream->pad,
5743 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5744 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5745 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5748 GST_DEBUG_OBJECT (stream->pad,
5749 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5750 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5751 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5752 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5760 /* prepare to get the current sample of @stream, getting essential values.
5762 * This function will also prepare and send the segment when needed.
5764 * Return FALSE if the stream is EOS.
5769 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5770 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5771 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5772 gboolean * keyframe)
5774 QtDemuxSample *sample;
5775 GstClockTime time_position;
5778 g_return_val_if_fail (stream != NULL, FALSE);
5780 time_position = stream->time_position;
5781 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5784 seg_idx = stream->segment_index;
5785 if (G_UNLIKELY (seg_idx == -1)) {
5786 /* find segment corresponding to time_position if we are looking
5788 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5791 /* different segment, activate it, sample_index will be set. */
5792 if (G_UNLIKELY (stream->segment_index != seg_idx))
5793 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5795 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5796 segments[stream->segment_index]))) {
5797 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5799 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5800 " prepare empty sample");
5803 *pts = *dts = time_position;
5804 *duration = seg->duration - (time_position - seg->time);
5811 if (stream->sample_index == -1)
5812 stream->sample_index = 0;
5814 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5815 stream->sample_index, stream->n_samples);
5817 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5818 if (!qtdemux->fragmented)
5821 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5825 GST_OBJECT_LOCK (qtdemux);
5826 flow = qtdemux_add_fragmented_samples (qtdemux);
5827 GST_OBJECT_UNLOCK (qtdemux);
5829 if (flow != GST_FLOW_OK)
5832 while (stream->sample_index >= stream->n_samples);
5835 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5836 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5837 stream->sample_index);
5841 /* now get the info for the sample we're at */
5842 sample = &stream->samples[stream->sample_index];
5844 *dts = QTSAMPLE_DTS (stream, sample);
5845 *pts = QTSAMPLE_PTS (stream, sample);
5846 *offset = sample->offset;
5847 *size = sample->size;
5848 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5849 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5856 stream->time_position = GST_CLOCK_TIME_NONE;
5861 /* move to the next sample in @stream.
5863 * Moves to the next segment when needed.
5866 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5868 QtDemuxSample *sample;
5869 QtDemuxSegment *segment;
5871 /* get current segment */
5872 segment = &stream->segments[stream->segment_index];
5874 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5875 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5879 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5880 /* Mark the stream as EOS */
5881 GST_DEBUG_OBJECT (qtdemux,
5882 "reached max allowed sample %u, mark EOS", stream->to_sample);
5883 stream->time_position = GST_CLOCK_TIME_NONE;
5887 /* move to next sample */
5888 stream->sample_index++;
5889 stream->offset_in_sample = 0;
5891 /* reached the last sample, we need the next segment */
5892 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5895 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5896 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5897 stream->sample_index);
5901 /* get next sample */
5902 sample = &stream->samples[stream->sample_index];
5904 /* see if we are past the segment */
5905 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5908 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5909 /* inside the segment, update time_position, looks very familiar to
5910 * GStreamer segments, doesn't it? */
5911 stream->time_position =
5912 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5914 /* not yet in segment, time does not yet increment. This means
5915 * that we are still prerolling keyframes to the decoder so it can
5916 * decode the first sample of the segment. */
5917 stream->time_position = segment->time;
5921 /* move to the next segment */
5924 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5926 if (stream->segment_index == stream->n_segments - 1) {
5927 /* are we at the end of the last segment, we're EOS */
5928 stream->time_position = GST_CLOCK_TIME_NONE;
5930 /* else we're only at the end of the current segment */
5931 stream->time_position = segment->stop_time;
5933 /* make sure we select a new segment */
5935 /* accumulate previous segments */
5936 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5937 stream->accumulated_base +=
5938 (stream->segment.stop -
5939 stream->segment.start) / ABS (stream->segment.rate);
5941 stream->segment_index = -1;
5946 gst_qtdemux_sync_streams (GstQTDemux * demux)
5950 if (QTDEMUX_N_STREAMS (demux) <= 1)
5953 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5954 QtDemuxStream *stream;
5955 GstClockTime end_time;
5957 stream = QTDEMUX_NTH_STREAM (demux, i);
5962 /* TODO advance time on subtitle streams here, if any some day */
5964 /* some clips/trailers may have unbalanced streams at the end,
5965 * so send EOS on shorter stream to prevent stalling others */
5967 /* do not mess with EOS if SEGMENT seeking */
5968 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5971 if (demux->pullbased) {
5972 /* loop mode is sample time based */
5973 if (!STREAM_IS_EOS (stream))
5976 /* push mode is byte position based */
5977 if (stream->n_samples &&
5978 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5982 if (stream->sent_eos)
5985 /* only act if some gap */
5986 end_time = stream->segments[stream->n_segments - 1].stop_time;
5987 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5988 ", stream end: %" GST_TIME_FORMAT,
5989 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5990 if (GST_CLOCK_TIME_IS_VALID (end_time)
5991 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5994 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5995 GST_PAD_NAME (stream->pad));
5996 stream->sent_eos = TRUE;
5997 event = gst_event_new_eos ();
5998 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5999 gst_event_set_seqnum (event, demux->segment_seqnum);
6000 gst_pad_push_event (stream->pad, event);
6005 /* EOS and NOT_LINKED need to be combined. This means that we return:
6007 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
6008 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
6010 static GstFlowReturn
6011 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
6014 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
6017 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
6020 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
6022 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
6026 /* the input buffer metadata must be writable. Returns NULL when the buffer is
6027 * completely clipped
6029 * Should be used only with raw buffers */
6031 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6034 guint64 start, stop, cstart, cstop, diff;
6035 GstClockTime pts, duration;
6037 gint num_rate, denom_rate;
6042 osize = size = gst_buffer_get_size (buf);
6045 /* depending on the type, setup the clip parameters */
6046 if (stream->subtype == FOURCC_soun) {
6047 frame_size = CUR_STREAM (stream)->bytes_per_frame;
6048 num_rate = GST_SECOND;
6049 denom_rate = (gint) CUR_STREAM (stream)->rate;
6051 } else if (stream->subtype == FOURCC_vide) {
6053 num_rate = CUR_STREAM (stream)->fps_n;
6054 denom_rate = CUR_STREAM (stream)->fps_d;
6059 if (frame_size <= 0)
6060 goto bad_frame_size;
6062 /* we can only clip if we have a valid pts */
6063 pts = GST_BUFFER_PTS (buf);
6064 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
6067 duration = GST_BUFFER_DURATION (buf);
6069 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
6071 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
6075 stop = start + duration;
6077 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
6078 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
6081 /* see if some clipping happened */
6082 diff = cstart - start;
6088 /* bring clipped time to samples and to bytes */
6089 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6092 GST_DEBUG_OBJECT (qtdemux,
6093 "clipping start to %" GST_TIME_FORMAT " %"
6094 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
6100 diff = stop - cstop;
6105 /* bring clipped time to samples and then to bytes */
6106 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6108 GST_DEBUG_OBJECT (qtdemux,
6109 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
6110 " bytes", GST_TIME_ARGS (cstop), diff);
6115 if (offset != 0 || size != osize)
6116 gst_buffer_resize (buf, offset, size);
6118 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
6119 GST_BUFFER_PTS (buf) = pts;
6120 GST_BUFFER_DURATION (buf) = duration;
6124 /* dropped buffer */
6127 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
6132 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
6137 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
6142 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
6143 gst_buffer_unref (buf);
6149 gst_qtdemux_align_buffer (GstQTDemux * demux,
6150 GstBuffer * buffer, gsize alignment)
6154 gst_buffer_map (buffer, &map, GST_MAP_READ);
6156 if (map.size < sizeof (guintptr)) {
6157 gst_buffer_unmap (buffer, &map);
6161 if (((guintptr) map.data) & (alignment - 1)) {
6162 GstBuffer *new_buffer;
6163 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
6165 new_buffer = gst_buffer_new_allocate (NULL,
6166 gst_buffer_get_size (buffer), ¶ms);
6168 /* Copy data "by hand", so ensure alignment is kept: */
6169 gst_buffer_fill (new_buffer, 0, map.data, map.size);
6171 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
6172 GST_DEBUG_OBJECT (demux,
6173 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
6176 gst_buffer_unmap (buffer, &map);
6177 gst_buffer_unref (buffer);
6182 gst_buffer_unmap (buffer, &map);
6187 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
6193 /* We are converting from pairs to triplets */
6194 *res = ccpair_size / 2 * 3;
6195 storage = g_malloc (*res);
6196 for (i = 0; i * 2 < ccpair_size; i += 1) {
6197 /* FIXME: Use line offset 0 as we simply can't know here */
6199 storage[i * 3] = 0x80 | 0x00;
6201 storage[i * 3] = 0x00 | 0x00;
6202 storage[i * 3 + 1] = ccpair[i * 2];
6203 storage[i * 3 + 2] = ccpair[i * 2 + 1];
6210 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
6214 guint32 atom_length, fourcc;
6215 QtDemuxStreamStsdEntry *stsd_entry;
6217 GST_MEMDUMP ("caption atom", data, size);
6219 /* There might be multiple atoms */
6224 atom_length = QT_UINT32 (data);
6225 fourcc = QT_FOURCC (data + 4);
6226 if (G_UNLIKELY (atom_length > size || atom_length == 8))
6229 GST_DEBUG_OBJECT (stream->pad, "here");
6231 /* Check if we have somethig compatible */
6232 stsd_entry = CUR_STREAM (stream);
6233 switch (stsd_entry->fourcc) {
6235 guint8 *cdat = NULL, *cdt2 = NULL;
6236 gsize cdat_size = 0, cdt2_size = 0;
6237 /* Should be cdat or cdt2 */
6238 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
6239 GST_WARNING_OBJECT (stream->pad,
6240 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
6241 GST_FOURCC_ARGS (fourcc));
6245 /* Convert to S334-1 Annex A byte triplet */
6246 if (fourcc == FOURCC_cdat)
6247 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
6249 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
6250 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
6253 /* Check for another atom ? */
6254 if (size > atom_length + 8) {
6255 guint32 new_atom_length = QT_UINT32 (data + atom_length);
6256 if (size >= atom_length + new_atom_length) {
6257 fourcc = QT_FOURCC (data + atom_length + 4);
6258 if (fourcc == FOURCC_cdat) {
6261 convert_to_s334_1a (data + atom_length + 8,
6262 new_atom_length - 8, 1, &cdat_size);
6264 GST_WARNING_OBJECT (stream->pad,
6265 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6269 convert_to_s334_1a (data + atom_length + 8,
6270 new_atom_length - 8, 2, &cdt2_size);
6272 GST_WARNING_OBJECT (stream->pad,
6273 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6278 *cclen = cdat_size + cdt2_size;
6279 res = g_malloc (*cclen);
6281 memcpy (res, cdat, cdat_size);
6283 memcpy (res + cdat_size, cdt2, cdt2_size);
6289 if (fourcc != FOURCC_ccdp) {
6290 GST_WARNING_OBJECT (stream->pad,
6291 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
6292 GST_FOURCC_ARGS (fourcc));
6295 *cclen = atom_length - 8;
6296 res = g_memdup (data + 8, *cclen);
6299 /* Keep this here in case other closed caption formats are added */
6300 g_assert_not_reached ();
6304 GST_MEMDUMP ("Output", res, *cclen);
6309 GST_WARNING ("[cdat] atom is too small or invalid");
6313 /* the input buffer metadata must be writable,
6314 * but time/duration etc not yet set and need not be preserved */
6316 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6323 /* not many cases for now */
6324 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
6325 /* send a one time dvd clut event */
6326 if (stream->pending_event && stream->pad)
6327 gst_pad_push_event (stream->pad, stream->pending_event);
6328 stream->pending_event = NULL;
6331 if (G_UNLIKELY (stream->subtype != FOURCC_text
6332 && stream->subtype != FOURCC_sbtl &&
6333 stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
6337 gst_buffer_map (buf, &map, GST_MAP_READ);
6339 /* empty buffer is sent to terminate previous subtitle */
6340 if (map.size <= 2) {
6341 gst_buffer_unmap (buf, &map);
6342 gst_buffer_unref (buf);
6345 if (stream->subtype == FOURCC_subp) {
6346 /* That's all the processing needed for subpictures */
6347 gst_buffer_unmap (buf, &map);
6351 if (stream->subtype == FOURCC_clcp) {
6354 /* For closed caption, we need to extract the information from the
6355 * [cdat],[cdt2] or [ccdp] atom */
6356 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
6357 gst_buffer_unmap (buf, &map);
6358 gst_buffer_unref (buf);
6360 buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
6362 /* Conversion failed or there's nothing */
6368 nsize = GST_READ_UINT16_BE (map.data);
6369 nsize = MIN (nsize, map.size - 2);
6371 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6374 /* takes care of UTF-8 validation or UTF-16 recognition,
6375 * no other encoding expected */
6376 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6377 gst_buffer_unmap (buf, &map);
6379 gst_buffer_unref (buf);
6380 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
6382 /* this should not really happen unless the subtitle is corrupted */
6383 gst_buffer_unref (buf);
6387 /* FIXME ? convert optional subsequent style info to markup */
6392 static GstFlowReturn
6393 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6396 GstFlowReturn ret = GST_FLOW_OK;
6397 GstClockTime pts, duration;
6399 if (stream->need_clip)
6400 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6402 if (G_UNLIKELY (buf == NULL))
6405 if (G_UNLIKELY (stream->discont)) {
6406 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6407 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6408 stream->discont = FALSE;
6410 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6413 GST_LOG_OBJECT (qtdemux,
6414 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6415 ", duration %" GST_TIME_FORMAT " on pad %s",
6416 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6417 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6418 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
6420 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
6421 GstStructure *crypto_info;
6422 QtDemuxCencSampleSetInfo *info =
6423 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6427 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6428 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6429 GST_PTR_FORMAT, event);
6430 gst_pad_push_event (stream->pad, event);
6433 if (info->crypto_info == NULL) {
6434 GST_DEBUG_OBJECT (qtdemux,
6435 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
6437 /* The end of the crypto_info array matches our n_samples position,
6438 * so count backward from there */
6439 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6440 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6441 /* steal structure from array */
6442 crypto_info = g_ptr_array_index (info->crypto_info, index);
6443 g_ptr_array_index (info->crypto_info, index) = NULL;
6444 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6445 info->crypto_info->len);
6446 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6447 GST_ERROR_OBJECT (qtdemux,
6448 "failed to attach cenc metadata to buffer");
6450 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6451 index, stream->sample_index);
6456 if (stream->alignment > 1)
6457 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6459 pts = GST_BUFFER_PTS (buf);
6460 duration = GST_BUFFER_DURATION (buf);
6462 ret = gst_pad_push (stream->pad, buf);
6464 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6465 /* mark position in stream, we'll need this to know when to send GAP event */
6466 stream->segment.position = pts + duration;
6474 static GstFlowReturn
6475 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6478 GstFlowReturn ret = GST_FLOW_OK;
6480 if (stream->subtype == FOURCC_clcp
6481 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6483 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6484 guint n_triplets, i;
6485 guint field1_off = 0, field2_off = 0;
6487 /* We have to split CEA608 buffers so that each outgoing buffer contains
6488 * one byte pair per field according to the framerate of the video track.
6490 * If there is only a single byte pair per field we don't have to do
6494 gst_buffer_map (buf, &map, GST_MAP_READ);
6496 n_triplets = map.size / 3;
6497 for (i = 0; i < n_triplets; i++) {
6498 if (map.data[3 * i] & 0x80)
6504 g_assert (n_field1 || n_field2);
6506 /* If there's more than 1 frame we have to split, otherwise we can just
6508 if (n_field1 > 1 || n_field2 > 1) {
6510 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6511 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6513 for (i = 0; i < n_output_buffers; i++) {
6515 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6519 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6520 outptr = outmap.data;
6523 gboolean found = FALSE;
6525 while (map.data + field1_off < map.data + map.size) {
6526 if (map.data[field1_off] & 0x80) {
6527 memcpy (outptr, &map.data[field1_off], 3);
6536 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6538 memcpy (outptr, empty, 3);
6545 gboolean found = FALSE;
6547 while (map.data + field2_off < map.data + map.size) {
6548 if ((map.data[field2_off] & 0x80) == 0) {
6549 memcpy (outptr, &map.data[field2_off], 3);
6558 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6560 memcpy (outptr, empty, 3);
6566 gst_buffer_unmap (outbuf, &outmap);
6568 GST_BUFFER_PTS (outbuf) =
6569 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6570 GST_SECOND * CUR_STREAM (stream)->fps_d,
6571 CUR_STREAM (stream)->fps_n);
6572 GST_BUFFER_DURATION (outbuf) =
6573 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6574 CUR_STREAM (stream)->fps_n);
6575 GST_BUFFER_OFFSET (outbuf) = -1;
6576 GST_BUFFER_OFFSET_END (outbuf) = -1;
6578 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6580 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6583 gst_buffer_unmap (buf, &map);
6584 gst_buffer_unref (buf);
6586 gst_buffer_unmap (buf, &map);
6587 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6590 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6596 /* Sets a buffer's attributes properly and pushes it downstream.
6597 * Also checks for additional actions and custom processing that may
6598 * need to be done first.
6600 static GstFlowReturn
6601 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6602 QtDemuxStream * stream, GstBuffer * buf,
6603 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6604 gboolean keyframe, GstClockTime position, guint64 byte_position)
6606 GstFlowReturn ret = GST_FLOW_OK;
6608 /* offset the timestamps according to the edit list */
6610 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6614 gst_buffer_map (buf, &map, GST_MAP_READ);
6615 url = g_strndup ((gchar *) map.data, map.size);
6616 gst_buffer_unmap (buf, &map);
6617 if (url != NULL && strlen (url) != 0) {
6618 /* we have RTSP redirect now */
6619 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6620 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6621 gst_structure_new ("redirect",
6622 "new-location", G_TYPE_STRING, url, NULL)));
6623 qtdemux->posted_redirect = TRUE;
6625 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6631 /* position reporting */
6632 if (qtdemux->segment.rate >= 0) {
6633 qtdemux->segment.position = position;
6634 gst_qtdemux_sync_streams (qtdemux);
6637 if (G_UNLIKELY (!stream->pad)) {
6638 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6639 gst_buffer_unref (buf);
6643 /* send out pending buffers */
6644 while (stream->buffers) {
6645 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6647 if (G_UNLIKELY (stream->discont)) {
6648 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6649 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6650 stream->discont = FALSE;
6652 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6655 if (stream->alignment > 1)
6656 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6657 gst_pad_push (stream->pad, buffer);
6659 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6662 /* we're going to modify the metadata */
6663 buf = gst_buffer_make_writable (buf);
6665 if (G_UNLIKELY (stream->need_process))
6666 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6672 GST_BUFFER_DTS (buf) = dts;
6673 GST_BUFFER_PTS (buf) = pts;
6674 GST_BUFFER_DURATION (buf) = duration;
6675 GST_BUFFER_OFFSET (buf) = -1;
6676 GST_BUFFER_OFFSET_END (buf) = -1;
6679 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6680 stream->on_keyframe = FALSE;
6682 stream->on_keyframe = TRUE;
6685 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6686 gst_buffer_append_memory (buf,
6687 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6689 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6690 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6693 if (G_UNLIKELY (qtdemux->element_index)) {
6694 GstClockTime stream_time;
6697 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6699 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6700 GST_LOG_OBJECT (qtdemux,
6701 "adding association %" GST_TIME_FORMAT "-> %"
6702 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6703 gst_index_add_association (qtdemux->element_index,
6705 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6706 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6707 GST_FORMAT_BYTES, byte_position, NULL);
6712 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6718 static const QtDemuxRandomAccessEntry *
6719 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6720 GstClockTime pos, gboolean after)
6722 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6723 guint n_entries = stream->n_ra_entries;
6726 /* we assume the table is sorted */
6727 for (i = 0; i < n_entries; ++i) {
6728 if (entries[i].ts > pos)
6732 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6733 * probably okay to assume that the index lists the very first fragment */
6740 return &entries[i - 1];
6744 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6746 const QtDemuxRandomAccessEntry *best_entry = NULL;
6749 GST_OBJECT_LOCK (qtdemux);
6751 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6753 /* first see if we can determine where to go to using mfra,
6754 * before we start clearing things */
6755 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6756 const QtDemuxRandomAccessEntry *entry;
6757 QtDemuxStream *stream;
6758 gboolean is_audio_or_video;
6760 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6762 if (stream->ra_entries == NULL)
6765 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6766 is_audio_or_video = TRUE;
6768 is_audio_or_video = FALSE;
6771 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6772 stream->time_position, !is_audio_or_video);
6774 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6775 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6777 stream->pending_seek = entry;
6779 /* decide position to jump to just based on audio/video tracks, not subs */
6780 if (!is_audio_or_video)
6783 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6787 /* no luck, will handle seek otherwise */
6788 if (best_entry == NULL) {
6789 GST_OBJECT_UNLOCK (qtdemux);
6793 /* ok, now we can prepare for processing as of located moof */
6794 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6795 QtDemuxStream *stream;
6797 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6799 g_free (stream->samples);
6800 stream->samples = NULL;
6801 stream->n_samples = 0;
6802 stream->stbl_index = -1; /* no samples have yet been parsed */
6803 stream->sample_index = -1;
6805 if (stream->protection_scheme_info) {
6806 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6807 if (stream->protection_scheme_type == FOURCC_cenc) {
6808 QtDemuxCencSampleSetInfo *info =
6809 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6810 if (info->crypto_info) {
6811 g_ptr_array_free (info->crypto_info, TRUE);
6812 info->crypto_info = NULL;
6818 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6819 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6820 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6821 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6823 qtdemux->moof_offset = best_entry->moof_offset;
6825 qtdemux_add_fragmented_samples (qtdemux);
6827 GST_OBJECT_UNLOCK (qtdemux);
6831 static GstFlowReturn
6832 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6834 GstFlowReturn ret = GST_FLOW_OK;
6835 GstBuffer *buf = NULL;
6836 QtDemuxStream *stream, *target_stream = NULL;
6837 GstClockTime min_time;
6839 GstClockTime dts = GST_CLOCK_TIME_NONE;
6840 GstClockTime pts = GST_CLOCK_TIME_NONE;
6841 GstClockTime duration = 0;
6842 gboolean keyframe = FALSE;
6843 guint sample_size = 0;
6848 if (qtdemux->fragmented_seek_pending) {
6849 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6850 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6851 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6852 qtdemux->fragmented_seek_pending = FALSE;
6854 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6858 /* Figure out the next stream sample to output, min_time is expressed in
6859 * global time and runs over the edit list segments. */
6860 min_time = G_MAXUINT64;
6861 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6862 GstClockTime position;
6864 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6865 position = stream->time_position;
6867 /* position of -1 is EOS */
6868 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6869 min_time = position;
6870 target_stream = stream;
6874 if (G_UNLIKELY (target_stream == NULL)) {
6875 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6879 /* check for segment end */
6880 if (G_UNLIKELY (qtdemux->segment.stop != -1
6881 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6882 || (qtdemux->segment.rate < 0
6883 && qtdemux->segment.start > min_time))
6884 && target_stream->on_keyframe)) {
6885 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6886 target_stream->time_position = GST_CLOCK_TIME_NONE;
6890 /* gap events for subtitle streams */
6891 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6892 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6893 if (stream->pad && (stream->subtype == FOURCC_subp
6894 || stream->subtype == FOURCC_text
6895 || stream->subtype == FOURCC_sbtl)) {
6896 /* send one second gap events until the stream catches up */
6897 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6898 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6899 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6900 stream->segment.position + GST_SECOND < min_time) {
6902 gst_event_new_gap (stream->segment.position, GST_SECOND);
6903 gst_pad_push_event (stream->pad, gap);
6904 stream->segment.position += GST_SECOND;
6909 stream = target_stream;
6910 /* fetch info for the current sample of this stream */
6911 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6912 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6915 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6916 if (stream->new_caps) {
6917 gst_qtdemux_configure_stream (qtdemux, stream);
6918 qtdemux_do_allocation (stream, qtdemux);
6921 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6922 if (G_UNLIKELY (qtdemux->segment.
6923 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6924 if (stream->subtype == FOURCC_vide && !keyframe) {
6925 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6931 GST_DEBUG_OBJECT (qtdemux,
6932 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6933 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6934 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6935 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6936 GST_TIME_ARGS (duration));
6938 if (G_UNLIKELY (empty)) {
6939 /* empty segment, push a gap if there's a second or more
6940 * difference and move to the next one */
6941 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6942 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6943 stream->segment.position = pts + duration;
6947 /* hmm, empty sample, skip and move to next sample */
6948 if (G_UNLIKELY (sample_size <= 0))
6951 /* last pushed sample was out of boundary, goto next sample */
6952 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6955 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6958 GST_DEBUG_OBJECT (qtdemux,
6959 "size %d larger than stream max_buffer_size %d, trimming",
6960 sample_size, stream->max_buffer_size);
6962 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6965 if (qtdemux->cenc_aux_info_offset > 0) {
6968 GstBuffer *aux_info = NULL;
6970 /* pull the data stored before the sample */
6972 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6973 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6974 if (G_UNLIKELY (ret != GST_FLOW_OK))
6976 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6977 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6978 gst_byte_reader_init (&br, map.data + 8, map.size);
6979 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6980 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6981 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6982 gst_buffer_unmap (aux_info, &map);
6983 gst_buffer_unref (aux_info);
6984 ret = GST_FLOW_ERROR;
6987 gst_buffer_unmap (aux_info, &map);
6988 gst_buffer_unref (aux_info);
6991 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6994 if (stream->use_allocator) {
6995 /* if we have a per-stream allocator, use it */
6996 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6999 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
7001 if (G_UNLIKELY (ret != GST_FLOW_OK))
7004 if (size != sample_size) {
7005 pts += gst_util_uint64_scale_int (GST_SECOND,
7006 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7009 gst_util_uint64_scale_int (GST_SECOND,
7010 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7013 gst_util_uint64_scale_int (GST_SECOND,
7014 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
7017 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
7018 dts, pts, duration, keyframe, min_time, offset);
7020 if (size != sample_size) {
7021 QtDemuxSample *sample = &stream->samples[stream->sample_index];
7022 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
7024 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
7026 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
7027 if (time_position >= segment->media_start) {
7028 /* inside the segment, update time_position, looks very familiar to
7029 * GStreamer segments, doesn't it? */
7030 stream->time_position = (time_position - segment->media_start) +
7033 /* not yet in segment, time does not yet increment. This means
7034 * that we are still prerolling keyframes to the decoder so it can
7035 * decode the first sample of the segment. */
7036 stream->time_position = segment->time;
7041 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
7042 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
7043 * we have no more data for the pad to push */
7044 if (ret == GST_FLOW_EOS)
7047 stream->offset_in_sample += size;
7048 if (stream->offset_in_sample >= sample_size) {
7049 gst_qtdemux_advance_sample (qtdemux, stream);
7054 gst_qtdemux_advance_sample (qtdemux, stream);
7062 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
7068 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
7069 /* EOS will be raised if all are EOS */
7076 gst_qtdemux_loop (GstPad * pad)
7078 GstQTDemux *qtdemux;
7082 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
7084 cur_offset = qtdemux->offset;
7085 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
7086 cur_offset, qt_demux_state_string (qtdemux->state));
7088 switch (qtdemux->state) {
7089 case QTDEMUX_STATE_INITIAL:
7090 case QTDEMUX_STATE_HEADER:
7091 ret = gst_qtdemux_loop_state_header (qtdemux);
7093 case QTDEMUX_STATE_MOVIE:
7094 ret = gst_qtdemux_loop_state_movie (qtdemux);
7095 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
7096 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
7104 /* if something went wrong, pause */
7105 if (ret != GST_FLOW_OK)
7109 gst_object_unref (qtdemux);
7115 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7116 (NULL), ("streaming stopped, invalid state"));
7117 gst_pad_pause_task (pad);
7118 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7123 const gchar *reason = gst_flow_get_name (ret);
7125 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7127 gst_pad_pause_task (pad);
7129 /* fatal errors need special actions */
7131 if (ret == GST_FLOW_EOS) {
7132 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
7133 /* we have no streams, post an error */
7134 gst_qtdemux_post_no_playable_stream_error (qtdemux);
7136 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7139 if ((stop = qtdemux->segment.stop) == -1)
7140 stop = qtdemux->segment.duration;
7142 if (qtdemux->segment.rate >= 0) {
7143 GstMessage *message;
7146 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7147 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7148 GST_FORMAT_TIME, stop);
7149 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7150 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7151 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7152 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7154 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7155 gst_qtdemux_push_event (qtdemux, event);
7157 GstMessage *message;
7160 /* For Reverse Playback */
7161 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7162 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7163 GST_FORMAT_TIME, qtdemux->segment.start);
7164 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7165 qtdemux->segment.start);
7166 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7167 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7168 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7170 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7171 gst_qtdemux_push_event (qtdemux, event);
7176 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7177 event = gst_event_new_eos ();
7178 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7179 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7180 gst_qtdemux_push_event (qtdemux, event);
7182 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7183 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
7184 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7193 * Returns if there are samples to be played.
7196 has_next_entry (GstQTDemux * demux)
7198 QtDemuxStream *stream;
7201 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7203 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7204 stream = QTDEMUX_NTH_STREAM (demux, i);
7206 if (stream->sample_index == -1) {
7207 stream->sample_index = 0;
7208 stream->offset_in_sample = 0;
7211 if (stream->sample_index >= stream->n_samples) {
7212 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7215 GST_DEBUG_OBJECT (demux, "Found a sample");
7219 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7226 * Returns the size of the first entry at the current offset.
7227 * If -1, there are none (which means EOS or empty file).
7230 next_entry_size (GstQTDemux * demux)
7232 QtDemuxStream *stream, *target_stream = NULL;
7233 guint64 smalloffs = (guint64) - 1;
7234 QtDemuxSample *sample;
7237 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7240 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7241 stream = QTDEMUX_NTH_STREAM (demux, i);
7243 if (stream->sample_index == -1) {
7244 stream->sample_index = 0;
7245 stream->offset_in_sample = 0;
7248 if (stream->sample_index >= stream->n_samples) {
7249 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7253 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
7254 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7255 stream->sample_index);
7259 sample = &stream->samples[stream->sample_index];
7261 GST_LOG_OBJECT (demux,
7262 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7263 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7264 stream->sample_index, sample->offset, sample->size);
7266 if (((smalloffs == -1)
7267 || (sample->offset < smalloffs)) && (sample->size)) {
7268 smalloffs = sample->offset;
7269 target_stream = stream;
7276 GST_LOG_OBJECT (demux,
7277 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7278 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7280 stream = target_stream;
7281 sample = &stream->samples[stream->sample_index];
7283 if (sample->offset >= demux->offset) {
7284 demux->todrop = sample->offset - demux->offset;
7285 return sample->size + demux->todrop;
7288 GST_DEBUG_OBJECT (demux,
7289 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7294 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
7296 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7298 gst_element_post_message (GST_ELEMENT_CAST (demux),
7299 gst_message_new_element (GST_OBJECT_CAST (demux),
7300 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7304 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
7309 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7312 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7313 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7314 GST_SEEK_TYPE_NONE, -1);
7316 /* store seqnum to drop flush events, they don't need to reach downstream */
7317 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7318 res = gst_pad_push_event (demux->sinkpad, event);
7319 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7324 /* check for seekable upstream, above and beyond a mere query */
7326 gst_qtdemux_check_seekability (GstQTDemux * demux)
7329 gboolean seekable = FALSE;
7330 gint64 start = -1, stop = -1;
7332 if (demux->upstream_size)
7335 if (demux->upstream_format_is_time)
7338 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7339 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7340 GST_DEBUG_OBJECT (demux, "seeking query failed");
7344 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7346 /* try harder to query upstream size if we didn't get it the first time */
7347 if (seekable && stop == -1) {
7348 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7349 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7352 /* if upstream doesn't know the size, it's likely that it's not seekable in
7353 * practice even if it technically may be seekable */
7354 if (seekable && (start != 0 || stop <= start)) {
7355 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7360 gst_query_unref (query);
7362 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7363 G_GUINT64_FORMAT ")", seekable, start, stop);
7364 demux->upstream_seekable = seekable;
7365 demux->upstream_size = seekable ? stop : -1;
7369 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7371 g_return_if_fail (bytes <= demux->todrop);
7373 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7374 gst_adapter_flush (demux->adapter, bytes);
7375 demux->neededbytes -= bytes;
7376 demux->offset += bytes;
7377 demux->todrop -= bytes;
7380 /* PUSH-MODE only: Send a segment, if not done already. */
7382 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7384 if (G_UNLIKELY (demux->need_segment)) {
7387 if (!demux->upstream_format_is_time) {
7388 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7390 GstEvent *segment_event;
7391 segment_event = gst_event_new_segment (&demux->segment);
7392 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7393 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7394 gst_qtdemux_push_event (demux, segment_event);
7397 demux->need_segment = FALSE;
7399 /* clear to send tags on all streams */
7400 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7401 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7402 gst_qtdemux_push_tags (demux, stream);
7403 if (CUR_STREAM (stream)->sparse) {
7404 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7405 gst_pad_push_event (stream->pad,
7406 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7412 /* Used for push mode only. */
7414 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7415 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7417 GstClockTime ts, dur;
7421 stream->segments[segment_index].duration - (pos -
7422 stream->segments[segment_index].time);
7423 stream->time_position += dur;
7425 /* Only gaps with a duration of at least one second are propagated.
7426 * Same workaround as in pull mode.
7427 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7428 if (dur >= GST_SECOND) {
7430 gap = gst_event_new_gap (ts, dur);
7432 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7433 "segment: %" GST_PTR_FORMAT, gap);
7434 gst_pad_push_event (stream->pad, gap);
7438 static GstFlowReturn
7439 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7443 demux = GST_QTDEMUX (parent);
7445 GST_DEBUG_OBJECT (demux,
7446 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7447 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7448 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7449 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7450 gst_buffer_get_size (inbuf), demux->offset);
7452 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7453 gboolean is_gap_input = FALSE;
7456 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7458 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7459 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7462 /* Check if we can land back on our feet in the case where upstream is
7463 * handling the seeking/pushing of samples with gaps in between (like
7464 * in the case of trick-mode DASH for example) */
7465 if (demux->upstream_format_is_time
7466 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7467 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7469 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7470 GST_LOG_OBJECT (demux,
7471 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7472 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7474 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7475 stream, GST_BUFFER_OFFSET (inbuf));
7477 QtDemuxSample *sample = &stream->samples[res];
7478 GST_LOG_OBJECT (demux,
7479 "Checking if sample %d from track-id %u is valid (offset:%"
7480 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7481 stream->track_id, sample->offset, sample->size);
7482 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7483 GST_LOG_OBJECT (demux,
7484 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7486 is_gap_input = TRUE;
7487 /* We can go back to standard playback mode */
7488 demux->state = QTDEMUX_STATE_MOVIE;
7489 /* Remember which sample this stream is at */
7490 stream->sample_index = res;
7491 /* Finally update all push-based values to the expected values */
7492 demux->neededbytes = stream->samples[res].size;
7493 demux->offset = GST_BUFFER_OFFSET (inbuf);
7495 demux->mdatsize - demux->offset + demux->mdatoffset;
7500 if (!is_gap_input) {
7501 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7502 /* Reset state if it's a real discont */
7503 demux->neededbytes = 16;
7504 demux->state = QTDEMUX_STATE_INITIAL;
7505 demux->offset = GST_BUFFER_OFFSET (inbuf);
7506 gst_adapter_clear (demux->adapter);
7509 /* Reverse fragmented playback, need to flush all we have before
7510 * consuming a new fragment.
7511 * The samples array have the timestamps calculated by accumulating the
7512 * durations but this won't work for reverse playback of fragments as
7513 * the timestamps of a subsequent fragment should be smaller than the
7514 * previously received one. */
7515 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7516 gst_qtdemux_process_adapter (demux, TRUE);
7517 g_ptr_array_foreach (demux->active_streams,
7518 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7522 gst_adapter_push (demux->adapter, inbuf);
7524 GST_DEBUG_OBJECT (demux,
7525 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7526 demux->neededbytes, gst_adapter_available (demux->adapter));
7528 return gst_qtdemux_process_adapter (demux, FALSE);
7531 static GstFlowReturn
7532 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7534 GstFlowReturn ret = GST_FLOW_OK;
7536 /* we never really mean to buffer that much */
7537 if (demux->neededbytes == -1) {
7541 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7542 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7544 #ifndef GST_DISABLE_GST_DEBUG
7546 guint64 discont_offset, distance_from_discont;
7548 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7549 distance_from_discont =
7550 gst_adapter_distance_from_discont (demux->adapter);
7552 GST_DEBUG_OBJECT (demux,
7553 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7554 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7555 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7556 demux->offset, discont_offset, distance_from_discont);
7560 switch (demux->state) {
7561 case QTDEMUX_STATE_INITIAL:{
7566 gst_qtdemux_check_seekability (demux);
7568 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7570 /* get fourcc/length, set neededbytes */
7571 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7573 gst_adapter_unmap (demux->adapter);
7575 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7576 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7578 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7579 (_("This file is invalid and cannot be played.")),
7580 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7581 GST_FOURCC_ARGS (fourcc)));
7582 ret = GST_FLOW_ERROR;
7585 if (fourcc == FOURCC_mdat) {
7586 gint next_entry = next_entry_size (demux);
7587 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7588 || !demux->fragmented)) {
7589 /* we have the headers, start playback */
7590 demux->state = QTDEMUX_STATE_MOVIE;
7591 demux->neededbytes = next_entry;
7592 demux->mdatleft = size;
7593 demux->mdatsize = demux->mdatleft;
7595 /* no headers yet, try to get them */
7598 guint64 old, target;
7601 old = demux->offset;
7602 target = old + size;
7604 /* try to jump over the atom with a seek */
7605 /* only bother if it seems worth doing so,
7606 * and avoids possible upstream/server problems */
7607 if (demux->upstream_seekable &&
7608 demux->upstream_size > 4 * (1 << 20)) {
7609 res = qtdemux_seek_offset (demux, target);
7611 GST_DEBUG_OBJECT (demux, "skipping seek");
7616 GST_DEBUG_OBJECT (demux, "seek success");
7617 /* remember the offset fo the first mdat so we can seek back to it
7618 * after we have the headers */
7619 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7620 demux->first_mdat = old;
7621 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7624 /* seek worked, continue reading */
7625 demux->offset = target;
7626 demux->neededbytes = 16;
7627 demux->state = QTDEMUX_STATE_INITIAL;
7629 /* seek failed, need to buffer */
7630 demux->offset = old;
7631 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7632 /* there may be multiple mdat (or alike) buffers */
7634 if (demux->mdatbuffer)
7635 bs = gst_buffer_get_size (demux->mdatbuffer);
7638 if (size + bs > 10 * (1 << 20))
7640 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7641 demux->neededbytes = size;
7642 if (!demux->mdatbuffer)
7643 demux->mdatoffset = demux->offset;
7646 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7647 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7648 (_("This file is invalid and cannot be played.")),
7649 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7650 GST_FOURCC_ARGS (fourcc), size));
7651 ret = GST_FLOW_ERROR;
7654 /* this means we already started buffering and still no moov header,
7655 * let's continue buffering everything till we get moov */
7656 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7657 || fourcc == FOURCC_moof))
7659 demux->neededbytes = size;
7660 demux->state = QTDEMUX_STATE_HEADER;
7664 case QTDEMUX_STATE_HEADER:{
7668 GST_DEBUG_OBJECT (demux, "In header");
7670 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7672 /* parse the header */
7673 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7675 if (fourcc == FOURCC_moov) {
7676 /* in usual fragmented setup we could try to scan for more
7677 * and end up at the the moov (after mdat) again */
7678 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7680 || demux->last_moov_offset == demux->offset)) {
7681 GST_DEBUG_OBJECT (demux,
7682 "Skipping moov atom as we have (this) one already");
7684 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7686 if (demux->got_moov && demux->fragmented) {
7687 GST_DEBUG_OBJECT (demux,
7688 "Got a second moov, clean up data from old one");
7689 if (demux->moov_node_compressed) {
7690 g_node_destroy (demux->moov_node_compressed);
7691 if (demux->moov_node)
7692 g_free (demux->moov_node->data);
7694 demux->moov_node_compressed = NULL;
7695 if (demux->moov_node)
7696 g_node_destroy (demux->moov_node);
7697 demux->moov_node = NULL;
7700 demux->last_moov_offset = demux->offset;
7702 /* Update streams with new moov */
7703 gst_qtdemux_stream_concat (demux,
7704 demux->old_streams, demux->active_streams);
7706 qtdemux_parse_moov (demux, data, demux->neededbytes);
7707 qtdemux_node_dump (demux, demux->moov_node);
7708 qtdemux_parse_tree (demux);
7709 qtdemux_prepare_streams (demux);
7710 QTDEMUX_EXPOSE_LOCK (demux);
7711 qtdemux_expose_streams (demux);
7712 QTDEMUX_EXPOSE_UNLOCK (demux);
7714 demux->got_moov = TRUE;
7716 gst_qtdemux_check_send_pending_segment (demux);
7718 if (demux->moov_node_compressed) {
7719 g_node_destroy (demux->moov_node_compressed);
7720 g_free (demux->moov_node->data);
7722 demux->moov_node_compressed = NULL;
7723 g_node_destroy (demux->moov_node);
7724 demux->moov_node = NULL;
7725 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7727 } else if (fourcc == FOURCC_moof) {
7728 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7730 GstClockTime prev_pts;
7731 guint64 prev_offset;
7732 guint64 adapter_discont_offset, adapter_discont_dist;
7734 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7737 * The timestamp of the moof buffer is relevant as some scenarios
7738 * won't have the initial timestamp in the atoms. Whenever a new
7739 * buffer has started, we get that buffer's PTS and use it as a base
7740 * timestamp for the trun entries.
7742 * To keep track of the current buffer timestamp and starting point
7743 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7744 * from the beggining of the buffer, with the distance and demux->offset
7745 * we know if it is still the same buffer or not.
7747 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7748 prev_offset = demux->offset - dist;
7749 if (demux->fragment_start_offset == -1
7750 || prev_offset > demux->fragment_start_offset) {
7751 demux->fragment_start_offset = prev_offset;
7752 demux->fragment_start = prev_pts;
7753 GST_DEBUG_OBJECT (demux,
7754 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7755 GST_TIME_FORMAT, demux->fragment_start_offset,
7756 GST_TIME_ARGS (demux->fragment_start));
7759 /* We can't use prev_offset() here because this would require
7760 * upstream to set consistent and correct offsets on all buffers
7761 * since the discont. Nothing ever did that in the past and we
7762 * would break backwards compatibility here then.
7763 * Instead take the offset we had at the last discont and count
7764 * the bytes from there. This works with old code as there would
7765 * be no discont between moov and moof, and also works with
7766 * adaptivedemux which correctly sets offset and will set the
7767 * DISCONT flag accordingly when needed.
7769 * We also only do this for upstream TIME segments as otherwise
7770 * there are potential backwards compatibility problems with
7771 * seeking in PUSH mode and upstream providing inconsistent
7773 adapter_discont_offset =
7774 gst_adapter_offset_at_discont (demux->adapter);
7775 adapter_discont_dist =
7776 gst_adapter_distance_from_discont (demux->adapter);
7778 GST_DEBUG_OBJECT (demux,
7779 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7780 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7781 demux->offset, adapter_discont_offset, adapter_discont_dist);
7783 if (demux->upstream_format_is_time) {
7784 demux->moof_offset = adapter_discont_offset;
7785 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7786 demux->moof_offset += adapter_discont_dist;
7787 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7788 demux->moof_offset = demux->offset;
7790 demux->moof_offset = demux->offset;
7793 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7794 demux->moof_offset, NULL)) {
7795 gst_adapter_unmap (demux->adapter);
7796 ret = GST_FLOW_ERROR;
7800 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7801 if (demux->mss_mode && !demux->exposed) {
7802 QTDEMUX_EXPOSE_LOCK (demux);
7803 qtdemux_expose_streams (demux);
7804 QTDEMUX_EXPOSE_UNLOCK (demux);
7807 gst_qtdemux_check_send_pending_segment (demux);
7809 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7811 } else if (fourcc == FOURCC_ftyp) {
7812 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7813 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7814 } else if (fourcc == FOURCC_uuid) {
7815 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7816 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7817 } else if (fourcc == FOURCC_sidx) {
7818 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7819 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7823 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7827 /* [free] and [skip] are padding atoms */
7828 GST_DEBUG_OBJECT (demux,
7829 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7830 GST_FOURCC_ARGS (fourcc));
7833 GST_WARNING_OBJECT (demux,
7834 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7835 GST_FOURCC_ARGS (fourcc));
7836 /* Let's jump that one and go back to initial state */
7840 gst_adapter_unmap (demux->adapter);
7843 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7844 gsize remaining_data_size = 0;
7846 /* the mdat was before the header */
7847 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7848 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7849 /* restore our adapter/offset view of things with upstream;
7850 * put preceding buffered data ahead of current moov data.
7851 * This should also handle evil mdat, moov, mdat cases and alike */
7852 gst_adapter_flush (demux->adapter, demux->neededbytes);
7854 /* Store any remaining data after the mdat for later usage */
7855 remaining_data_size = gst_adapter_available (demux->adapter);
7856 if (remaining_data_size > 0) {
7857 g_assert (demux->restoredata_buffer == NULL);
7858 demux->restoredata_buffer =
7859 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7860 demux->restoredata_offset = demux->offset + demux->neededbytes;
7861 GST_DEBUG_OBJECT (demux,
7862 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7863 G_GUINT64_FORMAT, remaining_data_size,
7864 demux->restoredata_offset);
7867 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7868 demux->mdatbuffer = NULL;
7869 demux->offset = demux->mdatoffset;
7870 demux->neededbytes = next_entry_size (demux);
7871 demux->state = QTDEMUX_STATE_MOVIE;
7872 demux->mdatleft = gst_adapter_available (demux->adapter);
7873 demux->mdatsize = demux->mdatleft;
7875 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7876 gst_adapter_flush (demux->adapter, demux->neededbytes);
7878 /* only go back to the mdat if there are samples to play */
7879 if (demux->got_moov && demux->first_mdat != -1
7880 && has_next_entry (demux)) {
7883 /* we need to seek back */
7884 res = qtdemux_seek_offset (demux, demux->first_mdat);
7886 demux->offset = demux->first_mdat;
7888 GST_DEBUG_OBJECT (demux, "Seek back failed");
7891 demux->offset += demux->neededbytes;
7893 demux->neededbytes = 16;
7894 demux->state = QTDEMUX_STATE_INITIAL;
7899 case QTDEMUX_STATE_BUFFER_MDAT:{
7903 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7905 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7906 gst_buffer_extract (buf, 0, fourcc, 4);
7907 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7908 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7909 if (demux->mdatbuffer)
7910 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7912 demux->mdatbuffer = buf;
7913 demux->offset += demux->neededbytes;
7914 demux->neededbytes = 16;
7915 demux->state = QTDEMUX_STATE_INITIAL;
7916 gst_qtdemux_post_progress (demux, 1, 1);
7920 case QTDEMUX_STATE_MOVIE:{
7921 QtDemuxStream *stream = NULL;
7922 QtDemuxSample *sample;
7923 GstClockTime dts, pts, duration;
7927 GST_DEBUG_OBJECT (demux,
7928 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7930 if (demux->fragmented) {
7931 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7933 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7934 /* if needed data starts within this atom,
7935 * then it should not exceed this atom */
7936 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7937 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7938 (_("This file is invalid and cannot be played.")),
7939 ("sample data crosses atom boundary"));
7940 ret = GST_FLOW_ERROR;
7943 demux->mdatleft -= demux->neededbytes;
7945 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7946 /* so we are dropping more than left in this atom */
7947 gst_qtdemux_drop_data (demux, demux->mdatleft);
7948 demux->mdatleft = 0;
7950 /* need to resume atom parsing so we do not miss any other pieces */
7951 demux->state = QTDEMUX_STATE_INITIAL;
7952 demux->neededbytes = 16;
7954 /* check if there was any stored post mdat data from previous buffers */
7955 if (demux->restoredata_buffer) {
7956 g_assert (gst_adapter_available (demux->adapter) == 0);
7958 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7959 demux->restoredata_buffer = NULL;
7960 demux->offset = demux->restoredata_offset;
7967 if (demux->todrop) {
7968 if (demux->cenc_aux_info_offset > 0) {
7972 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7973 data = gst_adapter_map (demux->adapter, demux->todrop);
7974 gst_byte_reader_init (&br, data + 8, demux->todrop);
7975 if (!qtdemux_parse_cenc_aux_info (demux,
7976 QTDEMUX_NTH_STREAM (demux, 0), &br,
7977 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7978 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7979 ret = GST_FLOW_ERROR;
7980 gst_adapter_unmap (demux->adapter);
7981 g_free (demux->cenc_aux_info_sizes);
7982 demux->cenc_aux_info_sizes = NULL;
7985 demux->cenc_aux_info_offset = 0;
7986 g_free (demux->cenc_aux_info_sizes);
7987 demux->cenc_aux_info_sizes = NULL;
7988 gst_adapter_unmap (demux->adapter);
7990 gst_qtdemux_drop_data (demux, demux->todrop);
7994 /* initial newsegment sent here after having added pads,
7995 * possible others in sink_event */
7996 gst_qtdemux_check_send_pending_segment (demux);
7998 /* Figure out which stream this packet belongs to */
7999 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8000 stream = QTDEMUX_NTH_STREAM (demux, i);
8001 if (stream->sample_index >= stream->n_samples) {
8002 /* reset to be checked below G_UNLIKELY (stream == NULL) */
8006 GST_LOG_OBJECT (demux,
8007 "Checking track-id %u (sample_index:%d / offset:%"
8008 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
8009 stream->sample_index,
8010 stream->samples[stream->sample_index].offset,
8011 stream->samples[stream->sample_index].size);
8013 if (stream->samples[stream->sample_index].offset == demux->offset)
8017 if (G_UNLIKELY (stream == NULL))
8018 goto unknown_stream;
8020 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
8022 if (stream->new_caps) {
8023 gst_qtdemux_configure_stream (demux, stream);
8026 /* Put data in a buffer, set timestamps, caps, ... */
8027 sample = &stream->samples[stream->sample_index];
8029 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
8030 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
8031 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
8033 dts = QTSAMPLE_DTS (stream, sample);
8034 pts = QTSAMPLE_PTS (stream, sample);
8035 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
8036 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
8038 /* check for segment end */
8039 if (G_UNLIKELY (demux->segment.stop != -1
8040 && demux->segment.stop <= pts && stream->on_keyframe)
8041 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
8042 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
8043 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
8045 /* skip this data, stream is EOS */
8046 gst_adapter_flush (demux->adapter, demux->neededbytes);
8047 demux->offset += demux->neededbytes;
8049 /* check if all streams are eos */
8051 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8052 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
8061 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8063 /* FIXME: should either be an assert or a plain check */
8064 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8066 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
8067 dts, pts, duration, keyframe, dts, demux->offset);
8071 ret = gst_qtdemux_combine_flows (demux, stream, ret);
8073 /* skip this data, stream is EOS */
8074 gst_adapter_flush (demux->adapter, demux->neededbytes);
8077 stream->sample_index++;
8078 stream->offset_in_sample = 0;
8080 /* update current offset and figure out size of next buffer */
8081 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8082 demux->offset, demux->neededbytes);
8083 demux->offset += demux->neededbytes;
8084 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8088 if (ret == GST_FLOW_EOS) {
8089 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8090 demux->neededbytes = -1;
8094 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
8095 if (demux->fragmented) {
8096 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8097 /* there may be more to follow, only finish this atom */
8098 demux->todrop = demux->mdatleft;
8099 demux->neededbytes = demux->todrop;
8104 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8105 goto non_ok_unlinked_flow;
8114 /* when buffering movie data, at least show user something is happening */
8115 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
8116 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
8117 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
8118 demux->neededbytes);
8125 non_ok_unlinked_flow:
8127 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8128 gst_flow_get_name (ret));
8133 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8134 ret = GST_FLOW_ERROR;
8139 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8145 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8146 (NULL), ("qtdemuxer invalid state %d", demux->state));
8147 ret = GST_FLOW_ERROR;
8152 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8153 (NULL), ("no 'moov' atom within the first 10 MB"));
8154 ret = GST_FLOW_ERROR;
8160 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
8165 query = gst_query_new_scheduling ();
8167 if (!gst_pad_peer_query (sinkpad, query)) {
8168 gst_query_unref (query);
8172 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8173 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8174 gst_query_unref (query);
8179 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8180 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8184 GST_DEBUG_OBJECT (sinkpad, "activating push");
8185 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8190 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
8191 GstPadMode mode, gboolean active)
8194 GstQTDemux *demux = GST_QTDEMUX (parent);
8197 case GST_PAD_MODE_PUSH:
8198 demux->pullbased = FALSE;
8201 case GST_PAD_MODE_PULL:
8203 demux->pullbased = TRUE;
8204 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
8207 res = gst_pad_stop_task (sinkpad);
8219 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8225 memset (&z, 0, sizeof (z));
8230 if ((ret = inflateInit (&z)) != Z_OK) {
8231 GST_ERROR ("inflateInit() returned %d", ret);
8235 z.next_in = z_buffer;
8236 z.avail_in = z_length;
8238 buffer = (guint8 *) g_malloc (*length);
8239 z.avail_out = *length;
8240 z.next_out = (Bytef *) buffer;
8242 ret = inflate (&z, Z_NO_FLUSH);
8243 if (ret == Z_STREAM_END) {
8245 } else if (ret != Z_OK) {
8246 GST_WARNING ("inflate() returned %d", ret);
8251 buffer = (guint8 *) g_realloc (buffer, *length);
8252 z.next_out = (Bytef *) (buffer + z.total_out);
8253 z.avail_out += 4096;
8254 } while (z.avail_in > 0);
8256 if (ret != Z_STREAM_END) {
8261 *length = z.total_out;
8268 #endif /* HAVE_ZLIB */
8271 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
8275 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8277 /* counts as header data */
8278 qtdemux->header_size += length;
8280 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
8281 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
8283 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
8290 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
8291 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
8292 if (dcom == NULL || cmvd == NULL)
8293 goto invalid_compression;
8295 dcom_len = QT_UINT32 (dcom->data);
8297 goto invalid_compression;
8299 method = QT_FOURCC ((guint8 *) dcom->data + 8);
8303 guint uncompressed_length;
8304 guint compressed_length;
8308 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
8310 goto invalid_compression;
8312 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8313 compressed_length = cmvd_len - 12;
8314 GST_LOG ("length = %u", uncompressed_length);
8317 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8318 compressed_length, &uncompressed_length);
8321 qtdemux->moov_node_compressed = qtdemux->moov_node;
8322 qtdemux->moov_node = g_node_new (buf);
8324 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8325 uncompressed_length);
8329 #endif /* HAVE_ZLIB */
8331 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8332 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8339 invalid_compression:
8341 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8347 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8350 while (G_UNLIKELY (buf < end)) {
8354 if (G_UNLIKELY (buf + 4 > end)) {
8355 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8358 len = QT_UINT32 (buf);
8359 if (G_UNLIKELY (len == 0)) {
8360 GST_LOG_OBJECT (qtdemux, "empty container");
8363 if (G_UNLIKELY (len < 8)) {
8364 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8367 if (G_UNLIKELY (len > (end - buf))) {
8368 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8369 (gint) (end - buf));
8373 child = g_node_new ((guint8 *) buf);
8374 g_node_append (node, child);
8375 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8376 qtdemux_parse_node (qtdemux, child, buf, len);
8384 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8387 int len = QT_UINT32 (xdxt->data);
8388 guint8 *buf = xdxt->data;
8389 guint8 *end = buf + len;
8392 /* skip size and type */
8400 size = QT_UINT32 (buf);
8401 type = QT_FOURCC (buf + 4);
8403 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8405 if (buf + size > end || size <= 0)
8411 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8412 GST_FOURCC_ARGS (type));
8416 buffer = gst_buffer_new_and_alloc (size);
8417 gst_buffer_fill (buffer, 0, buf, size);
8418 stream->buffers = g_slist_append (stream->buffers, buffer);
8419 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8422 buffer = gst_buffer_new_and_alloc (size);
8423 gst_buffer_fill (buffer, 0, buf, size);
8424 stream->buffers = g_slist_append (stream->buffers, buffer);
8425 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8428 buffer = gst_buffer_new_and_alloc (size);
8429 gst_buffer_fill (buffer, 0, buf, size);
8430 stream->buffers = g_slist_append (stream->buffers, buffer);
8431 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8434 GST_WARNING_OBJECT (qtdemux,
8435 "unknown theora cookie %" GST_FOURCC_FORMAT,
8436 GST_FOURCC_ARGS (type));
8445 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8449 guint32 node_length = 0;
8450 const QtNodeType *type;
8453 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8455 if (G_UNLIKELY (length < 8))
8456 goto not_enough_data;
8458 node_length = QT_UINT32 (buffer);
8459 fourcc = QT_FOURCC (buffer + 4);
8461 /* ignore empty nodes */
8462 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8465 type = qtdemux_type_get (fourcc);
8467 end = buffer + length;
8469 GST_LOG_OBJECT (qtdemux,
8470 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8471 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8473 if (node_length > length)
8474 goto broken_atom_size;
8476 if (type->flags & QT_FLAG_CONTAINER) {
8477 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8482 if (node_length < 20) {
8483 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8486 GST_DEBUG_OBJECT (qtdemux,
8487 "parsing stsd (sample table, sample description) atom");
8488 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8489 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8500 /* also read alac (or whatever) in stead of mp4a in the following,
8501 * since a similar layout is used in other cases as well */
8502 if (fourcc == FOURCC_mp4a)
8504 else if (fourcc == FOURCC_fLaC)
8509 /* There are two things we might encounter here: a true mp4a atom, and
8510 an mp4a entry in an stsd atom. The latter is what we're interested
8511 in, and it looks like an atom, but isn't really one. The true mp4a
8512 atom is short, so we detect it based on length here. */
8513 if (length < min_size) {
8514 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8515 GST_FOURCC_ARGS (fourcc));
8519 /* 'version' here is the sound sample description version. Types 0 and
8520 1 are documented in the QTFF reference, but type 2 is not: it's
8521 described in Apple header files instead (struct SoundDescriptionV2
8523 version = QT_UINT16 (buffer + 16);
8525 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8526 GST_FOURCC_ARGS (fourcc), version);
8528 /* parse any esds descriptors */
8540 GST_WARNING_OBJECT (qtdemux,
8541 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8542 GST_FOURCC_ARGS (fourcc), version);
8547 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8573 /* codec_data is contained inside these atoms, which all have
8574 * the same format. */
8575 /* video sample description size is 86 bytes without extension.
8576 * node_length have to be bigger than 86 bytes because video sample
8577 * description can include extenstions such as esds, fiel, glbl, etc. */
8578 if (node_length < 86) {
8579 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8580 " sample description length too short (%u < 86)",
8581 GST_FOURCC_ARGS (fourcc), node_length);
8585 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8586 GST_FOURCC_ARGS (fourcc));
8588 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8590 * revision level (2 bytes) : must be set to 0. */
8591 version = QT_UINT32 (buffer + 16);
8592 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8594 /* compressor name : PASCAL string and informative purposes
8595 * first byte : the number of bytes to be displayed.
8596 * it has to be less than 32 because it is reserved
8597 * space of 32 bytes total including itself. */
8598 str_len = QT_UINT8 (buffer + 50);
8600 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8601 (char *) buffer + 51);
8603 GST_WARNING_OBJECT (qtdemux,
8604 "compressorname length too big (%u > 31)", str_len);
8606 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8608 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8613 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8615 /* You are reading this correctly. QTFF specifies that the
8616 * metadata atom is a short atom, whereas ISO BMFF specifies
8617 * it's a full atom. But since so many people are doing things
8618 * differently, we actually peek into the atom to see which
8621 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8622 GST_FOURCC_ARGS (fourcc));
8625 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8626 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8627 * starts with a 'hdlr' atom */
8628 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8629 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8630 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8631 * with version/flags both set to zero */
8632 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8634 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8639 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8640 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8641 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8650 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8651 GST_FOURCC_ARGS (fourcc));
8655 version = QT_UINT32 (buffer + 12);
8656 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8663 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8668 if (length < offset) {
8669 GST_WARNING_OBJECT (qtdemux,
8670 "skipping too small %" GST_FOURCC_FORMAT " box",
8671 GST_FOURCC_ARGS (fourcc));
8674 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8680 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8685 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8690 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8693 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
8696 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
8699 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
8701 if (!strcmp (type->name, "unknown"))
8702 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8706 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8707 GST_FOURCC_ARGS (fourcc));
8713 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8714 (_("This file is corrupt and cannot be played.")),
8715 ("Not enough data for an atom header, got only %u bytes", length));
8720 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8721 (_("This file is corrupt and cannot be played.")),
8722 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8723 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8730 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8734 guint32 child_fourcc;
8736 for (child = g_node_first_child (node); child;
8737 child = g_node_next_sibling (child)) {
8738 buffer = (guint8 *) child->data;
8740 child_fourcc = QT_FOURCC (buffer + 4);
8742 if (G_UNLIKELY (child_fourcc == fourcc)) {
8750 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8751 GstByteReader * parser)
8755 guint32 child_fourcc, child_len;
8757 for (child = g_node_first_child (node); child;
8758 child = g_node_next_sibling (child)) {
8759 buffer = (guint8 *) child->data;
8761 child_len = QT_UINT32 (buffer);
8762 child_fourcc = QT_FOURCC (buffer + 4);
8764 if (G_UNLIKELY (child_fourcc == fourcc)) {
8765 if (G_UNLIKELY (child_len < (4 + 4)))
8767 /* FIXME: must verify if atom length < parent atom length */
8768 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8776 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8778 return g_node_nth_child (node, index);
8782 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8783 GstByteReader * parser)
8787 guint32 child_fourcc, child_len;
8789 for (child = g_node_next_sibling (node); child;
8790 child = g_node_next_sibling (child)) {
8791 buffer = (guint8 *) child->data;
8793 child_fourcc = QT_FOURCC (buffer + 4);
8795 if (child_fourcc == fourcc) {
8797 child_len = QT_UINT32 (buffer);
8798 if (G_UNLIKELY (child_len < (4 + 4)))
8800 /* FIXME: must verify if atom length < parent atom length */
8801 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8810 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8812 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8816 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8818 /* FIXME: This can only reliably work if demuxers have a
8819 * separate streaming thread per srcpad. This should be
8820 * done in a demuxer base class, which integrates parts
8823 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8828 query = gst_query_new_allocation (stream->caps, FALSE);
8830 if (!gst_pad_peer_query (stream->pad, query)) {
8831 /* not a problem, just debug a little */
8832 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8835 if (stream->allocator)
8836 gst_object_unref (stream->allocator);
8838 if (gst_query_get_n_allocation_params (query) > 0) {
8839 /* try the allocator */
8840 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8842 stream->use_allocator = TRUE;
8844 stream->allocator = NULL;
8845 gst_allocation_params_init (&stream->params);
8846 stream->use_allocator = FALSE;
8848 gst_query_unref (query);
8853 pad_query (const GValue * item, GValue * value, gpointer user_data)
8855 GstPad *pad = g_value_get_object (item);
8856 GstQuery *query = user_data;
8859 res = gst_pad_peer_query (pad, query);
8862 g_value_set_boolean (value, TRUE);
8866 GST_INFO_OBJECT (pad, "pad peer query failed");
8871 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8872 GstPadDirection direction)
8875 GstIteratorFoldFunction func = pad_query;
8876 GValue res = { 0, };
8878 g_value_init (&res, G_TYPE_BOOLEAN);
8879 g_value_set_boolean (&res, FALSE);
8882 if (direction == GST_PAD_SRC)
8883 it = gst_element_iterate_src_pads (element);
8885 it = gst_element_iterate_sink_pads (element);
8887 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8888 gst_iterator_resync (it);
8890 gst_iterator_free (it);
8892 return g_value_get_boolean (&res);
8896 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8897 QtDemuxStream * stream)
8901 GstElement *element = GST_ELEMENT (qtdemux);
8903 gchar **filtered_sys_ids;
8904 GValue event_list = G_VALUE_INIT;
8907 /* 1. Check if we already have the context. */
8908 if (qtdemux->preferred_protection_system_id != NULL) {
8909 GST_LOG_OBJECT (element,
8910 "already have the protection context, no need to request it again");
8914 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8915 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8916 (const gchar **) qtdemux->protection_system_ids->pdata);
8918 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8919 qtdemux->protection_system_ids->len - 1);
8920 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8921 "decryptors for %u of them, running context request",
8922 qtdemux->protection_system_ids->len,
8923 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8926 if (stream->protection_scheme_event_queue.length) {
8927 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8928 stream->protection_scheme_event_queue.length);
8929 walk = stream->protection_scheme_event_queue.tail;
8931 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8932 qtdemux->protection_event_queue.length);
8933 walk = qtdemux->protection_event_queue.tail;
8936 g_value_init (&event_list, GST_TYPE_LIST);
8937 for (; walk; walk = g_list_previous (walk)) {
8938 GValue *event_value = g_new0 (GValue, 1);
8939 g_value_init (event_value, GST_TYPE_EVENT);
8940 g_value_set_boxed (event_value, walk->data);
8941 gst_value_list_append_and_take_value (&event_list, event_value);
8944 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8945 * check if downstream already has a context of the specific type
8946 * 2b) Query upstream as above.
8948 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8949 st = gst_query_writable_structure (query);
8950 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8951 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8953 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8954 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8955 gst_query_parse_context (query, &ctxt);
8956 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8957 gst_element_set_context (element, ctxt);
8958 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8959 gst_query_parse_context (query, &ctxt);
8960 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8961 gst_element_set_context (element, ctxt);
8963 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8964 * the required context type and afterwards check if a
8965 * usable context was set now as in 1). The message could
8966 * be handled by the parent bins of the element and the
8971 GST_INFO_OBJECT (element, "posting need context message");
8972 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8973 "drm-preferred-decryption-system-id");
8974 st = (GstStructure *) gst_message_get_structure (msg);
8975 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8976 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8979 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8980 gst_element_post_message (element, msg);
8983 g_strfreev (filtered_sys_ids);
8984 g_value_unset (&event_list);
8985 gst_query_unref (query);
8989 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8990 QtDemuxStream * stream)
8993 const gchar *selected_system = NULL;
8995 g_return_val_if_fail (qtdemux != NULL, FALSE);
8996 g_return_val_if_fail (stream != NULL, FALSE);
8997 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
9000 if (stream->protection_scheme_type != FOURCC_cenc) {
9001 GST_ERROR_OBJECT (qtdemux,
9002 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
9003 GST_FOURCC_ARGS (stream->protection_scheme_type));
9006 if (qtdemux->protection_system_ids == NULL) {
9007 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
9008 "cenc protection system information has been found");
9012 gst_qtdemux_request_protection_context (qtdemux, stream);
9013 if (qtdemux->preferred_protection_system_id != NULL) {
9014 const gchar *preferred_system_array[] =
9015 { qtdemux->preferred_protection_system_id, NULL };
9017 selected_system = gst_protection_select_system (preferred_system_array);
9019 if (selected_system) {
9020 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
9021 qtdemux->preferred_protection_system_id);
9023 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
9024 "because there is no available decryptor",
9025 qtdemux->preferred_protection_system_id);
9029 if (!selected_system) {
9030 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9031 selected_system = gst_protection_select_system ((const gchar **)
9032 qtdemux->protection_system_ids->pdata);
9033 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9034 qtdemux->protection_system_ids->len - 1);
9037 if (!selected_system) {
9038 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
9039 "suitable decryptor element has been found");
9043 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
9046 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9047 if (!gst_structure_has_name (s, "application/x-cenc")) {
9048 gst_structure_set (s,
9049 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
9050 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
9052 gst_structure_set_name (s, "application/x-cenc");
9058 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
9060 /* fps is calculated base on the duration of the average framerate since
9061 * qt does not have a fixed framerate. */
9062 gboolean fps_available = TRUE;
9063 guint32 first_duration = 0;
9065 if (stream->n_samples > 0)
9066 first_duration = stream->samples[0].duration;
9068 if ((stream->n_samples == 1 && first_duration == 0)
9069 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
9071 CUR_STREAM (stream)->fps_n = 0;
9072 CUR_STREAM (stream)->fps_d = 1;
9074 if (stream->duration == 0 || stream->n_samples < 2) {
9075 CUR_STREAM (stream)->fps_n = stream->timescale;
9076 CUR_STREAM (stream)->fps_d = 1;
9077 fps_available = FALSE;
9079 GstClockTime avg_duration;
9083 /* duration and n_samples can be updated for fragmented format
9084 * so, framerate of fragmented format is calculated using data in a moof */
9085 if (qtdemux->fragmented && stream->n_samples_moof > 0
9086 && stream->duration_moof > 0) {
9087 n_samples = stream->n_samples_moof;
9088 duration = stream->duration_moof;
9090 n_samples = stream->n_samples;
9091 duration = stream->duration;
9094 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
9095 /* stream->duration is guint64, timescale, n_samples are guint32 */
9097 gst_util_uint64_scale_round (duration -
9098 first_duration, GST_SECOND,
9099 (guint64) (stream->timescale) * (n_samples - 1));
9101 GST_LOG_OBJECT (qtdemux,
9102 "Calculating avg sample duration based on stream (or moof) duration %"
9104 " minus first sample %u, leaving %d samples gives %"
9105 GST_TIME_FORMAT, duration, first_duration,
9106 n_samples - 1, GST_TIME_ARGS (avg_duration));
9109 gst_video_guess_framerate (avg_duration,
9110 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9112 GST_DEBUG_OBJECT (qtdemux,
9113 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
9114 stream->timescale, CUR_STREAM (stream)->fps_n,
9115 CUR_STREAM (stream)->fps_d);
9119 return fps_available;
9123 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
9125 if (stream->subtype == FOURCC_vide) {
9126 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9128 if (CUR_STREAM (stream)->caps) {
9129 CUR_STREAM (stream)->caps =
9130 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9132 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
9133 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9134 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
9135 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
9137 /* set framerate if calculated framerate is reliable */
9138 if (fps_available) {
9139 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9140 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9141 CUR_STREAM (stream)->fps_d, NULL);
9144 /* calculate pixel-aspect-ratio using display width and height */
9145 GST_DEBUG_OBJECT (qtdemux,
9146 "video size %dx%d, target display size %dx%d",
9147 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
9148 stream->display_width, stream->display_height);
9149 /* qt file might have pasp atom */
9150 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9151 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
9152 CUR_STREAM (stream)->par_h);
9153 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9154 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9155 CUR_STREAM (stream)->par_h, NULL);
9156 } else if (stream->display_width > 0 && stream->display_height > 0
9157 && CUR_STREAM (stream)->width > 0
9158 && CUR_STREAM (stream)->height > 0) {
9161 /* calculate the pixel aspect ratio using the display and pixel w/h */
9162 n = stream->display_width * CUR_STREAM (stream)->height;
9163 d = stream->display_height * CUR_STREAM (stream)->width;
9166 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
9167 CUR_STREAM (stream)->par_w = n;
9168 CUR_STREAM (stream)->par_h = d;
9169 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9170 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9171 CUR_STREAM (stream)->par_h, NULL);
9174 if (CUR_STREAM (stream)->interlace_mode > 0) {
9175 if (CUR_STREAM (stream)->interlace_mode == 1) {
9176 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9177 G_TYPE_STRING, "progressive", NULL);
9178 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
9179 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9180 G_TYPE_STRING, "interleaved", NULL);
9181 if (CUR_STREAM (stream)->field_order == 9) {
9182 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9183 G_TYPE_STRING, "top-field-first", NULL);
9184 } else if (CUR_STREAM (stream)->field_order == 14) {
9185 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9186 G_TYPE_STRING, "bottom-field-first", NULL);
9191 /* Create incomplete colorimetry here if needed */
9192 if (CUR_STREAM (stream)->colorimetry.range ||
9193 CUR_STREAM (stream)->colorimetry.matrix ||
9194 CUR_STREAM (stream)->colorimetry.transfer
9195 || CUR_STREAM (stream)->colorimetry.primaries) {
9196 gchar *colorimetry =
9197 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
9198 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
9199 G_TYPE_STRING, colorimetry, NULL);
9200 g_free (colorimetry);
9203 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9204 guint par_w = 1, par_h = 1;
9206 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9207 par_w = CUR_STREAM (stream)->par_w;
9208 par_h = CUR_STREAM (stream)->par_h;
9211 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
9212 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
9214 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9217 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9218 "multiview-mode", G_TYPE_STRING,
9219 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9220 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9221 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9226 else if (stream->subtype == FOURCC_soun) {
9227 if (CUR_STREAM (stream)->caps) {
9228 CUR_STREAM (stream)->caps =
9229 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9230 if (CUR_STREAM (stream)->rate > 0)
9231 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9232 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
9233 if (CUR_STREAM (stream)->n_channels > 0)
9234 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9235 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
9236 if (CUR_STREAM (stream)->n_channels > 2) {
9237 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9238 * correctly; this is just the minimum we can do - assume
9239 * we don't actually have any channel positions. */
9240 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9241 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9246 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
9247 const GstStructure *s;
9248 QtDemuxStream *fps_stream = NULL;
9249 gboolean fps_available = FALSE;
9251 /* CEA608 closed caption tracks are a bit special in that each sample
9252 * can contain CCs for multiple frames, and CCs can be omitted and have to
9253 * be inferred from the duration of the sample then.
9255 * As such we take the framerate from the (first) video track here for
9256 * CEA608 as there must be one CC byte pair for every video frame
9257 * according to the spec.
9259 * For CEA708 all is fine and there is one sample per frame.
9262 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9263 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9266 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
9267 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
9269 if (tmp->subtype == FOURCC_vide) {
9276 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
9277 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
9278 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
9281 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9282 fps_stream = stream;
9285 CUR_STREAM (stream)->caps =
9286 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9288 /* set framerate if calculated framerate is reliable */
9289 if (fps_available) {
9290 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9291 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9292 CUR_STREAM (stream)->fps_d, NULL);
9297 GstCaps *prev_caps = NULL;
9299 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
9300 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
9301 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
9302 gst_pad_set_active (stream->pad, TRUE);
9304 gst_pad_use_fixed_caps (stream->pad);
9306 if (stream->protected) {
9307 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
9308 GST_ERROR_OBJECT (qtdemux,
9309 "Failed to configure protected stream caps.");
9314 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9315 CUR_STREAM (stream)->caps);
9316 if (stream->new_stream) {
9318 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9321 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9324 gst_event_parse_stream_flags (event, &stream_flags);
9325 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9326 qtdemux->have_group_id = TRUE;
9328 qtdemux->have_group_id = FALSE;
9329 gst_event_unref (event);
9330 } else if (!qtdemux->have_group_id) {
9331 qtdemux->have_group_id = TRUE;
9332 qtdemux->group_id = gst_util_group_id_next ();
9335 stream->new_stream = FALSE;
9336 event = gst_event_new_stream_start (stream->stream_id);
9337 if (qtdemux->have_group_id)
9338 gst_event_set_group_id (event, qtdemux->group_id);
9339 if (stream->disabled)
9340 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9341 if (CUR_STREAM (stream)->sparse) {
9342 stream_flags |= GST_STREAM_FLAG_SPARSE;
9344 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9346 gst_event_set_stream_flags (event, stream_flags);
9347 gst_pad_push_event (stream->pad, event);
9350 prev_caps = gst_pad_get_current_caps (stream->pad);
9352 if (CUR_STREAM (stream)->caps) {
9354 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9355 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9356 CUR_STREAM (stream)->caps);
9357 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9359 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9362 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9366 gst_caps_unref (prev_caps);
9367 stream->new_caps = FALSE;
9373 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9374 QtDemuxStream * stream)
9376 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9379 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9380 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9381 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9382 stream->stsd_entries_length)) {
9383 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9384 (_("This file is invalid and cannot be played.")),
9385 ("New sample description id is out of bounds (%d >= %d)",
9386 stream->stsd_sample_description_id, stream->stsd_entries_length));
9388 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9389 stream->new_caps = TRUE;
9394 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9395 QtDemuxStream * stream, GstTagList * list)
9397 gboolean ret = TRUE;
9399 if (stream->subtype == FOURCC_vide) {
9400 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9403 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9406 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9407 gst_object_unref (stream->pad);
9413 qtdemux->n_video_streams++;
9414 } else if (stream->subtype == FOURCC_soun) {
9415 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9418 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9420 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9421 gst_object_unref (stream->pad);
9426 qtdemux->n_audio_streams++;
9427 } else if (stream->subtype == FOURCC_strm) {
9428 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9429 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9430 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9431 || stream->subtype == FOURCC_clcp) {
9432 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9435 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9437 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9438 gst_object_unref (stream->pad);
9443 qtdemux->n_sub_streams++;
9444 } else if (CUR_STREAM (stream)->caps) {
9445 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9448 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9450 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9451 gst_object_unref (stream->pad);
9456 qtdemux->n_video_streams++;
9458 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9465 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9466 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9467 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9468 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9470 if (stream->stream_tags)
9471 gst_tag_list_unref (stream->stream_tags);
9472 stream->stream_tags = list;
9474 /* global tags go on each pad anyway */
9475 stream->send_global_tags = TRUE;
9476 /* send upstream GST_EVENT_PROTECTION events that were received before
9477 this source pad was created */
9478 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9479 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9483 gst_tag_list_unref (list);
9487 /* find next atom with @fourcc starting at @offset */
9488 static GstFlowReturn
9489 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9490 guint64 * length, guint32 fourcc)
9496 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9497 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9503 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9504 if (G_UNLIKELY (ret != GST_FLOW_OK))
9506 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9509 gst_buffer_unref (buf);
9512 gst_buffer_map (buf, &map, GST_MAP_READ);
9513 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9514 gst_buffer_unmap (buf, &map);
9515 gst_buffer_unref (buf);
9517 if (G_UNLIKELY (*length == 0)) {
9518 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9519 ret = GST_FLOW_ERROR;
9523 if (lfourcc == fourcc) {
9524 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
9528 GST_LOG_OBJECT (qtdemux,
9529 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9530 GST_FOURCC_ARGS (fourcc), *offset);
9539 /* might simply have had last one */
9540 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9545 /* should only do something in pull mode */
9546 /* call with OBJECT lock */
9547 static GstFlowReturn
9548 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9550 guint64 length, offset;
9551 GstBuffer *buf = NULL;
9552 GstFlowReturn ret = GST_FLOW_OK;
9553 GstFlowReturn res = GST_FLOW_OK;
9556 offset = qtdemux->moof_offset;
9557 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9560 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9561 return GST_FLOW_EOS;
9564 /* best not do pull etc with lock held */
9565 GST_OBJECT_UNLOCK (qtdemux);
9567 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9568 if (ret != GST_FLOW_OK)
9571 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9572 if (G_UNLIKELY (ret != GST_FLOW_OK))
9574 gst_buffer_map (buf, &map, GST_MAP_READ);
9575 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9576 gst_buffer_unmap (buf, &map);
9577 gst_buffer_unref (buf);
9582 gst_buffer_unmap (buf, &map);
9583 gst_buffer_unref (buf);
9587 /* look for next moof */
9588 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9589 if (G_UNLIKELY (ret != GST_FLOW_OK))
9593 GST_OBJECT_LOCK (qtdemux);
9595 qtdemux->moof_offset = offset;
9601 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9603 res = GST_FLOW_ERROR;
9608 /* maybe upstream temporarily flushing */
9609 if (ret != GST_FLOW_FLUSHING) {
9610 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9613 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9614 /* resume at current position next time */
9621 /* initialise bytereaders for stbl sub-atoms */
9623 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9625 stream->stbl_index = -1; /* no samples have yet been parsed */
9626 stream->sample_index = -1;
9628 /* time-to-sample atom */
9629 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9632 /* copy atom data into a new buffer for later use */
9633 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
9635 /* skip version + flags */
9636 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9637 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9639 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9641 /* make sure there's enough data */
9642 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9643 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9644 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9645 stream->n_sample_times);
9646 if (!stream->n_sample_times)
9650 /* sync sample atom */
9651 stream->stps_present = FALSE;
9652 if ((stream->stss_present =
9653 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9654 &stream->stss) ? TRUE : FALSE) == TRUE) {
9655 /* copy atom data into a new buffer for later use */
9656 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
9658 /* skip version + flags */
9659 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9660 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9663 if (stream->n_sample_syncs) {
9664 /* make sure there's enough data */
9665 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9669 /* partial sync sample atom */
9670 if ((stream->stps_present =
9671 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9672 &stream->stps) ? TRUE : FALSE) == TRUE) {
9673 /* copy atom data into a new buffer for later use */
9674 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
9676 /* skip version + flags */
9677 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9678 !gst_byte_reader_get_uint32_be (&stream->stps,
9679 &stream->n_sample_partial_syncs))
9682 /* if there are no entries, the stss table contains the real
9684 if (stream->n_sample_partial_syncs) {
9685 /* make sure there's enough data */
9686 if (!qt_atom_parser_has_chunks (&stream->stps,
9687 stream->n_sample_partial_syncs, 4))
9694 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9697 /* copy atom data into a new buffer for later use */
9698 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
9700 /* skip version + flags */
9701 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9702 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9705 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9708 if (!stream->n_samples)
9711 /* sample-to-chunk atom */
9712 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9715 /* copy atom data into a new buffer for later use */
9716 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
9718 /* skip version + flags */
9719 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9720 !gst_byte_reader_get_uint32_be (&stream->stsc,
9721 &stream->n_samples_per_chunk))
9724 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9725 stream->n_samples_per_chunk);
9727 /* make sure there's enough data */
9728 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9734 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9735 stream->co_size = sizeof (guint32);
9736 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9738 stream->co_size = sizeof (guint64);
9742 /* copy atom data into a new buffer for later use */
9743 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
9745 /* skip version + flags */
9746 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9749 /* chunks_are_samples == TRUE means treat chunks as samples */
9750 stream->chunks_are_samples = stream->sample_size
9751 && !CUR_STREAM (stream)->sampled;
9752 if (stream->chunks_are_samples) {
9753 /* treat chunks as samples */
9754 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9757 /* skip number of entries */
9758 if (!gst_byte_reader_skip (&stream->stco, 4))
9761 /* make sure there are enough data in the stsz atom */
9762 if (!stream->sample_size) {
9763 /* different sizes for each sample */
9764 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9769 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9770 stream->n_samples, (guint) sizeof (QtDemuxSample),
9771 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9773 if (stream->n_samples >=
9774 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9775 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9776 "be larger than %uMB (broken file?)", stream->n_samples,
9777 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9781 g_assert (stream->samples == NULL);
9782 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9783 if (!stream->samples) {
9784 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9789 /* composition time-to-sample */
9790 if ((stream->ctts_present =
9791 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9792 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9793 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9795 /* copy atom data into a new buffer for later use */
9796 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
9798 /* skip version + flags */
9799 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9800 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9801 &stream->n_composition_times))
9804 /* make sure there's enough data */
9805 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9809 /* This is optional, if missing we iterate the ctts */
9810 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9811 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9812 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9813 g_free ((gpointer) cslg.data);
9817 gint32 cslg_least = 0;
9818 guint num_entries, pos;
9821 pos = gst_byte_reader_get_pos (&stream->ctts);
9822 num_entries = stream->n_composition_times;
9824 stream->cslg_shift = 0;
9826 for (i = 0; i < num_entries; i++) {
9829 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9830 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9831 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9832 * slightly inaccurate PTS could be more usable than corrupted one */
9833 if (G_UNLIKELY ((ABS (offset) / 2) > stream->duration)) {
9834 GST_WARNING_OBJECT (qtdemux,
9835 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9836 " larger than duration %" G_GUINT64_FORMAT,
9837 offset, stream->duration);
9839 stream->cslg_shift = 0;
9840 stream->ctts_present = FALSE;
9844 if (offset < cslg_least)
9845 cslg_least = offset;
9849 stream->cslg_shift = ABS (cslg_least);
9851 stream->cslg_shift = 0;
9853 /* reset the reader so we can generate sample table */
9854 gst_byte_reader_set_pos (&stream->ctts, pos);
9857 /* Ensure the cslg_shift value is consistent so we can use it
9858 * unconditionnally to produce TS and Segment */
9859 stream->cslg_shift = 0;
9866 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9867 (_("This file is corrupt and cannot be played.")), (NULL));
9872 gst_qtdemux_stbl_free (stream);
9873 if (!qtdemux->fragmented) {
9874 /* not quite good */
9875 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9878 /* may pick up samples elsewhere */
9884 /* collect samples from the next sample to be parsed up to sample @n for @stream
9885 * by reading the info from @stbl
9887 * This code can be executed from both the streaming thread and the seeking
9888 * thread so it takes the object lock to protect itself
9891 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9894 QtDemuxSample *samples, *first, *cur, *last;
9895 guint32 n_samples_per_chunk;
9898 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9899 GST_FOURCC_FORMAT ", pad %s",
9900 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9901 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9903 n_samples = stream->n_samples;
9906 goto out_of_samples;
9908 GST_OBJECT_LOCK (qtdemux);
9909 if (n <= stream->stbl_index)
9910 goto already_parsed;
9912 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9914 if (!stream->stsz.data) {
9915 /* so we already parsed and passed all the moov samples;
9916 * onto fragmented ones */
9917 g_assert (qtdemux->fragmented);
9921 /* pointer to the sample table */
9922 samples = stream->samples;
9924 /* starts from -1, moves to the next sample index to parse */
9925 stream->stbl_index++;
9927 /* keep track of the first and last sample to fill */
9928 first = &samples[stream->stbl_index];
9931 if (!stream->chunks_are_samples) {
9932 /* set the sample sizes */
9933 if (stream->sample_size == 0) {
9934 /* different sizes for each sample */
9935 for (cur = first; cur <= last; cur++) {
9936 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9937 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9938 (guint) (cur - samples), cur->size);
9941 /* samples have the same size */
9942 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9943 for (cur = first; cur <= last; cur++)
9944 cur->size = stream->sample_size;
9948 n_samples_per_chunk = stream->n_samples_per_chunk;
9951 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9954 if (stream->stsc_chunk_index >= stream->last_chunk
9955 || stream->stsc_chunk_index < stream->first_chunk) {
9956 stream->first_chunk =
9957 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9958 stream->samples_per_chunk =
9959 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9961 stream->stsd_sample_description_id =
9962 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9964 /* chunk numbers are counted from 1 it seems */
9965 if (G_UNLIKELY (stream->first_chunk == 0))
9968 --stream->first_chunk;
9970 /* the last chunk of each entry is calculated by taking the first chunk
9971 * of the next entry; except if there is no next, where we fake it with
9973 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9974 stream->last_chunk = G_MAXUINT32;
9976 stream->last_chunk =
9977 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9978 if (G_UNLIKELY (stream->last_chunk == 0))
9981 --stream->last_chunk;
9984 GST_LOG_OBJECT (qtdemux,
9985 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9986 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9987 stream->samples_per_chunk, stream->stsd_sample_description_id);
9989 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9992 if (stream->last_chunk != G_MAXUINT32) {
9993 if (!qt_atom_parser_peek_sub (&stream->stco,
9994 stream->first_chunk * stream->co_size,
9995 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10000 stream->co_chunk = stream->stco;
10001 if (!gst_byte_reader_skip (&stream->co_chunk,
10002 stream->first_chunk * stream->co_size))
10006 stream->stsc_chunk_index = stream->first_chunk;
10009 last_chunk = stream->last_chunk;
10011 if (stream->chunks_are_samples) {
10012 cur = &samples[stream->stsc_chunk_index];
10014 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10017 stream->stsc_chunk_index = j;
10022 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
10025 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10026 "%" G_GUINT64_FORMAT, j, cur->offset);
10028 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
10029 CUR_STREAM (stream)->bytes_per_frame > 0) {
10031 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
10032 CUR_STREAM (stream)->samples_per_frame *
10033 CUR_STREAM (stream)->bytes_per_frame;
10035 cur->size = stream->samples_per_chunk;
10038 GST_DEBUG_OBJECT (qtdemux,
10039 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
10040 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
10041 stream->stco_sample_index)), cur->size);
10043 cur->timestamp = stream->stco_sample_index;
10044 cur->duration = stream->samples_per_chunk;
10045 cur->keyframe = TRUE;
10048 stream->stco_sample_index += stream->samples_per_chunk;
10050 stream->stsc_chunk_index = j;
10052 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10053 guint32 samples_per_chunk;
10054 guint64 chunk_offset;
10056 if (!stream->stsc_sample_index
10057 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
10058 &stream->chunk_offset))
10061 samples_per_chunk = stream->samples_per_chunk;
10062 chunk_offset = stream->chunk_offset;
10064 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10065 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10066 G_GUINT64_FORMAT " and size %d",
10067 (guint) (cur - samples), chunk_offset, cur->size);
10069 cur->offset = chunk_offset;
10070 chunk_offset += cur->size;
10073 if (G_UNLIKELY (cur > last)) {
10075 stream->stsc_sample_index = k + 1;
10076 stream->chunk_offset = chunk_offset;
10077 stream->stsc_chunk_index = j;
10081 stream->stsc_sample_index = 0;
10083 stream->stsc_chunk_index = j;
10085 stream->stsc_index++;
10088 if (stream->chunks_are_samples)
10092 guint32 n_sample_times;
10094 n_sample_times = stream->n_sample_times;
10097 for (i = stream->stts_index; i < n_sample_times; i++) {
10098 guint32 stts_samples;
10099 gint32 stts_duration;
10102 if (stream->stts_sample_index >= stream->stts_samples
10103 || !stream->stts_sample_index) {
10105 stream->stts_samples =
10106 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10107 stream->stts_duration =
10108 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10110 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10111 i, stream->stts_samples, stream->stts_duration);
10113 stream->stts_sample_index = 0;
10116 stts_samples = stream->stts_samples;
10117 stts_duration = stream->stts_duration;
10118 stts_time = stream->stts_time;
10120 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10121 GST_DEBUG_OBJECT (qtdemux,
10122 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10123 (guint) (cur - samples), j,
10124 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10126 cur->timestamp = stts_time;
10127 cur->duration = stts_duration;
10129 /* avoid 32-bit wrap-around,
10130 * but still mind possible 'negative' duration */
10131 stts_time += (gint64) stts_duration;
10134 if (G_UNLIKELY (cur > last)) {
10136 stream->stts_time = stts_time;
10137 stream->stts_sample_index = j + 1;
10138 if (stream->stts_sample_index >= stream->stts_samples)
10139 stream->stts_index++;
10143 stream->stts_sample_index = 0;
10144 stream->stts_time = stts_time;
10145 stream->stts_index++;
10147 /* fill up empty timestamps with the last timestamp, this can happen when
10148 * the last samples do not decode and so we don't have timestamps for them.
10149 * We however look at the last timestamp to estimate the track length so we
10150 * need something in here. */
10151 for (; cur < last; cur++) {
10152 GST_DEBUG_OBJECT (qtdemux,
10153 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10154 (guint) (cur - samples),
10155 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10156 cur->timestamp = stream->stts_time;
10157 cur->duration = -1;
10162 /* sample sync, can be NULL */
10163 if (stream->stss_present == TRUE) {
10164 guint32 n_sample_syncs;
10166 n_sample_syncs = stream->n_sample_syncs;
10168 if (!n_sample_syncs) {
10169 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10170 stream->all_keyframe = TRUE;
10172 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10173 /* note that the first sample is index 1, not 0 */
10176 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10178 if (G_LIKELY (index > 0 && index <= n_samples)) {
10180 samples[index].keyframe = TRUE;
10181 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10182 /* and exit if we have enough samples */
10183 if (G_UNLIKELY (index >= n)) {
10190 stream->stss_index = i;
10193 /* stps marks partial sync frames like open GOP I-Frames */
10194 if (stream->stps_present == TRUE) {
10195 guint32 n_sample_partial_syncs;
10197 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10199 /* if there are no entries, the stss table contains the real
10201 if (n_sample_partial_syncs) {
10202 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10203 /* note that the first sample is index 1, not 0 */
10206 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10208 if (G_LIKELY (index > 0 && index <= n_samples)) {
10210 samples[index].keyframe = TRUE;
10211 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10212 /* and exit if we have enough samples */
10213 if (G_UNLIKELY (index >= n)) {
10220 stream->stps_index = i;
10224 /* no stss, all samples are keyframes */
10225 stream->all_keyframe = TRUE;
10226 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10231 /* composition time to sample */
10232 if (stream->ctts_present == TRUE) {
10233 guint32 n_composition_times;
10234 guint32 ctts_count;
10235 gint32 ctts_soffset;
10237 /* Fill in the pts_offsets */
10239 n_composition_times = stream->n_composition_times;
10241 for (i = stream->ctts_index; i < n_composition_times; i++) {
10242 if (stream->ctts_sample_index >= stream->ctts_count
10243 || !stream->ctts_sample_index) {
10244 stream->ctts_count =
10245 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10246 stream->ctts_soffset =
10247 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10248 stream->ctts_sample_index = 0;
10251 ctts_count = stream->ctts_count;
10252 ctts_soffset = stream->ctts_soffset;
10254 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10255 cur->pts_offset = ctts_soffset;
10258 if (G_UNLIKELY (cur > last)) {
10260 stream->ctts_sample_index = j + 1;
10264 stream->ctts_sample_index = 0;
10265 stream->ctts_index++;
10269 stream->stbl_index = n;
10270 /* if index has been completely parsed, free data that is no-longer needed */
10271 if (n + 1 == stream->n_samples) {
10272 gst_qtdemux_stbl_free (stream);
10273 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10274 if (qtdemux->pullbased) {
10275 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10276 while (n + 1 == stream->n_samples)
10277 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10281 GST_OBJECT_UNLOCK (qtdemux);
10288 GST_LOG_OBJECT (qtdemux,
10289 "Tried to parse up to sample %u but this sample has already been parsed",
10291 /* if fragmented, there may be more */
10292 if (qtdemux->fragmented && n == stream->stbl_index)
10294 GST_OBJECT_UNLOCK (qtdemux);
10300 GST_LOG_OBJECT (qtdemux,
10301 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10302 stream->n_samples);
10303 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10304 (_("This file is corrupt and cannot be played.")), (NULL));
10309 GST_OBJECT_UNLOCK (qtdemux);
10310 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10311 (_("This file is corrupt and cannot be played.")), (NULL));
10316 /* collect all segment info for @stream.
10319 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10323 /* accept edts if they contain gaps at start and there is only
10324 * one media segment */
10325 gboolean allow_pushbased_edts = TRUE;
10326 gint media_segments_count = 0;
10328 /* parse and prepare segment info from the edit list */
10329 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10330 stream->n_segments = 0;
10331 stream->segments = NULL;
10332 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10335 gint segment_number, entry_size;
10337 GstClockTime stime;
10338 const guint8 *buffer;
10342 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10343 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10346 buffer = elst->data;
10348 size = QT_UINT32 (buffer);
10349 /* version, flags, n_segments */
10351 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10354 version = QT_UINT8 (buffer + 8);
10355 entry_size = (version == 1) ? 20 : 12;
10357 n_segments = QT_UINT32 (buffer + 12);
10359 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10360 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10364 /* we might allocate a bit too much, at least allocate 1 segment */
10365 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10367 /* segments always start from 0 */
10371 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10373 guint64 media_time;
10374 gboolean empty_edit = FALSE;
10375 QtDemuxSegment *segment;
10377 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10379 if (version == 1) {
10380 media_time = QT_UINT64 (buffer + 8);
10381 duration = QT_UINT64 (buffer);
10382 if (media_time == G_MAXUINT64)
10385 media_time = QT_UINT32 (buffer + 4);
10386 duration = QT_UINT32 (buffer);
10387 if (media_time == G_MAXUINT32)
10392 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
10394 segment = &stream->segments[segment_number];
10396 /* time and duration expressed in global timescale */
10397 segment->time = stime;
10398 if (duration != 0 || empty_edit) {
10399 /* edge case: empty edits with duration=zero are treated here.
10400 * (files should not have these anyway). */
10402 /* add non scaled values so we don't cause roundoff errors */
10404 stime = QTTIME_TO_GSTTIME (qtdemux, time);
10405 segment->duration = stime - segment->time;
10407 /* zero duration does not imply media_start == media_stop
10408 * but, only specify media_start. The edit ends with the track. */
10409 stime = segment->duration = GST_CLOCK_TIME_NONE;
10410 /* Don't allow more edits after this one. */
10411 n_segments = segment_number + 1;
10413 segment->stop_time = stime;
10415 segment->trak_media_start = media_time;
10416 /* media_time expressed in stream timescale */
10418 segment->media_start = media_start;
10419 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10420 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10421 media_segments_count++;
10423 segment->media_start = GST_CLOCK_TIME_NONE;
10424 segment->media_stop = GST_CLOCK_TIME_NONE;
10426 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
10428 if (rate_int <= 1) {
10429 /* 0 is not allowed, some programs write 1 instead of the floating point
10431 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10435 segment->rate = rate_int / 65536.0;
10438 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10439 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10440 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10441 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10442 segment_number, GST_TIME_ARGS (segment->time),
10443 GST_TIME_ARGS (segment->duration),
10444 GST_TIME_ARGS (segment->media_start), media_time,
10445 GST_TIME_ARGS (segment->media_stop),
10446 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10447 stream->timescale);
10448 if (segment->stop_time > qtdemux->segment.stop &&
10449 !qtdemux->upstream_format_is_time) {
10450 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10451 " extends to %" GST_TIME_FORMAT
10452 " past the end of the declared movie duration %" GST_TIME_FORMAT
10453 " movie segment will be extended", segment_number,
10454 GST_TIME_ARGS (segment->stop_time),
10455 GST_TIME_ARGS (qtdemux->segment.stop));
10456 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10459 buffer += entry_size;
10461 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10462 stream->n_segments = n_segments;
10463 if (media_segments_count != 1)
10464 allow_pushbased_edts = FALSE;
10468 /* push based does not handle segments, so act accordingly here,
10469 * and warn if applicable */
10470 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10471 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10472 /* remove and use default one below, we stream like it anyway */
10473 g_free (stream->segments);
10474 stream->segments = NULL;
10475 stream->n_segments = 0;
10478 /* no segments, create one to play the complete trak */
10479 if (stream->n_segments == 0) {
10480 GstClockTime stream_duration =
10481 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
10483 if (stream->segments == NULL)
10484 stream->segments = g_new (QtDemuxSegment, 1);
10486 /* represent unknown our way */
10487 if (stream_duration == 0)
10488 stream_duration = GST_CLOCK_TIME_NONE;
10490 stream->segments[0].time = 0;
10491 stream->segments[0].stop_time = stream_duration;
10492 stream->segments[0].duration = stream_duration;
10493 stream->segments[0].media_start = 0;
10494 stream->segments[0].media_stop = stream_duration;
10495 stream->segments[0].rate = 1.0;
10496 stream->segments[0].trak_media_start = 0;
10498 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10499 GST_TIME_ARGS (stream_duration));
10500 stream->n_segments = 1;
10501 stream->dummy_segment = TRUE;
10503 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10509 * Parses the stsd atom of a svq3 trak looking for
10510 * the SMI and gama atoms.
10513 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
10514 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10516 const guint8 *_gamma = NULL;
10517 GstBuffer *_seqh = NULL;
10518 const guint8 *stsd_data = stsd_entry_data;
10519 guint32 length = QT_UINT32 (stsd_data);
10523 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10529 version = QT_UINT16 (stsd_data);
10530 if (version == 3) {
10531 if (length >= 70) {
10534 while (length > 8) {
10535 guint32 fourcc, size;
10536 const guint8 *data;
10537 size = QT_UINT32 (stsd_data);
10538 fourcc = QT_FOURCC (stsd_data + 4);
10539 data = stsd_data + 8;
10542 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10543 "svq3 atom parsing");
10552 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10553 " for gama atom, expected 12", size);
10558 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
10560 if (_seqh != NULL) {
10561 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10562 " found, ignoring");
10564 seqh_size = QT_UINT32 (data + 4);
10565 if (seqh_size > 0) {
10566 _seqh = gst_buffer_new_and_alloc (seqh_size);
10567 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10574 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10575 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10579 if (size <= length) {
10585 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10588 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10589 G_GUINT16_FORMAT, version);
10599 } else if (_seqh) {
10600 gst_buffer_unref (_seqh);
10605 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10608 GstByteReader dref;
10612 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10613 * atom that might contain a 'data' atom with the rtsp uri.
10614 * This case was reported in bug #597497, some info about
10615 * the hndl atom can be found in TN1195
10617 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10618 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10621 guint32 dref_num_entries = 0;
10622 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10623 gst_byte_reader_skip (&dref, 4) &&
10624 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10627 /* search dref entries for hndl atom */
10628 for (i = 0; i < dref_num_entries; i++) {
10629 guint32 size = 0, type;
10630 guint8 string_len = 0;
10631 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10632 qt_atom_parser_get_fourcc (&dref, &type)) {
10633 if (type == FOURCC_hndl) {
10634 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10636 /* skip data reference handle bytes and the
10637 * following pascal string and some extra 4
10638 * bytes I have no idea what are */
10639 if (!gst_byte_reader_skip (&dref, 4) ||
10640 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10641 !gst_byte_reader_skip (&dref, string_len + 4)) {
10642 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10646 /* iterate over the atoms to find the data atom */
10647 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10651 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10652 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10653 if (atom_type == FOURCC_data) {
10654 const guint8 *uri_aux = NULL;
10656 /* found the data atom that might contain the rtsp uri */
10657 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10658 "hndl atom, interpreting it as an URI");
10659 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10661 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10662 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10664 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10665 "didn't contain a rtsp address");
10667 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10672 /* skipping to the next entry */
10673 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10676 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10683 /* skip to the next entry */
10684 if (!gst_byte_reader_skip (&dref, size - 8))
10687 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10690 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10696 #define AMR_NB_ALL_MODES 0x81ff
10697 #define AMR_WB_ALL_MODES 0x83ff
10699 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10701 /* The 'damr' atom is of the form:
10703 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10704 * 32 b 8 b 16 b 8 b 8 b
10706 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10707 * represents the highest mode used in the stream (and thus the maximum
10708 * bitrate), with a couple of special cases as seen below.
10711 /* Map of frame type ID -> bitrate */
10712 static const guint nb_bitrates[] = {
10713 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10715 static const guint wb_bitrates[] = {
10716 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10722 gst_buffer_map (buf, &map, GST_MAP_READ);
10724 if (map.size != 0x11) {
10725 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10729 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10730 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10731 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10735 mode_set = QT_UINT16 (map.data + 13);
10737 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10738 max_mode = 7 + (wb ? 1 : 0);
10740 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10741 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10743 if (max_mode == -1) {
10744 GST_DEBUG ("No mode indication was found (mode set) = %x",
10749 gst_buffer_unmap (buf, &map);
10750 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10753 gst_buffer_unmap (buf, &map);
10758 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10759 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10762 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10768 if (gst_byte_reader_get_remaining (reader) < 36)
10771 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10772 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10773 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10774 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10775 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10776 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10777 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10778 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10779 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10781 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10782 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10783 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10785 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10786 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10788 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10789 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10796 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10797 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10804 * This macro will only compare value abdegh, it expects cfi to have already
10807 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10808 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10810 /* only handle the cases where the last column has standard values */
10811 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10812 const gchar *rotation_tag = NULL;
10814 /* no rotation needed */
10815 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10817 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10818 rotation_tag = "rotate-90";
10819 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10820 rotation_tag = "rotate-180";
10821 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10822 rotation_tag = "rotate-270";
10824 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10827 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10829 if (rotation_tag != NULL) {
10830 if (*taglist == NULL)
10831 *taglist = gst_tag_list_new_empty ();
10832 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10833 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10836 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10840 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10841 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10842 * Common Encryption (cenc), the function will also parse the tenc box (defined
10843 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10844 * (typically an enc[v|a|t|s] sample entry); the function will set
10845 * @original_fmt to the fourcc of the original unencrypted stream format.
10846 * Returns TRUE if successful; FALSE otherwise. */
10848 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10849 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10855 QtDemuxCencSampleSetInfo *info;
10857 const guint8 *tenc_data;
10859 g_return_val_if_fail (qtdemux != NULL, FALSE);
10860 g_return_val_if_fail (stream != NULL, FALSE);
10861 g_return_val_if_fail (container != NULL, FALSE);
10862 g_return_val_if_fail (original_fmt != NULL, FALSE);
10864 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10865 if (G_UNLIKELY (!sinf)) {
10866 if (stream->protection_scheme_type == FOURCC_cenc) {
10867 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10868 "mandatory for Common Encryption");
10874 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10875 if (G_UNLIKELY (!frma)) {
10876 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10880 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10881 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10882 GST_FOURCC_ARGS (*original_fmt));
10884 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10886 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10889 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10890 stream->protection_scheme_version =
10891 QT_UINT32 ((const guint8 *) schm->data + 16);
10893 GST_DEBUG_OBJECT (qtdemux,
10894 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10895 "protection_scheme_version: %#010x",
10896 GST_FOURCC_ARGS (stream->protection_scheme_type),
10897 stream->protection_scheme_version);
10899 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10901 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10904 if (stream->protection_scheme_type != FOURCC_cenc &&
10905 stream->protection_scheme_type != FOURCC_piff) {
10906 GST_ERROR_OBJECT (qtdemux,
10907 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10908 GST_FOURCC_ARGS (stream->protection_scheme_type));
10912 if (G_UNLIKELY (!stream->protection_scheme_info))
10913 stream->protection_scheme_info =
10914 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10916 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10918 if (stream->protection_scheme_type == FOURCC_cenc) {
10919 guint32 is_encrypted;
10921 const guint8 *default_kid;
10923 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10925 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10926 "which is mandatory for Common Encryption");
10929 tenc_data = (const guint8 *) tenc->data + 12;
10930 is_encrypted = QT_UINT24 (tenc_data);
10931 iv_size = QT_UINT8 (tenc_data + 3);
10932 default_kid = (tenc_data + 4);
10933 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
10934 is_encrypted, iv_size, default_kid);
10935 } else if (stream->protection_scheme_type == FOURCC_piff) {
10937 static const guint8 piff_track_encryption_uuid[] = {
10938 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10939 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10942 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10944 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10945 "which is mandatory for Common Encryption");
10949 tenc_data = (const guint8 *) tenc->data + 8;
10950 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10951 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10952 GST_ERROR_OBJECT (qtdemux,
10953 "Unsupported track encryption box with uuid: %s", box_uuid);
10957 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10958 gst_byte_reader_init (&br, tenc_data, 20);
10959 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10960 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10963 stream->protection_scheme_type = FOURCC_cenc;
10970 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10971 QtDemuxStream ** stream2)
10973 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10976 /* parse the traks.
10977 * With each track we associate a new QtDemuxStream that contains all the info
10979 * traks that do not decode to something (like strm traks) will not have a pad.
10982 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10984 GstByteReader tkhd;
10999 QtDemuxStream *stream = NULL;
11000 const guint8 *stsd_data;
11001 const guint8 *stsd_entry_data;
11002 guint remaining_stsd_len;
11003 guint stsd_entry_count;
11005 guint16 lang_code; /* quicktime lang code or packed iso code */
11007 guint32 tkhd_flags = 0;
11008 guint8 tkhd_version = 0;
11009 guint32 w = 0, h = 0;
11010 guint value_size, stsd_len, len;
11014 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11016 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11017 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11018 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11021 /* pick between 64 or 32 bits */
11022 value_size = tkhd_version == 1 ? 8 : 4;
11023 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11024 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11027 /* Check if current moov has duplicated track_id */
11028 if (qtdemux_find_stream (qtdemux, track_id))
11029 goto existing_stream;
11031 stream = _create_stream (qtdemux, track_id);
11032 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11034 /* need defaults for fragments */
11035 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11037 if ((tkhd_flags & 1) == 0)
11038 stream->disabled = TRUE;
11040 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11041 tkhd_version, tkhd_flags, stream->track_id);
11043 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11046 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11047 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11048 if (qtdemux->major_brand != FOURCC_mjp2 ||
11049 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11053 len = QT_UINT32 ((guint8 *) mdhd->data);
11054 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11055 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11056 if (version == 0x01000000) {
11059 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11060 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11061 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11065 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11066 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11067 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11070 if (lang_code < 0x400) {
11071 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11072 } else if (lang_code == 0x7fff) {
11073 stream->lang_id[0] = 0; /* unspecified */
11075 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11076 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11077 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11078 stream->lang_id[3] = 0;
11081 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11082 stream->timescale);
11083 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11085 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11086 lang_code, stream->lang_id);
11088 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11091 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11092 /* chapters track reference */
11093 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11095 gsize length = GST_READ_UINT32_BE (chap->data);
11096 if (qtdemux->chapters_track_id)
11097 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11099 if (length >= 12) {
11100 qtdemux->chapters_track_id =
11101 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11106 /* fragmented files may have bogus duration in moov */
11107 if (!qtdemux->fragmented &&
11108 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11109 guint64 tdur1, tdur2;
11111 /* don't overflow */
11112 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11113 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11116 * some of those trailers, nowadays, have prologue images that are
11117 * themselves video tracks as well. I haven't really found a way to
11118 * identify those yet, except for just looking at their duration. */
11119 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11120 GST_WARNING_OBJECT (qtdemux,
11121 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11122 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11123 "found, assuming preview image or something; skipping track",
11124 stream->duration, stream->timescale, qtdemux->duration,
11125 qtdemux->timescale);
11126 gst_qtdemux_stream_unref (stream);
11131 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11134 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11135 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11137 len = QT_UINT32 ((guint8 *) hdlr->data);
11139 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11140 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11141 GST_FOURCC_ARGS (stream->subtype));
11143 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11146 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11149 /*parse svmi header if existing */
11150 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11152 len = QT_UINT32 ((guint8 *) svmi->data);
11153 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11155 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11156 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11157 guint8 frame_type, frame_layout;
11159 /* MPEG-A stereo video */
11160 if (qtdemux->major_brand == FOURCC_ss02)
11161 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11163 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11164 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11165 switch (frame_type) {
11167 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11170 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11173 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11176 /* mode 3 is primary/secondary view sequence, ie
11177 * left/right views in separate tracks. See section 7.2
11178 * of ISO/IEC 23000-11:2009 */
11179 GST_FIXME_OBJECT (qtdemux,
11180 "Implement stereo video in separate streams");
11183 if ((frame_layout & 0x1) == 0)
11184 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11186 GST_LOG_OBJECT (qtdemux,
11187 "StereoVideo: composition type: %u, is_left_first: %u",
11188 frame_type, frame_layout);
11189 stream->multiview_mode = mode;
11190 stream->multiview_flags = flags;
11194 /* parse rest of tkhd */
11195 if (stream->subtype == FOURCC_vide) {
11198 /* version 1 uses some 64-bit ints */
11199 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11202 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11205 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11206 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11209 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11210 &stream->stream_tags);
11214 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11216 stsd_data = (const guint8 *) stsd->data;
11218 /* stsd should at least have one entry */
11219 stsd_len = QT_UINT32 (stsd_data);
11220 if (stsd_len < 24) {
11221 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11222 if (stream->subtype == FOURCC_vivo) {
11223 gst_qtdemux_stream_unref (stream);
11230 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11231 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11232 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11233 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11235 stsd_entry_data = stsd_data + 16;
11236 remaining_stsd_len = stsd_len - 16;
11237 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11239 gchar *codec = NULL;
11240 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11242 /* and that entry should fit within stsd */
11243 len = QT_UINT32 (stsd_entry_data);
11244 if (len > remaining_stsd_len)
11247 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11248 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11249 GST_FOURCC_ARGS (entry->fourcc));
11250 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11252 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11253 goto error_encrypted;
11255 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11256 /* FIXME this looks wrong, there might be multiple children
11257 * with the same type */
11258 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11259 stream->protected = TRUE;
11260 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11261 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11264 if (stream->subtype == FOURCC_vide) {
11269 gint depth, palette_size, palette_count;
11270 guint32 *palette_data = NULL;
11272 entry->sampled = TRUE;
11274 stream->display_width = w >> 16;
11275 stream->display_height = h >> 16;
11278 if (len < 86) /* TODO verify */
11281 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
11282 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
11283 entry->fps_n = 0; /* this is filled in later */
11284 entry->fps_d = 0; /* this is filled in later */
11285 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
11286 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
11288 /* if color_table_id is 0, ctab atom must follow; however some files
11289 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11290 * if color table is not present we'll correct the value */
11291 if (entry->color_table_id == 0 &&
11293 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
11294 entry->color_table_id = -1;
11297 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11298 entry->width, entry->height, entry->bits_per_sample,
11299 entry->color_table_id);
11301 depth = entry->bits_per_sample;
11303 /* more than 32 bits means grayscale */
11304 gray = (depth > 32);
11305 /* low 32 bits specify the depth */
11308 /* different number of palette entries is determined by depth. */
11310 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11311 palette_count = (1 << depth);
11312 palette_size = palette_count * 4;
11314 if (entry->color_table_id) {
11315 switch (palette_count) {
11319 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
11322 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
11327 g_memdup (ff_qt_grayscale_palette_16, palette_size);
11329 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
11334 g_memdup (ff_qt_grayscale_palette_256, palette_size);
11336 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
11339 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11340 (_("The video in this file might not play correctly.")),
11341 ("unsupported palette depth %d", depth));
11345 gint i, j, start, end;
11351 start = QT_UINT32 (stsd_entry_data + offset + 70);
11352 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
11353 end = QT_UINT16 (stsd_entry_data + offset + 76);
11355 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11356 start, end, palette_count);
11363 if (len < 94 + (end - start) * 8)
11366 /* palette is always the same size */
11367 palette_data = g_malloc0 (256 * 4);
11368 palette_size = 256 * 4;
11370 for (j = 0, i = start; i <= end; j++, i++) {
11371 guint32 a, r, g, b;
11373 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11374 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11375 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11376 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
11378 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11379 (g & 0xff00) | (b >> 8);
11384 gst_caps_unref (entry->caps);
11387 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
11389 if (G_UNLIKELY (!entry->caps)) {
11390 g_free (palette_data);
11391 goto unknown_stream;
11395 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11396 GST_TAG_VIDEO_CODEC, codec, NULL);
11401 if (palette_data) {
11404 if (entry->rgb8_palette)
11405 gst_memory_unref (entry->rgb8_palette);
11406 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
11407 palette_data, palette_size, 0, palette_size, palette_data, g_free);
11409 s = gst_caps_get_structure (entry->caps, 0);
11411 /* non-raw video has a palette_data property. raw video has the palette as
11412 * an extra plane that we append to the output buffers before we push
11414 if (!gst_structure_has_name (s, "video/x-raw")) {
11415 GstBuffer *palette;
11417 palette = gst_buffer_new ();
11418 gst_buffer_append_memory (palette, entry->rgb8_palette);
11419 entry->rgb8_palette = NULL;
11421 gst_caps_set_simple (entry->caps, "palette_data",
11422 GST_TYPE_BUFFER, palette, NULL);
11423 gst_buffer_unref (palette);
11425 } else if (palette_count != 0) {
11426 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
11427 (NULL), ("Unsupported palette depth %d", depth));
11430 GST_LOG_OBJECT (qtdemux, "frame count: %u",
11431 QT_UINT16 (stsd_entry_data + offset + 32));
11437 /* pick 'the' stsd child */
11438 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11439 // We should skip parsing the stsd for non-protected streams if
11440 // the entry doesn't match the fourcc, since they don't change
11441 // format. However, for protected streams we can have partial
11442 // encryption, where parts of the stream are encrypted and parts
11443 // not. For both parts of such streams, we should ensure the
11444 // esds overrides are parsed for both from the stsd.
11445 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
11446 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
11448 else if (!stream->protected)
11453 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
11454 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
11455 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
11456 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
11460 const guint8 *pasp_data = (const guint8 *) pasp->data;
11461 gint len = QT_UINT32 (pasp_data);
11464 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
11465 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
11467 CUR_STREAM (stream)->par_w = 0;
11468 CUR_STREAM (stream)->par_h = 0;
11471 CUR_STREAM (stream)->par_w = 0;
11472 CUR_STREAM (stream)->par_h = 0;
11476 const guint8 *fiel_data = (const guint8 *) fiel->data;
11477 gint len = QT_UINT32 (fiel_data);
11480 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
11481 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
11486 const guint8 *colr_data = (const guint8 *) colr->data;
11487 gint len = QT_UINT32 (colr_data);
11489 if (len == 19 || len == 18) {
11490 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
11492 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
11493 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
11494 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
11495 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
11496 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
11498 switch (primaries) {
11500 CUR_STREAM (stream)->colorimetry.primaries =
11501 GST_VIDEO_COLOR_PRIMARIES_BT709;
11504 CUR_STREAM (stream)->colorimetry.primaries =
11505 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
11508 CUR_STREAM (stream)->colorimetry.primaries =
11509 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
11512 CUR_STREAM (stream)->colorimetry.primaries =
11513 GST_VIDEO_COLOR_PRIMARIES_BT2020;
11519 switch (transfer_function) {
11521 CUR_STREAM (stream)->colorimetry.transfer =
11522 GST_VIDEO_TRANSFER_BT709;
11525 CUR_STREAM (stream)->colorimetry.transfer =
11526 GST_VIDEO_TRANSFER_SMPTE240M;
11534 CUR_STREAM (stream)->colorimetry.matrix =
11535 GST_VIDEO_COLOR_MATRIX_BT709;
11538 CUR_STREAM (stream)->colorimetry.matrix =
11539 GST_VIDEO_COLOR_MATRIX_BT601;
11542 CUR_STREAM (stream)->colorimetry.matrix =
11543 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
11546 CUR_STREAM (stream)->colorimetry.matrix =
11547 GST_VIDEO_COLOR_MATRIX_BT2020;
11553 CUR_STREAM (stream)->colorimetry.range =
11554 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
11555 GST_VIDEO_COLOR_RANGE_16_235;
11557 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
11560 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
11565 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11566 stream->stream_tags);
11573 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11574 const guint8 *avc_data = stsd_entry_data + 0x56;
11577 while (len >= 0x8) {
11580 if (QT_UINT32 (avc_data) <= len)
11581 size = QT_UINT32 (avc_data) - 0x8;
11586 /* No real data, so break out */
11589 switch (QT_FOURCC (avc_data + 0x4)) {
11592 /* parse, if found */
11595 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11597 /* First 4 bytes are the length of the atom, the next 4 bytes
11598 * are the fourcc, the next 1 byte is the version, and the
11599 * subsequent bytes are profile_tier_level structure like data. */
11600 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11601 avc_data + 8 + 1, size - 1);
11602 buf = gst_buffer_new_and_alloc (size);
11603 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11604 gst_caps_set_simple (entry->caps,
11605 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11606 gst_buffer_unref (buf);
11614 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11616 /* First 4 bytes are the length of the atom, the next 4 bytes
11617 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11618 * next 1 byte is the version, and the
11619 * subsequent bytes are sequence parameter set like data. */
11621 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11623 gst_codec_utils_h264_caps_set_level_and_profile
11624 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11626 buf = gst_buffer_new_and_alloc (size);
11627 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11628 gst_caps_set_simple (entry->caps,
11629 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11630 gst_buffer_unref (buf);
11636 guint avg_bitrate, max_bitrate;
11638 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11642 max_bitrate = QT_UINT32 (avc_data + 0xc);
11643 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11645 if (!max_bitrate && !avg_bitrate)
11648 /* Some muxers seem to swap the average and maximum bitrates
11649 * (I'm looking at you, YouTube), so we swap for sanity. */
11650 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11651 guint temp = avg_bitrate;
11653 avg_bitrate = max_bitrate;
11654 max_bitrate = temp;
11657 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11658 gst_tag_list_add (stream->stream_tags,
11659 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11660 max_bitrate, NULL);
11662 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11663 gst_tag_list_add (stream->stream_tags,
11664 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11676 avc_data += size + 8;
11685 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11686 const guint8 *hevc_data = stsd_entry_data + 0x56;
11689 while (len >= 0x8) {
11692 if (QT_UINT32 (hevc_data) <= len)
11693 size = QT_UINT32 (hevc_data) - 0x8;
11698 /* No real data, so break out */
11701 switch (QT_FOURCC (hevc_data + 0x4)) {
11704 /* parse, if found */
11707 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11709 /* First 4 bytes are the length of the atom, the next 4 bytes
11710 * are the fourcc, the next 1 byte is the version, and the
11711 * subsequent bytes are sequence parameter set like data. */
11712 gst_codec_utils_h265_caps_set_level_tier_and_profile
11713 (entry->caps, hevc_data + 8 + 1, size - 1);
11715 buf = gst_buffer_new_and_alloc (size);
11716 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11717 gst_caps_set_simple (entry->caps,
11718 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11719 gst_buffer_unref (buf);
11726 hevc_data += size + 8;
11739 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11740 GST_FOURCC_ARGS (fourcc));
11742 /* codec data might be in glbl extension atom */
11744 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11750 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11752 len = QT_UINT32 (data);
11755 buf = gst_buffer_new_and_alloc (len);
11756 gst_buffer_fill (buf, 0, data + 8, len);
11757 gst_caps_set_simple (entry->caps,
11758 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11759 gst_buffer_unref (buf);
11766 /* see annex I of the jpeg2000 spec */
11767 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11768 const guint8 *data;
11769 const gchar *colorspace = NULL;
11771 guint32 ncomp_map = 0;
11772 gint32 *comp_map = NULL;
11773 guint32 nchan_def = 0;
11774 gint32 *chan_def = NULL;
11776 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11777 /* some required atoms */
11778 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11781 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11785 /* number of components; redundant with info in codestream, but useful
11787 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11788 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11790 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11792 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11795 GST_DEBUG_OBJECT (qtdemux, "found colr");
11796 /* extract colour space info */
11797 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11798 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11800 colorspace = "sRGB";
11803 colorspace = "GRAY";
11806 colorspace = "sYUV";
11814 /* colr is required, and only values 16, 17, and 18 are specified,
11815 so error if we have no colorspace */
11818 /* extract component mapping */
11819 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11821 guint32 cmap_len = 0;
11823 cmap_len = QT_UINT32 (cmap->data);
11824 if (cmap_len >= 8) {
11825 /* normal box, subtract off header */
11827 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11828 if (cmap_len % 4 == 0) {
11829 ncomp_map = (cmap_len / 4);
11830 comp_map = g_new0 (gint32, ncomp_map);
11831 for (i = 0; i < ncomp_map; i++) {
11834 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11835 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11836 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11837 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11842 /* extract channel definitions */
11843 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11845 guint32 cdef_len = 0;
11847 cdef_len = QT_UINT32 (cdef->data);
11848 if (cdef_len >= 10) {
11849 /* normal box, subtract off header and len */
11851 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11852 if (cdef_len % 6 == 0) {
11853 nchan_def = (cdef_len / 6);
11854 chan_def = g_new0 (gint32, nchan_def);
11855 for (i = 0; i < nchan_def; i++)
11857 for (i = 0; i < nchan_def; i++) {
11858 guint16 cn, typ, asoc;
11859 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11860 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11861 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11862 if (cn < nchan_def) {
11865 chan_def[cn] = asoc;
11868 chan_def[cn] = 0; /* alpha */
11871 chan_def[cn] = -typ;
11879 gst_caps_set_simple (entry->caps,
11880 "num-components", G_TYPE_INT, ncomp, NULL);
11881 gst_caps_set_simple (entry->caps,
11882 "colorspace", G_TYPE_STRING, colorspace, NULL);
11885 GValue arr = { 0, };
11886 GValue elt = { 0, };
11888 g_value_init (&arr, GST_TYPE_ARRAY);
11889 g_value_init (&elt, G_TYPE_INT);
11890 for (i = 0; i < ncomp_map; i++) {
11891 g_value_set_int (&elt, comp_map[i]);
11892 gst_value_array_append_value (&arr, &elt);
11894 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11895 "component-map", &arr);
11896 g_value_unset (&elt);
11897 g_value_unset (&arr);
11902 GValue arr = { 0, };
11903 GValue elt = { 0, };
11905 g_value_init (&arr, GST_TYPE_ARRAY);
11906 g_value_init (&elt, G_TYPE_INT);
11907 for (i = 0; i < nchan_def; i++) {
11908 g_value_set_int (&elt, chan_def[i]);
11909 gst_value_array_append_value (&arr, &elt);
11911 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11912 "channel-definitions", &arr);
11913 g_value_unset (&elt);
11914 g_value_unset (&arr);
11918 /* some optional atoms */
11919 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11920 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11922 /* indicate possible fields in caps */
11924 data = (guint8 *) field->data + 8;
11926 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11927 (gint) * data, NULL);
11929 /* add codec_data if provided */
11934 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11935 data = prefix->data;
11936 len = QT_UINT32 (data);
11939 buf = gst_buffer_new_and_alloc (len);
11940 gst_buffer_fill (buf, 0, data + 8, len);
11941 gst_caps_set_simple (entry->caps,
11942 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11943 gst_buffer_unref (buf);
11952 GstBuffer *seqh = NULL;
11953 const guint8 *gamma_data = NULL;
11954 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11956 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11959 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11960 QT_FP32 (gamma_data), NULL);
11963 /* sorry for the bad name, but we don't know what this is, other
11964 * than its own fourcc */
11965 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11967 gst_buffer_unref (seqh);
11970 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11971 buf = gst_buffer_new_and_alloc (len);
11972 gst_buffer_fill (buf, 0, stsd_data, len);
11973 gst_caps_set_simple (entry->caps,
11974 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11975 gst_buffer_unref (buf);
11980 /* https://developer.apple.com/standards/qtff-2001.pdf,
11981 * page 92, "Video Sample Description", under table 3.1 */
11984 const gint compressor_offset =
11985 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11986 const gint min_size = compressor_offset + 32 + 2 + 2;
11989 guint16 color_table_id = 0;
11992 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11994 /* recover information on interlaced/progressive */
11995 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11999 len = QT_UINT32 (jpeg->data);
12000 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12002 if (len >= min_size) {
12003 gst_byte_reader_init (&br, jpeg->data, len);
12005 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12006 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12007 if (color_table_id != 0) {
12008 /* the spec says there can be concatenated chunks in the data, and we want
12009 * to find one called field. Walk through them. */
12010 gint offset = min_size;
12011 while (offset + 8 < len) {
12012 guint32 size = 0, tag;
12013 ok = gst_byte_reader_get_uint32_le (&br, &size);
12014 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12015 if (!ok || size < 8) {
12016 GST_WARNING_OBJECT (qtdemux,
12017 "Failed to walk optional chunk list");
12020 GST_DEBUG_OBJECT (qtdemux,
12021 "Found optional %4.4s chunk, size %u",
12022 (const char *) &tag, size);
12023 if (tag == FOURCC_fiel) {
12024 guint8 n_fields = 0, ordering = 0;
12025 gst_byte_reader_get_uint8 (&br, &n_fields);
12026 gst_byte_reader_get_uint8 (&br, &ordering);
12027 if (n_fields == 1 || n_fields == 2) {
12028 GST_DEBUG_OBJECT (qtdemux,
12029 "Found fiel tag with %u fields, ordering %u",
12030 n_fields, ordering);
12032 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12033 "interlace-mode", G_TYPE_STRING, "interleaved",
12036 GST_WARNING_OBJECT (qtdemux,
12037 "Found fiel tag with invalid fields (%u)", n_fields);
12043 GST_DEBUG_OBJECT (qtdemux,
12044 "Color table ID is 0, not trying to get interlacedness");
12047 GST_WARNING_OBJECT (qtdemux,
12048 "Length of jpeg chunk is too small, not trying to get interlacedness");
12056 gst_caps_set_simple (entry->caps,
12057 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12063 GNode *xith, *xdxt;
12065 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12066 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12070 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12074 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12075 /* collect the headers and store them in a stream list so that we can
12076 * send them out first */
12077 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12087 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12088 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12091 ovc1_data = ovc1->data;
12092 ovc1_len = QT_UINT32 (ovc1_data);
12093 if (ovc1_len <= 198) {
12094 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12097 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12098 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12099 gst_caps_set_simple (entry->caps,
12100 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12101 gst_buffer_unref (buf);
12106 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12107 const guint8 *vc1_data = stsd_entry_data + 0x56;
12113 if (QT_UINT32 (vc1_data) <= len)
12114 size = QT_UINT32 (vc1_data) - 8;
12119 /* No real data, so break out */
12122 switch (QT_FOURCC (vc1_data + 0x4)) {
12123 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12127 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12128 buf = gst_buffer_new_and_alloc (size);
12129 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12130 gst_caps_set_simple (entry->caps,
12131 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12132 gst_buffer_unref (buf);
12139 vc1_data += size + 8;
12145 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12146 const guint8 *av1_data = stsd_entry_data + 0x56;
12149 while (len >= 0x8) {
12152 if (QT_UINT32 (av1_data) <= len)
12153 size = QT_UINT32 (av1_data) - 0x8;
12158 /* No real data, so break out */
12161 switch (QT_FOURCC (av1_data + 0x4)) {
12164 /* parse, if found */
12166 guint8 pres_delay_field;
12168 GST_DEBUG_OBJECT (qtdemux,
12169 "found av1C codec_data in stsd of size %d", size);
12171 /* not enough data, just ignore and hope for the best */
12176 * 4 bytes: atom length
12181 * 1 bits: initial_presentation_delay_present
12182 * 4 bits: initial_presentation_delay (if present else reserved
12186 if (av1_data[9] != 0) {
12187 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
12191 /* We skip initial_presentation_delay* for now */
12192 pres_delay_field = *(av1_data + 12);
12193 if (pres_delay_field & (1 << 5)) {
12194 gst_caps_set_simple (entry->caps,
12195 "presentation-delay", G_TYPE_INT,
12196 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12199 buf = gst_buffer_new_and_alloc (size - 5);
12200 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12201 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
12202 gst_caps_set_simple (entry->caps,
12203 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12204 gst_buffer_unref (buf);
12213 av1_data += size + 8;
12223 GST_INFO_OBJECT (qtdemux,
12224 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12225 GST_FOURCC_ARGS (fourcc), entry->caps);
12227 } else if (stream->subtype == FOURCC_soun) {
12229 int version, samplesize;
12230 guint16 compression_id;
12231 gboolean amrwb = FALSE;
12234 /* sample description entry (16) + sound sample description v0 (20) */
12238 version = QT_UINT32 (stsd_entry_data + offset);
12239 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
12240 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
12241 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
12242 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
12244 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
12245 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
12246 QT_UINT32 (stsd_entry_data + offset + 4));
12247 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12248 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
12249 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
12250 GST_LOG_OBJECT (qtdemux, "packet size: %d",
12251 QT_UINT16 (stsd_entry_data + offset + 14));
12252 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12254 if (compression_id == 0xfffe)
12255 entry->sampled = TRUE;
12257 /* first assume uncompressed audio */
12258 entry->bytes_per_sample = samplesize / 8;
12259 entry->samples_per_frame = entry->n_channels;
12260 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12261 entry->samples_per_packet = entry->samples_per_frame;
12262 entry->bytes_per_packet = entry->bytes_per_sample;
12266 /* Yes, these have to be hard-coded */
12269 entry->samples_per_packet = 6;
12270 entry->bytes_per_packet = 1;
12271 entry->bytes_per_frame = 1 * entry->n_channels;
12272 entry->bytes_per_sample = 1;
12273 entry->samples_per_frame = 6 * entry->n_channels;
12278 entry->samples_per_packet = 3;
12279 entry->bytes_per_packet = 1;
12280 entry->bytes_per_frame = 1 * entry->n_channels;
12281 entry->bytes_per_sample = 1;
12282 entry->samples_per_frame = 3 * entry->n_channels;
12287 entry->samples_per_packet = 64;
12288 entry->bytes_per_packet = 34;
12289 entry->bytes_per_frame = 34 * entry->n_channels;
12290 entry->bytes_per_sample = 2;
12291 entry->samples_per_frame = 64 * entry->n_channels;
12297 entry->samples_per_packet = 1;
12298 entry->bytes_per_packet = 1;
12299 entry->bytes_per_frame = 1 * entry->n_channels;
12300 entry->bytes_per_sample = 1;
12301 entry->samples_per_frame = 1 * entry->n_channels;
12306 entry->samples_per_packet = 160;
12307 entry->bytes_per_packet = 33;
12308 entry->bytes_per_frame = 33 * entry->n_channels;
12309 entry->bytes_per_sample = 2;
12310 entry->samples_per_frame = 160 * entry->n_channels;
12317 if (version == 0x00010000) {
12318 /* sample description entry (16) + sound sample description v1 (20+16) */
12330 /* only parse extra decoding config for non-pcm audio */
12331 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
12332 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
12333 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
12334 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
12336 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
12337 entry->samples_per_packet);
12338 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12339 entry->bytes_per_packet);
12340 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
12341 entry->bytes_per_frame);
12342 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
12343 entry->bytes_per_sample);
12345 if (!entry->sampled && entry->bytes_per_packet) {
12346 entry->samples_per_frame = (entry->bytes_per_frame /
12347 entry->bytes_per_packet) * entry->samples_per_packet;
12348 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
12349 entry->samples_per_frame);
12354 } else if (version == 0x00020000) {
12361 /* sample description entry (16) + sound sample description v2 (56) */
12365 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
12366 entry->rate = qtfp.fp;
12367 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
12369 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
12370 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12371 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12372 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
12373 QT_UINT32 (stsd_entry_data + offset + 20));
12374 GST_LOG_OBJECT (qtdemux, "format flags: %X",
12375 QT_UINT32 (stsd_entry_data + offset + 24));
12376 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12377 QT_UINT32 (stsd_entry_data + offset + 28));
12378 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
12379 QT_UINT32 (stsd_entry_data + offset + 32));
12380 } else if (version != 0x00000) {
12381 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
12386 gst_caps_unref (entry->caps);
12388 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
12389 stsd_entry_data + 32, len - 16, &codec);
12397 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
12399 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
12401 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
12403 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
12406 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
12407 gst_caps_set_simple (entry->caps,
12408 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
12415 const guint8 *owma_data;
12416 const gchar *codec_name = NULL;
12420 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12421 /* FIXME this should also be gst_riff_strf_auds,
12422 * but the latter one is actually missing bits-per-sample :( */
12427 gint32 nSamplesPerSec;
12428 gint32 nAvgBytesPerSec;
12429 gint16 nBlockAlign;
12430 gint16 wBitsPerSample;
12433 WAVEFORMATEX *wfex;
12435 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12436 owma_data = stsd_entry_data;
12437 owma_len = QT_UINT32 (owma_data);
12438 if (owma_len <= 54) {
12439 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12442 wfex = (WAVEFORMATEX *) (owma_data + 36);
12443 buf = gst_buffer_new_and_alloc (owma_len - 54);
12444 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12445 if (wfex->wFormatTag == 0x0161) {
12446 codec_name = "Windows Media Audio";
12448 } else if (wfex->wFormatTag == 0x0162) {
12449 codec_name = "Windows Media Audio 9 Pro";
12451 } else if (wfex->wFormatTag == 0x0163) {
12452 codec_name = "Windows Media Audio 9 Lossless";
12453 /* is that correct? gstffmpegcodecmap.c is missing it, but
12454 * fluendo codec seems to support it */
12458 gst_caps_set_simple (entry->caps,
12459 "codec_data", GST_TYPE_BUFFER, buf,
12460 "wmaversion", G_TYPE_INT, version,
12461 "block_align", G_TYPE_INT,
12462 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12463 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
12464 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
12465 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
12466 gst_buffer_unref (buf);
12470 codec = g_strdup (codec_name);
12476 gint len = QT_UINT32 (stsd_entry_data) - offset;
12477 const guint8 *wfex_data = stsd_entry_data + offset;
12478 const gchar *codec_name = NULL;
12480 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12481 /* FIXME this should also be gst_riff_strf_auds,
12482 * but the latter one is actually missing bits-per-sample :( */
12487 gint32 nSamplesPerSec;
12488 gint32 nAvgBytesPerSec;
12489 gint16 nBlockAlign;
12490 gint16 wBitsPerSample;
12495 /* FIXME: unify with similar wavformatex parsing code above */
12496 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
12502 if (QT_UINT32 (wfex_data) <= len)
12503 size = QT_UINT32 (wfex_data) - 8;
12508 /* No real data, so break out */
12511 switch (QT_FOURCC (wfex_data + 4)) {
12512 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
12514 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
12519 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
12520 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
12521 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
12522 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
12523 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
12524 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
12525 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
12527 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
12528 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
12529 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
12530 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
12531 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
12532 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
12534 if (wfex.wFormatTag == 0x0161) {
12535 codec_name = "Windows Media Audio";
12537 } else if (wfex.wFormatTag == 0x0162) {
12538 codec_name = "Windows Media Audio 9 Pro";
12540 } else if (wfex.wFormatTag == 0x0163) {
12541 codec_name = "Windows Media Audio 9 Lossless";
12542 /* is that correct? gstffmpegcodecmap.c is missing it, but
12543 * fluendo codec seems to support it */
12547 gst_caps_set_simple (entry->caps,
12548 "wmaversion", G_TYPE_INT, version,
12549 "block_align", G_TYPE_INT, wfex.nBlockAlign,
12550 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
12551 "width", G_TYPE_INT, wfex.wBitsPerSample,
12552 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
12554 if (size > wfex.cbSize) {
12557 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
12558 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
12559 size - wfex.cbSize);
12560 gst_caps_set_simple (entry->caps,
12561 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12562 gst_buffer_unref (buf);
12564 GST_WARNING_OBJECT (qtdemux, "no codec data");
12569 codec = g_strdup (codec_name);
12577 wfex_data += size + 8;
12583 const guint8 *opus_data;
12584 guint8 *channel_mapping = NULL;
12587 guint8 channel_mapping_family;
12588 guint8 stream_count;
12589 guint8 coupled_count;
12592 opus_data = stsd_entry_data;
12594 channels = GST_READ_UINT8 (opus_data + 45);
12595 rate = GST_READ_UINT32_LE (opus_data + 48);
12596 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12597 stream_count = GST_READ_UINT8 (opus_data + 55);
12598 coupled_count = GST_READ_UINT8 (opus_data + 56);
12600 if (channels > 0) {
12601 channel_mapping = g_malloc (channels * sizeof (guint8));
12602 for (i = 0; i < channels; i++)
12603 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12606 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12607 channel_mapping_family, stream_count, coupled_count,
12619 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12620 GST_TAG_AUDIO_CODEC, codec, NULL);
12624 /* some bitrate info may have ended up in caps */
12625 s = gst_caps_get_structure (entry->caps, 0);
12626 gst_structure_get_int (s, "bitrate", &bitrate);
12628 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12629 GST_TAG_BITRATE, bitrate, NULL);
12632 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12633 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12634 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12636 else if (!stream->protected)
12643 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12645 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12647 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12651 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12652 16 bits is a byte-swapped wave-style codec identifier,
12653 and we can find a WAVE header internally to a 'wave' atom here.
12654 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12655 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12658 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12659 if (len < offset + 20) {
12660 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12662 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12663 const guint8 *data = stsd_entry_data + offset + 16;
12665 GNode *waveheadernode;
12667 wavenode = g_node_new ((guint8 *) data);
12668 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12669 const guint8 *waveheader;
12672 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12673 if (waveheadernode) {
12674 waveheader = (const guint8 *) waveheadernode->data;
12675 headerlen = QT_UINT32 (waveheader);
12677 if (headerlen > 8) {
12678 gst_riff_strf_auds *header = NULL;
12679 GstBuffer *headerbuf;
12685 headerbuf = gst_buffer_new_and_alloc (headerlen);
12686 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12688 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12689 headerbuf, &header, &extra)) {
12690 gst_caps_unref (entry->caps);
12691 /* FIXME: Need to do something with the channel reorder map */
12693 gst_riff_create_audio_caps (header->format, NULL, header,
12694 extra, NULL, NULL, NULL);
12697 gst_buffer_unref (extra);
12702 GST_DEBUG ("Didn't find waveheadernode for this codec");
12704 g_node_destroy (wavenode);
12707 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12708 stream->stream_tags);
12712 /* FIXME: what is in the chunk? */
12715 gint len = QT_UINT32 (stsd_data);
12717 /* seems to be always = 116 = 0x74 */
12723 gint len = QT_UINT32 (stsd_entry_data);
12726 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12728 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12729 gst_caps_set_simple (entry->caps,
12730 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12731 gst_buffer_unref (buf);
12733 gst_caps_set_simple (entry->caps,
12734 "samplesize", G_TYPE_INT, samplesize, NULL);
12739 GNode *alac, *wave = NULL;
12741 /* apparently, m4a has this atom appended directly in the stsd entry,
12742 * while mov has it in a wave atom */
12743 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12745 /* alac now refers to stsd entry atom */
12746 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12748 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12750 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12753 const guint8 *alac_data = alac->data;
12754 gint len = QT_UINT32 (alac->data);
12758 GST_DEBUG_OBJECT (qtdemux,
12759 "discarding alac atom with unexpected len %d", len);
12761 /* codec-data contains alac atom size and prefix,
12762 * ffmpeg likes it that way, not quite gst-ish though ...*/
12763 buf = gst_buffer_new_and_alloc (len);
12764 gst_buffer_fill (buf, 0, alac->data, len);
12765 gst_caps_set_simple (entry->caps,
12766 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12767 gst_buffer_unref (buf);
12769 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12770 entry->n_channels = QT_UINT8 (alac_data + 21);
12771 entry->rate = QT_UINT32 (alac_data + 32);
12774 gst_caps_set_simple (entry->caps,
12775 "samplesize", G_TYPE_INT, samplesize, NULL);
12780 /* The codingname of the sample entry is 'fLaC' */
12781 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12784 /* The 'dfLa' box is added to the sample entry to convey
12785 initializing information for the decoder. */
12786 const GNode *dfla =
12787 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12790 const guint32 len = QT_UINT32 (dfla->data);
12792 /* Must contain at least dfLa box header (12),
12793 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12795 GST_DEBUG_OBJECT (qtdemux,
12796 "discarding dfla atom with unexpected len %d", len);
12798 /* skip dfLa header to get the METADATA_BLOCKs */
12799 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12800 const guint32 metadata_blocks_len = len - 12;
12802 gchar *stream_marker = g_strdup ("fLaC");
12803 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12804 strlen (stream_marker));
12807 guint32 remainder = 0;
12808 guint32 block_size = 0;
12809 gboolean is_last = FALSE;
12811 GValue array = G_VALUE_INIT;
12812 GValue value = G_VALUE_INIT;
12814 g_value_init (&array, GST_TYPE_ARRAY);
12815 g_value_init (&value, GST_TYPE_BUFFER);
12817 gst_value_set_buffer (&value, block);
12818 gst_value_array_append_value (&array, &value);
12819 g_value_reset (&value);
12821 gst_buffer_unref (block);
12823 /* check there's at least one METADATA_BLOCK_HEADER's worth
12824 * of data, and we haven't already finished parsing */
12825 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12826 remainder = metadata_blocks_len - index;
12828 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12830 (metadata_blocks[index + 1] << 16) +
12831 (metadata_blocks[index + 2] << 8) +
12832 metadata_blocks[index + 3];
12834 /* be careful not to read off end of box */
12835 if (block_size > remainder) {
12839 is_last = metadata_blocks[index] >> 7;
12841 block = gst_buffer_new_and_alloc (block_size);
12843 gst_buffer_fill (block, 0, &metadata_blocks[index],
12846 gst_value_set_buffer (&value, block);
12847 gst_value_array_append_value (&array, &value);
12848 g_value_reset (&value);
12850 gst_buffer_unref (block);
12852 index += block_size;
12855 /* only append the metadata if we successfully read all of it */
12857 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12858 (stream)->caps, 0), "streamheader", &array);
12860 GST_WARNING_OBJECT (qtdemux,
12861 "discarding all METADATA_BLOCKs due to invalid "
12862 "block_size %d at idx %d, rem %d", block_size, index,
12866 g_value_unset (&value);
12867 g_value_unset (&array);
12869 /* The sample rate obtained from the stsd may not be accurate
12870 * since it cannot represent rates greater than 65535Hz, so
12871 * override that value with the sample rate from the
12872 * METADATA_BLOCK_STREAMINFO block */
12873 CUR_STREAM (stream)->rate =
12874 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12885 gint len = QT_UINT32 (stsd_entry_data);
12888 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12891 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12893 /* If we have enough data, let's try to get the 'damr' atom. See
12894 * the 3GPP container spec (26.244) for more details. */
12895 if ((len - 0x34) > 8 &&
12896 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12897 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12898 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12901 gst_caps_set_simple (entry->caps,
12902 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12903 gst_buffer_unref (buf);
12909 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12910 gint len = QT_UINT32 (stsd_entry_data);
12913 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12915 if (sound_version == 1) {
12916 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12917 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12918 guint8 codec_data[2];
12920 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12922 gint sample_rate_index =
12923 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12925 /* build AAC codec data */
12926 codec_data[0] = profile << 3;
12927 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12928 codec_data[1] = (sample_rate_index & 0x01) << 7;
12929 codec_data[1] |= (channels & 0xF) << 3;
12931 buf = gst_buffer_new_and_alloc (2);
12932 gst_buffer_fill (buf, 0, codec_data, 2);
12933 gst_caps_set_simple (entry->caps,
12934 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12935 gst_buffer_unref (buf);
12941 /* Fully handled elsewhere */
12944 GST_INFO_OBJECT (qtdemux,
12945 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12949 GST_INFO_OBJECT (qtdemux,
12950 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12951 GST_FOURCC_ARGS (fourcc), entry->caps);
12953 } else if (stream->subtype == FOURCC_strm) {
12954 if (fourcc == FOURCC_rtsp) {
12955 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12957 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12958 GST_FOURCC_ARGS (fourcc));
12959 goto unknown_stream;
12961 entry->sampled = TRUE;
12962 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12963 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12964 || stream->subtype == FOURCC_clcp) {
12966 entry->sampled = TRUE;
12967 entry->sparse = TRUE;
12970 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12973 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12974 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12979 /* hunt for sort-of codec data */
12983 GNode *mp4s = NULL;
12984 GNode *esds = NULL;
12986 /* look for palette in a stsd->mp4s->esds sub-atom */
12987 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12989 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12990 if (esds == NULL) {
12992 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12996 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12997 stream->stream_tags);
13001 GST_INFO_OBJECT (qtdemux,
13002 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13005 GST_INFO_OBJECT (qtdemux,
13006 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13007 GST_FOURCC_ARGS (fourcc), entry->caps);
13009 /* everything in 1 sample */
13010 entry->sampled = TRUE;
13013 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13016 if (entry->caps == NULL)
13017 goto unknown_stream;
13020 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13021 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13027 /* promote to sampled format */
13028 if (entry->fourcc == FOURCC_samr) {
13029 /* force mono 8000 Hz for AMR */
13030 entry->sampled = TRUE;
13031 entry->n_channels = 1;
13032 entry->rate = 8000;
13033 } else if (entry->fourcc == FOURCC_sawb) {
13034 /* force mono 16000 Hz for AMR-WB */
13035 entry->sampled = TRUE;
13036 entry->n_channels = 1;
13037 entry->rate = 16000;
13038 } else if (entry->fourcc == FOURCC_mp4a) {
13039 entry->sampled = TRUE;
13043 stsd_entry_data += len;
13044 remaining_stsd_len -= len;
13048 /* collect sample information */
13049 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
13050 goto samples_failed;
13052 if (qtdemux->fragmented) {
13055 /* need all moov samples as basis; probably not many if any at all */
13056 /* prevent moof parsing taking of at this time */
13057 offset = qtdemux->moof_offset;
13058 qtdemux->moof_offset = 0;
13059 if (stream->n_samples &&
13060 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
13061 qtdemux->moof_offset = offset;
13062 goto samples_failed;
13064 qtdemux->moof_offset = 0;
13065 /* movie duration more reliable in this case (e.g. mehd) */
13066 if (qtdemux->segment.duration &&
13067 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13069 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
13072 /* configure segments */
13073 if (!qtdemux_parse_segments (qtdemux, stream, trak))
13074 goto segments_failed;
13076 /* add some language tag, if useful */
13077 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13078 strcmp (stream->lang_id, "und")) {
13079 const gchar *lang_code;
13081 /* convert ISO 639-2 code to ISO 639-1 */
13082 lang_code = gst_tag_get_language_code (stream->lang_id);
13083 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13084 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13087 /* Check for UDTA tags */
13088 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
13089 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
13092 /* Insert and sort new stream in track-id order.
13093 * This will help in comparing old/new streams during stream update check */
13094 g_ptr_array_add (qtdemux->active_streams, stream);
13095 g_ptr_array_sort (qtdemux->active_streams,
13096 (GCompareFunc) qtdemux_track_id_compare_func);
13097 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
13098 QTDEMUX_N_STREAMS (qtdemux));
13105 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
13106 (_("This file is corrupt and cannot be played.")), (NULL));
13108 gst_qtdemux_stream_unref (stream);
13113 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
13114 gst_qtdemux_stream_unref (stream);
13120 /* we posted an error already */
13121 /* free stbl sub-atoms */
13122 gst_qtdemux_stbl_free (stream);
13123 gst_qtdemux_stream_unref (stream);
13128 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
13134 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
13135 GST_FOURCC_ARGS (stream->subtype));
13136 gst_qtdemux_stream_unref (stream);
13141 /* If we can estimate the overall bitrate, and don't have information about the
13142 * stream bitrate for exactly one stream, this guesses the stream bitrate as
13143 * the overall bitrate minus the sum of the bitrates of all other streams. This
13144 * should be useful for the common case where we have one audio and one video
13145 * stream and can estimate the bitrate of one, but not the other. */
13147 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
13149 QtDemuxStream *stream = NULL;
13150 gint64 size, sys_bitrate, sum_bitrate = 0;
13151 GstClockTime duration;
13155 if (qtdemux->fragmented)
13158 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
13160 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
13162 GST_DEBUG_OBJECT (qtdemux,
13163 "Size in bytes of the stream not known - bailing");
13167 /* Subtract the header size */
13168 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
13169 size, qtdemux->header_size);
13171 if (size < qtdemux->header_size)
13174 size = size - qtdemux->header_size;
13176 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
13177 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
13181 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13182 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
13183 switch (str->subtype) {
13186 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
13187 CUR_STREAM (str)->caps);
13188 /* retrieve bitrate, prefer avg then max */
13190 if (str->stream_tags) {
13191 if (gst_tag_list_get_uint (str->stream_tags,
13192 GST_TAG_MAXIMUM_BITRATE, &bitrate))
13193 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
13194 if (gst_tag_list_get_uint (str->stream_tags,
13195 GST_TAG_NOMINAL_BITRATE, &bitrate))
13196 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
13197 if (gst_tag_list_get_uint (str->stream_tags,
13198 GST_TAG_BITRATE, &bitrate))
13199 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
13202 sum_bitrate += bitrate;
13205 GST_DEBUG_OBJECT (qtdemux,
13206 ">1 stream with unknown bitrate - bailing");
13213 /* For other subtypes, we assume no significant impact on bitrate */
13219 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
13223 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
13225 if (sys_bitrate < sum_bitrate) {
13226 /* This can happen, since sum_bitrate might be derived from maximum
13227 * bitrates and not average bitrates */
13228 GST_DEBUG_OBJECT (qtdemux,
13229 "System bitrate less than sum bitrate - bailing");
13233 bitrate = sys_bitrate - sum_bitrate;
13234 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
13235 ", Stream bitrate = %u", sys_bitrate, bitrate);
13237 if (!stream->stream_tags)
13238 stream->stream_tags = gst_tag_list_new_empty ();
13240 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
13242 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13243 GST_TAG_BITRATE, bitrate, NULL);
13246 static GstFlowReturn
13247 qtdemux_prepare_streams (GstQTDemux * qtdemux)
13249 GstFlowReturn ret = GST_FLOW_OK;
13252 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
13254 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13255 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13256 guint32 sample_num = 0;
13258 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13259 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13261 if (qtdemux->fragmented) {
13262 /* need all moov samples first */
13263 GST_OBJECT_LOCK (qtdemux);
13264 while (stream->n_samples == 0)
13265 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
13267 GST_OBJECT_UNLOCK (qtdemux);
13269 /* discard any stray moof */
13270 qtdemux->moof_offset = 0;
13273 /* prepare braking */
13274 if (ret != GST_FLOW_ERROR)
13277 /* in pull mode, we should have parsed some sample info by now;
13278 * and quite some code will not handle no samples.
13279 * in push mode, we'll just have to deal with it */
13280 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
13281 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
13282 g_ptr_array_remove_index (qtdemux->active_streams, i);
13285 } else if (stream->track_id == qtdemux->chapters_track_id &&
13286 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
13287 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
13288 so that it doesn't look like a subtitle track */
13289 g_ptr_array_remove_index (qtdemux->active_streams, i);
13294 /* parse the initial sample for use in setting the frame rate cap */
13295 while (sample_num == 0 && sample_num < stream->n_samples) {
13296 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
13306 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
13308 return g_strcmp0 (stream->stream_id, stream_id) == 0;
13312 qtdemux_is_streams_update (GstQTDemux * qtdemux)
13316 /* Different length, updated */
13317 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
13320 /* streams in list are sorted in track-id order */
13321 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13322 /* Different stream-id, updated */
13323 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
13324 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
13332 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
13333 QtDemuxStream * oldstream, QtDemuxStream * newstream)
13335 /* Connect old stream's srcpad to new stream */
13336 newstream->pad = oldstream->pad;
13337 oldstream->pad = NULL;
13339 /* unset new_stream to prevent stream-start event */
13340 newstream->new_stream = FALSE;
13342 return gst_qtdemux_configure_stream (qtdemux, newstream);
13345 /* g_ptr_array_find_with_equal_func is available since 2.54,
13346 * replacement until we can depend unconditionally on the real one in GLib */
13347 #if !GLIB_CHECK_VERSION(2,54,0)
13348 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
13350 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
13351 gconstpointer needle, GEqualFunc equal_func, guint * index_)
13355 g_return_val_if_fail (haystack != NULL, FALSE);
13357 if (equal_func == NULL)
13358 equal_func = g_direct_equal;
13360 for (i = 0; i < haystack->len; i++) {
13361 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
13362 if (index_ != NULL)
13373 qtdemux_update_streams (GstQTDemux * qtdemux)
13376 g_assert (qtdemux->streams_aware);
13378 /* At below, figure out which stream in active_streams has identical stream-id
13379 * with that of in old_streams. If there is matching stream-id,
13380 * corresponding newstream will not be exposed again,
13381 * but demux will reuse srcpad of matched old stream
13383 * active_streams : newly created streams from the latest moov
13384 * old_streams : existing streams (belong to previous moov)
13387 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13388 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13389 QtDemuxStream *oldstream = NULL;
13392 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13393 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13395 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
13396 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
13397 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
13399 /* null pad stream cannot be reused */
13400 if (oldstream->pad == NULL)
13405 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13407 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13410 /* we don't need to preserve order of old streams */
13411 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13415 /* now we have all info and can expose */
13416 list = stream->stream_tags;
13417 stream->stream_tags = NULL;
13418 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13426 /* Must be called with expose lock */
13427 static GstFlowReturn
13428 qtdemux_expose_streams (GstQTDemux * qtdemux)
13432 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13434 if (!qtdemux_is_streams_update (qtdemux)) {
13435 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13436 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13437 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13438 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13439 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13440 return GST_FLOW_ERROR;
13443 g_ptr_array_set_size (qtdemux->old_streams, 0);
13444 qtdemux->need_segment = TRUE;
13446 return GST_FLOW_OK;
13449 if (qtdemux->streams_aware) {
13450 if (!qtdemux_update_streams (qtdemux))
13451 return GST_FLOW_ERROR;
13453 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13454 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13457 /* now we have all info and can expose */
13458 list = stream->stream_tags;
13459 stream->stream_tags = NULL;
13460 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13461 return GST_FLOW_ERROR;
13466 gst_qtdemux_guess_bitrate (qtdemux);
13468 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
13470 /* If we have still old_streams, it's no more used stream */
13471 for (i = 0; i < qtdemux->old_streams->len; i++) {
13472 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13477 event = gst_event_new_eos ();
13478 if (qtdemux->segment_seqnum)
13479 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
13481 gst_pad_push_event (stream->pad, event);
13485 g_ptr_array_set_size (qtdemux->old_streams, 0);
13487 /* check if we should post a redirect in case there is a single trak
13488 * and it is a redirecting trak */
13489 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
13490 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
13493 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
13494 "an external content");
13495 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
13496 gst_structure_new ("redirect",
13497 "new-location", G_TYPE_STRING,
13498 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
13499 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
13500 qtdemux->posted_redirect = TRUE;
13503 g_ptr_array_foreach (qtdemux->active_streams,
13504 (GFunc) qtdemux_do_allocation, qtdemux);
13506 qtdemux->need_segment = TRUE;
13508 qtdemux->exposed = TRUE;
13509 return GST_FLOW_OK;
13512 /* check if major or compatible brand is 3GP */
13513 static inline gboolean
13514 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
13517 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
13519 } else if (qtdemux->comp_brands != NULL) {
13523 gboolean res = FALSE;
13525 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
13528 while (size >= 4) {
13529 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
13534 gst_buffer_unmap (qtdemux->comp_brands, &map);
13541 /* check if tag is a spec'ed 3GP tag keyword storing a string */
13542 static inline gboolean
13543 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
13545 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
13546 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
13547 || fourcc == FOURCC_albm;
13551 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
13552 const char *tag, const char *dummy, GNode * node)
13554 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13558 gdouble longitude, latitude, altitude;
13561 len = QT_UINT32 (node->data);
13568 /* TODO: language code skipped */
13570 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
13573 /* do not alarm in trivial case, but bail out otherwise */
13574 if (*(data + offset) != 0) {
13575 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
13579 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13580 GST_TAG_GEO_LOCATION_NAME, name, NULL);
13581 offset += strlen (name);
13585 if (len < offset + 2 + 4 + 4 + 4)
13588 /* +1 +1 = skip null-terminator and location role byte */
13590 /* table in spec says unsigned, semantics say negative has meaning ... */
13591 longitude = QT_SFP32 (data + offset);
13594 latitude = QT_SFP32 (data + offset);
13597 altitude = QT_SFP32 (data + offset);
13599 /* one invalid means all are invalid */
13600 if (longitude >= -180.0 && longitude <= 180.0 &&
13601 latitude >= -90.0 && latitude <= 90.0) {
13602 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13603 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
13604 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
13605 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
13608 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
13615 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
13622 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
13623 const char *tag, const char *dummy, GNode * node)
13629 len = QT_UINT32 (node->data);
13633 y = QT_UINT16 ((guint8 *) node->data + 12);
13635 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
13638 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
13640 date = g_date_new_dmy (1, 1, y);
13641 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13642 g_date_free (date);
13646 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
13647 const char *tag, const char *dummy, GNode * node)
13650 char *tag_str = NULL;
13655 len = QT_UINT32 (node->data);
13660 entity = (guint8 *) node->data + offset;
13661 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
13662 GST_DEBUG_OBJECT (qtdemux,
13663 "classification info: %c%c%c%c invalid classification entity",
13664 entity[0], entity[1], entity[2], entity[3]);
13669 table = QT_UINT16 ((guint8 *) node->data + offset);
13671 /* Language code skipped */
13675 /* Tag format: "XXXX://Y[YYYY]/classification info string"
13676 * XXXX: classification entity, fixed length 4 chars.
13677 * Y[YYYY]: classification table, max 5 chars.
13679 tag_str = g_strdup_printf ("----://%u/%s",
13680 table, (char *) node->data + offset);
13682 /* memcpy To be sure we're preserving byte order */
13683 memcpy (tag_str, entity, 4);
13684 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
13686 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
13695 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
13701 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
13702 const char *tag, const char *dummy, GNode * node)
13704 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13710 gboolean ret = TRUE;
13711 const gchar *charset = NULL;
13713 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13715 len = QT_UINT32 (data->data);
13716 type = QT_UINT32 ((guint8 *) data->data + 8);
13717 if (type == 0x00000001 && len > 16) {
13718 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
13721 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13722 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13725 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13729 len = QT_UINT32 (node->data);
13730 type = QT_UINT32 ((guint8 *) node->data + 4);
13731 if ((type >> 24) == 0xa9 && len > 8 + 4) {
13735 /* Type starts with the (C) symbol, so the next data is a list
13736 * of (string size(16), language code(16), string) */
13738 str_len = QT_UINT16 ((guint8 *) node->data + 8);
13739 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
13741 /* the string + fourcc + size + 2 16bit fields,
13742 * means that there are more tags in this atom */
13743 if (len > str_len + 8 + 4) {
13744 /* TODO how to represent the same tag in different languages? */
13745 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
13746 "text alternatives, reading only first one");
13750 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
13751 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
13753 if (lang_code < 0x800) { /* MAC encoded string */
13756 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
13757 QT_FOURCC ((guint8 *) node->data + 4))) {
13758 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
13760 /* we go for 3GP style encoding if major brands claims so,
13761 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
13762 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13763 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
13764 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
13766 /* 16-bit Language code is ignored here as well */
13767 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
13774 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
13775 ret = FALSE; /* may have to fallback */
13778 GError *err = NULL;
13780 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
13781 charset, NULL, NULL, &err);
13783 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
13784 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
13786 g_error_free (err);
13789 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13790 len - offset, env_vars);
13793 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13794 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13798 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13805 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
13806 const char *tag, const char *dummy, GNode * node)
13808 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
13812 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
13813 const char *tag, const char *dummy, GNode * node)
13815 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13817 char *s, *t, *k = NULL;
13822 /* first try normal string tag if major brand not 3GP */
13823 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
13824 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
13825 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
13826 * let's try it 3gpp way after minor safety check */
13828 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
13834 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
13838 len = QT_UINT32 (data);
13842 count = QT_UINT8 (data + 14);
13844 for (; count; count--) {
13847 if (offset + 1 > len)
13849 slen = QT_UINT8 (data + offset);
13851 if (offset + slen > len)
13853 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13856 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
13858 t = g_strjoin (",", k, s, NULL);
13866 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
13873 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
13874 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
13883 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
13889 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
13890 const char *tag1, const char *tag2, GNode * node)
13897 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13899 len = QT_UINT32 (data->data);
13900 type = QT_UINT32 ((guint8 *) data->data + 8);
13901 if (type == 0x00000000 && len >= 22) {
13902 n1 = QT_UINT16 ((guint8 *) data->data + 18);
13903 n2 = QT_UINT16 ((guint8 *) data->data + 20);
13905 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
13906 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
13909 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
13910 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
13917 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
13918 const char *tag1, const char *dummy, GNode * node)
13925 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13927 len = QT_UINT32 (data->data);
13928 type = QT_UINT32 ((guint8 *) data->data + 8);
13929 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
13930 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13931 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
13932 n1 = QT_UINT16 ((guint8 *) data->data + 16);
13934 /* do not add bpm=0 */
13935 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
13936 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
13944 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
13945 const char *tag1, const char *dummy, GNode * node)
13952 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13954 len = QT_UINT32 (data->data);
13955 type = QT_UINT32 ((guint8 *) data->data + 8);
13956 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
13957 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13958 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
13959 num = QT_UINT32 ((guint8 *) data->data + 16);
13961 /* do not add num=0 */
13962 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
13963 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
13970 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
13971 const char *tag1, const char *dummy, GNode * node)
13978 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13980 len = QT_UINT32 (data->data);
13981 type = QT_UINT32 ((guint8 *) data->data + 8);
13982 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
13983 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
13984 GstTagImageType image_type;
13986 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
13987 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
13989 image_type = GST_TAG_IMAGE_TYPE_NONE;
13992 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
13993 len - 16, image_type))) {
13994 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
13995 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
13996 gst_sample_unref (sample);
14003 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
14004 const char *tag, const char *dummy, GNode * node)
14007 GstDateTime *datetime = NULL;
14012 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14014 len = QT_UINT32 (data->data);
14015 type = QT_UINT32 ((guint8 *) data->data + 8);
14016 if (type == 0x00000001 && len > 16) {
14017 guint y, m = 1, d = 1;
14020 s = g_strndup ((char *) data->data + 16, len - 16);
14021 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
14022 datetime = gst_date_time_new_from_iso8601_string (s);
14023 if (datetime != NULL) {
14024 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
14026 gst_date_time_unref (datetime);
14029 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
14030 if (ret >= 1 && y > 1500 && y < 3000) {
14033 date = g_date_new_dmy (d, m, y);
14034 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
14035 g_date_free (date);
14037 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
14045 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
14046 const char *tag, const char *dummy, GNode * node)
14050 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14052 /* re-route to normal string tag if major brand says so
14053 * or no data atom and compatible brand suggests so */
14054 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
14055 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
14056 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
14061 guint len, type, n;
14063 len = QT_UINT32 (data->data);
14064 type = QT_UINT32 ((guint8 *) data->data + 8);
14065 if (type == 0x00000000 && len >= 18) {
14066 n = QT_UINT16 ((guint8 *) data->data + 16);
14068 const gchar *genre;
14070 genre = gst_tag_id3_genre_get (n - 1);
14071 if (genre != NULL) {
14072 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
14073 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
14081 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
14082 const gchar * tag, guint8 * data, guint32 datasize)
14087 /* make a copy to have \0 at the end */
14088 datacopy = g_strndup ((gchar *) data, datasize);
14090 /* convert the str to double */
14091 if (sscanf (datacopy, "%lf", &value) == 1) {
14092 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
14093 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
14095 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
14103 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
14104 const char *tag, const char *tag_bis, GNode * node)
14113 const gchar *meanstr;
14114 const gchar *namestr;
14116 /* checking the whole ---- atom size for consistency */
14117 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
14118 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
14122 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
14124 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
14128 meansize = QT_UINT32 (mean->data);
14129 if (meansize <= 12) {
14130 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
14133 meanstr = ((gchar *) mean->data) + 12;
14136 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
14138 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
14142 namesize = QT_UINT32 (name->data);
14143 if (namesize <= 12) {
14144 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
14147 namestr = ((gchar *) name->data) + 12;
14155 * uint24 - data type
14159 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14161 GST_WARNING_OBJECT (demux, "No data atom in this tag");
14164 datasize = QT_UINT32 (data->data);
14165 if (datasize <= 16) {
14166 GST_WARNING_OBJECT (demux, "Data atom too small");
14169 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
14171 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
14172 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
14173 static const struct
14175 const gchar name[28];
14176 const gchar tag[28];
14179 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
14180 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
14181 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
14182 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
14183 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
14184 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
14185 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
14186 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
14190 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
14191 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
14192 switch (gst_tag_get_type (tags[i].tag)) {
14193 case G_TYPE_DOUBLE:
14194 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
14195 ((guint8 *) data->data) + 16, datasize - 16);
14197 case G_TYPE_STRING:
14198 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
14207 if (i == G_N_ELEMENTS (tags))
14217 #ifndef GST_DISABLE_GST_DEBUG
14219 gchar *namestr_dbg;
14220 gchar *meanstr_dbg;
14222 meanstr_dbg = g_strndup (meanstr, meansize);
14223 namestr_dbg = g_strndup (namestr, namesize);
14225 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
14226 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
14228 g_free (namestr_dbg);
14229 g_free (meanstr_dbg);
14236 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
14237 const char *tag_bis, GNode * node)
14242 GstTagList *id32_taglist = NULL;
14244 GST_LOG_OBJECT (demux, "parsing ID32");
14247 len = GST_READ_UINT32_BE (data);
14249 /* need at least full box and language tag */
14253 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
14254 gst_buffer_fill (buf, 0, data + 14, len - 14);
14256 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
14257 if (id32_taglist) {
14258 GST_LOG_OBJECT (demux, "parsing ok");
14259 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
14260 gst_tag_list_unref (id32_taglist);
14262 GST_LOG_OBJECT (demux, "parsing failed");
14265 gst_buffer_unref (buf);
14268 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
14269 const char *tag, const char *tag_bis, GNode * node);
14272 FOURCC_pcst -> if media is a podcast -> bool
14273 FOURCC_cpil -> if media is part of a compilation -> bool
14274 FOURCC_pgap -> if media is part of a gapless context -> bool
14275 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
14278 static const struct
14281 const gchar *gst_tag;
14282 const gchar *gst_tag_bis;
14283 const GstQTDemuxAddTagFunc func;
14286 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
14287 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
14288 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
14289 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
14290 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
14291 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
14292 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
14293 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
14294 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
14295 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
14296 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
14297 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
14298 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
14299 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14300 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14301 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14302 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
14303 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
14304 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
14305 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
14306 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
14307 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
14308 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
14309 qtdemux_tag_add_num}, {
14310 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
14311 qtdemux_tag_add_num}, {
14312 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
14313 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
14314 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
14315 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
14316 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
14317 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
14318 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
14319 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
14320 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
14321 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
14322 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
14323 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
14324 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
14325 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
14326 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
14327 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
14328 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
14329 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
14330 qtdemux_tag_add_classification}, {
14331 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
14332 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
14333 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
14335 /* This is a special case, some tags are stored in this
14336 * 'reverse dns naming', according to:
14337 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
14340 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
14341 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
14342 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
14345 struct _GstQtDemuxTagList
14348 GstTagList *taglist;
14350 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
14353 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
14359 const gchar *style;
14364 GstQTDemux *demux = qtdemuxtaglist->demux;
14365 GstTagList *taglist = qtdemuxtaglist->taglist;
14368 len = QT_UINT32 (data);
14369 buf = gst_buffer_new_and_alloc (len);
14370 gst_buffer_fill (buf, 0, data, len);
14372 /* heuristic to determine style of tag */
14373 if (QT_FOURCC (data + 4) == FOURCC_____ ||
14374 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
14376 else if (demux->major_brand == FOURCC_qt__)
14377 style = "quicktime";
14378 /* fall back to assuming iso/3gp tag style */
14382 /* santize the name for the caps. */
14383 for (i = 0; i < 4; i++) {
14384 guint8 d = data[4 + i];
14385 if (g_ascii_isalnum (d))
14386 ndata[i] = g_ascii_tolower (d);
14391 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
14392 ndata[0], ndata[1], ndata[2], ndata[3]);
14393 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
14395 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
14396 sample = gst_sample_new (buf, NULL, NULL, s);
14397 gst_buffer_unref (buf);
14398 g_free (media_type);
14400 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
14403 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
14404 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
14406 gst_sample_unref (sample);
14410 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
14417 GstQtDemuxTagList demuxtaglist;
14419 demuxtaglist.demux = qtdemux;
14420 demuxtaglist.taglist = taglist;
14422 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
14423 if (meta != NULL) {
14424 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
14425 if (ilst == NULL) {
14426 GST_LOG_OBJECT (qtdemux, "no ilst");
14431 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
14435 while (i < G_N_ELEMENTS (add_funcs)) {
14436 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
14440 len = QT_UINT32 (node->data);
14442 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
14443 GST_FOURCC_ARGS (add_funcs[i].fourcc));
14445 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
14446 add_funcs[i].gst_tag_bis, node);
14448 g_node_destroy (node);
14454 /* parsed nodes have been removed, pass along remainder as blob */
14455 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
14456 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
14458 /* parse up XMP_ node if existing */
14459 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
14460 if (xmp_ != NULL) {
14462 GstTagList *xmptaglist;
14464 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
14465 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
14466 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
14467 gst_buffer_unref (buf);
14469 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
14471 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
14477 GstStructure *structure; /* helper for sort function */
14479 guint min_req_bitrate;
14480 guint min_req_qt_version;
14484 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14486 GstQtReference *ref_a = (GstQtReference *) a;
14487 GstQtReference *ref_b = (GstQtReference *) b;
14489 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14490 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14492 /* known bitrates go before unknown; higher bitrates go first */
14493 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14496 /* sort the redirects and post a message for the application.
14499 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14501 GstQtReference *best;
14504 GValue list_val = { 0, };
14507 g_assert (references != NULL);
14509 references = g_list_sort (references, qtdemux_redirects_sort_func);
14511 best = (GstQtReference *) references->data;
14513 g_value_init (&list_val, GST_TYPE_LIST);
14515 for (l = references; l != NULL; l = l->next) {
14516 GstQtReference *ref = (GstQtReference *) l->data;
14517 GValue struct_val = { 0, };
14519 ref->structure = gst_structure_new ("redirect",
14520 "new-location", G_TYPE_STRING, ref->location, NULL);
14522 if (ref->min_req_bitrate > 0) {
14523 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14524 ref->min_req_bitrate, NULL);
14527 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14528 g_value_set_boxed (&struct_val, ref->structure);
14529 gst_value_list_append_value (&list_val, &struct_val);
14530 g_value_unset (&struct_val);
14531 /* don't free anything here yet, since we need best->structure below */
14534 g_assert (best != NULL);
14535 s = gst_structure_copy (best->structure);
14537 if (g_list_length (references) > 1) {
14538 gst_structure_set_value (s, "locations", &list_val);
14541 g_value_unset (&list_val);
14543 for (l = references; l != NULL; l = l->next) {
14544 GstQtReference *ref = (GstQtReference *) l->data;
14546 gst_structure_free (ref->structure);
14547 g_free (ref->location);
14550 g_list_free (references);
14552 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14553 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14554 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14555 qtdemux->posted_redirect = TRUE;
14558 /* look for redirect nodes, collect all redirect information and
14562 qtdemux_parse_redirects (GstQTDemux * qtdemux)
14564 GNode *rmra, *rmda, *rdrf;
14566 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14568 GList *redirects = NULL;
14570 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14572 GstQtReference ref = { NULL, NULL, 0, 0 };
14573 GNode *rmdr, *rmvc;
14575 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14576 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14577 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14578 ref.min_req_bitrate);
14581 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14582 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14583 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14585 #ifndef GST_DISABLE_GST_DEBUG
14586 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14588 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14590 GST_LOG_OBJECT (qtdemux,
14591 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14592 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14593 bitmask, check_type);
14594 if (package == FOURCC_qtim && check_type == 0) {
14595 ref.min_req_qt_version = version;
14599 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14605 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14606 if (ref_len > 20) {
14607 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14608 ref_data = (guint8 *) rdrf->data + 20;
14609 if (ref_type == FOURCC_alis) {
14610 guint record_len, record_version, fn_len;
14612 if (ref_len > 70) {
14613 /* MacOSX alias record, google for alias-layout.txt */
14614 record_len = QT_UINT16 (ref_data + 4);
14615 record_version = QT_UINT16 (ref_data + 4 + 2);
14616 fn_len = QT_UINT8 (ref_data + 50);
14617 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14618 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14621 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14624 } else if (ref_type == FOURCC_url_) {
14625 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14627 GST_DEBUG_OBJECT (qtdemux,
14628 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14629 GST_FOURCC_ARGS (ref_type));
14631 if (ref.location != NULL) {
14632 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14634 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
14636 GST_WARNING_OBJECT (qtdemux,
14637 "Failed to extract redirect location from rdrf atom");
14640 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14644 /* look for others */
14645 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14648 if (redirects != NULL) {
14649 qtdemux_process_redirects (qtdemux, redirects);
14655 static GstTagList *
14656 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14660 if (tags == NULL) {
14661 tags = gst_tag_list_new_empty ();
14662 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14665 if (qtdemux->major_brand == FOURCC_mjp2)
14666 fmt = "Motion JPEG 2000";
14667 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14669 else if (qtdemux->major_brand == FOURCC_qt__)
14671 else if (qtdemux->fragmented)
14674 fmt = "ISO MP4/M4A";
14676 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14677 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14679 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14685 /* we have read the complete moov node now.
14686 * This function parses all of the relevant info, creates the traks and
14687 * prepares all data structures for playback
14690 qtdemux_parse_tree (GstQTDemux * qtdemux)
14697 guint64 creation_time;
14698 GstDateTime *datetime = NULL;
14701 /* make sure we have a usable taglist */
14702 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14704 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14705 if (mvhd == NULL) {
14706 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14707 return qtdemux_parse_redirects (qtdemux);
14710 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14711 if (version == 1) {
14712 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14713 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14714 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14715 } else if (version == 0) {
14716 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14717 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14718 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14720 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14724 /* Moving qt creation time (secs since 1904) to unix time */
14725 if (creation_time != 0) {
14726 /* Try to use epoch first as it should be faster and more commonly found */
14727 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14730 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14731 /* some data cleansing sanity */
14732 g_get_current_time (&now);
14733 if (now.tv_sec + 24 * 3600 < creation_time) {
14734 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14736 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14739 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14740 GDateTime *dt, *dt_local;
14742 dt = g_date_time_add_seconds (base_dt, creation_time);
14743 dt_local = g_date_time_to_local (dt);
14744 datetime = gst_date_time_new_from_g_date_time (dt_local);
14746 g_date_time_unref (base_dt);
14747 g_date_time_unref (dt);
14751 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14752 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14754 gst_date_time_unref (datetime);
14757 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14758 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14760 /* check for fragmented file and get some (default) data */
14761 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14764 GstByteReader mehd_data;
14766 /* let track parsing or anyone know weird stuff might happen ... */
14767 qtdemux->fragmented = TRUE;
14769 /* compensate for total duration */
14770 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14772 qtdemux_parse_mehd (qtdemux, &mehd_data);
14775 /* Update the movie segment duration, unless it was directly given to us
14776 * by upstream. Otherwise let it as is, as we don't want to mangle the
14777 * duration provided by upstream that may come e.g. from a MPD file. */
14778 if (!qtdemux->upstream_format_is_time) {
14779 GstClockTime duration;
14780 /* set duration in the segment info */
14781 gst_qtdemux_get_duration (qtdemux, &duration);
14782 qtdemux->segment.duration = duration;
14783 /* also do not exceed duration; stop is set that way post seek anyway,
14784 * and segment activation falls back to duration,
14785 * whereas loop only checks stop, so let's align this here as well */
14786 qtdemux->segment.stop = duration;
14789 /* parse all traks */
14790 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14792 qtdemux_parse_trak (qtdemux, trak);
14793 /* iterate all siblings */
14794 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14797 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14800 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14802 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14804 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14807 /* maybe also some tags in meta box */
14808 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14810 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14811 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14813 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14816 /* parse any protection system info */
14817 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14819 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14820 qtdemux_parse_pssh (qtdemux, pssh);
14821 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14824 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14829 /* taken from ffmpeg */
14831 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14843 len = (len << 7) | (c & 0x7f);
14852 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14853 gsize codec_data_size)
14855 GList *list = NULL;
14856 guint8 *p = codec_data;
14857 gint i, offset, num_packets;
14858 guint *length, last;
14860 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14862 if (codec_data == NULL || codec_data_size == 0)
14865 /* start of the stream and vorbis audio or theora video, need to
14866 * send the codec_priv data as first three packets */
14867 num_packets = p[0] + 1;
14868 GST_DEBUG_OBJECT (qtdemux,
14869 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14870 (guint) num_packets, codec_data_size);
14872 /* Let's put some limits, Don't think there even is a xiph codec
14873 * with more than 3-4 headers */
14874 if (G_UNLIKELY (num_packets > 16)) {
14875 GST_WARNING_OBJECT (qtdemux,
14876 "Unlikely number of xiph headers, most likely not valid");
14880 length = g_alloca (num_packets * sizeof (guint));
14884 /* first packets, read length values */
14885 for (i = 0; i < num_packets - 1; i++) {
14887 while (offset < codec_data_size) {
14888 length[i] += p[offset];
14889 if (p[offset++] != 0xff)
14894 if (offset + last > codec_data_size)
14897 /* last packet is the remaining size */
14898 length[i] = codec_data_size - offset - last;
14900 for (i = 0; i < num_packets; i++) {
14903 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14905 if (offset + length[i] > codec_data_size)
14908 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
14909 list = g_list_append (list, hdr);
14911 offset += length[i];
14920 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14926 /* this can change the codec originally present in @list */
14928 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14929 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14931 int len = QT_UINT32 (esds->data);
14932 guint8 *ptr = esds->data;
14933 guint8 *end = ptr + len;
14935 guint8 *data_ptr = NULL;
14937 guint8 object_type_id = 0;
14938 guint8 stream_type = 0;
14939 const char *codec_name = NULL;
14940 GstCaps *caps = NULL;
14942 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14944 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14946 while (ptr + 1 < end) {
14947 tag = QT_UINT8 (ptr);
14948 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14950 len = read_descr_size (ptr, end, &ptr);
14951 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14953 /* Check the stated amount of data is available for reading */
14954 if (len < 0 || ptr + len > end)
14958 case ES_DESCRIPTOR_TAG:
14959 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14960 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14963 case DECODER_CONFIG_DESC_TAG:{
14964 guint max_bitrate, avg_bitrate;
14966 object_type_id = QT_UINT8 (ptr);
14967 stream_type = QT_UINT8 (ptr + 1) >> 2;
14968 max_bitrate = QT_UINT32 (ptr + 5);
14969 avg_bitrate = QT_UINT32 (ptr + 9);
14970 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14971 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14972 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14973 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14974 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14975 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14976 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14977 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14979 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14980 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14981 avg_bitrate, NULL);
14986 case DECODER_SPECIFIC_INFO_TAG:
14987 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14988 if (object_type_id == 0xe0 && len == 0x40) {
14994 GST_DEBUG_OBJECT (qtdemux,
14995 "Have VOBSUB palette. Creating palette event");
14996 /* move to decConfigDescr data and read palette */
14998 for (i = 0; i < 16; i++) {
14999 clut[i] = QT_UINT32 (data);
15003 s = gst_structure_new ("application/x-gst-dvd", "event",
15004 G_TYPE_STRING, "dvd-spu-clut-change",
15005 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
15006 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
15007 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
15008 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
15009 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
15010 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
15011 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
15012 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
15015 /* store event and trigger custom processing */
15016 stream->pending_event =
15017 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
15019 /* Generic codec_data handler puts it on the caps */
15026 case SL_CONFIG_DESC_TAG:
15027 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
15031 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
15033 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
15039 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
15040 * in use, and should also be used to override some other parameters for some
15042 switch (object_type_id) {
15043 case 0x20: /* MPEG-4 */
15044 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
15045 * profile_and_level_indication */
15046 if (data_ptr != NULL && data_len >= 5 &&
15047 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
15048 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
15049 data_ptr + 4, data_len - 4);
15051 break; /* Nothing special needed here */
15052 case 0x21: /* H.264 */
15053 codec_name = "H.264 / AVC";
15054 caps = gst_caps_new_simple ("video/x-h264",
15055 "stream-format", G_TYPE_STRING, "avc",
15056 "alignment", G_TYPE_STRING, "au", NULL);
15058 case 0x40: /* AAC (any) */
15059 case 0x66: /* AAC Main */
15060 case 0x67: /* AAC LC */
15061 case 0x68: /* AAC SSR */
15062 /* Override channels and rate based on the codec_data, as it's often
15064 /* Only do so for basic setup without HE-AAC extension */
15065 if (data_ptr && data_len == 2) {
15066 guint channels, rate;
15068 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
15070 entry->n_channels = channels;
15072 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15074 entry->rate = rate;
15077 /* Set level and profile if possible */
15078 if (data_ptr != NULL && data_len >= 2) {
15079 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15080 data_ptr, data_len);
15082 const gchar *profile_str = NULL;
15085 guint8 *codec_data;
15086 gint rate_idx, profile;
15088 /* No codec_data, let's invent something.
15089 * FIXME: This is wrong for SBR! */
15091 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15093 buffer = gst_buffer_new_and_alloc (2);
15094 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15095 codec_data = map.data;
15098 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
15101 switch (object_type_id) {
15103 profile_str = "main";
15107 profile_str = "lc";
15111 profile_str = "ssr";
15119 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15121 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
15123 gst_buffer_unmap (buffer, &map);
15124 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
15125 GST_TYPE_BUFFER, buffer, NULL);
15126 gst_buffer_unref (buffer);
15129 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
15130 G_TYPE_STRING, profile_str, NULL);
15134 case 0x60: /* MPEG-2, various profiles */
15140 codec_name = "MPEG-2 video";
15141 caps = gst_caps_new_simple ("video/mpeg",
15142 "mpegversion", G_TYPE_INT, 2,
15143 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15145 case 0x69: /* MPEG-2 BC audio */
15146 case 0x6B: /* MPEG-1 audio */
15147 caps = gst_caps_new_simple ("audio/mpeg",
15148 "mpegversion", G_TYPE_INT, 1, NULL);
15149 codec_name = "MPEG-1 audio";
15151 case 0x6A: /* MPEG-1 */
15152 codec_name = "MPEG-1 video";
15153 caps = gst_caps_new_simple ("video/mpeg",
15154 "mpegversion", G_TYPE_INT, 1,
15155 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15157 case 0x6C: /* MJPEG */
15159 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15161 codec_name = "Motion-JPEG";
15163 case 0x6D: /* PNG */
15165 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15167 codec_name = "PNG still images";
15169 case 0x6E: /* JPEG2000 */
15170 codec_name = "JPEG-2000";
15171 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15173 case 0xA4: /* Dirac */
15174 codec_name = "Dirac";
15175 caps = gst_caps_new_empty_simple ("video/x-dirac");
15177 case 0xA5: /* AC3 */
15178 codec_name = "AC-3 audio";
15179 caps = gst_caps_new_simple ("audio/x-ac3",
15180 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15182 case 0xA9: /* AC3 */
15183 codec_name = "DTS audio";
15184 caps = gst_caps_new_simple ("audio/x-dts",
15185 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15188 if (stream_type == 0x05 && data_ptr) {
15190 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
15193 GValue arr_val = G_VALUE_INIT;
15194 GValue buf_val = G_VALUE_INIT;
15197 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15198 codec_name = "Vorbis";
15199 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15200 g_value_init (&arr_val, GST_TYPE_ARRAY);
15201 g_value_init (&buf_val, GST_TYPE_BUFFER);
15202 for (tmp = headers; tmp; tmp = tmp->next) {
15203 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15204 gst_value_array_append_value (&arr_val, &buf_val);
15206 s = gst_caps_get_structure (caps, 0);
15207 gst_structure_take_value (s, "streamheader", &arr_val);
15208 g_value_unset (&buf_val);
15209 g_list_free (headers);
15216 case 0xE1: /* QCELP */
15217 /* QCELP, the codec_data is a riff tag (little endian) with
15218 * 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). */
15219 caps = gst_caps_new_empty_simple ("audio/qcelp");
15220 codec_name = "QCELP";
15226 /* If we have a replacement caps, then change our caps for this stream */
15228 gst_caps_unref (entry->caps);
15229 entry->caps = caps;
15232 if (codec_name && list)
15233 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15234 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15236 /* Add the codec_data attribute to caps, if we have it */
15240 buffer = gst_buffer_new_and_alloc (data_len);
15241 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15243 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15244 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15246 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15248 gst_buffer_unref (buffer);
15253 static inline GstCaps *
15254 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
15258 char *s, fourstr[5];
15260 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15261 for (i = 0; i < 4; i++) {
15262 if (!g_ascii_isalnum (fourstr[i]))
15265 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15266 caps = gst_caps_new_empty_simple (s);
15271 #define _codec(name) \
15273 if (codec_name) { \
15274 *codec_name = g_strdup (name); \
15279 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15280 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15281 const guint8 * stsd_entry_data, gchar ** codec_name)
15283 GstCaps *caps = NULL;
15284 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15288 _codec ("PNG still images");
15289 caps = gst_caps_new_empty_simple ("image/png");
15292 _codec ("JPEG still images");
15294 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15297 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15298 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15299 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15300 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
15301 _codec ("Motion-JPEG");
15303 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15306 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
15307 _codec ("Motion-JPEG format B");
15308 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15311 _codec ("JPEG-2000");
15312 /* override to what it should be according to spec, avoid palette_data */
15313 entry->bits_per_sample = 24;
15314 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15317 _codec ("Sorensen video v.3");
15318 caps = gst_caps_new_simple ("video/x-svq",
15319 "svqversion", G_TYPE_INT, 3, NULL);
15321 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15322 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
15323 _codec ("Sorensen video v.1");
15324 caps = gst_caps_new_simple ("video/x-svq",
15325 "svqversion", G_TYPE_INT, 1, NULL);
15327 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15328 caps = gst_caps_new_empty_simple ("video/x-raw");
15329 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
15330 _codec ("Windows Raw RGB");
15331 stream->alignment = 32;
15337 bps = QT_UINT16 (stsd_entry_data + 82);
15340 format = GST_VIDEO_FORMAT_RGB15;
15343 format = GST_VIDEO_FORMAT_RGB16;
15346 format = GST_VIDEO_FORMAT_RGB;
15349 format = GST_VIDEO_FORMAT_ARGB;
15357 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15358 format = GST_VIDEO_FORMAT_I420;
15360 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15361 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15362 format = GST_VIDEO_FORMAT_I420;
15365 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15366 format = GST_VIDEO_FORMAT_UYVY;
15368 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15369 format = GST_VIDEO_FORMAT_v308;
15371 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15372 format = GST_VIDEO_FORMAT_v216;
15375 format = GST_VIDEO_FORMAT_v210;
15377 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15378 format = GST_VIDEO_FORMAT_r210;
15380 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15381 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15382 format = GST_VIDEO_FORMAT_v410;
15385 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15386 * but different order than AYUV
15387 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15388 format = GST_VIDEO_FORMAT_v408;
15391 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15392 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
15393 _codec ("MPEG-1 video");
15394 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15395 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15397 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15398 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15399 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15400 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15401 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15402 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15403 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15404 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15405 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15406 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15407 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15408 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15409 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15410 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15411 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15412 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15413 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15414 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15415 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15416 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15417 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15418 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15419 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15420 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15421 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15422 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15423 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15424 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15425 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15426 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15427 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15428 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15429 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15430 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15431 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15432 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15433 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15434 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15435 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15436 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15437 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15438 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15439 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15440 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15441 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15442 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15443 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15444 _codec ("MPEG-2 video");
15445 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15446 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15448 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15449 _codec ("GIF still images");
15450 caps = gst_caps_new_empty_simple ("image/gif");
15453 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15455 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15457 /* ffmpeg uses the height/width props, don't know why */
15458 caps = gst_caps_new_simple ("video/x-h263",
15459 "variant", G_TYPE_STRING, "itu", NULL);
15463 _codec ("MPEG-4 video");
15464 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15465 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15467 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15468 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15469 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15470 caps = gst_caps_new_simple ("video/x-msmpeg",
15471 "msmpegversion", G_TYPE_INT, 43, NULL);
15473 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15475 caps = gst_caps_new_simple ("video/x-divx",
15476 "divxversion", G_TYPE_INT, 3, NULL);
15478 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15479 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15481 caps = gst_caps_new_simple ("video/x-divx",
15482 "divxversion", G_TYPE_INT, 4, NULL);
15484 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15486 caps = gst_caps_new_simple ("video/x-divx",
15487 "divxversion", G_TYPE_INT, 5, NULL);
15490 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15492 caps = gst_caps_new_simple ("video/x-ffv",
15493 "ffvversion", G_TYPE_INT, 1, NULL);
15496 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15497 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15502 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15503 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15504 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15508 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15509 _codec ("Cinepak");
15510 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15512 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15513 _codec ("Apple QuickDraw");
15514 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15516 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15517 _codec ("Apple video");
15518 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15522 _codec ("H.264 / AVC");
15523 caps = gst_caps_new_simple ("video/x-h264",
15524 "stream-format", G_TYPE_STRING, "avc",
15525 "alignment", G_TYPE_STRING, "au", NULL);
15528 _codec ("H.264 / AVC");
15529 caps = gst_caps_new_simple ("video/x-h264",
15530 "stream-format", G_TYPE_STRING, "avc3",
15531 "alignment", G_TYPE_STRING, "au", NULL);
15535 _codec ("H.265 / HEVC");
15536 caps = gst_caps_new_simple ("video/x-h265",
15537 "stream-format", G_TYPE_STRING, "hvc1",
15538 "alignment", G_TYPE_STRING, "au", NULL);
15541 _codec ("H.265 / HEVC");
15542 caps = gst_caps_new_simple ("video/x-h265",
15543 "stream-format", G_TYPE_STRING, "hev1",
15544 "alignment", G_TYPE_STRING, "au", NULL);
15547 _codec ("Run-length encoding");
15548 caps = gst_caps_new_simple ("video/x-rle",
15549 "layout", G_TYPE_STRING, "quicktime", NULL);
15552 _codec ("Run-length encoding");
15553 caps = gst_caps_new_simple ("video/x-rle",
15554 "layout", G_TYPE_STRING, "microsoft", NULL);
15556 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15557 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15558 _codec ("Indeo Video 3");
15559 caps = gst_caps_new_simple ("video/x-indeo",
15560 "indeoversion", G_TYPE_INT, 3, NULL);
15562 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15563 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15564 _codec ("Intel Video 4");
15565 caps = gst_caps_new_simple ("video/x-indeo",
15566 "indeoversion", G_TYPE_INT, 4, NULL);
15570 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15571 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15572 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15573 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15574 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15575 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15576 _codec ("DV Video");
15577 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15578 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15580 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15581 case FOURCC_dv5p: /* DVCPRO50 PAL */
15582 _codec ("DVCPro50 Video");
15583 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15584 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15586 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15587 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15588 _codec ("DVCProHD Video");
15589 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15590 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15592 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15593 _codec ("Apple Graphics (SMC)");
15594 caps = gst_caps_new_empty_simple ("video/x-smc");
15596 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15598 caps = gst_caps_new_empty_simple ("video/x-vp3");
15600 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15601 _codec ("VP6 Flash");
15602 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15606 caps = gst_caps_new_empty_simple ("video/x-theora");
15607 /* theora uses one byte of padding in the data stream because it does not
15608 * allow 0 sized packets while theora does */
15609 entry->padding = 1;
15613 caps = gst_caps_new_empty_simple ("video/x-dirac");
15615 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15616 _codec ("TIFF still images");
15617 caps = gst_caps_new_empty_simple ("image/tiff");
15619 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15620 _codec ("Apple Intermediate Codec");
15621 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15623 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15624 _codec ("AVID DNxHD");
15625 caps = gst_caps_from_string ("video/x-dnxhd");
15629 _codec ("On2 VP8");
15630 caps = gst_caps_from_string ("video/x-vp8");
15633 _codec ("Google VP9");
15634 caps = gst_caps_from_string ("video/x-vp9");
15637 _codec ("Apple ProRes LT");
15639 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15643 _codec ("Apple ProRes HQ");
15645 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15649 _codec ("Apple ProRes");
15651 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15655 _codec ("Apple ProRes Proxy");
15657 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15661 _codec ("Apple ProRes 4444");
15663 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15667 _codec ("Apple ProRes 4444 XQ");
15669 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15673 _codec ("GoPro CineForm");
15674 caps = gst_caps_from_string ("video/x-cineform");
15679 caps = gst_caps_new_simple ("video/x-wmv",
15680 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15684 caps = gst_caps_new_empty_simple ("video/x-av1");
15686 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15689 caps = _get_unknown_codec_name ("video", fourcc);
15694 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15697 gst_video_info_init (&info);
15698 gst_video_info_set_format (&info, format, entry->width, entry->height);
15700 caps = gst_video_info_to_caps (&info);
15701 *codec_name = gst_pb_utils_get_codec_description (caps);
15703 /* enable clipping for raw video streams */
15704 stream->need_clip = TRUE;
15705 stream->alignment = 32;
15712 round_up_pow2 (guint n)
15724 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15725 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15726 int len, gchar ** codec_name)
15729 const GstStructure *s;
15732 GstAudioFormat format = 0;
15735 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15737 depth = entry->bytes_per_packet * 8;
15740 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15742 /* 8-bit audio is unsigned */
15744 format = GST_AUDIO_FORMAT_U8;
15745 /* otherwise it's signed and big-endian just like 'twos' */
15747 endian = G_BIG_ENDIAN;
15754 endian = G_LITTLE_ENDIAN;
15757 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15759 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15763 caps = gst_caps_new_simple ("audio/x-raw",
15764 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15765 "layout", G_TYPE_STRING, "interleaved", NULL);
15766 stream->alignment = GST_ROUND_UP_8 (depth);
15767 stream->alignment = round_up_pow2 (stream->alignment);
15770 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
15771 _codec ("Raw 64-bit floating-point audio");
15772 caps = gst_caps_new_simple ("audio/x-raw",
15773 "format", G_TYPE_STRING, "F64BE",
15774 "layout", G_TYPE_STRING, "interleaved", NULL);
15775 stream->alignment = 8;
15777 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
15778 _codec ("Raw 32-bit floating-point audio");
15779 caps = gst_caps_new_simple ("audio/x-raw",
15780 "format", G_TYPE_STRING, "F32BE",
15781 "layout", G_TYPE_STRING, "interleaved", NULL);
15782 stream->alignment = 4;
15785 _codec ("Raw 24-bit PCM audio");
15786 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15788 caps = gst_caps_new_simple ("audio/x-raw",
15789 "format", G_TYPE_STRING, "S24BE",
15790 "layout", G_TYPE_STRING, "interleaved", NULL);
15791 stream->alignment = 4;
15793 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
15794 _codec ("Raw 32-bit PCM audio");
15795 caps = gst_caps_new_simple ("audio/x-raw",
15796 "format", G_TYPE_STRING, "S32BE",
15797 "layout", G_TYPE_STRING, "interleaved", NULL);
15798 stream->alignment = 4;
15800 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
15801 _codec ("Raw 16-bit PCM audio");
15802 caps = gst_caps_new_simple ("audio/x-raw",
15803 "format", G_TYPE_STRING, "S16LE",
15804 "layout", G_TYPE_STRING, "interleaved", NULL);
15805 stream->alignment = 2;
15808 _codec ("Mu-law audio");
15809 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15812 _codec ("A-law audio");
15813 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15817 _codec ("Microsoft ADPCM");
15818 /* Microsoft ADPCM-ACM code 2 */
15819 caps = gst_caps_new_simple ("audio/x-adpcm",
15820 "layout", G_TYPE_STRING, "microsoft", NULL);
15824 _codec ("DVI/IMA ADPCM");
15825 caps = gst_caps_new_simple ("audio/x-adpcm",
15826 "layout", G_TYPE_STRING, "dvi", NULL);
15830 _codec ("DVI/Intel IMA ADPCM");
15831 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15832 caps = gst_caps_new_simple ("audio/x-adpcm",
15833 "layout", G_TYPE_STRING, "quicktime", NULL);
15837 /* MPEG layer 3, CBR only (pre QT4.1) */
15839 _codec ("MPEG-1 layer 3");
15840 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15841 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15842 "mpegversion", G_TYPE_INT, 1, NULL);
15844 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15845 _codec ("MPEG-1 layer 2");
15847 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15848 "mpegversion", G_TYPE_INT, 1, NULL);
15851 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15852 _codec ("EAC-3 audio");
15853 caps = gst_caps_new_simple ("audio/x-eac3",
15854 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15855 entry->sampled = TRUE;
15857 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15859 _codec ("AC-3 audio");
15860 caps = gst_caps_new_simple ("audio/x-ac3",
15861 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15862 entry->sampled = TRUE;
15864 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15865 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15866 _codec ("DTS audio");
15867 caps = gst_caps_new_simple ("audio/x-dts",
15868 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15869 entry->sampled = TRUE;
15871 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15872 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15873 _codec ("DTS-HD audio");
15874 caps = gst_caps_new_simple ("audio/x-dts",
15875 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15876 entry->sampled = TRUE;
15880 caps = gst_caps_new_simple ("audio/x-mace",
15881 "maceversion", G_TYPE_INT, 3, NULL);
15885 caps = gst_caps_new_simple ("audio/x-mace",
15886 "maceversion", G_TYPE_INT, 6, NULL);
15888 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15890 caps = gst_caps_new_empty_simple ("application/ogg");
15892 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15893 _codec ("DV audio");
15894 caps = gst_caps_new_empty_simple ("audio/x-dv");
15897 _codec ("MPEG-4 AAC audio");
15898 caps = gst_caps_new_simple ("audio/mpeg",
15899 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15900 "stream-format", G_TYPE_STRING, "raw", NULL);
15902 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15903 _codec ("QDesign Music");
15904 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15907 _codec ("QDesign Music v.2");
15908 /* FIXME: QDesign music version 2 (no constant) */
15909 if (FALSE && data) {
15910 caps = gst_caps_new_simple ("audio/x-qdm2",
15911 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15912 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15913 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15915 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15919 _codec ("GSM audio");
15920 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15923 _codec ("AMR audio");
15924 caps = gst_caps_new_empty_simple ("audio/AMR");
15927 _codec ("AMR-WB audio");
15928 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15931 _codec ("Quicktime IMA ADPCM");
15932 caps = gst_caps_new_simple ("audio/x-adpcm",
15933 "layout", G_TYPE_STRING, "quicktime", NULL);
15936 _codec ("Apple lossless audio");
15937 caps = gst_caps_new_empty_simple ("audio/x-alac");
15940 _codec ("Free Lossless Audio Codec");
15941 caps = gst_caps_new_simple ("audio/x-flac",
15942 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15944 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15945 _codec ("QualComm PureVoice");
15946 caps = gst_caps_from_string ("audio/qcelp");
15951 caps = gst_caps_new_empty_simple ("audio/x-wma");
15955 caps = gst_caps_new_empty_simple ("audio/x-opus");
15962 GstAudioFormat format;
15965 FLAG_IS_FLOAT = 0x1,
15966 FLAG_IS_BIG_ENDIAN = 0x2,
15967 FLAG_IS_SIGNED = 0x4,
15968 FLAG_IS_PACKED = 0x8,
15969 FLAG_IS_ALIGNED_HIGH = 0x10,
15970 FLAG_IS_NON_INTERLEAVED = 0x20
15972 _codec ("Raw LPCM audio");
15974 if (data && len >= 36) {
15975 depth = QT_UINT32 (data + 24);
15976 flags = QT_UINT32 (data + 28);
15977 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15979 if ((flags & FLAG_IS_FLOAT) == 0) {
15984 if ((flags & FLAG_IS_ALIGNED_HIGH))
15987 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15988 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15989 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15990 caps = gst_caps_new_simple ("audio/x-raw",
15991 "format", G_TYPE_STRING,
15993 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15994 "UNKNOWN", "layout", G_TYPE_STRING,
15995 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15996 "interleaved", NULL);
15997 stream->alignment = GST_ROUND_UP_8 (depth);
15998 stream->alignment = round_up_pow2 (stream->alignment);
16003 if (flags & FLAG_IS_BIG_ENDIAN)
16004 format = GST_AUDIO_FORMAT_F64BE;
16006 format = GST_AUDIO_FORMAT_F64LE;
16008 if (flags & FLAG_IS_BIG_ENDIAN)
16009 format = GST_AUDIO_FORMAT_F32BE;
16011 format = GST_AUDIO_FORMAT_F32LE;
16013 caps = gst_caps_new_simple ("audio/x-raw",
16014 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
16015 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
16016 "non-interleaved" : "interleaved", NULL);
16017 stream->alignment = width / 8;
16021 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
16025 caps = _get_unknown_codec_name ("audio", fourcc);
16031 GstCaps *templ_caps =
16032 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
16033 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16034 gst_caps_unref (caps);
16035 gst_caps_unref (templ_caps);
16036 caps = intersection;
16039 /* enable clipping for raw audio streams */
16040 s = gst_caps_get_structure (caps, 0);
16041 name = gst_structure_get_name (s);
16042 if (g_str_has_prefix (name, "audio/x-raw")) {
16043 stream->need_clip = TRUE;
16044 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
16045 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
16051 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16052 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16053 const guint8 * stsd_entry_data, gchar ** codec_name)
16057 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16061 _codec ("DVD subtitle");
16062 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
16063 stream->need_process = TRUE;
16066 _codec ("Quicktime timed text");
16069 _codec ("3GPP timed text");
16071 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16073 /* actual text piece needs to be extracted */
16074 stream->need_process = TRUE;
16077 _codec ("XML subtitles");
16078 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16081 _codec ("CEA 608 Closed Caption");
16083 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16084 G_TYPE_STRING, "s334-1a", NULL);
16085 stream->need_process = TRUE;
16086 stream->need_split = TRUE;
16089 _codec ("CEA 708 Closed Caption");
16091 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16092 G_TYPE_STRING, "cdp", NULL);
16093 stream->need_process = TRUE;
16098 caps = _get_unknown_codec_name ("text", fourcc);
16106 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16107 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16108 const guint8 * stsd_entry_data, gchar ** codec_name)
16114 _codec ("MPEG 1 video");
16115 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16116 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16126 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
16127 const gchar * system_id)
16131 if (!qtdemux->protection_system_ids)
16132 qtdemux->protection_system_ids =
16133 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16134 /* Check whether we already have an entry for this system ID. */
16135 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16136 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16137 if (g_ascii_strcasecmp (system_id, id) == 0) {
16141 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16142 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,