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 */
333 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
334 guint64 tkhd_duration;
338 guint64 duration; /* in timescale units */
342 gchar lang_id[4]; /* ISO 639-2T language code */
346 QtDemuxSample *samples;
347 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
348 guint32 n_samples_moof; /* sample count in a moof */
349 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
350 * the framerate of fragmented format stream */
351 guint64 duration_last_moof;
353 guint32 offset_in_sample; /* Offset in the current sample, used for
354 * streams which have got exceedingly big
355 * sample size (such as 24s of raw audio).
356 * Only used when max_buffer_size is non-NULL */
357 guint32 max_buffer_size; /* Maximum allowed size for output buffers.
358 * Currently only set for raw audio streams*/
366 gboolean use_allocator;
367 GstAllocator *allocator;
368 GstAllocationParams params;
372 /* when a discontinuity is pending */
375 /* list of buffers to push first */
378 /* if we need to clip this buffer. This is only needed for uncompressed
382 /* buffer needs some custom processing, e.g. subtitles */
383 gboolean need_process;
384 /* buffer needs potentially be split, e.g. CEA608 subtitles */
387 /* current position */
388 guint32 segment_index;
389 guint32 sample_index;
390 GstClockTime time_position; /* in gst time */
391 guint64 accumulated_base;
393 /* the Gst segment we are processing out, used for clipping */
396 /* quicktime segments */
398 QtDemuxSegment *segments;
399 gboolean dummy_segment;
404 GstTagList *stream_tags;
405 gboolean send_global_tags;
407 GstEvent *pending_event;
417 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
421 GstByteReader co_chunk;
423 guint32 current_chunk;
425 guint32 samples_per_chunk;
426 guint32 stsd_sample_description_id;
427 guint32 stco_sample_index;
429 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
432 guint32 n_samples_per_chunk;
433 guint32 stsc_chunk_index;
434 guint32 stsc_sample_index;
435 guint64 chunk_offset;
438 guint32 stts_samples;
439 guint32 n_sample_times;
440 guint32 stts_sample_index;
442 guint32 stts_duration;
444 gboolean stss_present;
445 guint32 n_sample_syncs;
448 gboolean stps_present;
449 guint32 n_sample_partial_syncs;
451 QtDemuxRandomAccessEntry *ra_entries;
454 const QtDemuxRandomAccessEntry *pending_seek;
457 gboolean ctts_present;
458 guint32 n_composition_times;
460 guint32 ctts_sample_index;
468 gboolean parsed_trex;
469 guint32 def_sample_description_index; /* index is 1-based */
470 guint32 def_sample_duration;
471 guint32 def_sample_size;
472 guint32 def_sample_flags;
476 /* stereoscopic video streams */
477 GstVideoMultiviewMode multiview_mode;
478 GstVideoMultiviewFlags multiview_flags;
480 /* protected streams */
482 guint32 protection_scheme_type;
483 guint32 protection_scheme_version;
484 gpointer protection_scheme_info; /* specific to the protection scheme */
485 GQueue protection_scheme_event_queue;
487 gint ref_count; /* atomic */
490 /* Contains properties and cryptographic info for a set of samples from a
491 * track protected using Common Encryption (cenc) */
492 struct _QtDemuxCencSampleSetInfo
494 GstStructure *default_properties;
496 /* @crypto_info holds one GstStructure per sample */
497 GPtrArray *crypto_info;
501 qt_demux_state_string (enum QtDemuxState state)
504 case QTDEMUX_STATE_INITIAL:
506 case QTDEMUX_STATE_HEADER:
508 case QTDEMUX_STATE_MOVIE:
510 case QTDEMUX_STATE_BUFFER_MDAT:
511 return "<BUFFER_MDAT>";
517 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
518 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
519 guint32 fourcc, GstByteReader * parser);
520 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
521 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
522 guint32 fourcc, GstByteReader * parser);
524 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
526 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
528 static GstStaticPadTemplate gst_qtdemux_sink_template =
529 GST_STATIC_PAD_TEMPLATE ("sink",
532 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
536 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
537 GST_STATIC_PAD_TEMPLATE ("video_%u",
540 GST_STATIC_CAPS_ANY);
542 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
543 GST_STATIC_PAD_TEMPLATE ("audio_%u",
546 GST_STATIC_CAPS_ANY);
548 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
549 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
552 GST_STATIC_CAPS_ANY);
554 #define gst_qtdemux_parent_class parent_class
555 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
557 static void gst_qtdemux_dispose (GObject * object);
560 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
561 GstClockTime media_time);
563 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
564 QtDemuxStream * str, gint64 media_offset);
567 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
568 static GstIndex *gst_qtdemux_get_index (GstElement * element);
570 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
571 GstStateChange transition);
572 static void gst_qtdemux_set_context (GstElement * element,
573 GstContext * context);
574 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
575 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
576 GstObject * parent, GstPadMode mode, gboolean active);
578 static void gst_qtdemux_loop (GstPad * pad);
579 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
581 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
583 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
585 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
586 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
587 QtDemuxStream * stream);
588 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
589 QtDemuxStream * stream);
590 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
593 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
595 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
596 const guint8 * buffer, guint length);
597 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
598 const guint8 * buffer, guint length);
599 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
600 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
603 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
604 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
606 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
607 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
608 const guint8 * stsd_entry_data, gchar ** codec_name);
609 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
610 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
611 const guint8 * data, int len, gchar ** codec_name);
612 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
613 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
614 gchar ** codec_name);
615 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
616 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
617 const guint8 * stsd_entry_data, gchar ** codec_name);
619 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
620 QtDemuxStream * stream, guint32 n);
621 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
622 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
623 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
624 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
625 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
626 static void qtdemux_do_allocation (QtDemuxStream * stream,
627 GstQTDemux * qtdemux);
628 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
629 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
630 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
631 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
632 GstClockTime * _start, GstClockTime * _stop);
633 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
634 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
636 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
637 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
639 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
641 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
642 QtDemuxStream * stream, guint sample_index);
643 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
645 static void qtdemux_gst_structure_free (GstStructure * gststructure);
646 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
648 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
649 static void gst_tag_register_spherical_tags (void);
650 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
653 gst_qtdemux_class_init (GstQTDemuxClass * klass)
655 GObjectClass *gobject_class;
656 GstElementClass *gstelement_class;
658 gobject_class = (GObjectClass *) klass;
659 gstelement_class = (GstElementClass *) klass;
661 parent_class = g_type_class_peek_parent (klass);
663 gobject_class->dispose = gst_qtdemux_dispose;
665 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
667 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
668 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
670 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
672 gst_tag_register_musicbrainz_tags ();
674 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
675 gst_tag_register_spherical_tags ();
676 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
678 gst_element_class_add_static_pad_template (gstelement_class,
679 &gst_qtdemux_sink_template);
680 gst_element_class_add_static_pad_template (gstelement_class,
681 &gst_qtdemux_videosrc_template);
682 gst_element_class_add_static_pad_template (gstelement_class,
683 &gst_qtdemux_audiosrc_template);
684 gst_element_class_add_static_pad_template (gstelement_class,
685 &gst_qtdemux_subsrc_template);
686 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
688 "Demultiplex a QuickTime file into audio and video streams",
689 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
691 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
696 gst_qtdemux_init (GstQTDemux * qtdemux)
699 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
700 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
701 gst_pad_set_activatemode_function (qtdemux->sinkpad,
702 qtdemux_sink_activate_mode);
703 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
704 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
705 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
706 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
708 qtdemux->adapter = gst_adapter_new ();
709 g_queue_init (&qtdemux->protection_event_queue);
710 qtdemux->flowcombiner = gst_flow_combiner_new ();
711 g_mutex_init (&qtdemux->expose_lock);
713 qtdemux->active_streams = g_ptr_array_new_with_free_func
714 ((GDestroyNotify) gst_qtdemux_stream_unref);
715 qtdemux->old_streams = g_ptr_array_new_with_free_func
716 ((GDestroyNotify) gst_qtdemux_stream_unref);
718 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
719 qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
720 malloc (sizeof (QtDemuxSphericalMetadata));
722 if (qtdemux->spherical_metadata) {
723 qtdemux->spherical_metadata->is_spherical = FALSE;
724 qtdemux->spherical_metadata->is_stitched = FALSE;
725 qtdemux->spherical_metadata->stitching_software = NULL;
726 qtdemux->spherical_metadata->projection_type = NULL;
727 qtdemux->spherical_metadata->stereo_mode = NULL;
728 qtdemux->spherical_metadata->source_count = 0;
729 qtdemux->spherical_metadata->init_view_heading = 0;
730 qtdemux->spherical_metadata->init_view_pitch = 0;
731 qtdemux->spherical_metadata->init_view_roll = 0;
732 qtdemux->spherical_metadata->timestamp = 0;
733 qtdemux->spherical_metadata->full_pano_width_pixels = 0;
734 qtdemux->spherical_metadata->full_pano_height_pixels = 0;
735 qtdemux->spherical_metadata->cropped_area_image_width = 0;
736 qtdemux->spherical_metadata->cropped_area_image_height = 0;
737 qtdemux->spherical_metadata->cropped_area_left = 0;
738 qtdemux->spherical_metadata->cropped_area_top = 0;
739 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
740 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
741 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
743 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
745 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
747 gst_qtdemux_reset (qtdemux, TRUE);
751 gst_qtdemux_dispose (GObject * object)
753 GstQTDemux *qtdemux = GST_QTDEMUX (object);
755 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
756 if (qtdemux->spherical_metadata) {
757 if (qtdemux->spherical_metadata->stitching_software)
758 free(qtdemux->spherical_metadata->stitching_software);
759 if (qtdemux->spherical_metadata->projection_type)
760 free(qtdemux->spherical_metadata->projection_type);
761 if (qtdemux->spherical_metadata->stereo_mode)
762 free(qtdemux->spherical_metadata->stereo_mode);
764 free(qtdemux->spherical_metadata);
765 qtdemux->spherical_metadata = NULL;
767 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
769 if (qtdemux->adapter) {
770 g_object_unref (G_OBJECT (qtdemux->adapter));
771 qtdemux->adapter = NULL;
773 gst_tag_list_unref (qtdemux->tag_list);
774 gst_flow_combiner_free (qtdemux->flowcombiner);
775 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
777 g_queue_clear (&qtdemux->protection_event_queue);
779 g_free (qtdemux->cenc_aux_info_sizes);
780 qtdemux->cenc_aux_info_sizes = NULL;
781 g_mutex_clear (&qtdemux->expose_lock);
783 g_ptr_array_free (qtdemux->active_streams, TRUE);
784 g_ptr_array_free (qtdemux->old_streams, TRUE);
786 G_OBJECT_CLASS (parent_class)->dispose (object);
790 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
792 if (qtdemux->posted_redirect) {
793 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
794 (_("This file contains no playable streams.")),
795 ("no known streams found, a redirect message has been posted"));
797 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
798 (_("This file contains no playable streams.")),
799 ("no known streams found"));
804 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
806 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
807 mem, size, 0, size, mem, free_func);
811 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
818 if (G_UNLIKELY (size == 0)) {
820 GstBuffer *tmp = NULL;
822 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
823 if (ret != GST_FLOW_OK)
826 gst_buffer_map (tmp, &map, GST_MAP_READ);
827 size = QT_UINT32 (map.data);
828 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
830 gst_buffer_unmap (tmp, &map);
831 gst_buffer_unref (tmp);
834 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
835 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
836 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
837 /* we're pulling header but already got most interesting bits,
838 * so never mind the rest (e.g. tags) (that much) */
839 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
843 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
844 (_("This file is invalid and cannot be played.")),
845 ("atom has bogus size %" G_GUINT64_FORMAT, size));
846 return GST_FLOW_ERROR;
850 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
852 if (G_UNLIKELY (flow != GST_FLOW_OK))
855 bsize = gst_buffer_get_size (*buf);
856 /* Catch short reads - we don't want any partial atoms */
857 if (G_UNLIKELY (bsize < size)) {
858 GST_WARNING_OBJECT (qtdemux,
859 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
860 gst_buffer_unref (*buf);
870 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
871 GstFormat src_format, gint64 src_value, GstFormat dest_format,
875 QtDemuxStream *stream = gst_pad_get_element_private (pad);
878 if (stream->subtype != FOURCC_vide) {
883 switch (src_format) {
884 case GST_FORMAT_TIME:
885 switch (dest_format) {
886 case GST_FORMAT_BYTES:{
887 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
893 *dest_value = stream->samples[index].offset;
895 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
896 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
897 GST_TIME_ARGS (src_value), *dest_value);
905 case GST_FORMAT_BYTES:
906 switch (dest_format) {
907 case GST_FORMAT_TIME:{
909 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
918 QTSTREAMTIME_TO_GSTTIME (stream,
919 stream->samples[index].timestamp);
920 GST_DEBUG_OBJECT (qtdemux,
921 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
922 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
941 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
943 gboolean res = FALSE;
945 *duration = GST_CLOCK_TIME_NONE;
947 if (qtdemux->duration != 0 &&
948 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
949 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
952 *duration = GST_CLOCK_TIME_NONE;
959 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
962 gboolean res = FALSE;
963 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
965 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
967 switch (GST_QUERY_TYPE (query)) {
968 case GST_QUERY_POSITION:{
971 gst_query_parse_position (query, &fmt, NULL);
972 if (fmt == GST_FORMAT_TIME
973 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
974 gst_query_set_position (query, GST_FORMAT_TIME,
975 qtdemux->segment.position);
980 case GST_QUERY_DURATION:{
983 gst_query_parse_duration (query, &fmt, NULL);
984 if (fmt == GST_FORMAT_TIME) {
985 /* First try to query upstream */
986 res = gst_pad_query_default (pad, parent, query);
988 GstClockTime duration;
989 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
990 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
997 case GST_QUERY_CONVERT:{
998 GstFormat src_fmt, dest_fmt;
999 gint64 src_value, dest_value = 0;
1001 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
1003 res = gst_qtdemux_src_convert (qtdemux, pad,
1004 src_fmt, src_value, dest_fmt, &dest_value);
1006 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
1010 case GST_QUERY_FORMATS:
1011 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
1014 case GST_QUERY_SEEKING:{
1018 /* try upstream first */
1019 res = gst_pad_query_default (pad, parent, query);
1022 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
1023 if (fmt == GST_FORMAT_TIME) {
1024 GstClockTime duration;
1026 gst_qtdemux_get_duration (qtdemux, &duration);
1028 if (!qtdemux->pullbased) {
1031 /* we might be able with help from upstream */
1033 q = gst_query_new_seeking (GST_FORMAT_BYTES);
1034 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
1035 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
1036 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
1038 gst_query_unref (q);
1040 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
1046 case GST_QUERY_SEGMENT:
1051 format = qtdemux->segment.format;
1054 gst_segment_to_stream_time (&qtdemux->segment, format,
1055 qtdemux->segment.start);
1056 if ((stop = qtdemux->segment.stop) == -1)
1057 stop = qtdemux->segment.duration;
1059 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
1061 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
1066 res = gst_pad_query_default (pad, parent, query);
1074 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
1076 if (G_LIKELY (stream->pad)) {
1077 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
1078 GST_DEBUG_PAD_NAME (stream->pad));
1080 if (!gst_tag_list_is_empty (stream->stream_tags)) {
1081 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
1082 stream->stream_tags);
1083 gst_pad_push_event (stream->pad,
1084 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
1085 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
1086 /* post message qtdemux tag (for early recive application) */
1087 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1088 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
1089 gst_tag_list_copy (stream->stream_tags)));
1093 if (G_UNLIKELY (stream->send_global_tags)) {
1094 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1096 gst_pad_push_event (stream->pad,
1097 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1098 stream->send_global_tags = FALSE;
1103 /* push event on all source pads; takes ownership of the event */
1105 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1107 gboolean has_valid_stream = FALSE;
1108 GstEventType etype = GST_EVENT_TYPE (event);
1111 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1112 GST_EVENT_TYPE_NAME (event));
1114 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1116 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1117 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
1119 if ((pad = stream->pad)) {
1120 has_valid_stream = TRUE;
1122 if (etype == GST_EVENT_EOS) {
1123 /* let's not send twice */
1124 if (stream->sent_eos)
1126 stream->sent_eos = TRUE;
1129 gst_pad_push_event (pad, gst_event_ref (event));
1133 gst_event_unref (event);
1135 /* if it is EOS and there are no pads, post an error */
1136 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1137 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1147 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1149 if ((gint64) s1->timestamp > *media_time)
1151 if ((gint64) s1->timestamp == *media_time)
1157 /* find the index of the sample that includes the data for @media_time using a
1158 * binary search. Only to be called in optimized cases of linear search below.
1160 * Returns the index of the sample with the corresponding *DTS*.
1163 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1166 QtDemuxSample *result;
1169 /* convert media_time to mov format */
1171 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1173 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1174 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1175 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1177 if (G_LIKELY (result))
1178 index = result - str->samples;
1187 /* find the index of the sample that includes the data for @media_offset using a
1190 * Returns the index of the sample.
1193 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1194 QtDemuxStream * str, gint64 media_offset)
1196 QtDemuxSample *result = str->samples;
1199 if (result == NULL || str->n_samples == 0)
1202 if (media_offset == result->offset)
1206 while (index < str->n_samples - 1) {
1207 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1210 if (media_offset < result->offset)
1221 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1226 /* find the index of the sample that includes the data for @media_time using a
1227 * linear search, and keeping in mind that not all samples may have been parsed
1228 * yet. If possible, it will delegate to binary search.
1230 * Returns the index of the sample.
1233 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1234 GstClockTime media_time)
1238 QtDemuxSample *sample;
1240 /* convert media_time to mov format */
1242 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1244 sample = str->samples;
1245 if (mov_time == sample->timestamp + sample->pts_offset)
1248 /* use faster search if requested time in already parsed range */
1249 sample = str->samples + str->stbl_index;
1250 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
1251 index = gst_qtdemux_find_index (qtdemux, str, media_time);
1252 sample = str->samples + index;
1254 while (index < str->n_samples - 1) {
1255 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1258 sample = str->samples + index + 1;
1259 if (mov_time < sample->timestamp) {
1260 sample = str->samples + index;
1268 /* sample->timestamp is now <= media_time, need to find the corresponding
1269 * PTS now by looking backwards */
1270 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
1272 sample = str->samples + index;
1280 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1285 /* find the index of the keyframe needed to decode the sample at @index
1286 * of stream @str, or of a subsequent keyframe (depending on @next)
1288 * Returns the index of the keyframe.
1291 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1292 guint32 index, gboolean next)
1294 guint32 new_index = index;
1296 if (index >= str->n_samples) {
1297 new_index = str->n_samples;
1301 /* all keyframes, return index */
1302 if (str->all_keyframe) {
1307 /* else search until we have a keyframe */
1308 while (new_index < str->n_samples) {
1309 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1312 if (str->samples[new_index].keyframe)
1324 if (new_index == str->n_samples) {
1325 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1330 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1331 "gave %u", next ? "after" : "before", index, new_index);
1338 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1343 /* find the segment for @time_position for @stream
1345 * Returns the index of the segment containing @time_position.
1346 * Returns the last segment and sets the @eos variable to TRUE
1347 * if the time is beyond the end. @eos may be NULL
1350 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1351 GstClockTime time_position)
1356 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1357 GST_TIME_ARGS (time_position));
1360 for (i = 0; i < stream->n_segments; i++) {
1361 QtDemuxSegment *segment = &stream->segments[i];
1363 GST_LOG_OBJECT (stream->pad,
1364 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1365 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1367 /* For the last segment we include stop_time in the last segment */
1368 if (i < stream->n_segments - 1) {
1369 if (segment->time <= time_position && time_position < segment->stop_time) {
1370 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1375 /* Last segment always matches */
1383 /* move the stream @str to the sample position @index.
1385 * Updates @str->sample_index and marks discontinuity if needed.
1388 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1391 /* no change needed */
1392 if (index == str->sample_index)
1395 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1398 /* position changed, we have a discont */
1399 str->sample_index = index;
1400 str->offset_in_sample = 0;
1401 /* Each time we move in the stream we store the position where we are
1403 str->from_sample = index;
1404 str->discont = TRUE;
1408 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1409 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1412 gint64 min_byte_offset = -1;
1415 min_offset = desired_time;
1417 /* for each stream, find the index of the sample in the segment
1418 * and move back to the previous keyframe. */
1419 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1421 guint32 index, kindex;
1423 GstClockTime media_start;
1424 GstClockTime media_time;
1425 GstClockTime seg_time;
1426 QtDemuxSegment *seg;
1427 gboolean empty_segment = FALSE;
1429 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1431 if (CUR_STREAM (str)->sparse && !use_sparse)
1434 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1435 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1437 /* get segment and time in the segment */
1438 seg = &str->segments[seg_idx];
1439 seg_time = (desired_time - seg->time) * seg->rate;
1441 while (QTSEGMENT_IS_EMPTY (seg)) {
1443 empty_segment = TRUE;
1444 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1447 if (seg_idx == str->n_segments)
1449 seg = &str->segments[seg_idx];
1452 if (seg_idx == str->n_segments) {
1453 /* FIXME track shouldn't have the last segment as empty, but if it
1454 * happens we better handle it */
1458 /* get the media time in the segment */
1459 media_start = seg->media_start + seg_time;
1461 /* get the index of the sample with media time */
1462 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1463 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1464 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1465 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1468 /* shift to next frame if we are looking for next keyframe */
1469 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1470 && index < str->stbl_index)
1473 if (!empty_segment) {
1474 /* find previous keyframe */
1475 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1477 /* we will settle for one before if none found after */
1478 if (next && kindex == -1)
1479 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1481 /* if the keyframe is at a different position, we need to update the
1482 * requested seek time */
1483 if (index != kindex) {
1486 /* get timestamp of keyframe */
1487 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1488 GST_DEBUG_OBJECT (qtdemux,
1489 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1490 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1491 str->samples[kindex].offset);
1493 /* keyframes in the segment get a chance to change the
1494 * desired_offset. keyframes out of the segment are
1496 if (media_time >= seg->media_start) {
1497 GstClockTime seg_time;
1499 /* this keyframe is inside the segment, convert back to
1501 seg_time = (media_time - seg->media_start) + seg->time;
1502 if ((!next && (seg_time < min_offset)) ||
1503 (next && (seg_time > min_offset)))
1504 min_offset = seg_time;
1509 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1510 min_byte_offset = str->samples[index].offset;
1514 *key_time = min_offset;
1516 *key_offset = min_byte_offset;
1520 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1521 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1525 g_return_val_if_fail (format != NULL, FALSE);
1526 g_return_val_if_fail (cur != NULL, FALSE);
1527 g_return_val_if_fail (stop != NULL, FALSE);
1529 if (*format == GST_FORMAT_TIME)
1533 if (cur_type != GST_SEEK_TYPE_NONE)
1534 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1535 if (res && stop_type != GST_SEEK_TYPE_NONE)
1536 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1539 *format = GST_FORMAT_TIME;
1544 /* perform seek in push based mode:
1545 find BYTE position to move to based on time and delegate to upstream
1548 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1553 GstSeekType cur_type, stop_type;
1554 gint64 cur, stop, key_cur;
1557 gint64 original_stop;
1560 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1562 gst_event_parse_seek (event, &rate, &format, &flags,
1563 &cur_type, &cur, &stop_type, &stop);
1564 seqnum = gst_event_get_seqnum (event);
1566 /* only forward streaming and seeking is possible */
1568 goto unsupported_seek;
1570 /* convert to TIME if needed and possible */
1571 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1575 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1576 * the original stop position to use when upstream pushes the new segment
1578 original_stop = stop;
1581 /* find reasonable corresponding BYTE position,
1582 * also try to mind about keyframes, since we can not go back a bit for them
1584 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1585 * mostly just work, but let's not yet boldly go there ... */
1586 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1591 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1592 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1595 GST_OBJECT_LOCK (qtdemux);
1596 qtdemux->seek_offset = byte_cur;
1597 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1598 qtdemux->push_seek_start = cur;
1600 qtdemux->push_seek_start = key_cur;
1603 if (stop_type == GST_SEEK_TYPE_NONE) {
1604 qtdemux->push_seek_stop = qtdemux->segment.stop;
1606 qtdemux->push_seek_stop = original_stop;
1608 GST_OBJECT_UNLOCK (qtdemux);
1610 qtdemux->segment_seqnum = seqnum;
1611 /* BYTE seek event */
1612 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1614 gst_event_set_seqnum (event, seqnum);
1615 res = gst_pad_push_event (qtdemux->sinkpad, event);
1622 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1628 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1633 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1638 /* perform the seek.
1640 * We set all segment_indexes in the streams to unknown and
1641 * adjust the time_position to the desired position. this is enough
1642 * to trigger a segment switch in the streaming thread to start
1643 * streaming from the desired position.
1645 * Keyframe seeking is a little more complicated when dealing with
1646 * segments. Ideally we want to move to the previous keyframe in
1647 * the segment but there might not be a keyframe in the segment. In
1648 * fact, none of the segments could contain a keyframe. We take a
1649 * practical approach: seek to the previous keyframe in the segment,
1650 * if there is none, seek to the beginning of the segment.
1652 * Called with STREAM_LOCK
1655 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1656 guint32 seqnum, GstSeekFlags flags)
1658 gint64 desired_offset;
1661 desired_offset = segment->position;
1663 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1664 GST_TIME_ARGS (desired_offset));
1666 /* may not have enough fragmented info to do this adjustment,
1667 * and we can't scan (and probably should not) at this time with
1668 * possibly flushing upstream */
1669 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1671 gboolean next, before, after;
1673 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1674 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1675 next = after && !before;
1676 if (segment->rate < 0)
1679 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1681 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1682 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1683 desired_offset = min_offset;
1686 /* and set all streams to the final position */
1687 gst_flow_combiner_reset (qtdemux->flowcombiner);
1688 qtdemux->segment_seqnum = seqnum;
1689 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1690 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1692 stream->time_position = desired_offset;
1693 stream->accumulated_base = 0;
1694 stream->sample_index = -1;
1695 stream->offset_in_sample = 0;
1696 stream->segment_index = -1;
1697 stream->sent_eos = FALSE;
1699 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1700 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1702 segment->position = desired_offset;
1703 segment->time = desired_offset;
1704 if (segment->rate >= 0) {
1705 segment->start = desired_offset;
1707 /* we stop at the end */
1708 if (segment->stop == -1)
1709 segment->stop = segment->duration;
1711 segment->stop = desired_offset;
1714 if (qtdemux->fragmented)
1715 qtdemux->fragmented_seek_pending = TRUE;
1720 /* do a seek in pull based mode */
1722 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1727 GstSeekType cur_type, stop_type;
1731 GstSegment seeksegment;
1732 guint32 seqnum = GST_SEQNUM_INVALID;
1733 GstEvent *flush_event;
1737 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1739 gst_event_parse_seek (event, &rate, &format, &flags,
1740 &cur_type, &cur, &stop_type, &stop);
1741 seqnum = gst_event_get_seqnum (event);
1743 /* we have to have a format as the segment format. Try to convert
1745 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1749 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1751 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1755 flush = flags & GST_SEEK_FLAG_FLUSH;
1757 /* stop streaming, either by flushing or by pausing the task */
1759 flush_event = gst_event_new_flush_start ();
1760 if (seqnum != GST_SEQNUM_INVALID)
1761 gst_event_set_seqnum (flush_event, seqnum);
1762 /* unlock upstream pull_range */
1763 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1764 /* make sure out loop function exits */
1765 gst_qtdemux_push_event (qtdemux, flush_event);
1767 /* non flushing seek, pause the task */
1768 gst_pad_pause_task (qtdemux->sinkpad);
1771 /* wait for streaming to finish */
1772 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1774 /* copy segment, we need this because we still need the old
1775 * segment when we close the current segment. */
1776 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1779 /* configure the segment with the seek variables */
1780 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1781 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1782 cur_type, cur, stop_type, stop, &update)) {
1784 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1786 /* now do the seek */
1787 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1790 /* now do the seek */
1791 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1794 /* prepare for streaming again */
1796 flush_event = gst_event_new_flush_stop (TRUE);
1797 if (seqnum != GST_SEQNUM_INVALID)
1798 gst_event_set_seqnum (flush_event, seqnum);
1800 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1801 gst_qtdemux_push_event (qtdemux, flush_event);
1804 /* commit the new segment */
1805 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1807 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1808 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1809 qtdemux->segment.format, qtdemux->segment.position);
1810 if (seqnum != GST_SEQNUM_INVALID)
1811 gst_message_set_seqnum (msg, seqnum);
1812 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1815 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1816 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1817 qtdemux->sinkpad, NULL);
1819 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1826 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1832 qtdemux_ensure_index (GstQTDemux * qtdemux)
1836 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1838 /* Build complete index */
1839 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1840 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1842 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1843 GST_LOG_OBJECT (qtdemux,
1844 "Building complete index of track-id %u for seeking failed!",
1854 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1857 gboolean res = TRUE;
1858 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1860 switch (GST_EVENT_TYPE (event)) {
1861 case GST_EVENT_SEEK:
1863 #ifndef GST_DISABLE_GST_DEBUG
1864 GstClockTime ts = gst_util_get_timestamp ();
1866 guint32 seqnum = gst_event_get_seqnum (event);
1868 qtdemux->received_seek = TRUE;
1870 if (seqnum == qtdemux->segment_seqnum) {
1871 GST_LOG_OBJECT (pad,
1872 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1873 gst_event_unref (event);
1877 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1878 /* seek should be handled by upstream, we might need to re-download fragments */
1879 GST_DEBUG_OBJECT (qtdemux,
1880 "let upstream handle seek for fragmented playback");
1884 /* Build complete index for seeking;
1885 * if not a fragmented file at least */
1886 if (!qtdemux->fragmented)
1887 if (!qtdemux_ensure_index (qtdemux))
1889 #ifndef GST_DISABLE_GST_DEBUG
1890 ts = gst_util_get_timestamp () - ts;
1891 GST_INFO_OBJECT (qtdemux,
1892 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1895 if (qtdemux->pullbased) {
1896 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1897 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1898 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1900 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1901 && QTDEMUX_N_STREAMS (qtdemux)
1902 && !qtdemux->fragmented) {
1903 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1905 GST_DEBUG_OBJECT (qtdemux,
1906 "ignoring seek in push mode in current state");
1909 gst_event_unref (event);
1913 res = gst_pad_event_default (pad, parent, event);
1923 GST_ERROR_OBJECT (qtdemux, "Index failed");
1924 gst_event_unref (event);
1930 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1932 * If @fw is false, the coding order is explored backwards.
1934 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1935 * sample is found for that track.
1937 * The stream and sample index of the sample with the minimum offset in the direction explored
1938 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1940 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1941 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1942 * @_stream and @_index. */
1944 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1945 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1948 gint64 time, min_time;
1949 QtDemuxStream *stream;
1956 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1959 gboolean set_sample;
1961 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1968 i = str->n_samples - 1;
1972 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1973 if (str->samples[i].size == 0)
1976 if (fw && (str->samples[i].offset < byte_pos))
1979 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1982 /* move stream to first available sample */
1984 gst_qtdemux_move_stream (qtdemux, str, i);
1988 /* avoid index from sparse streams since they might be far away */
1989 if (!CUR_STREAM (str)->sparse) {
1990 /* determine min/max time */
1991 time = QTSAMPLE_PTS (str, &str->samples[i]);
1992 if (min_time == -1 || (!fw && time > min_time) ||
1993 (fw && time < min_time)) {
1997 /* determine stream with leading sample, to get its position */
1999 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
2000 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
2008 /* no sample for this stream, mark eos */
2010 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
2021 /* Copied from mpegtsbase code */
2022 /* FIXME: replace this function when we add new util function for stream-id creation */
2024 _get_upstream_id (GstQTDemux * demux)
2026 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
2029 /* Try to create one from the upstream URI, else use a randome number */
2033 /* Try to generate one from the URI query and
2034 * if it fails take a random number instead */
2035 query = gst_query_new_uri ();
2036 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
2037 gst_query_parse_uri (query, &uri);
2043 /* And then generate an SHA256 sum of the URI */
2044 cs = g_checksum_new (G_CHECKSUM_SHA256);
2045 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
2047 upstream_id = g_strdup (g_checksum_get_string (cs));
2048 g_checksum_free (cs);
2050 /* Just get some random number if the URI query fails */
2051 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
2052 "implementing a deterministic way of creating a stream-id");
2054 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
2055 g_random_int (), g_random_int ());
2058 gst_query_unref (query);
2063 static QtDemuxStream *
2064 _create_stream (GstQTDemux * demux, guint32 track_id)
2066 QtDemuxStream *stream;
2069 stream = g_new0 (QtDemuxStream, 1);
2070 stream->demux = demux;
2071 stream->track_id = track_id;
2072 upstream_id = _get_upstream_id (demux);
2073 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
2074 g_free (upstream_id);
2075 /* new streams always need a discont */
2076 stream->discont = TRUE;
2077 /* we enable clipping for raw audio/video streams */
2078 stream->need_clip = FALSE;
2079 stream->need_process = FALSE;
2080 stream->segment_index = -1;
2081 stream->time_position = 0;
2082 stream->sample_index = -1;
2083 stream->offset_in_sample = 0;
2084 stream->new_stream = TRUE;
2085 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
2086 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2087 stream->protected = FALSE;
2088 stream->protection_scheme_type = 0;
2089 stream->protection_scheme_version = 0;
2090 stream->protection_scheme_info = NULL;
2091 stream->n_samples_moof = 0;
2092 stream->duration_moof = 0;
2093 stream->duration_last_moof = 0;
2094 stream->alignment = 1;
2095 stream->stream_tags = gst_tag_list_new_empty ();
2096 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2097 g_queue_init (&stream->protection_scheme_event_queue);
2098 stream->ref_count = 1;
2099 /* consistent default for push based mode */
2100 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
2105 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
2107 GstStructure *structure;
2108 const gchar *variant;
2109 const GstCaps *mediacaps = NULL;
2111 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
2113 structure = gst_caps_get_structure (caps, 0);
2114 variant = gst_structure_get_string (structure, "variant");
2116 if (variant && strcmp (variant, "mss-fragmented") == 0) {
2117 QtDemuxStream *stream;
2118 const GValue *value;
2120 demux->fragmented = TRUE;
2121 demux->mss_mode = TRUE;
2123 if (QTDEMUX_N_STREAMS (demux) > 1) {
2124 /* can't do this, we can only renegotiate for another mss format */
2128 value = gst_structure_get_value (structure, "media-caps");
2131 const GValue *timescale_v;
2133 /* TODO update when stream changes during playback */
2135 if (QTDEMUX_N_STREAMS (demux) == 0) {
2136 stream = _create_stream (demux, 1);
2137 g_ptr_array_add (demux->active_streams, stream);
2138 /* mss has no stsd/stsd entry, use id 0 as default */
2139 stream->stsd_entries_length = 1;
2140 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2141 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2143 stream = QTDEMUX_NTH_STREAM (demux, 0);
2146 timescale_v = gst_structure_get_value (structure, "timescale");
2148 stream->timescale = g_value_get_uint64 (timescale_v);
2150 /* default mss timescale */
2151 stream->timescale = 10000000;
2153 demux->timescale = stream->timescale;
2155 mediacaps = gst_value_get_caps (value);
2156 if (!CUR_STREAM (stream)->caps
2157 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2158 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2160 stream->new_caps = TRUE;
2162 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2163 structure = gst_caps_get_structure (mediacaps, 0);
2164 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2165 stream->subtype = FOURCC_vide;
2167 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2168 gst_structure_get_int (structure, "height",
2169 &CUR_STREAM (stream)->height);
2170 gst_structure_get_fraction (structure, "framerate",
2171 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2172 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2174 stream->subtype = FOURCC_soun;
2175 gst_structure_get_int (structure, "channels",
2176 &CUR_STREAM (stream)->n_channels);
2177 gst_structure_get_int (structure, "rate", &rate);
2178 CUR_STREAM (stream)->rate = rate;
2181 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2183 demux->mss_mode = FALSE;
2190 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2194 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2195 gst_pad_stop_task (qtdemux->sinkpad);
2197 if (hard || qtdemux->upstream_format_is_time) {
2198 qtdemux->state = QTDEMUX_STATE_INITIAL;
2199 qtdemux->neededbytes = 16;
2200 qtdemux->todrop = 0;
2201 qtdemux->pullbased = FALSE;
2202 qtdemux->posted_redirect = FALSE;
2203 qtdemux->first_mdat = -1;
2204 qtdemux->header_size = 0;
2205 qtdemux->mdatoffset = -1;
2206 qtdemux->restoredata_offset = -1;
2207 if (qtdemux->mdatbuffer)
2208 gst_buffer_unref (qtdemux->mdatbuffer);
2209 if (qtdemux->restoredata_buffer)
2210 gst_buffer_unref (qtdemux->restoredata_buffer);
2211 qtdemux->mdatbuffer = NULL;
2212 qtdemux->restoredata_buffer = NULL;
2213 qtdemux->mdatleft = 0;
2214 qtdemux->mdatsize = 0;
2215 if (qtdemux->comp_brands)
2216 gst_buffer_unref (qtdemux->comp_brands);
2217 qtdemux->comp_brands = NULL;
2218 qtdemux->last_moov_offset = -1;
2219 if (qtdemux->moov_node_compressed) {
2220 g_node_destroy (qtdemux->moov_node_compressed);
2221 if (qtdemux->moov_node)
2222 g_free (qtdemux->moov_node->data);
2224 qtdemux->moov_node_compressed = NULL;
2225 if (qtdemux->moov_node)
2226 g_node_destroy (qtdemux->moov_node);
2227 qtdemux->moov_node = NULL;
2228 if (qtdemux->tag_list)
2229 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2230 qtdemux->tag_list = gst_tag_list_new_empty ();
2231 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2233 if (qtdemux->element_index)
2234 gst_object_unref (qtdemux->element_index);
2235 qtdemux->element_index = NULL;
2237 qtdemux->major_brand = 0;
2238 qtdemux->upstream_format_is_time = FALSE;
2239 qtdemux->upstream_seekable = FALSE;
2240 qtdemux->upstream_size = 0;
2242 qtdemux->fragment_start = -1;
2243 qtdemux->fragment_start_offset = -1;
2244 qtdemux->duration = 0;
2245 qtdemux->moof_offset = 0;
2246 qtdemux->chapters_track_id = 0;
2247 qtdemux->have_group_id = FALSE;
2248 qtdemux->group_id = G_MAXUINT;
2250 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2252 g_queue_clear (&qtdemux->protection_event_queue);
2254 qtdemux->received_seek = FALSE;
2255 qtdemux->first_moof_already_parsed = FALSE;
2257 qtdemux->offset = 0;
2258 gst_adapter_clear (qtdemux->adapter);
2259 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2260 qtdemux->need_segment = TRUE;
2263 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2264 g_ptr_array_set_size (qtdemux->active_streams, 0);
2265 g_ptr_array_set_size (qtdemux->old_streams, 0);
2266 qtdemux->n_video_streams = 0;
2267 qtdemux->n_audio_streams = 0;
2268 qtdemux->n_sub_streams = 0;
2269 qtdemux->exposed = FALSE;
2270 qtdemux->fragmented = FALSE;
2271 qtdemux->mss_mode = FALSE;
2272 gst_caps_replace (&qtdemux->media_caps, NULL);
2273 qtdemux->timescale = 0;
2274 qtdemux->got_moov = FALSE;
2275 qtdemux->cenc_aux_info_offset = 0;
2276 qtdemux->cenc_aux_info_sizes = NULL;
2277 qtdemux->cenc_aux_sample_count = 0;
2278 if (qtdemux->protection_system_ids) {
2279 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2280 qtdemux->protection_system_ids = NULL;
2282 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2283 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2284 GST_BIN_FLAG_STREAMS_AWARE);
2286 if (qtdemux->preferred_protection_system_id) {
2287 g_free (qtdemux->preferred_protection_system_id);
2288 qtdemux->preferred_protection_system_id = NULL;
2290 } else if (qtdemux->mss_mode) {
2291 gst_flow_combiner_reset (qtdemux->flowcombiner);
2292 g_ptr_array_foreach (qtdemux->active_streams,
2293 (GFunc) gst_qtdemux_stream_clear, NULL);
2295 gst_flow_combiner_reset (qtdemux->flowcombiner);
2296 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2297 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2298 stream->sent_eos = FALSE;
2299 stream->time_position = 0;
2300 stream->accumulated_base = 0;
2306 /* Maps the @segment to the qt edts internal segments and pushes
2307 * the correspnding segment event.
2309 * If it ends up being at a empty segment, a gap will be pushed and the next
2310 * edts segment will be activated in sequence.
2312 * To be used in push-mode only */
2314 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2318 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2319 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2321 stream->time_position = segment->start;
2323 /* in push mode we should be guaranteed that we will have empty segments
2324 * at the beginning and then one segment after, other scenarios are not
2325 * supported and are discarded when parsing the edts */
2326 for (i = 0; i < stream->n_segments; i++) {
2327 if (stream->segments[i].stop_time > segment->start) {
2328 /* push the empty segment and move to the next one */
2329 gst_qtdemux_activate_segment (qtdemux, stream, i,
2330 stream->time_position);
2331 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2332 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2333 stream->time_position);
2335 /* accumulate previous segments */
2336 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2337 stream->accumulated_base +=
2338 (stream->segment.stop -
2339 stream->segment.start) / ABS (stream->segment.rate);
2343 g_assert (i == stream->n_segments - 1);
2350 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2361 for (i = 0; i < len; i++) {
2362 QtDemuxStream *stream = g_ptr_array_index (src, i);
2364 #ifndef GST_DISABLE_GST_DEBUG
2365 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2366 stream, GST_STR_NULL (stream->stream_id), dest);
2368 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2371 g_ptr_array_set_size (src, 0);
2375 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2378 GstQTDemux *demux = GST_QTDEMUX (parent);
2379 gboolean res = TRUE;
2381 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2383 switch (GST_EVENT_TYPE (event)) {
2384 case GST_EVENT_SEGMENT:
2387 QtDemuxStream *stream;
2391 /* some debug output */
2392 gst_event_copy_segment (event, &segment);
2393 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2396 if (segment.format == GST_FORMAT_TIME) {
2397 demux->upstream_format_is_time = TRUE;
2398 demux->segment_seqnum = gst_event_get_seqnum (event);
2400 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2401 "not in time format");
2403 /* chain will send initial newsegment after pads have been added */
2404 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2405 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2410 /* check if this matches a time seek we received previously
2411 * FIXME for backwards compatibility reasons we use the
2412 * seek_offset here to compare. In the future we might want to
2413 * change this to use the seqnum as it uniquely should identify
2414 * the segment that corresponds to the seek. */
2415 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2416 ", received segment offset %" G_GINT64_FORMAT,
2417 demux->seek_offset, segment.start);
2418 if (segment.format == GST_FORMAT_BYTES
2419 && demux->seek_offset == segment.start) {
2420 GST_OBJECT_LOCK (demux);
2421 offset = segment.start;
2423 segment.format = GST_FORMAT_TIME;
2424 segment.start = demux->push_seek_start;
2425 segment.stop = demux->push_seek_stop;
2426 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2427 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2428 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2429 GST_OBJECT_UNLOCK (demux);
2432 /* we only expect a BYTE segment, e.g. following a seek */
2433 if (segment.format == GST_FORMAT_BYTES) {
2434 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2435 offset = segment.start;
2437 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2438 NULL, (gint64 *) & segment.start);
2439 if ((gint64) segment.start < 0)
2442 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2443 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2444 NULL, (gint64 *) & segment.stop);
2445 /* keyframe seeking should already arrange for start >= stop,
2446 * but make sure in other rare cases */
2447 segment.stop = MAX (segment.stop, segment.start);
2449 } else if (segment.format == GST_FORMAT_TIME) {
2450 /* push all data on the adapter before starting this
2452 gst_qtdemux_process_adapter (demux, TRUE);
2454 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2458 /* We shouldn't modify upstream driven TIME FORMAT segment */
2459 if (!demux->upstream_format_is_time) {
2460 /* accept upstream's notion of segment and distribute along */
2461 segment.format = GST_FORMAT_TIME;
2462 segment.position = segment.time = segment.start;
2463 segment.duration = demux->segment.duration;
2464 segment.base = gst_segment_to_running_time (&demux->segment,
2465 GST_FORMAT_TIME, demux->segment.position);
2468 gst_segment_copy_into (&segment, &demux->segment);
2469 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2471 /* map segment to internal qt segments and push on each stream */
2472 if (QTDEMUX_N_STREAMS (demux)) {
2473 demux->need_segment = TRUE;
2474 gst_qtdemux_check_send_pending_segment (demux);
2477 /* clear leftover in current segment, if any */
2478 gst_adapter_clear (demux->adapter);
2480 /* set up streaming thread */
2481 demux->offset = offset;
2482 if (demux->upstream_format_is_time) {
2483 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2484 "set values to restart reading from a new atom");
2485 demux->neededbytes = 16;
2488 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2491 demux->todrop = stream->samples[idx].offset - offset;
2492 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2494 /* set up for EOS */
2495 demux->neededbytes = -1;
2500 gst_event_unref (event);
2504 case GST_EVENT_FLUSH_START:
2506 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2507 gst_event_unref (event);
2510 QTDEMUX_EXPOSE_LOCK (demux);
2511 res = gst_pad_event_default (demux->sinkpad, parent, event);
2512 QTDEMUX_EXPOSE_UNLOCK (demux);
2515 case GST_EVENT_FLUSH_STOP:
2519 dur = demux->segment.duration;
2520 gst_qtdemux_reset (demux, FALSE);
2521 demux->segment.duration = dur;
2523 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2524 gst_event_unref (event);
2530 /* If we are in push mode, and get an EOS before we've seen any streams,
2531 * then error out - we have nowhere to send the EOS */
2532 if (!demux->pullbased) {
2534 gboolean has_valid_stream = FALSE;
2535 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2536 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2537 has_valid_stream = TRUE;
2541 if (!has_valid_stream)
2542 gst_qtdemux_post_no_playable_stream_error (demux);
2544 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2545 (guint) gst_adapter_available (demux->adapter));
2546 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2552 case GST_EVENT_CAPS:{
2553 GstCaps *caps = NULL;
2555 gst_event_parse_caps (event, &caps);
2556 gst_qtdemux_setcaps (demux, caps);
2558 gst_event_unref (event);
2561 case GST_EVENT_PROTECTION:
2563 const gchar *system_id = NULL;
2565 gst_event_parse_protection (event, &system_id, NULL, NULL);
2566 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2568 gst_qtdemux_append_protection_system_id (demux, system_id);
2569 /* save the event for later, for source pads that have not been created */
2570 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2571 /* send it to all pads that already exist */
2572 gst_qtdemux_push_event (demux, event);
2576 case GST_EVENT_STREAM_START:
2579 gst_event_unref (event);
2581 /* Drain all the buffers */
2582 gst_qtdemux_process_adapter (demux, TRUE);
2583 gst_qtdemux_reset (demux, FALSE);
2584 /* We expect new moov box after new stream-start event */
2585 if (demux->exposed) {
2586 gst_qtdemux_stream_concat (demux,
2587 demux->old_streams, demux->active_streams);
2596 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2603 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2606 GstQTDemux *demux = GST_QTDEMUX (parent);
2607 gboolean res = FALSE;
2609 switch (GST_QUERY_TYPE (query)) {
2610 case GST_QUERY_BITRATE:
2612 GstClockTime duration;
2614 /* populate demux->upstream_size if not done yet */
2615 gst_qtdemux_check_seekability (demux);
2617 if (demux->upstream_size != -1
2618 && gst_qtdemux_get_duration (demux, &duration)) {
2620 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2623 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2624 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2625 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2627 /* TODO: better results based on ranges/index tables */
2628 gst_query_set_bitrate (query, bitrate);
2634 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2644 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2646 GstQTDemux *demux = GST_QTDEMUX (element);
2648 GST_OBJECT_LOCK (demux);
2649 if (demux->element_index)
2650 gst_object_unref (demux->element_index);
2652 demux->element_index = gst_object_ref (index);
2654 demux->element_index = NULL;
2656 GST_OBJECT_UNLOCK (demux);
2657 /* object lock might be taken again */
2659 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2660 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2661 demux->element_index, demux->index_id);
2665 gst_qtdemux_get_index (GstElement * element)
2667 GstIndex *result = NULL;
2668 GstQTDemux *demux = GST_QTDEMUX (element);
2670 GST_OBJECT_LOCK (demux);
2671 if (demux->element_index)
2672 result = gst_object_ref (demux->element_index);
2673 GST_OBJECT_UNLOCK (demux);
2675 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2682 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2684 g_free ((gpointer) stream->stco.data);
2685 stream->stco.data = NULL;
2686 g_free ((gpointer) stream->stsz.data);
2687 stream->stsz.data = NULL;
2688 g_free ((gpointer) stream->stsc.data);
2689 stream->stsc.data = NULL;
2690 g_free ((gpointer) stream->stts.data);
2691 stream->stts.data = NULL;
2692 g_free ((gpointer) stream->stss.data);
2693 stream->stss.data = NULL;
2694 g_free ((gpointer) stream->stps.data);
2695 stream->stps.data = NULL;
2696 g_free ((gpointer) stream->ctts.data);
2697 stream->ctts.data = NULL;
2701 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2703 g_free (stream->segments);
2704 stream->segments = NULL;
2705 stream->segment_index = -1;
2706 stream->accumulated_base = 0;
2710 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2712 g_free (stream->samples);
2713 stream->samples = NULL;
2714 gst_qtdemux_stbl_free (stream);
2717 g_free (stream->ra_entries);
2718 stream->ra_entries = NULL;
2719 stream->n_ra_entries = 0;
2721 stream->sample_index = -1;
2722 stream->stbl_index = -1;
2723 stream->n_samples = 0;
2724 stream->time_position = 0;
2726 stream->n_samples_moof = 0;
2727 stream->duration_moof = 0;
2728 stream->duration_last_moof = 0;
2732 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2735 if (stream->allocator)
2736 gst_object_unref (stream->allocator);
2737 while (stream->buffers) {
2738 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2739 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2741 for (i = 0; i < stream->stsd_entries_length; i++) {
2742 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2743 if (entry->rgb8_palette) {
2744 gst_memory_unref (entry->rgb8_palette);
2745 entry->rgb8_palette = NULL;
2747 entry->sparse = FALSE;
2750 if (stream->stream_tags)
2751 gst_tag_list_unref (stream->stream_tags);
2753 stream->stream_tags = gst_tag_list_new_empty ();
2754 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2755 g_free (stream->redirect_uri);
2756 stream->redirect_uri = NULL;
2757 stream->sent_eos = FALSE;
2758 stream->protected = FALSE;
2759 if (stream->protection_scheme_info) {
2760 if (stream->protection_scheme_type == FOURCC_cenc) {
2761 QtDemuxCencSampleSetInfo *info =
2762 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2763 if (info->default_properties)
2764 gst_structure_free (info->default_properties);
2765 if (info->crypto_info)
2766 g_ptr_array_free (info->crypto_info, TRUE);
2768 g_free (stream->protection_scheme_info);
2769 stream->protection_scheme_info = NULL;
2771 stream->protection_scheme_type = 0;
2772 stream->protection_scheme_version = 0;
2773 g_queue_foreach (&stream->protection_scheme_event_queue,
2774 (GFunc) gst_event_unref, NULL);
2775 g_queue_clear (&stream->protection_scheme_event_queue);
2776 gst_qtdemux_stream_flush_segments_data (stream);
2777 gst_qtdemux_stream_flush_samples_data (stream);
2781 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2784 gst_qtdemux_stream_clear (stream);
2785 for (i = 0; i < stream->stsd_entries_length; i++) {
2786 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2788 gst_caps_unref (entry->caps);
2792 g_free (stream->stsd_entries);
2793 stream->stsd_entries = NULL;
2794 stream->stsd_entries_length = 0;
2797 static QtDemuxStream *
2798 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2800 g_atomic_int_add (&stream->ref_count, 1);
2806 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2808 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2809 gst_qtdemux_stream_reset (stream);
2810 gst_tag_list_unref (stream->stream_tags);
2812 GstQTDemux *demux = stream->demux;
2813 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2814 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2816 g_free (stream->stream_id);
2821 static GstStateChangeReturn
2822 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2824 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2825 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2827 switch (transition) {
2828 case GST_STATE_CHANGE_READY_TO_PAUSED:
2829 gst_qtdemux_reset (qtdemux, TRUE);
2835 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2837 switch (transition) {
2838 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2839 gst_qtdemux_reset (qtdemux, TRUE);
2850 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2852 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2854 g_return_if_fail (GST_IS_CONTEXT (context));
2856 if (gst_context_has_context_type (context,
2857 "drm-preferred-decryption-system-id")) {
2858 const GstStructure *s;
2860 s = gst_context_get_structure (context);
2861 g_free (qtdemux->preferred_protection_system_id);
2862 qtdemux->preferred_protection_system_id =
2863 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2864 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2865 qtdemux->preferred_protection_system_id);
2868 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2872 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2874 /* counts as header data */
2875 qtdemux->header_size += length;
2877 /* only consider at least a sufficiently complete ftyp atom */
2881 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2882 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2883 GST_FOURCC_ARGS (qtdemux->major_brand));
2884 if (qtdemux->comp_brands)
2885 gst_buffer_unref (qtdemux->comp_brands);
2886 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2887 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2892 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2893 GstTagList * xmptaglist)
2895 /* Strip out bogus fields */
2897 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2898 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2899 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2901 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2904 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2906 /* prioritize native tags using _KEEP mode */
2907 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2908 gst_tag_list_unref (xmptaglist);
2912 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2914 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2915 const char *xml_str, const char *param_name, int *value)
2917 char *value_start, *value_end, *endptr;
2918 const short value_length_max = 12;
2919 char init_view_ret[12];
2920 int value_length = 0;
2923 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2926 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2931 value_start += strlen (param_name);
2932 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2935 value_end = strchr (value_start, '<');
2937 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2941 value_length = value_end - value_start;
2942 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2943 || (value_start[value_length - 1] == '\t')))
2946 if (value_start[i] == '+' || value_start[i] == '-')
2948 while (i < value_length) {
2949 if (value_start[i] < '0' || value_start[i] > '9') {
2950 GST_ERROR_OBJECT (qtdemux,
2951 "error: incorrect value, integer was expected\n");
2957 if (value_length >= value_length_max || value_length < 1) {
2958 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2962 strncpy (init_view_ret, value_start, value_length_max);
2963 init_view_ret[value_length] = '\0';
2965 *value = strtol (init_view_ret, &endptr, 10);
2966 if (endptr == init_view_ret) {
2967 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2975 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2976 const char *xml_str, const char *param_name, char **value)
2978 char *value_start, *value_end;
2979 const short value_length_max = 256;
2980 int value_length = 0;
2982 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2985 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2990 value_start += strlen (param_name);
2991 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2994 value_end = strchr (value_start, '<');
2996 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
3000 value_length = value_end - value_start;
3001 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
3002 || (value_start[value_length - 1] == '\t')))
3005 if (value_length >= value_length_max || value_length < 1) {
3006 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
3010 *value = strndup(value_start, value_length);
3016 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
3017 const char *xml_str, const char *param_name, gboolean * value)
3019 char *value_start, *value_end;
3020 int value_length = 0;
3022 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
3025 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
3030 value_start += strlen (param_name);
3031 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
3034 value_end = strchr (value_start, '<');
3036 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
3040 value_length = value_end - value_start;
3041 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
3042 || (value_start[value_length - 1] == '\t')))
3045 if (value_length < 1) {
3046 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
3050 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
3056 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
3058 const char is_spherical_str[] = "<GSpherical:Spherical>";
3059 const char is_stitched_str[] = "<GSpherical:Stitched>";
3060 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
3061 const char projection_type_str[] = "<GSpherical:ProjectionType>";
3062 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
3063 const char source_count_str[] = "<GSpherical:SourceCount>";
3064 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
3065 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
3066 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
3067 const char timestamp_str[] = "<GSpherical:Timestamp>";
3068 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
3069 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
3070 const char cropped_area_image_width_str[] =
3071 "<GSpherical:CroppedAreaImageWidthPixels>";
3072 const char cropped_area_image_height_str[] =
3073 "<GSpherical:CroppedAreaImageHeightPixels>";
3074 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
3075 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
3077 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
3079 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
3080 (gboolean *) & spherical_metadata->is_spherical);
3081 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
3082 (gboolean *) & spherical_metadata->is_stitched);
3084 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
3085 _get_string_value_from_xml_string (qtdemux, xmlStr,
3086 stitching_software_str, &spherical_metadata->stitching_software);
3087 _get_string_value_from_xml_string (qtdemux, xmlStr,
3088 projection_type_str, &spherical_metadata->projection_type);
3089 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
3090 &spherical_metadata->stereo_mode);
3091 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
3092 &spherical_metadata->source_count);
3093 _get_int_value_from_xml_string (qtdemux, xmlStr,
3094 init_view_heading_str, &spherical_metadata->init_view_heading);
3095 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
3096 &spherical_metadata->init_view_pitch);
3097 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
3098 &spherical_metadata->init_view_roll);
3099 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
3100 &spherical_metadata->timestamp);
3101 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
3102 &spherical_metadata->full_pano_width_pixels);
3103 _get_int_value_from_xml_string (qtdemux, xmlStr,
3104 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
3105 _get_int_value_from_xml_string (qtdemux, xmlStr,
3106 cropped_area_image_width_str,
3107 &spherical_metadata->cropped_area_image_width);
3108 _get_int_value_from_xml_string (qtdemux, xmlStr,
3109 cropped_area_image_height_str,
3110 &spherical_metadata->cropped_area_image_height);
3111 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
3112 &spherical_metadata->cropped_area_left);
3113 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
3114 &spherical_metadata->cropped_area_top);
3121 gst_tag_register_spherical_tags (void) {
3122 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
3125 _("Flag indicating if the video is a spherical video"),
3127 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
3130 _("Flag indicating if the video is stitched"),
3132 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
3134 _("tag-stitching-software"),
3135 _("Software used to stitch the spherical video"),
3137 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
3139 _("tag-projection-type"),
3140 _("Projection type used in the video frames"),
3142 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
3144 _("tag-stereo-mode"),
3145 _("Description of stereoscopic 3D layout"),
3147 gst_tag_register ("source_count", GST_TAG_FLAG_META,
3149 _("tag-source-count"),
3150 _("Number of cameras used to create the spherical video"),
3152 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
3154 _("tag-init-view-heading"),
3155 _("The heading angle of the initial view in degrees"),
3157 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
3159 _("tag-init-view-pitch"),
3160 _("The pitch angle of the initial view in degrees"),
3162 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
3164 _("tag-init-view-roll"),
3165 _("The roll angle of the initial view in degrees"),
3167 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
3170 _("Epoch timestamp of when the first frame in the video was recorded"),
3172 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
3174 _("tag-full-pano-width"),
3175 _("Width of the encoded video frame in pixels"),
3177 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
3179 _("tag-full-pano-height"),
3180 _("Height of the encoded video frame in pixels"),
3182 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
3184 _("tag-cropped-area-image-width"),
3185 _("Width of the video frame to display (e.g. cropping)"),
3187 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
3189 _("tag-cropped-area-image-height"),
3190 _("Height of the video frame to display (e.g. cropping)"),
3192 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3194 _("tag-cropped-area-left"),
3195 _("Column where the left edge of the image was cropped from the"
3196 " full sized panorama"),
3198 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3200 _("tag-cropped-area-top"),
3201 _("Row where the top edge of the image was cropped from the"
3202 " full sized panorama"),
3204 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3206 _("tag-ambisonic-type"),
3207 _("Specifies the type of ambisonic audio represented"),
3209 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3211 _("tag-ambisonic-format"),
3212 _("Specifies the ambisonic audio format"),
3214 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3216 _("tag-ambisonic-order"),
3217 _("Specifies the ambisonic audio channel order"),
3224 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3226 GstTagList *taglist;
3227 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3229 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3230 spherical_metadata->is_spherical);
3231 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3232 spherical_metadata->is_stitched);
3233 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3234 spherical_metadata->stitching_software);
3235 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3236 spherical_metadata->projection_type);
3237 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3238 spherical_metadata->stereo_mode);
3239 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3240 spherical_metadata->source_count);
3241 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3242 spherical_metadata->init_view_heading);
3243 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3244 spherical_metadata->init_view_pitch);
3245 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3246 spherical_metadata->init_view_roll);
3247 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3248 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3249 spherical_metadata->full_pano_width_pixels);
3250 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3251 spherical_metadata->full_pano_height_pixels);
3252 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3253 spherical_metadata->cropped_area_image_width);
3254 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3255 spherical_metadata->cropped_area_image_height);
3256 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3257 spherical_metadata->cropped_area_left);
3258 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3259 spherical_metadata->cropped_area_top);
3260 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3261 spherical_metadata->ambisonic_type);
3262 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3263 spherical_metadata->ambisonic_order);
3264 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3265 spherical_metadata->ambisonic_format);
3267 taglist = gst_tag_list_new_empty ();
3268 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3269 "is_spherical", spherical_metadata->is_spherical,
3270 "is_stitched", spherical_metadata->is_stitched,
3271 "source_count", spherical_metadata->source_count,
3272 "init_view_heading", spherical_metadata->init_view_heading,
3273 "init_view_pitch", spherical_metadata->init_view_pitch,
3274 "init_view_roll", spherical_metadata->init_view_roll,
3275 "timestamp", spherical_metadata->timestamp,
3276 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3277 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3278 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3279 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3280 "cropped_area_left", spherical_metadata->cropped_area_left,
3281 "cropped_area_top", spherical_metadata->cropped_area_top,
3282 "ambisonic_type", spherical_metadata->ambisonic_type,
3283 "ambisonic_format", spherical_metadata->ambisonic_format,
3284 "ambisonic_order", spherical_metadata->ambisonic_order,
3287 if (spherical_metadata->stitching_software)
3288 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3289 "stitching_software", spherical_metadata->stitching_software,
3291 if (spherical_metadata->projection_type)
3292 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3293 "projection_type", spherical_metadata->projection_type,
3295 if (spherical_metadata->stereo_mode)
3296 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3297 "stereo_mode", spherical_metadata->stereo_mode,
3300 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3301 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3302 gst_tag_list_copy (taglist)));
3304 gst_tag_list_unref(taglist);
3310 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3315 guint8 ambisonic_type = 0;
3316 guint32 ambisonic_order = 0;
3317 guint8 ambisonic_channel_ordering = 0;
3318 guint8 ambisonic_normalization = 0;
3319 guint32 num_channels = 0;
3320 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3324 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3326 qtdemux->header_size += length;
3327 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3329 if (length <= offset + 16) {
3330 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3334 version = QT_UINT8 (buffer + offset);
3335 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3336 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3337 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3338 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3339 num_channels = QT_UINT32 (buffer + offset + 8);
3340 for (i = 0; i < num_channels; ++i)
3341 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3343 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3344 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3345 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3346 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3347 ambisonic_channel_ordering);
3348 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3349 ambisonic_normalization);
3350 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3351 for (i = 0; i < num_channels; ++i)
3352 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3354 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3355 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3356 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3358 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3359 if (num_channels == 4) {
3360 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3362 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3363 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3364 && (channel_map[0] == 0) && (channel_map[1] == 1)
3365 && (channel_map[2] == 2) && (channel_map[3] == 3))
3366 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3368 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3369 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3370 && (channel_map[0] == 0) && (channel_map[1] == 3)
3371 && (channel_map[2] == 1) && (channel_map[3] == 2))
3372 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3379 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3382 qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
3383 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
3386 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3387 gst_buffer_fill (kid_buf, 0, kid, 16);
3388 if (info->default_properties)
3389 gst_structure_free (info->default_properties);
3390 info->default_properties =
3391 gst_structure_new ("application/x-cenc",
3392 "iv_size", G_TYPE_UINT, iv_size,
3393 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
3394 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3395 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3396 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3397 gst_buffer_unref (kid_buf);
3401 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
3402 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
3404 guint32 algorithm_id = 0;
3406 gboolean is_encrypted = TRUE;
3409 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
3410 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3415 if (algorithm_id == 0) {
3416 is_encrypted = FALSE;
3417 } else if (algorithm_id == 1) {
3418 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3419 } else if (algorithm_id == 2) {
3420 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3423 if (!gst_byte_reader_get_uint8 (br, &iv_size))
3426 if (!gst_byte_reader_get_data (br, 16, &kid))
3429 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
3430 is_encrypted, iv_size, kid);
3431 gst_structure_set (info->default_properties, "piff_algorithm_id",
3432 G_TYPE_UINT, algorithm_id, NULL);
3438 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3446 QtDemuxStream *stream;
3447 GstStructure *structure;
3448 QtDemuxCencSampleSetInfo *ss_info = NULL;
3449 const gchar *system_id;
3450 gboolean uses_sub_sample_encryption = FALSE;
3451 guint32 sample_count;
3453 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
3456 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
3458 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3459 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3460 GST_WARNING_OBJECT (qtdemux,
3461 "Attempting PIFF box parsing on an unencrypted stream.");
3465 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3466 G_TYPE_STRING, &system_id, NULL);
3467 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3469 stream->protected = TRUE;
3470 stream->protection_scheme_type = FOURCC_cenc;
3472 if (!stream->protection_scheme_info)
3473 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3475 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3476 if (!ss_info->default_properties) {
3477 ss_info->default_properties =
3478 gst_structure_new ("application/x-cenc",
3479 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
3484 if (ss_info->crypto_info) {
3485 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3486 g_ptr_array_free (ss_info->crypto_info, TRUE);
3487 ss_info->crypto_info = NULL;
3491 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3493 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3494 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3498 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3499 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3503 if ((flags & 0x000001)) {
3504 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
3507 } else if ((flags & 0x000002)) {
3508 uses_sub_sample_encryption = TRUE;
3511 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
3513 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
3517 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
3518 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3522 ss_info->crypto_info =
3523 g_ptr_array_new_full (sample_count,
3524 (GDestroyNotify) qtdemux_gst_structure_free);
3526 for (i = 0; i < sample_count; ++i) {
3527 GstStructure *properties;
3531 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3532 if (properties == NULL) {
3533 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3534 qtdemux->cenc_aux_sample_count = i;
3538 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3539 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3540 gst_structure_free (properties);
3541 qtdemux->cenc_aux_sample_count = i;
3544 buf = gst_buffer_new_wrapped (data, iv_size);
3545 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3546 gst_buffer_unref (buf);
3548 if (uses_sub_sample_encryption) {
3549 guint16 n_subsamples;
3550 const GValue *kid_buf_value;
3552 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3553 || n_subsamples == 0) {
3554 GST_ERROR_OBJECT (qtdemux,
3555 "failed to get subsample count for sample %u", i);
3556 gst_structure_free (properties);
3557 qtdemux->cenc_aux_sample_count = i;
3560 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3561 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3562 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3564 gst_structure_free (properties);
3565 qtdemux->cenc_aux_sample_count = i;
3568 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3571 gst_structure_get_value (ss_info->default_properties, "kid");
3573 gst_structure_set (properties,
3574 "subsample_count", G_TYPE_UINT, n_subsamples,
3575 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3576 gst_structure_set_value (properties, "kid", kid_buf_value);
3577 gst_buffer_unref (buf);
3579 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3582 g_ptr_array_add (ss_info->crypto_info, properties);
3585 qtdemux->cenc_aux_sample_count = sample_count;
3589 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3591 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3592 0x97, 0xA9, 0x42, 0xE8,
3593 0x9C, 0x71, 0x99, 0x94,
3594 0x91, 0xE3, 0xAF, 0xAC
3596 static const guint8 playready_uuid[] = {
3597 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3598 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3601 static const guint8 piff_sample_encryption_uuid[] = {
3602 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3603 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3606 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3607 static const guint8 spherical_uuid[] = {
3608 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3609 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3611 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3615 /* counts as header data */
3616 qtdemux->header_size += length;
3618 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3620 if (length <= offset + 16) {
3621 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3625 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3626 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3627 const char *contents;
3629 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3630 contents = (char *) (buffer + offset + 16);
3631 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3633 if (qtdemux->spherical_metadata)
3634 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3638 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3640 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3642 GstTagList *taglist;
3644 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3645 length - offset - 16, NULL);
3646 taglist = gst_tag_list_from_xmp_buffer (buf);
3647 gst_buffer_unref (buf);
3649 /* make sure we have a usable taglist */
3650 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3652 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3654 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3656 const gunichar2 *s_utf16;
3659 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3660 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3661 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3662 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3666 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3667 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3669 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3670 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3672 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3673 GST_READ_UINT32_LE (buffer + offset),
3674 GST_READ_UINT32_LE (buffer + offset + 4),
3675 GST_READ_UINT32_LE (buffer + offset + 8),
3676 GST_READ_UINT32_LE (buffer + offset + 12));
3681 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3683 GstSidxParser sidx_parser;
3684 GstIsoffParserResult res;
3687 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3690 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3692 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3693 if (res == GST_ISOFF_QT_PARSER_DONE) {
3694 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3696 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3699 /* caller verifies at least 8 bytes in buf */
3701 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3702 guint64 * plength, guint32 * pfourcc)
3707 length = QT_UINT32 (data);
3708 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3709 fourcc = QT_FOURCC (data + 4);
3710 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3713 length = G_MAXUINT64;
3714 } else if (length == 1 && size >= 16) {
3715 /* this means we have an extended size, which is the 64 bit value of
3716 * the next 8 bytes */
3717 length = QT_UINT64 (data + 8);
3718 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3728 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3730 guint32 version = 0;
3731 GstClockTime duration = 0;
3733 if (!gst_byte_reader_get_uint32_be (br, &version))
3738 if (!gst_byte_reader_get_uint64_be (br, &duration))
3743 if (!gst_byte_reader_get_uint32_be (br, &dur))
3748 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3749 qtdemux->duration = duration;
3755 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3761 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3762 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3764 if (!stream->parsed_trex && qtdemux->moov_node) {
3766 GstByteReader trex_data;
3768 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3770 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3773 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3775 /* skip version/flags */
3776 if (!gst_byte_reader_skip (&trex_data, 4))
3778 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3780 if (id != stream->track_id)
3782 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3784 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3786 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3788 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3791 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3792 "duration %d, size %d, flags 0x%x", stream->track_id,
3795 stream->parsed_trex = TRUE;
3796 stream->def_sample_description_index = sdi;
3797 stream->def_sample_duration = dur;
3798 stream->def_sample_size = size;
3799 stream->def_sample_flags = flags;
3802 /* iterate all siblings */
3803 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3809 *ds_duration = stream->def_sample_duration;
3810 *ds_size = stream->def_sample_size;
3811 *ds_flags = stream->def_sample_flags;
3813 /* even then, above values are better than random ... */
3814 if (G_UNLIKELY (!stream->parsed_trex)) {
3815 GST_WARNING_OBJECT (qtdemux,
3816 "failed to find fragment defaults for stream %d", stream->track_id);
3823 /* This method should be called whenever a more accurate duration might
3824 * have been found. It will update all relevant variables if/where needed
3827 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3831 GstClockTime prevdur;
3833 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3835 if (movdur > qtdemux->duration) {
3836 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3837 GST_DEBUG_OBJECT (qtdemux,
3838 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3839 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3840 qtdemux->duration = movdur;
3841 GST_DEBUG_OBJECT (qtdemux,
3842 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3843 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3844 GST_TIME_ARGS (qtdemux->segment.stop));
3845 if (qtdemux->segment.duration == prevdur) {
3846 /* If the current segment has duration/stop identical to previous duration
3847 * update them also (because they were set at that point in time with
3848 * the wrong duration */
3849 /* We convert the value *from* the timescale version to avoid rounding errors */
3850 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3851 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3852 qtdemux->segment.duration = fixeddur;
3853 qtdemux->segment.stop = fixeddur;
3857 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3858 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3860 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3861 if (movdur > stream->duration) {
3862 GST_DEBUG_OBJECT (qtdemux,
3863 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3864 GST_TIME_ARGS (duration));
3865 stream->duration = movdur;
3866 /* internal duration tracking state has been updated above, so */
3867 /* preserve an open-ended dummy segment rather than repeatedly updating
3868 * it and spamming downstream accordingly with segment events */
3869 if (stream->dummy_segment &&
3870 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3871 /* Update all dummy values to new duration */
3872 stream->segments[0].stop_time = duration;
3873 stream->segments[0].duration = duration;
3874 stream->segments[0].media_stop = duration;
3876 /* let downstream know we possibly have a new stop time */
3877 if (stream->segment_index != -1) {
3880 if (qtdemux->segment.rate >= 0) {
3881 pos = stream->segment.start;
3883 pos = stream->segment.stop;
3886 gst_qtdemux_stream_update_segment (qtdemux, stream,
3887 stream->segment_index, pos, NULL, NULL);
3895 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3896 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3897 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3898 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3901 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3903 gint32 data_offset = 0;
3904 guint32 flags = 0, first_flags = 0, samples_count = 0;
3907 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3908 QtDemuxSample *sample;
3909 gboolean ismv = FALSE;
3910 gint64 initial_offset;
3912 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3913 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3914 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3915 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3917 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3918 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3922 /* presence of stss or not can't really tell us much,
3923 * and flags and so on tend to be marginally reliable in these files */
3924 if (stream->subtype == FOURCC_soun) {
3925 GST_DEBUG_OBJECT (qtdemux,
3926 "sound track in fragmented file; marking all keyframes");
3927 stream->all_keyframe = TRUE;
3930 if (!gst_byte_reader_skip (trun, 1) ||
3931 !gst_byte_reader_get_uint24_be (trun, &flags))
3934 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3937 if (flags & TR_DATA_OFFSET) {
3938 /* note this is really signed */
3939 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3941 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3942 /* default base offset = first byte of moof */
3943 if (*base_offset == -1) {
3944 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3945 *base_offset = moof_offset;
3947 *running_offset = *base_offset + data_offset;
3949 /* if no offset at all, that would mean data starts at moof start,
3950 * which is a bit wrong and is ismv crappy way, so compensate
3951 * assuming data is in mdat following moof */
3952 if (*base_offset == -1) {
3953 *base_offset = moof_offset + moof_length + 8;
3954 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3957 if (*running_offset == -1)
3958 *running_offset = *base_offset;
3961 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3963 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3964 data_offset, flags, samples_count);
3966 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3967 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3968 GST_DEBUG_OBJECT (qtdemux,
3969 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3970 flags ^= TR_FIRST_SAMPLE_FLAGS;
3972 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3974 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3978 /* FIXME ? spec says other bits should also be checked to determine
3979 * entry size (and prefix size for that matter) */
3981 dur_offset = size_offset = 0;
3982 if (flags & TR_SAMPLE_DURATION) {
3983 GST_LOG_OBJECT (qtdemux, "entry duration present");
3984 dur_offset = entry_size;
3987 if (flags & TR_SAMPLE_SIZE) {
3988 GST_LOG_OBJECT (qtdemux, "entry size present");
3989 size_offset = entry_size;
3992 if (flags & TR_SAMPLE_FLAGS) {
3993 GST_LOG_OBJECT (qtdemux, "entry flags present");
3994 flags_offset = entry_size;
3997 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3998 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3999 ct_offset = entry_size;
4003 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
4005 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
4007 if (stream->n_samples + samples_count >=
4008 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
4011 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
4012 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
4013 (stream->n_samples + samples_count) *
4014 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
4016 /* create a new array of samples if it's the first sample parsed */
4017 if (stream->n_samples == 0) {
4018 g_assert (stream->samples == NULL);
4019 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
4020 /* or try to reallocate it with space enough to insert the new samples */
4022 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
4023 stream->n_samples + samples_count);
4024 if (stream->samples == NULL)
4027 if (qtdemux->fragment_start != -1) {
4028 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
4029 qtdemux->fragment_start = -1;
4031 if (stream->n_samples == 0) {
4032 if (decode_ts > 0) {
4033 timestamp = decode_ts;
4034 } else if (stream->pending_seek != NULL) {
4035 /* if we don't have a timestamp from a tfdt box, we'll use the one
4036 * from the mfra seek table */
4037 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
4038 GST_TIME_ARGS (stream->pending_seek->ts));
4040 /* FIXME: this is not fully correct, the timestamp refers to the random
4041 * access sample refered to in the tfra entry, which may not necessarily
4042 * be the first sample in the tfrag/trun (but hopefully/usually is) */
4043 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
4048 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4049 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
4050 GST_TIME_ARGS (gst_ts));
4052 /* subsequent fragments extend stream */
4054 stream->samples[stream->n_samples - 1].timestamp +
4055 stream->samples[stream->n_samples - 1].duration;
4057 /* If this is a GST_FORMAT_BYTES stream and there's a significant
4058 * difference (1 sec.) between decode_ts and timestamp, prefer the
4060 if (has_tfdt && !qtdemux->upstream_format_is_time
4061 && ABSDIFF (decode_ts, timestamp) >
4062 MAX (stream->duration_last_moof / 2,
4063 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
4064 GST_INFO_OBJECT (qtdemux,
4065 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
4066 ") are significantly different (more than %" GST_TIME_FORMAT
4067 "), using decode_ts",
4068 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
4069 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
4070 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
4071 MAX (stream->duration_last_moof / 2,
4072 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
4073 timestamp = decode_ts;
4076 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4077 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4078 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
4082 initial_offset = *running_offset;
4084 sample = stream->samples + stream->n_samples;
4085 for (i = 0; i < samples_count; i++) {
4086 guint32 dur, size, sflags, ct;
4088 /* first read sample data */
4089 if (flags & TR_SAMPLE_DURATION) {
4090 dur = QT_UINT32 (data + dur_offset);
4092 dur = d_sample_duration;
4094 if (flags & TR_SAMPLE_SIZE) {
4095 size = QT_UINT32 (data + size_offset);
4097 size = d_sample_size;
4099 if (flags & TR_FIRST_SAMPLE_FLAGS) {
4101 sflags = first_flags;
4103 sflags = d_sample_flags;
4105 } else if (flags & TR_SAMPLE_FLAGS) {
4106 sflags = QT_UINT32 (data + flags_offset);
4108 sflags = d_sample_flags;
4110 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4111 ct = QT_UINT32 (data + ct_offset);
4117 /* fill the sample information */
4118 sample->offset = *running_offset;
4119 sample->pts_offset = ct;
4120 sample->size = size;
4121 sample->timestamp = timestamp;
4122 sample->duration = dur;
4123 /* sample-is-difference-sample */
4124 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
4125 * now idea how it relates to bitfield other than massive LE/BE confusion */
4126 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
4127 *running_offset += size;
4129 stream->duration_moof += dur;
4133 /* Update total duration if needed */
4134 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
4136 /* Pre-emptively figure out size of mdat based on trun information.
4137 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
4138 * size, else we will still be able to use this when dealing with gap'ed
4140 qtdemux->mdatleft = *running_offset - initial_offset;
4141 qtdemux->mdatoffset = initial_offset;
4142 qtdemux->mdatsize = qtdemux->mdatleft;
4144 stream->n_samples += samples_count;
4145 stream->n_samples_moof += samples_count;
4147 if (stream->pending_seek != NULL)
4148 stream->pending_seek = NULL;
4154 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
4159 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
4165 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
4166 "be larger than %uMB (broken file?)", stream->n_samples,
4167 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
4172 /* find stream with @id */
4173 static inline QtDemuxStream *
4174 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
4176 QtDemuxStream *stream;
4180 if (G_UNLIKELY (!id)) {
4181 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
4185 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4186 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4187 if (stream->track_id == id)
4190 if (qtdemux->mss_mode) {
4191 /* mss should have only 1 stream anyway */
4192 return QTDEMUX_NTH_STREAM (qtdemux, 0);
4199 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
4200 guint32 * fragment_number)
4202 if (!gst_byte_reader_skip (mfhd, 4))
4204 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
4209 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
4215 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
4216 QtDemuxStream ** stream, guint32 * default_sample_duration,
4217 guint32 * default_sample_size, guint32 * default_sample_flags,
4218 gint64 * base_offset)
4221 guint32 track_id = 0;
4223 if (!gst_byte_reader_skip (tfhd, 1) ||
4224 !gst_byte_reader_get_uint24_be (tfhd, &flags))
4227 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4230 *stream = qtdemux_find_stream (qtdemux, track_id);
4231 if (G_UNLIKELY (!*stream))
4232 goto unknown_stream;
4234 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4235 *base_offset = qtdemux->moof_offset;
4237 if (flags & TF_BASE_DATA_OFFSET)
4238 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4241 /* obtain stream defaults */
4242 qtdemux_parse_trex (qtdemux, *stream,
4243 default_sample_duration, default_sample_size, default_sample_flags);
4245 (*stream)->stsd_sample_description_id =
4246 (*stream)->def_sample_description_index - 1;
4248 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4249 guint32 sample_description_index;
4250 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4252 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4255 if (qtdemux->mss_mode) {
4256 /* mss has no stsd entry */
4257 (*stream)->stsd_sample_description_id = 0;
4260 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4261 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4264 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4265 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4268 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4269 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4276 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4281 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
4287 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4288 guint64 * decode_time)
4290 guint32 version = 0;
4292 if (!gst_byte_reader_get_uint32_be (br, &version))
4297 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4300 guint32 dec_time = 0;
4301 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4303 *decode_time = dec_time;
4306 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4313 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4318 /* Returns a pointer to a GstStructure containing the properties of
4319 * the stream sample identified by @sample_index. The caller must unref
4320 * the returned object after use. Returns NULL if unsuccessful. */
4321 static GstStructure *
4322 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4323 QtDemuxStream * stream, guint sample_index)
4325 QtDemuxCencSampleSetInfo *info = NULL;
4327 g_return_val_if_fail (stream != NULL, NULL);
4328 g_return_val_if_fail (stream->protected, NULL);
4329 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4331 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4333 /* Currently, cenc properties for groups of samples are not supported, so
4334 * simply return a copy of the default sample properties */
4335 return gst_structure_copy (info->default_properties);
4338 /* Parses the sizes of sample auxiliary information contained within a stream,
4339 * as given in a saiz box. Returns array of sample_count guint8 size values,
4340 * or NULL on failure */
4342 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4343 GstByteReader * br, guint32 * sample_count)
4347 guint8 default_info_size;
4349 g_return_val_if_fail (qtdemux != NULL, NULL);
4350 g_return_val_if_fail (stream != NULL, NULL);
4351 g_return_val_if_fail (br != NULL, NULL);
4352 g_return_val_if_fail (sample_count != NULL, NULL);
4354 if (!gst_byte_reader_get_uint32_be (br, &flags))
4358 /* aux_info_type and aux_info_type_parameter are ignored */
4359 if (!gst_byte_reader_skip (br, 8))
4363 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4365 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4367 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4369 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4372 if (default_info_size == 0) {
4373 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4377 info_sizes = g_new (guint8, *sample_count);
4378 memset (info_sizes, default_info_size, *sample_count);
4384 /* Parses the offset of sample auxiliary information contained within a stream,
4385 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4387 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4388 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4393 guint32 aux_info_type = 0;
4394 guint32 aux_info_type_parameter = 0;
4395 guint32 entry_count;
4398 const guint8 *aux_info_type_data = NULL;
4400 g_return_val_if_fail (qtdemux != NULL, FALSE);
4401 g_return_val_if_fail (stream != NULL, FALSE);
4402 g_return_val_if_fail (br != NULL, FALSE);
4403 g_return_val_if_fail (offset != NULL, FALSE);
4405 if (!gst_byte_reader_get_uint8 (br, &version))
4408 if (!gst_byte_reader_get_uint24_be (br, &flags))
4413 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4415 aux_info_type = QT_FOURCC (aux_info_type_data);
4417 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4419 } else if (stream->protected) {
4420 aux_info_type = stream->protection_scheme_type;
4422 aux_info_type = CUR_STREAM (stream)->fourcc;
4426 *info_type = aux_info_type;
4427 if (info_type_parameter)
4428 *info_type_parameter = aux_info_type_parameter;
4430 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4431 "aux_info_type_parameter: %#06x",
4432 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4434 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4437 if (entry_count != 1) {
4438 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4443 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4445 *offset = (guint64) off_32;
4447 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4452 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4457 qtdemux_gst_structure_free (GstStructure * gststructure)
4460 gst_structure_free (gststructure);
4464 /* Parses auxiliary information relating to samples protected using Common
4465 * Encryption (cenc); the format of this information is defined in
4466 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
4468 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4469 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4471 QtDemuxCencSampleSetInfo *ss_info = NULL;
4474 GPtrArray *old_crypto_info = NULL;
4475 guint old_entries = 0;
4477 g_return_val_if_fail (qtdemux != NULL, FALSE);
4478 g_return_val_if_fail (stream != NULL, FALSE);
4479 g_return_val_if_fail (br != NULL, FALSE);
4480 g_return_val_if_fail (stream->protected, FALSE);
4481 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4483 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4485 if (ss_info->crypto_info) {
4486 old_crypto_info = ss_info->crypto_info;
4487 /* Count number of non-null entries remaining at the tail end */
4488 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4489 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4495 ss_info->crypto_info =
4496 g_ptr_array_new_full (sample_count + old_entries,
4497 (GDestroyNotify) qtdemux_gst_structure_free);
4499 /* We preserve old entries because we parse the next moof in advance
4500 * of consuming all samples from the previous moof, and otherwise
4501 * we'd discard the corresponding crypto info for the samples
4502 * from the previous fragment. */
4504 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4506 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4507 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4509 g_ptr_array_index (old_crypto_info, i) = NULL;
4513 if (old_crypto_info) {
4514 /* Everything now belongs to the new array */
4515 g_ptr_array_free (old_crypto_info, TRUE);
4518 for (i = 0; i < sample_count; ++i) {
4519 GstStructure *properties;
4520 guint16 n_subsamples = 0;
4525 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4526 if (properties == NULL) {
4527 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4530 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4531 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4532 gst_structure_free (properties);
4535 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
4536 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4537 gst_structure_free (properties);
4540 buf = gst_buffer_new_wrapped (data, iv_size);
4541 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4542 gst_buffer_unref (buf);
4543 size = info_sizes[i];
4544 if (size > iv_size) {
4545 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4546 || !(n_subsamples > 0)) {
4547 gst_structure_free (properties);
4548 GST_ERROR_OBJECT (qtdemux,
4549 "failed to get subsample count for sample %u", i);
4552 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4553 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4554 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4556 gst_structure_free (properties);
4559 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4561 gst_structure_free (properties);
4564 gst_structure_set (properties,
4565 "subsample_count", G_TYPE_UINT, n_subsamples,
4566 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4567 gst_buffer_unref (buf);
4569 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4571 g_ptr_array_add (ss_info->crypto_info, properties);
4576 /* Converts a UUID in raw byte form to a string representation, as defined in
4577 * RFC 4122. The caller takes ownership of the returned string and is
4578 * responsible for freeing it after use. */
4580 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4582 const guint8 *uuid = (const guint8 *) uuid_bytes;
4584 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4585 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4586 uuid[0], uuid[1], uuid[2], uuid[3],
4587 uuid[4], uuid[5], uuid[6], uuid[7],
4588 uuid[8], uuid[9], uuid[10], uuid[11],
4589 uuid[12], uuid[13], uuid[14], uuid[15]);
4592 /* Parses a Protection System Specific Header box (pssh), as defined in the
4593 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4594 * information needed by a specific content protection system in order to
4595 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4598 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4600 gchar *sysid_string;
4601 guint32 pssh_size = QT_UINT32 (node->data);
4602 GstBuffer *pssh = NULL;
4603 GstEvent *event = NULL;
4604 guint32 parent_box_type;
4607 if (G_UNLIKELY (pssh_size < 32U)) {
4608 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4613 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4615 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4617 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4618 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4619 gst_buffer_get_size (pssh));
4621 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4623 /* Push an event containing the pssh box onto the queues of all streams. */
4624 event = gst_event_new_protection (sysid_string, pssh,
4625 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4626 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4627 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4628 GST_TRACE_OBJECT (qtdemux,
4629 "adding protection event for stream %s and system %s",
4630 stream->stream_id, sysid_string);
4631 g_queue_push_tail (&stream->protection_scheme_event_queue,
4632 gst_event_ref (event));
4634 g_free (sysid_string);
4635 gst_event_unref (event);
4636 gst_buffer_unref (pssh);
4641 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4642 guint64 moof_offset, QtDemuxStream * stream)
4644 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4646 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4647 GNode *saiz_node, *saio_node, *pssh_node;
4648 GstByteReader saiz_data, saio_data;
4649 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4650 gint64 base_offset, running_offset;
4652 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4654 /* NOTE @stream ignored */
4656 moof_node = g_node_new ((guint8 *) buffer);
4657 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4658 qtdemux_node_dump (qtdemux, moof_node);
4660 /* Get fragment number from mfhd and check it's valid */
4662 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4663 if (mfhd_node == NULL)
4665 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4667 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4669 /* unknown base_offset to start with */
4670 base_offset = running_offset = -1;
4671 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4673 guint64 decode_time = 0;
4675 /* Fragment Header node */
4677 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4681 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4682 &ds_size, &ds_flags, &base_offset))
4685 /* The following code assumes at most a single set of sample auxiliary
4686 * data in the fragment (consisting of a saiz box and a corresponding saio
4687 * box); in theory, however, there could be multiple sets of sample
4688 * auxiliary data in a fragment. */
4690 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4693 guint32 info_type = 0;
4695 guint32 info_type_parameter = 0;
4697 g_free (qtdemux->cenc_aux_info_sizes);
4699 qtdemux->cenc_aux_info_sizes =
4700 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4701 &qtdemux->cenc_aux_sample_count);
4702 if (qtdemux->cenc_aux_info_sizes == NULL) {
4703 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4707 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4710 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4711 g_free (qtdemux->cenc_aux_info_sizes);
4712 qtdemux->cenc_aux_info_sizes = NULL;
4716 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4717 &info_type, &info_type_parameter, &offset))) {
4718 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4719 g_free (qtdemux->cenc_aux_info_sizes);
4720 qtdemux->cenc_aux_info_sizes = NULL;
4723 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4724 offset += (guint64) (base_offset - qtdemux->moof_offset);
4725 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4727 if (offset > length) {
4728 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4729 qtdemux->cenc_aux_info_offset = offset;
4731 gst_byte_reader_init (&br, buffer + offset, length - offset);
4732 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4733 qtdemux->cenc_aux_info_sizes,
4734 qtdemux->cenc_aux_sample_count)) {
4735 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4736 g_free (qtdemux->cenc_aux_info_sizes);
4737 qtdemux->cenc_aux_info_sizes = NULL;
4745 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4748 /* We'll use decode_time to interpolate timestamps
4749 * in case the input timestamps are missing */
4750 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4752 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4753 " (%" GST_TIME_FORMAT ")", decode_time,
4754 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4755 decode_time) : GST_CLOCK_TIME_NONE));
4757 /* Discard the fragment buffer timestamp info to avoid using it.
4758 * Rely on tfdt instead as it is more accurate than the timestamp
4759 * that is fetched from a manifest/playlist and is usually
4761 qtdemux->fragment_start = -1;
4764 if (G_UNLIKELY (!stream)) {
4765 /* we lost track of offset, we'll need to regain it,
4766 * but can delay complaining until later or avoid doing so altogether */
4770 if (G_UNLIKELY (base_offset < -1))
4773 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4775 if (!qtdemux->pullbased) {
4776 /* Sample tables can grow enough to be problematic if the system memory
4777 * is very low (e.g. embedded devices) and the videos very long
4778 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4779 * Fortunately, we can easily discard them for each new fragment when
4780 * we know qtdemux will not receive seeks outside of the current fragment.
4781 * adaptivedemux honors this assumption.
4782 * This optimization is also useful for applications that use qtdemux as
4783 * a push-based simple demuxer, like Media Source Extensions. */
4784 gst_qtdemux_stream_flush_samples_data (stream);
4787 /* initialise moof sample data */
4788 stream->n_samples_moof = 0;
4789 stream->duration_last_moof = stream->duration_moof;
4790 stream->duration_moof = 0;
4792 /* Track Run node */
4794 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4797 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4798 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4799 &running_offset, decode_time, (tfdt_node != NULL));
4800 /* iterate all siblings */
4801 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4805 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4807 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4808 guint32 box_length = QT_UINT32 (uuid_buffer);
4810 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4813 /* if no new base_offset provided for next traf,
4814 * base is end of current traf */
4815 base_offset = running_offset;
4816 running_offset = -1;
4818 if (stream->n_samples_moof && stream->duration_moof)
4819 stream->new_caps = TRUE;
4822 /* iterate all siblings */
4823 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4826 /* parse any protection system info */
4827 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4829 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4830 qtdemux_parse_pssh (qtdemux, pssh_node);
4831 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4834 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4835 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4837 /* Unless the user has explictly requested another seek, perform an
4838 * internal seek to the time specified in the tfdt.
4840 * This way if the user opens a file where the first tfdt is 1 hour
4841 * into the presentation, they will not have to wait 1 hour for run
4842 * time to catch up and actual playback to start. */
4845 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4846 "performing an internal seek to %" GST_TIME_FORMAT,
4847 GST_TIME_ARGS (min_dts));
4849 qtdemux->segment.start = min_dts;
4850 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4852 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4853 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4854 stream->time_position = min_dts;
4857 /* Before this code was run a segment was already sent when the moov was
4858 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4859 * be emitted after a moov, and we can emit a second segment anyway for
4860 * special cases like this. */
4861 qtdemux->need_segment = TRUE;
4864 qtdemux->first_moof_already_parsed = TRUE;
4866 g_node_destroy (moof_node);
4871 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4876 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4881 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4886 g_node_destroy (moof_node);
4887 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4888 (_("This file is corrupt and cannot be played.")), (NULL));
4894 /* might be used if some day we actually use mfra & co
4895 * for random access to fragments,
4896 * but that will require quite some modifications and much less relying
4897 * on a sample array */
4901 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4903 QtDemuxStream *stream;
4904 guint32 ver_flags, track_id, len, num_entries, i;
4905 guint value_size, traf_size, trun_size, sample_size;
4906 guint64 time = 0, moof_offset = 0;
4908 GstBuffer *buf = NULL;
4913 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4915 if (!gst_byte_reader_skip (&tfra, 8))
4918 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4921 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4922 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4923 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4926 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4928 stream = qtdemux_find_stream (qtdemux, track_id);
4930 goto unknown_trackid;
4932 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4933 sample_size = (len & 3) + 1;
4934 trun_size = ((len & 12) >> 2) + 1;
4935 traf_size = ((len & 48) >> 4) + 1;
4937 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4938 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4940 if (num_entries == 0)
4943 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4944 value_size + value_size + traf_size + trun_size + sample_size))
4947 g_free (stream->ra_entries);
4948 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4949 stream->n_ra_entries = num_entries;
4951 for (i = 0; i < num_entries; i++) {
4952 qt_atom_parser_get_offset (&tfra, value_size, &time);
4953 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4954 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4955 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4956 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4958 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4960 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4961 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4963 stream->ra_entries[i].ts = time;
4964 stream->ra_entries[i].moof_offset = moof_offset;
4966 /* don't want to go through the entire file and read all moofs at startup */
4968 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4969 if (ret != GST_FLOW_OK)
4971 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4972 moof_offset, stream);
4973 gst_buffer_unref (buf);
4977 check_update_duration (qtdemux, time);
4984 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4989 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4994 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
5000 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
5002 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
5003 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
5004 GstBuffer *mfro = NULL, *mfra = NULL;
5006 gboolean ret = FALSE;
5007 GNode *mfra_node, *tfra_node;
5008 guint64 mfra_offset = 0;
5009 guint32 fourcc, mfra_size;
5012 /* query upstream size in bytes */
5013 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
5014 goto size_query_failed;
5016 /* mfro box should be at the very end of the file */
5017 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
5018 if (flow != GST_FLOW_OK)
5021 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
5023 fourcc = QT_FOURCC (mfro_map.data + 4);
5024 if (fourcc != FOURCC_mfro)
5027 GST_INFO_OBJECT (qtdemux, "Found mfro box");
5028 if (mfro_map.size < 16)
5029 goto invalid_mfro_size;
5031 mfra_size = QT_UINT32 (mfro_map.data + 12);
5032 if (mfra_size >= len)
5033 goto invalid_mfra_size;
5035 mfra_offset = len - mfra_size;
5037 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
5038 mfra_offset, mfra_size);
5040 /* now get and parse mfra box */
5041 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
5042 if (flow != GST_FLOW_OK)
5045 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
5047 mfra_node = g_node_new ((guint8 *) mfra_map.data);
5048 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
5050 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
5053 qtdemux_parse_tfra (qtdemux, tfra_node);
5054 /* iterate all siblings */
5055 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
5057 g_node_destroy (mfra_node);
5059 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
5065 if (mfro_map.memory != NULL)
5066 gst_buffer_unmap (mfro, &mfro_map);
5067 gst_buffer_unref (mfro);
5070 if (mfra_map.memory != NULL)
5071 gst_buffer_unmap (mfra, &mfra_map);
5072 gst_buffer_unref (mfra);
5079 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
5084 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
5089 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
5094 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
5100 add_offset (guint64 offset, guint64 advance)
5102 /* Avoid 64-bit overflow by clamping */
5103 if (offset > G_MAXUINT64 - advance)
5105 return offset + advance;
5108 static GstFlowReturn
5109 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
5113 GstBuffer *buf = NULL;
5114 GstFlowReturn ret = GST_FLOW_OK;
5115 guint64 cur_offset = qtdemux->offset;
5118 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
5119 if (G_UNLIKELY (ret != GST_FLOW_OK))
5121 gst_buffer_map (buf, &map, GST_MAP_READ);
5122 if (G_LIKELY (map.size >= 8))
5123 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
5124 gst_buffer_unmap (buf, &map);
5125 gst_buffer_unref (buf);
5127 /* maybe we already got most we needed, so only consider this eof */
5128 if (G_UNLIKELY (length == 0)) {
5129 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5130 (_("Invalid atom size.")),
5131 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
5132 GST_FOURCC_ARGS (fourcc)));
5139 /* record for later parsing when needed */
5140 if (!qtdemux->moof_offset) {
5141 qtdemux->moof_offset = qtdemux->offset;
5143 if (qtdemux_pull_mfro_mfra (qtdemux)) {
5146 qtdemux->offset += length; /* skip moof and keep going */
5148 if (qtdemux->got_moov) {
5149 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
5161 GST_LOG_OBJECT (qtdemux,
5162 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5163 GST_FOURCC_ARGS (fourcc), cur_offset);
5164 qtdemux->offset = add_offset (qtdemux->offset, length);
5169 GstBuffer *moov = NULL;
5171 if (qtdemux->got_moov) {
5172 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
5173 qtdemux->offset = add_offset (qtdemux->offset, length);
5177 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
5178 if (ret != GST_FLOW_OK)
5180 gst_buffer_map (moov, &map, GST_MAP_READ);
5182 if (length != map.size) {
5183 /* Some files have a 'moov' atom at the end of the file which contains
5184 * a terminal 'free' atom where the body of the atom is missing.
5185 * Check for, and permit, this special case.
5187 if (map.size >= 8) {
5188 guint8 *final_data = map.data + (map.size - 8);
5189 guint32 final_length = QT_UINT32 (final_data);
5190 guint32 final_fourcc = QT_FOURCC (final_data + 4);
5192 if (final_fourcc == FOURCC_free
5193 && map.size + final_length - 8 == length) {
5194 /* Ok, we've found that special case. Allocate a new buffer with
5195 * that free atom actually present. */
5196 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
5197 gst_buffer_fill (newmoov, 0, map.data, map.size);
5198 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
5199 gst_buffer_unmap (moov, &map);
5200 gst_buffer_unref (moov);
5202 gst_buffer_map (moov, &map, GST_MAP_READ);
5207 if (length != map.size) {
5208 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5209 (_("This file is incomplete and cannot be played.")),
5210 ("We got less than expected (received %" G_GSIZE_FORMAT
5211 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
5212 (guint) length, cur_offset));
5213 gst_buffer_unmap (moov, &map);
5214 gst_buffer_unref (moov);
5215 ret = GST_FLOW_ERROR;
5218 qtdemux->offset += length;
5220 qtdemux_parse_moov (qtdemux, map.data, length);
5221 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
5223 qtdemux_parse_tree (qtdemux);
5224 if (qtdemux->moov_node_compressed) {
5225 g_node_destroy (qtdemux->moov_node_compressed);
5226 g_free (qtdemux->moov_node->data);
5228 qtdemux->moov_node_compressed = NULL;
5229 g_node_destroy (qtdemux->moov_node);
5230 qtdemux->moov_node = NULL;
5231 gst_buffer_unmap (moov, &map);
5232 gst_buffer_unref (moov);
5233 qtdemux->got_moov = TRUE;
5239 GstBuffer *ftyp = NULL;
5241 /* extract major brand; might come in handy for ISO vs QT issues */
5242 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
5243 if (ret != GST_FLOW_OK)
5245 qtdemux->offset += length;
5246 gst_buffer_map (ftyp, &map, GST_MAP_READ);
5247 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
5248 gst_buffer_unmap (ftyp, &map);
5249 gst_buffer_unref (ftyp);
5254 GstBuffer *uuid = NULL;
5256 /* uuid are extension atoms */
5257 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
5258 if (ret != GST_FLOW_OK)
5260 qtdemux->offset += length;
5261 gst_buffer_map (uuid, &map, GST_MAP_READ);
5262 qtdemux_parse_uuid (qtdemux, map.data, map.size);
5263 gst_buffer_unmap (uuid, &map);
5264 gst_buffer_unref (uuid);
5269 GstBuffer *sidx = NULL;
5270 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
5271 if (ret != GST_FLOW_OK)
5273 qtdemux->offset += length;
5274 gst_buffer_map (sidx, &map, GST_MAP_READ);
5275 qtdemux_parse_sidx (qtdemux, map.data, map.size);
5276 gst_buffer_unmap (sidx, &map);
5277 gst_buffer_unref (sidx);
5282 GstBuffer *unknown = NULL;
5284 GST_LOG_OBJECT (qtdemux,
5285 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5286 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5288 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5289 if (ret != GST_FLOW_OK)
5291 gst_buffer_map (unknown, &map, GST_MAP_READ);
5292 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5293 gst_buffer_unmap (unknown, &map);
5294 gst_buffer_unref (unknown);
5295 qtdemux->offset += length;
5301 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5302 /* digested all data, show what we have */
5303 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5304 if (qtdemux->spherical_metadata)
5305 _send_spherical_metadata_msg_to_bus (qtdemux);
5306 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5307 qtdemux_prepare_streams (qtdemux);
5308 QTDEMUX_EXPOSE_LOCK (qtdemux);
5309 ret = qtdemux_expose_streams (qtdemux);
5310 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
5312 qtdemux->state = QTDEMUX_STATE_MOVIE;
5313 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5320 /* Seeks to the previous keyframe of the indexed stream and
5321 * aligns other streams with respect to the keyframe timestamp
5322 * of indexed stream. Only called in case of Reverse Playback
5324 static GstFlowReturn
5325 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5327 guint32 seg_idx = 0, k_index = 0;
5328 guint32 ref_seg_idx, ref_k_index;
5329 GstClockTime k_pos = 0, last_stop = 0;
5330 QtDemuxSegment *seg = NULL;
5331 QtDemuxStream *ref_str = NULL;
5332 guint64 seg_media_start_mov; /* segment media start time in mov format */
5336 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5337 * and finally align all the other streams on that timestamp with their
5338 * respective keyframes */
5339 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5340 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5342 /* No candidate yet, take the first stream */
5348 /* So that stream has a segment, we prefer video streams */
5349 if (str->subtype == FOURCC_vide) {
5355 if (G_UNLIKELY (!ref_str)) {
5356 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5360 if (G_UNLIKELY (!ref_str->from_sample)) {
5361 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5365 /* So that stream has been playing from from_sample to to_sample. We will
5366 * get the timestamp of the previous sample and search for a keyframe before
5367 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5368 if (ref_str->subtype == FOURCC_vide) {
5369 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5370 ref_str->from_sample - 1, FALSE);
5372 if (ref_str->from_sample >= 10)
5373 k_index = ref_str->from_sample - 10;
5379 ref_str->samples[k_index].timestamp +
5380 ref_str->samples[k_index].pts_offset;
5382 /* get current segment for that stream */
5383 seg = &ref_str->segments[ref_str->segment_index];
5384 /* Use segment start in original timescale for comparisons */
5385 seg_media_start_mov = seg->trak_media_start;
5387 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5388 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
5389 k_index, target_ts, seg_media_start_mov,
5390 GST_TIME_ARGS (seg->media_start));
5392 /* Crawl back through segments to find the one containing this I frame */
5393 while (target_ts < seg_media_start_mov) {
5394 GST_DEBUG_OBJECT (qtdemux,
5395 "keyframe position (sample %u) is out of segment %u " " target %"
5396 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5397 ref_str->segment_index, target_ts, seg_media_start_mov);
5399 if (G_UNLIKELY (!ref_str->segment_index)) {
5400 /* Reached first segment, let's consider it's EOS */
5403 ref_str->segment_index--;
5404 seg = &ref_str->segments[ref_str->segment_index];
5405 /* Use segment start in original timescale for comparisons */
5406 seg_media_start_mov = seg->trak_media_start;
5408 /* Calculate time position of the keyframe and where we should stop */
5410 QTSTREAMTIME_TO_GSTTIME (ref_str,
5411 target_ts - seg->trak_media_start) + seg->time;
5413 QTSTREAMTIME_TO_GSTTIME (ref_str,
5414 ref_str->samples[ref_str->from_sample].timestamp -
5415 seg->trak_media_start) + seg->time;
5417 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5418 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5419 k_index, GST_TIME_ARGS (k_pos));
5421 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5422 qtdemux->segment.position = last_stop;
5423 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5424 GST_TIME_ARGS (last_stop));
5426 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5427 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5431 ref_seg_idx = ref_str->segment_index;
5432 ref_k_index = k_index;
5434 /* Align them all on this */
5435 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5437 GstClockTime seg_time = 0;
5438 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5440 /* aligning reference stream again might lead to backing up to yet another
5441 * keyframe (due to timestamp rounding issues),
5442 * potentially putting more load on downstream; so let's try to avoid */
5443 if (str == ref_str) {
5444 seg_idx = ref_seg_idx;
5445 seg = &str->segments[seg_idx];
5446 k_index = ref_k_index;
5447 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5448 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5450 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5451 GST_DEBUG_OBJECT (qtdemux,
5452 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5453 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5455 /* get segment and time in the segment */
5456 seg = &str->segments[seg_idx];
5457 seg_time = k_pos - seg->time;
5459 /* get the media time in the segment.
5460 * No adjustment for empty "filler" segments */
5461 if (seg->media_start != GST_CLOCK_TIME_NONE)
5462 seg_time += seg->media_start;
5464 /* get the index of the sample with media time */
5465 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5466 GST_DEBUG_OBJECT (qtdemux,
5467 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5468 GST_TIME_ARGS (seg_time), index);
5470 /* find previous keyframe */
5471 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5474 /* Remember until where we want to go */
5475 str->to_sample = str->from_sample - 1;
5476 /* Define our time position */
5478 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5479 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5480 if (seg->media_start != GST_CLOCK_TIME_NONE)
5481 str->time_position -= seg->media_start;
5483 /* Now seek back in time */
5484 gst_qtdemux_move_stream (qtdemux, str, k_index);
5485 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5486 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5487 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5493 return GST_FLOW_EOS;
5497 * Gets the current qt segment start, stop and position for the
5498 * given time offset. This is used in update_segment()
5501 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5502 QtDemuxStream * stream, GstClockTime offset,
5503 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5505 GstClockTime seg_time;
5506 GstClockTime start, stop, time;
5507 QtDemuxSegment *segment;
5509 segment = &stream->segments[stream->segment_index];
5511 /* get time in this segment */
5512 seg_time = (offset - segment->time) * segment->rate;
5514 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5515 GST_TIME_ARGS (seg_time));
5517 if (G_UNLIKELY (seg_time > segment->duration)) {
5518 GST_LOG_OBJECT (stream->pad,
5519 "seg_time > segment->duration %" GST_TIME_FORMAT,
5520 GST_TIME_ARGS (segment->duration));
5521 seg_time = segment->duration;
5524 /* qtdemux->segment.stop is in outside-time-realm, whereas
5525 * segment->media_stop is in track-time-realm.
5527 * In order to compare the two, we need to bring segment.stop
5528 * into the track-time-realm
5530 * FIXME - does this comment still hold? Don't see any conversion here */
5532 stop = qtdemux->segment.stop;
5533 if (stop == GST_CLOCK_TIME_NONE)
5534 stop = qtdemux->segment.duration;
5535 if (stop == GST_CLOCK_TIME_NONE)
5536 stop = segment->media_stop;
5539 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5541 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5542 start = segment->time + seg_time;
5544 stop = start - seg_time + segment->duration;
5545 } else if (qtdemux->segment.rate >= 0) {
5546 start = MIN (segment->media_start + seg_time, stop);
5549 if (segment->media_start >= qtdemux->segment.start) {
5550 time = segment->time;
5552 time = segment->time + (qtdemux->segment.start - segment->media_start);
5555 start = MAX (segment->media_start, qtdemux->segment.start);
5556 stop = MIN (segment->media_start + seg_time, stop);
5565 * Updates the qt segment used for the stream and pushes a new segment event
5566 * downstream on this stream's pad.
5569 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5570 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5571 GstClockTime * _stop)
5573 QtDemuxSegment *segment;
5574 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5578 /* update the current segment */
5579 stream->segment_index = seg_idx;
5581 /* get the segment */
5582 segment = &stream->segments[seg_idx];
5584 if (G_UNLIKELY (offset < segment->time)) {
5585 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5586 GST_TIME_ARGS (segment->time));
5590 /* segment lies beyond total indicated duration */
5591 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5592 segment->time > qtdemux->segment.duration)) {
5593 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5594 " < segment->time %" GST_TIME_FORMAT,
5595 GST_TIME_ARGS (qtdemux->segment.duration),
5596 GST_TIME_ARGS (segment->time));
5600 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5601 &start, &stop, &time);
5603 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5604 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5605 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5607 /* combine global rate with that of the segment */
5608 rate = segment->rate * qtdemux->segment.rate;
5610 /* Copy flags from main segment */
5611 stream->segment.flags = qtdemux->segment.flags;
5613 /* update the segment values used for clipping */
5614 stream->segment.offset = qtdemux->segment.offset;
5615 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5616 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5617 stream->segment.rate = rate;
5618 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5619 stream->cslg_shift);
5620 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5621 stream->cslg_shift);
5622 stream->segment.time = time;
5623 stream->segment.position = stream->segment.start;
5625 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5628 /* now prepare and send the segment */
5630 event = gst_event_new_segment (&stream->segment);
5631 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5632 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5634 gst_pad_push_event (stream->pad, event);
5635 /* assume we can send more data now */
5636 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5637 /* clear to send tags on this pad now */
5638 gst_qtdemux_push_tags (qtdemux, stream);
5649 /* activate the given segment number @seg_idx of @stream at time @offset.
5650 * @offset is an absolute global position over all the segments.
5652 * This will push out a NEWSEGMENT event with the right values and
5653 * position the stream index to the first decodable sample before
5657 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5658 guint32 seg_idx, GstClockTime offset)
5660 QtDemuxSegment *segment;
5661 guint32 index, kf_index;
5662 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5664 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5665 seg_idx, GST_TIME_ARGS (offset));
5667 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5671 segment = &stream->segments[stream->segment_index];
5673 /* in the fragmented case, we pick a fragment that starts before our
5674 * desired position and rely on downstream to wait for a keyframe
5675 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5676 * tfra entries tells us which trun/sample the key unit is in, but we don't
5677 * make use of this additional information at the moment) */
5678 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5679 stream->to_sample = G_MAXUINT32;
5682 /* well, it will be taken care of below */
5683 qtdemux->fragmented_seek_pending = FALSE;
5684 /* FIXME ideally the do_fragmented_seek can be done right here,
5685 * rather than at loop level
5686 * (which might even allow handling edit lists in a fragmented file) */
5689 /* We don't need to look for a sample in push-based */
5690 if (!qtdemux->pullbased)
5693 /* and move to the keyframe before the indicated media time of the
5695 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5696 if (qtdemux->segment.rate >= 0) {
5697 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5698 stream->to_sample = G_MAXUINT32;
5699 GST_DEBUG_OBJECT (stream->pad,
5700 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5701 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5702 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5704 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5705 stream->to_sample = index;
5706 GST_DEBUG_OBJECT (stream->pad,
5707 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5708 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5709 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5712 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5713 "this is an empty segment");
5717 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5718 * encountered an error and printed a message so we return appropriately */
5722 /* we're at the right spot */
5723 if (index == stream->sample_index) {
5724 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5728 /* find keyframe of the target index */
5729 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5732 /* indent does stupid stuff with stream->samples[].timestamp */
5734 /* if we move forwards, we don't have to go back to the previous
5735 * keyframe since we already sent that. We can also just jump to
5736 * the keyframe right before the target index if there is one. */
5737 if (index > stream->sample_index) {
5738 /* moving forwards check if we move past a keyframe */
5739 if (kf_index > stream->sample_index) {
5740 GST_DEBUG_OBJECT (stream->pad,
5741 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5742 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5743 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5744 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5746 GST_DEBUG_OBJECT (stream->pad,
5747 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5748 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5749 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5752 GST_DEBUG_OBJECT (stream->pad,
5753 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5754 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5755 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5756 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5764 /* prepare to get the current sample of @stream, getting essential values.
5766 * This function will also prepare and send the segment when needed.
5768 * Return FALSE if the stream is EOS.
5773 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5774 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5775 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5776 gboolean * keyframe)
5778 QtDemuxSample *sample;
5779 GstClockTime time_position;
5782 g_return_val_if_fail (stream != NULL, FALSE);
5784 time_position = stream->time_position;
5785 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5788 seg_idx = stream->segment_index;
5789 if (G_UNLIKELY (seg_idx == -1)) {
5790 /* find segment corresponding to time_position if we are looking
5792 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5795 /* different segment, activate it, sample_index will be set. */
5796 if (G_UNLIKELY (stream->segment_index != seg_idx))
5797 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5799 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5800 segments[stream->segment_index]))) {
5801 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5803 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5804 " prepare empty sample");
5807 *pts = *dts = time_position;
5808 *duration = seg->duration - (time_position - seg->time);
5815 if (stream->sample_index == -1)
5816 stream->sample_index = 0;
5818 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5819 stream->sample_index, stream->n_samples);
5821 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5822 if (!qtdemux->fragmented)
5825 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5829 GST_OBJECT_LOCK (qtdemux);
5830 flow = qtdemux_add_fragmented_samples (qtdemux);
5831 GST_OBJECT_UNLOCK (qtdemux);
5833 if (flow != GST_FLOW_OK)
5836 while (stream->sample_index >= stream->n_samples);
5839 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5840 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5841 stream->sample_index);
5845 /* now get the info for the sample we're at */
5846 sample = &stream->samples[stream->sample_index];
5848 *dts = QTSAMPLE_DTS (stream, sample);
5849 *pts = QTSAMPLE_PTS (stream, sample);
5850 *offset = sample->offset;
5851 *size = sample->size;
5852 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5853 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5860 stream->time_position = GST_CLOCK_TIME_NONE;
5865 /* move to the next sample in @stream.
5867 * Moves to the next segment when needed.
5870 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5872 QtDemuxSample *sample;
5873 QtDemuxSegment *segment;
5875 /* get current segment */
5876 segment = &stream->segments[stream->segment_index];
5878 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5879 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5883 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5884 /* Mark the stream as EOS */
5885 GST_DEBUG_OBJECT (qtdemux,
5886 "reached max allowed sample %u, mark EOS", stream->to_sample);
5887 stream->time_position = GST_CLOCK_TIME_NONE;
5891 /* move to next sample */
5892 stream->sample_index++;
5893 stream->offset_in_sample = 0;
5895 /* reached the last sample, we need the next segment */
5896 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5899 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5900 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5901 stream->sample_index);
5905 /* get next sample */
5906 sample = &stream->samples[stream->sample_index];
5908 /* see if we are past the segment */
5909 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5912 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5913 /* inside the segment, update time_position, looks very familiar to
5914 * GStreamer segments, doesn't it? */
5915 stream->time_position =
5916 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5918 /* not yet in segment, time does not yet increment. This means
5919 * that we are still prerolling keyframes to the decoder so it can
5920 * decode the first sample of the segment. */
5921 stream->time_position = segment->time;
5925 /* move to the next segment */
5928 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5930 if (stream->segment_index == stream->n_segments - 1) {
5931 /* are we at the end of the last segment, we're EOS */
5932 stream->time_position = GST_CLOCK_TIME_NONE;
5934 /* else we're only at the end of the current segment */
5935 stream->time_position = segment->stop_time;
5937 /* make sure we select a new segment */
5939 /* accumulate previous segments */
5940 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5941 stream->accumulated_base +=
5942 (stream->segment.stop -
5943 stream->segment.start) / ABS (stream->segment.rate);
5945 stream->segment_index = -1;
5950 gst_qtdemux_sync_streams (GstQTDemux * demux)
5954 if (QTDEMUX_N_STREAMS (demux) <= 1)
5957 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5958 QtDemuxStream *stream;
5959 GstClockTime end_time;
5961 stream = QTDEMUX_NTH_STREAM (demux, i);
5966 /* TODO advance time on subtitle streams here, if any some day */
5968 /* some clips/trailers may have unbalanced streams at the end,
5969 * so send EOS on shorter stream to prevent stalling others */
5971 /* do not mess with EOS if SEGMENT seeking */
5972 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5975 if (demux->pullbased) {
5976 /* loop mode is sample time based */
5977 if (!STREAM_IS_EOS (stream))
5980 /* push mode is byte position based */
5981 if (stream->n_samples &&
5982 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5986 if (stream->sent_eos)
5989 /* only act if some gap */
5990 end_time = stream->segments[stream->n_segments - 1].stop_time;
5991 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5992 ", stream end: %" GST_TIME_FORMAT,
5993 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5994 if (GST_CLOCK_TIME_IS_VALID (end_time)
5995 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5998 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5999 GST_PAD_NAME (stream->pad));
6000 stream->sent_eos = TRUE;
6001 event = gst_event_new_eos ();
6002 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6003 gst_event_set_seqnum (event, demux->segment_seqnum);
6004 gst_pad_push_event (stream->pad, event);
6009 /* EOS and NOT_LINKED need to be combined. This means that we return:
6011 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
6012 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
6014 static GstFlowReturn
6015 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
6018 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
6021 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
6024 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
6026 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
6030 /* the input buffer metadata must be writable. Returns NULL when the buffer is
6031 * completely clipped
6033 * Should be used only with raw buffers */
6035 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6038 guint64 start, stop, cstart, cstop, diff;
6039 GstClockTime pts, duration;
6041 gint num_rate, denom_rate;
6046 osize = size = gst_buffer_get_size (buf);
6049 /* depending on the type, setup the clip parameters */
6050 if (stream->subtype == FOURCC_soun) {
6051 frame_size = CUR_STREAM (stream)->bytes_per_frame;
6052 num_rate = GST_SECOND;
6053 denom_rate = (gint) CUR_STREAM (stream)->rate;
6055 } else if (stream->subtype == FOURCC_vide) {
6057 num_rate = CUR_STREAM (stream)->fps_n;
6058 denom_rate = CUR_STREAM (stream)->fps_d;
6063 if (frame_size <= 0)
6064 goto bad_frame_size;
6066 /* we can only clip if we have a valid pts */
6067 pts = GST_BUFFER_PTS (buf);
6068 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
6071 duration = GST_BUFFER_DURATION (buf);
6073 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
6075 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
6079 stop = start + duration;
6081 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
6082 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
6085 /* see if some clipping happened */
6086 diff = cstart - start;
6092 /* bring clipped time to samples and to bytes */
6093 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6096 GST_DEBUG_OBJECT (qtdemux,
6097 "clipping start to %" GST_TIME_FORMAT " %"
6098 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
6104 diff = stop - cstop;
6109 /* bring clipped time to samples and then to bytes */
6110 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6112 GST_DEBUG_OBJECT (qtdemux,
6113 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
6114 " bytes", GST_TIME_ARGS (cstop), diff);
6119 if (offset != 0 || size != osize)
6120 gst_buffer_resize (buf, offset, size);
6122 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
6123 GST_BUFFER_PTS (buf) = pts;
6124 GST_BUFFER_DURATION (buf) = duration;
6128 /* dropped buffer */
6131 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
6136 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
6141 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
6146 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
6147 gst_buffer_unref (buf);
6153 gst_qtdemux_align_buffer (GstQTDemux * demux,
6154 GstBuffer * buffer, gsize alignment)
6158 gst_buffer_map (buffer, &map, GST_MAP_READ);
6160 if (map.size < sizeof (guintptr)) {
6161 gst_buffer_unmap (buffer, &map);
6165 if (((guintptr) map.data) & (alignment - 1)) {
6166 GstBuffer *new_buffer;
6167 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
6169 new_buffer = gst_buffer_new_allocate (NULL,
6170 gst_buffer_get_size (buffer), ¶ms);
6172 /* Copy data "by hand", so ensure alignment is kept: */
6173 gst_buffer_fill (new_buffer, 0, map.data, map.size);
6175 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
6176 GST_DEBUG_OBJECT (demux,
6177 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
6180 gst_buffer_unmap (buffer, &map);
6181 gst_buffer_unref (buffer);
6186 gst_buffer_unmap (buffer, &map);
6191 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
6197 /* We are converting from pairs to triplets */
6198 *res = ccpair_size / 2 * 3;
6199 storage = g_malloc (*res);
6200 for (i = 0; i * 2 < ccpair_size; i += 1) {
6201 /* FIXME: Use line offset 0 as we simply can't know here */
6203 storage[i * 3] = 0x80 | 0x00;
6205 storage[i * 3] = 0x00 | 0x00;
6206 storage[i * 3 + 1] = ccpair[i * 2];
6207 storage[i * 3 + 2] = ccpair[i * 2 + 1];
6214 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
6218 guint32 atom_length, fourcc;
6219 QtDemuxStreamStsdEntry *stsd_entry;
6221 GST_MEMDUMP ("caption atom", data, size);
6223 /* There might be multiple atoms */
6228 atom_length = QT_UINT32 (data);
6229 fourcc = QT_FOURCC (data + 4);
6230 if (G_UNLIKELY (atom_length > size || atom_length == 8))
6233 GST_DEBUG_OBJECT (stream->pad, "here");
6235 /* Check if we have somethig compatible */
6236 stsd_entry = CUR_STREAM (stream);
6237 switch (stsd_entry->fourcc) {
6239 guint8 *cdat = NULL, *cdt2 = NULL;
6240 gsize cdat_size = 0, cdt2_size = 0;
6241 /* Should be cdat or cdt2 */
6242 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
6243 GST_WARNING_OBJECT (stream->pad,
6244 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
6245 GST_FOURCC_ARGS (fourcc));
6249 /* Convert to S334-1 Annex A byte triplet */
6250 if (fourcc == FOURCC_cdat)
6251 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
6253 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
6254 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
6257 /* Check for another atom ? */
6258 if (size > atom_length + 8) {
6259 guint32 new_atom_length = QT_UINT32 (data + atom_length);
6260 if (size >= atom_length + new_atom_length) {
6261 fourcc = QT_FOURCC (data + atom_length + 4);
6262 if (fourcc == FOURCC_cdat) {
6265 convert_to_s334_1a (data + atom_length + 8,
6266 new_atom_length - 8, 1, &cdat_size);
6268 GST_WARNING_OBJECT (stream->pad,
6269 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6273 convert_to_s334_1a (data + atom_length + 8,
6274 new_atom_length - 8, 2, &cdt2_size);
6276 GST_WARNING_OBJECT (stream->pad,
6277 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6282 *cclen = cdat_size + cdt2_size;
6283 res = g_malloc (*cclen);
6285 memcpy (res, cdat, cdat_size);
6287 memcpy (res + cdat_size, cdt2, cdt2_size);
6293 if (fourcc != FOURCC_ccdp) {
6294 GST_WARNING_OBJECT (stream->pad,
6295 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
6296 GST_FOURCC_ARGS (fourcc));
6299 *cclen = atom_length - 8;
6300 res = g_memdup (data + 8, *cclen);
6303 /* Keep this here in case other closed caption formats are added */
6304 g_assert_not_reached ();
6308 GST_MEMDUMP ("Output", res, *cclen);
6313 GST_WARNING ("[cdat] atom is too small or invalid");
6317 /* the input buffer metadata must be writable,
6318 * but time/duration etc not yet set and need not be preserved */
6320 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6327 /* not many cases for now */
6328 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
6329 /* send a one time dvd clut event */
6330 if (stream->pending_event && stream->pad)
6331 gst_pad_push_event (stream->pad, stream->pending_event);
6332 stream->pending_event = NULL;
6335 if (G_UNLIKELY (stream->subtype != FOURCC_text
6336 && stream->subtype != FOURCC_sbtl &&
6337 stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
6341 gst_buffer_map (buf, &map, GST_MAP_READ);
6343 /* empty buffer is sent to terminate previous subtitle */
6344 if (map.size <= 2) {
6345 gst_buffer_unmap (buf, &map);
6346 gst_buffer_unref (buf);
6349 if (stream->subtype == FOURCC_subp) {
6350 /* That's all the processing needed for subpictures */
6351 gst_buffer_unmap (buf, &map);
6355 if (stream->subtype == FOURCC_clcp) {
6358 /* For closed caption, we need to extract the information from the
6359 * [cdat],[cdt2] or [ccdp] atom */
6360 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
6361 gst_buffer_unmap (buf, &map);
6362 gst_buffer_unref (buf);
6364 buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
6366 /* Conversion failed or there's nothing */
6372 nsize = GST_READ_UINT16_BE (map.data);
6373 nsize = MIN (nsize, map.size - 2);
6375 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6378 /* takes care of UTF-8 validation or UTF-16 recognition,
6379 * no other encoding expected */
6380 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6381 gst_buffer_unmap (buf, &map);
6383 gst_buffer_unref (buf);
6384 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
6386 /* this should not really happen unless the subtitle is corrupted */
6387 gst_buffer_unref (buf);
6391 /* FIXME ? convert optional subsequent style info to markup */
6396 static GstFlowReturn
6397 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6400 GstFlowReturn ret = GST_FLOW_OK;
6401 GstClockTime pts, duration;
6403 if (stream->need_clip)
6404 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6406 if (G_UNLIKELY (buf == NULL))
6409 if (G_UNLIKELY (stream->discont)) {
6410 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6411 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6412 stream->discont = FALSE;
6414 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6417 GST_LOG_OBJECT (qtdemux,
6418 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6419 ", duration %" GST_TIME_FORMAT " on pad %s",
6420 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6421 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6422 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
6424 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
6425 GstStructure *crypto_info;
6426 QtDemuxCencSampleSetInfo *info =
6427 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6431 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6432 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6433 GST_PTR_FORMAT, event);
6434 gst_pad_push_event (stream->pad, event);
6437 if (info->crypto_info == NULL) {
6438 GST_DEBUG_OBJECT (qtdemux,
6439 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
6441 /* The end of the crypto_info array matches our n_samples position,
6442 * so count backward from there */
6443 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6444 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6445 /* steal structure from array */
6446 crypto_info = g_ptr_array_index (info->crypto_info, index);
6447 g_ptr_array_index (info->crypto_info, index) = NULL;
6448 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6449 info->crypto_info->len);
6450 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6451 GST_ERROR_OBJECT (qtdemux,
6452 "failed to attach cenc metadata to buffer");
6454 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6455 index, stream->sample_index);
6460 if (stream->alignment > 1)
6461 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6463 pts = GST_BUFFER_PTS (buf);
6464 duration = GST_BUFFER_DURATION (buf);
6466 ret = gst_pad_push (stream->pad, buf);
6468 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6469 /* mark position in stream, we'll need this to know when to send GAP event */
6470 stream->segment.position = pts + duration;
6478 static GstFlowReturn
6479 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6482 GstFlowReturn ret = GST_FLOW_OK;
6484 if (stream->subtype == FOURCC_clcp
6485 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6487 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6488 guint n_triplets, i;
6489 guint field1_off = 0, field2_off = 0;
6491 /* We have to split CEA608 buffers so that each outgoing buffer contains
6492 * one byte pair per field according to the framerate of the video track.
6494 * If there is only a single byte pair per field we don't have to do
6498 gst_buffer_map (buf, &map, GST_MAP_READ);
6500 n_triplets = map.size / 3;
6501 for (i = 0; i < n_triplets; i++) {
6502 if (map.data[3 * i] & 0x80)
6508 g_assert (n_field1 || n_field2);
6510 /* If there's more than 1 frame we have to split, otherwise we can just
6512 if (n_field1 > 1 || n_field2 > 1) {
6514 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6515 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6517 for (i = 0; i < n_output_buffers; i++) {
6519 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6523 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6524 outptr = outmap.data;
6527 gboolean found = FALSE;
6529 while (map.data + field1_off < map.data + map.size) {
6530 if (map.data[field1_off] & 0x80) {
6531 memcpy (outptr, &map.data[field1_off], 3);
6540 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6542 memcpy (outptr, empty, 3);
6549 gboolean found = FALSE;
6551 while (map.data + field2_off < map.data + map.size) {
6552 if ((map.data[field2_off] & 0x80) == 0) {
6553 memcpy (outptr, &map.data[field2_off], 3);
6562 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6564 memcpy (outptr, empty, 3);
6570 gst_buffer_unmap (outbuf, &outmap);
6572 GST_BUFFER_PTS (outbuf) =
6573 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6574 GST_SECOND * CUR_STREAM (stream)->fps_d,
6575 CUR_STREAM (stream)->fps_n);
6576 GST_BUFFER_DURATION (outbuf) =
6577 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6578 CUR_STREAM (stream)->fps_n);
6579 GST_BUFFER_OFFSET (outbuf) = -1;
6580 GST_BUFFER_OFFSET_END (outbuf) = -1;
6582 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6584 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6587 gst_buffer_unmap (buf, &map);
6588 gst_buffer_unref (buf);
6590 gst_buffer_unmap (buf, &map);
6591 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6594 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6600 /* Sets a buffer's attributes properly and pushes it downstream.
6601 * Also checks for additional actions and custom processing that may
6602 * need to be done first.
6604 static GstFlowReturn
6605 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6606 QtDemuxStream * stream, GstBuffer * buf,
6607 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6608 gboolean keyframe, GstClockTime position, guint64 byte_position)
6610 GstFlowReturn ret = GST_FLOW_OK;
6612 /* offset the timestamps according to the edit list */
6614 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6618 gst_buffer_map (buf, &map, GST_MAP_READ);
6619 url = g_strndup ((gchar *) map.data, map.size);
6620 gst_buffer_unmap (buf, &map);
6621 if (url != NULL && strlen (url) != 0) {
6622 /* we have RTSP redirect now */
6623 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6624 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6625 gst_structure_new ("redirect",
6626 "new-location", G_TYPE_STRING, url, NULL)));
6627 qtdemux->posted_redirect = TRUE;
6629 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6635 /* position reporting */
6636 if (qtdemux->segment.rate >= 0) {
6637 qtdemux->segment.position = position;
6638 gst_qtdemux_sync_streams (qtdemux);
6641 if (G_UNLIKELY (!stream->pad)) {
6642 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6643 gst_buffer_unref (buf);
6647 /* send out pending buffers */
6648 while (stream->buffers) {
6649 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6651 if (G_UNLIKELY (stream->discont)) {
6652 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6653 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6654 stream->discont = FALSE;
6656 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6659 if (stream->alignment > 1)
6660 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6661 gst_pad_push (stream->pad, buffer);
6663 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6666 /* we're going to modify the metadata */
6667 buf = gst_buffer_make_writable (buf);
6669 if (G_UNLIKELY (stream->need_process))
6670 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6676 GST_BUFFER_DTS (buf) = dts;
6677 GST_BUFFER_PTS (buf) = pts;
6678 GST_BUFFER_DURATION (buf) = duration;
6679 GST_BUFFER_OFFSET (buf) = -1;
6680 GST_BUFFER_OFFSET_END (buf) = -1;
6683 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6684 stream->on_keyframe = FALSE;
6686 stream->on_keyframe = TRUE;
6689 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6690 gst_buffer_append_memory (buf,
6691 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6693 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6694 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6697 if (G_UNLIKELY (qtdemux->element_index)) {
6698 GstClockTime stream_time;
6701 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6703 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6704 GST_LOG_OBJECT (qtdemux,
6705 "adding association %" GST_TIME_FORMAT "-> %"
6706 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6707 gst_index_add_association (qtdemux->element_index,
6709 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6710 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6711 GST_FORMAT_BYTES, byte_position, NULL);
6716 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6722 static const QtDemuxRandomAccessEntry *
6723 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6724 GstClockTime pos, gboolean after)
6726 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6727 guint n_entries = stream->n_ra_entries;
6730 /* we assume the table is sorted */
6731 for (i = 0; i < n_entries; ++i) {
6732 if (entries[i].ts > pos)
6736 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6737 * probably okay to assume that the index lists the very first fragment */
6744 return &entries[i - 1];
6748 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6750 const QtDemuxRandomAccessEntry *best_entry = NULL;
6753 GST_OBJECT_LOCK (qtdemux);
6755 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6757 /* first see if we can determine where to go to using mfra,
6758 * before we start clearing things */
6759 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6760 const QtDemuxRandomAccessEntry *entry;
6761 QtDemuxStream *stream;
6762 gboolean is_audio_or_video;
6764 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6766 if (stream->ra_entries == NULL)
6769 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6770 is_audio_or_video = TRUE;
6772 is_audio_or_video = FALSE;
6775 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6776 stream->time_position, !is_audio_or_video);
6778 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6779 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6781 stream->pending_seek = entry;
6783 /* decide position to jump to just based on audio/video tracks, not subs */
6784 if (!is_audio_or_video)
6787 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6791 /* no luck, will handle seek otherwise */
6792 if (best_entry == NULL) {
6793 GST_OBJECT_UNLOCK (qtdemux);
6797 /* ok, now we can prepare for processing as of located moof */
6798 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6799 QtDemuxStream *stream;
6801 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6803 g_free (stream->samples);
6804 stream->samples = NULL;
6805 stream->n_samples = 0;
6806 stream->stbl_index = -1; /* no samples have yet been parsed */
6807 stream->sample_index = -1;
6809 if (stream->protection_scheme_info) {
6810 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6811 if (stream->protection_scheme_type == FOURCC_cenc) {
6812 QtDemuxCencSampleSetInfo *info =
6813 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6814 if (info->crypto_info) {
6815 g_ptr_array_free (info->crypto_info, TRUE);
6816 info->crypto_info = NULL;
6822 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6823 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6824 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6825 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6827 qtdemux->moof_offset = best_entry->moof_offset;
6829 qtdemux_add_fragmented_samples (qtdemux);
6831 GST_OBJECT_UNLOCK (qtdemux);
6835 static GstFlowReturn
6836 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6838 GstFlowReturn ret = GST_FLOW_OK;
6839 GstBuffer *buf = NULL;
6840 QtDemuxStream *stream, *target_stream = NULL;
6841 GstClockTime min_time;
6843 GstClockTime dts = GST_CLOCK_TIME_NONE;
6844 GstClockTime pts = GST_CLOCK_TIME_NONE;
6845 GstClockTime duration = 0;
6846 gboolean keyframe = FALSE;
6847 guint sample_size = 0;
6852 if (qtdemux->fragmented_seek_pending) {
6853 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6854 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6855 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6856 qtdemux->fragmented_seek_pending = FALSE;
6858 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6862 /* Figure out the next stream sample to output, min_time is expressed in
6863 * global time and runs over the edit list segments. */
6864 min_time = G_MAXUINT64;
6865 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6866 GstClockTime position;
6868 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6869 position = stream->time_position;
6871 /* position of -1 is EOS */
6872 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6873 min_time = position;
6874 target_stream = stream;
6878 if (G_UNLIKELY (target_stream == NULL)) {
6879 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6883 /* check for segment end */
6884 if (G_UNLIKELY (qtdemux->segment.stop != -1
6885 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6886 || (qtdemux->segment.rate < 0
6887 && qtdemux->segment.start > min_time))
6888 && target_stream->on_keyframe)) {
6889 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6890 target_stream->time_position = GST_CLOCK_TIME_NONE;
6894 /* gap events for subtitle streams */
6895 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6896 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6897 if (stream->pad && (stream->subtype == FOURCC_subp
6898 || stream->subtype == FOURCC_text
6899 || stream->subtype == FOURCC_sbtl)) {
6900 /* send one second gap events until the stream catches up */
6901 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6902 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6903 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6904 stream->segment.position + GST_SECOND < min_time) {
6906 gst_event_new_gap (stream->segment.position, GST_SECOND);
6907 gst_pad_push_event (stream->pad, gap);
6908 stream->segment.position += GST_SECOND;
6913 stream = target_stream;
6914 /* fetch info for the current sample of this stream */
6915 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6916 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6919 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6920 if (stream->new_caps) {
6921 gst_qtdemux_configure_stream (qtdemux, stream);
6922 qtdemux_do_allocation (stream, qtdemux);
6925 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6926 if (G_UNLIKELY (qtdemux->segment.
6927 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6928 if (stream->subtype == FOURCC_vide && !keyframe) {
6929 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6935 GST_DEBUG_OBJECT (qtdemux,
6936 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6937 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6938 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6939 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6940 GST_TIME_ARGS (duration));
6942 if (G_UNLIKELY (empty)) {
6943 /* empty segment, push a gap if there's a second or more
6944 * difference and move to the next one */
6945 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6946 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6947 stream->segment.position = pts + duration;
6951 /* hmm, empty sample, skip and move to next sample */
6952 if (G_UNLIKELY (sample_size <= 0))
6955 /* last pushed sample was out of boundary, goto next sample */
6956 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6959 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6962 GST_DEBUG_OBJECT (qtdemux,
6963 "size %d larger than stream max_buffer_size %d, trimming",
6964 sample_size, stream->max_buffer_size);
6966 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6969 if (qtdemux->cenc_aux_info_offset > 0) {
6972 GstBuffer *aux_info = NULL;
6974 /* pull the data stored before the sample */
6976 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6977 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6978 if (G_UNLIKELY (ret != GST_FLOW_OK))
6980 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6981 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6982 gst_byte_reader_init (&br, map.data + 8, map.size);
6983 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6984 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6985 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6986 gst_buffer_unmap (aux_info, &map);
6987 gst_buffer_unref (aux_info);
6988 ret = GST_FLOW_ERROR;
6991 gst_buffer_unmap (aux_info, &map);
6992 gst_buffer_unref (aux_info);
6995 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6998 if (stream->use_allocator) {
6999 /* if we have a per-stream allocator, use it */
7000 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
7003 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
7005 if (G_UNLIKELY (ret != GST_FLOW_OK))
7008 if (size != sample_size) {
7009 pts += 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 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7017 gst_util_uint64_scale_int (GST_SECOND,
7018 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
7021 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
7022 dts, pts, duration, keyframe, min_time, offset);
7024 if (size != sample_size) {
7025 QtDemuxSample *sample = &stream->samples[stream->sample_index];
7026 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
7028 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
7030 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
7031 if (time_position >= segment->media_start) {
7032 /* inside the segment, update time_position, looks very familiar to
7033 * GStreamer segments, doesn't it? */
7034 stream->time_position = (time_position - segment->media_start) +
7037 /* not yet in segment, time does not yet increment. This means
7038 * that we are still prerolling keyframes to the decoder so it can
7039 * decode the first sample of the segment. */
7040 stream->time_position = segment->time;
7045 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
7046 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
7047 * we have no more data for the pad to push */
7048 if (ret == GST_FLOW_EOS)
7051 stream->offset_in_sample += size;
7052 if (stream->offset_in_sample >= sample_size) {
7053 gst_qtdemux_advance_sample (qtdemux, stream);
7058 gst_qtdemux_advance_sample (qtdemux, stream);
7066 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
7072 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
7073 /* EOS will be raised if all are EOS */
7080 gst_qtdemux_loop (GstPad * pad)
7082 GstQTDemux *qtdemux;
7086 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
7088 cur_offset = qtdemux->offset;
7089 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
7090 cur_offset, qt_demux_state_string (qtdemux->state));
7092 switch (qtdemux->state) {
7093 case QTDEMUX_STATE_INITIAL:
7094 case QTDEMUX_STATE_HEADER:
7095 ret = gst_qtdemux_loop_state_header (qtdemux);
7097 case QTDEMUX_STATE_MOVIE:
7098 ret = gst_qtdemux_loop_state_movie (qtdemux);
7099 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
7100 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
7108 /* if something went wrong, pause */
7109 if (ret != GST_FLOW_OK)
7113 gst_object_unref (qtdemux);
7119 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7120 (NULL), ("streaming stopped, invalid state"));
7121 gst_pad_pause_task (pad);
7122 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7127 const gchar *reason = gst_flow_get_name (ret);
7129 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7131 gst_pad_pause_task (pad);
7133 /* fatal errors need special actions */
7135 if (ret == GST_FLOW_EOS) {
7136 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
7137 /* we have no streams, post an error */
7138 gst_qtdemux_post_no_playable_stream_error (qtdemux);
7140 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7143 if ((stop = qtdemux->segment.stop) == -1)
7144 stop = qtdemux->segment.duration;
7146 if (qtdemux->segment.rate >= 0) {
7147 GstMessage *message;
7150 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7151 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7152 GST_FORMAT_TIME, stop);
7153 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7154 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7155 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7156 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7158 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7159 gst_qtdemux_push_event (qtdemux, event);
7161 GstMessage *message;
7164 /* For Reverse Playback */
7165 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7166 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7167 GST_FORMAT_TIME, qtdemux->segment.start);
7168 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7169 qtdemux->segment.start);
7170 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7171 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7172 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7174 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7175 gst_qtdemux_push_event (qtdemux, event);
7180 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7181 event = gst_event_new_eos ();
7182 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7183 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7184 gst_qtdemux_push_event (qtdemux, event);
7186 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7187 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
7188 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7197 * Returns if there are samples to be played.
7200 has_next_entry (GstQTDemux * demux)
7202 QtDemuxStream *stream;
7205 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7207 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7208 stream = QTDEMUX_NTH_STREAM (demux, i);
7210 if (stream->sample_index == -1) {
7211 stream->sample_index = 0;
7212 stream->offset_in_sample = 0;
7215 if (stream->sample_index >= stream->n_samples) {
7216 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7219 GST_DEBUG_OBJECT (demux, "Found a sample");
7223 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7230 * Returns the size of the first entry at the current offset.
7231 * If -1, there are none (which means EOS or empty file).
7234 next_entry_size (GstQTDemux * demux)
7236 QtDemuxStream *stream, *target_stream = NULL;
7237 guint64 smalloffs = (guint64) - 1;
7238 QtDemuxSample *sample;
7241 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7244 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7245 stream = QTDEMUX_NTH_STREAM (demux, i);
7247 if (stream->sample_index == -1) {
7248 stream->sample_index = 0;
7249 stream->offset_in_sample = 0;
7252 if (stream->sample_index >= stream->n_samples) {
7253 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7257 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
7258 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7259 stream->sample_index);
7263 sample = &stream->samples[stream->sample_index];
7265 GST_LOG_OBJECT (demux,
7266 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7267 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7268 stream->sample_index, sample->offset, sample->size);
7270 if (((smalloffs == -1)
7271 || (sample->offset < smalloffs)) && (sample->size)) {
7272 smalloffs = sample->offset;
7273 target_stream = stream;
7280 GST_LOG_OBJECT (demux,
7281 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7282 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7284 stream = target_stream;
7285 sample = &stream->samples[stream->sample_index];
7287 if (sample->offset >= demux->offset) {
7288 demux->todrop = sample->offset - demux->offset;
7289 return sample->size + demux->todrop;
7292 GST_DEBUG_OBJECT (demux,
7293 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7298 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
7300 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7302 gst_element_post_message (GST_ELEMENT_CAST (demux),
7303 gst_message_new_element (GST_OBJECT_CAST (demux),
7304 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7308 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
7313 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7316 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7317 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7318 GST_SEEK_TYPE_NONE, -1);
7320 /* store seqnum to drop flush events, they don't need to reach downstream */
7321 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7322 res = gst_pad_push_event (demux->sinkpad, event);
7323 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7328 /* check for seekable upstream, above and beyond a mere query */
7330 gst_qtdemux_check_seekability (GstQTDemux * demux)
7333 gboolean seekable = FALSE;
7334 gint64 start = -1, stop = -1;
7336 if (demux->upstream_size)
7339 if (demux->upstream_format_is_time)
7342 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7343 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7344 GST_DEBUG_OBJECT (demux, "seeking query failed");
7348 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7350 /* try harder to query upstream size if we didn't get it the first time */
7351 if (seekable && stop == -1) {
7352 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7353 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7356 /* if upstream doesn't know the size, it's likely that it's not seekable in
7357 * practice even if it technically may be seekable */
7358 if (seekable && (start != 0 || stop <= start)) {
7359 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7364 gst_query_unref (query);
7366 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7367 G_GUINT64_FORMAT ")", seekable, start, stop);
7368 demux->upstream_seekable = seekable;
7369 demux->upstream_size = seekable ? stop : -1;
7373 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7375 g_return_if_fail (bytes <= demux->todrop);
7377 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7378 gst_adapter_flush (demux->adapter, bytes);
7379 demux->neededbytes -= bytes;
7380 demux->offset += bytes;
7381 demux->todrop -= bytes;
7384 /* PUSH-MODE only: Send a segment, if not done already. */
7386 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7388 if (G_UNLIKELY (demux->need_segment)) {
7391 if (!demux->upstream_format_is_time) {
7392 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7394 GstEvent *segment_event;
7395 segment_event = gst_event_new_segment (&demux->segment);
7396 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7397 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7398 gst_qtdemux_push_event (demux, segment_event);
7401 demux->need_segment = FALSE;
7403 /* clear to send tags on all streams */
7404 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7405 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7406 gst_qtdemux_push_tags (demux, stream);
7407 if (CUR_STREAM (stream)->sparse) {
7408 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7409 gst_pad_push_event (stream->pad,
7410 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7416 /* Used for push mode only. */
7418 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7419 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7421 GstClockTime ts, dur;
7425 stream->segments[segment_index].duration - (pos -
7426 stream->segments[segment_index].time);
7427 stream->time_position += dur;
7429 /* Only gaps with a duration of at least one second are propagated.
7430 * Same workaround as in pull mode.
7431 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7432 if (dur >= GST_SECOND) {
7434 gap = gst_event_new_gap (ts, dur);
7436 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7437 "segment: %" GST_PTR_FORMAT, gap);
7438 gst_pad_push_event (stream->pad, gap);
7442 static GstFlowReturn
7443 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7447 demux = GST_QTDEMUX (parent);
7449 GST_DEBUG_OBJECT (demux,
7450 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7451 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7452 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7453 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7454 gst_buffer_get_size (inbuf), demux->offset);
7456 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7457 gboolean is_gap_input = FALSE;
7460 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7462 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7463 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7466 /* Check if we can land back on our feet in the case where upstream is
7467 * handling the seeking/pushing of samples with gaps in between (like
7468 * in the case of trick-mode DASH for example) */
7469 if (demux->upstream_format_is_time
7470 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7471 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7473 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7474 GST_LOG_OBJECT (demux,
7475 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7476 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7478 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7479 stream, GST_BUFFER_OFFSET (inbuf));
7481 QtDemuxSample *sample = &stream->samples[res];
7482 GST_LOG_OBJECT (demux,
7483 "Checking if sample %d from track-id %u is valid (offset:%"
7484 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7485 stream->track_id, sample->offset, sample->size);
7486 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7487 GST_LOG_OBJECT (demux,
7488 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7490 is_gap_input = TRUE;
7491 /* We can go back to standard playback mode */
7492 demux->state = QTDEMUX_STATE_MOVIE;
7493 /* Remember which sample this stream is at */
7494 stream->sample_index = res;
7495 /* Finally update all push-based values to the expected values */
7496 demux->neededbytes = stream->samples[res].size;
7497 demux->offset = GST_BUFFER_OFFSET (inbuf);
7499 demux->mdatsize - demux->offset + demux->mdatoffset;
7504 if (!is_gap_input) {
7505 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7506 /* Reset state if it's a real discont */
7507 demux->neededbytes = 16;
7508 demux->state = QTDEMUX_STATE_INITIAL;
7509 demux->offset = GST_BUFFER_OFFSET (inbuf);
7510 gst_adapter_clear (demux->adapter);
7513 /* Reverse fragmented playback, need to flush all we have before
7514 * consuming a new fragment.
7515 * The samples array have the timestamps calculated by accumulating the
7516 * durations but this won't work for reverse playback of fragments as
7517 * the timestamps of a subsequent fragment should be smaller than the
7518 * previously received one. */
7519 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7520 gst_qtdemux_process_adapter (demux, TRUE);
7521 g_ptr_array_foreach (demux->active_streams,
7522 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7526 gst_adapter_push (demux->adapter, inbuf);
7528 GST_DEBUG_OBJECT (demux,
7529 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7530 demux->neededbytes, gst_adapter_available (demux->adapter));
7532 return gst_qtdemux_process_adapter (demux, FALSE);
7535 static GstFlowReturn
7536 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7538 GstFlowReturn ret = GST_FLOW_OK;
7540 /* we never really mean to buffer that much */
7541 if (demux->neededbytes == -1) {
7545 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7546 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7548 #ifndef GST_DISABLE_GST_DEBUG
7550 guint64 discont_offset, distance_from_discont;
7552 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7553 distance_from_discont =
7554 gst_adapter_distance_from_discont (demux->adapter);
7556 GST_DEBUG_OBJECT (demux,
7557 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7558 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7559 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7560 demux->offset, discont_offset, distance_from_discont);
7564 switch (demux->state) {
7565 case QTDEMUX_STATE_INITIAL:{
7570 gst_qtdemux_check_seekability (demux);
7572 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7574 /* get fourcc/length, set neededbytes */
7575 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7577 gst_adapter_unmap (demux->adapter);
7579 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7580 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7582 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7583 (_("This file is invalid and cannot be played.")),
7584 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7585 GST_FOURCC_ARGS (fourcc)));
7586 ret = GST_FLOW_ERROR;
7589 if (fourcc == FOURCC_mdat) {
7590 gint next_entry = next_entry_size (demux);
7591 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7592 || !demux->fragmented)) {
7593 /* we have the headers, start playback */
7594 demux->state = QTDEMUX_STATE_MOVIE;
7595 demux->neededbytes = next_entry;
7596 demux->mdatleft = size;
7597 demux->mdatsize = demux->mdatleft;
7599 /* no headers yet, try to get them */
7602 guint64 old, target;
7605 old = demux->offset;
7606 target = old + size;
7608 /* try to jump over the atom with a seek */
7609 /* only bother if it seems worth doing so,
7610 * and avoids possible upstream/server problems */
7611 if (demux->upstream_seekable &&
7612 demux->upstream_size > 4 * (1 << 20)) {
7613 res = qtdemux_seek_offset (demux, target);
7615 GST_DEBUG_OBJECT (demux, "skipping seek");
7620 GST_DEBUG_OBJECT (demux, "seek success");
7621 /* remember the offset fo the first mdat so we can seek back to it
7622 * after we have the headers */
7623 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7624 demux->first_mdat = old;
7625 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7628 /* seek worked, continue reading */
7629 demux->offset = target;
7630 demux->neededbytes = 16;
7631 demux->state = QTDEMUX_STATE_INITIAL;
7633 /* seek failed, need to buffer */
7634 demux->offset = old;
7635 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7636 /* there may be multiple mdat (or alike) buffers */
7638 if (demux->mdatbuffer)
7639 bs = gst_buffer_get_size (demux->mdatbuffer);
7642 if (size + bs > 10 * (1 << 20))
7644 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7645 demux->neededbytes = size;
7646 if (!demux->mdatbuffer)
7647 demux->mdatoffset = demux->offset;
7650 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7651 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7652 (_("This file is invalid and cannot be played.")),
7653 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7654 GST_FOURCC_ARGS (fourcc), size));
7655 ret = GST_FLOW_ERROR;
7658 /* this means we already started buffering and still no moov header,
7659 * let's continue buffering everything till we get moov */
7660 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7661 || fourcc == FOURCC_moof))
7663 demux->neededbytes = size;
7664 demux->state = QTDEMUX_STATE_HEADER;
7668 case QTDEMUX_STATE_HEADER:{
7672 GST_DEBUG_OBJECT (demux, "In header");
7674 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7676 /* parse the header */
7677 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7679 if (fourcc == FOURCC_moov) {
7680 /* in usual fragmented setup we could try to scan for more
7681 * and end up at the the moov (after mdat) again */
7682 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7684 || demux->last_moov_offset == demux->offset)) {
7685 GST_DEBUG_OBJECT (demux,
7686 "Skipping moov atom as we have (this) one already");
7688 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7690 if (demux->got_moov && demux->fragmented) {
7691 GST_DEBUG_OBJECT (demux,
7692 "Got a second moov, clean up data from old one");
7693 if (demux->moov_node_compressed) {
7694 g_node_destroy (demux->moov_node_compressed);
7695 if (demux->moov_node)
7696 g_free (demux->moov_node->data);
7698 demux->moov_node_compressed = NULL;
7699 if (demux->moov_node)
7700 g_node_destroy (demux->moov_node);
7701 demux->moov_node = NULL;
7704 demux->last_moov_offset = demux->offset;
7706 /* Update streams with new moov */
7707 gst_qtdemux_stream_concat (demux,
7708 demux->old_streams, demux->active_streams);
7710 qtdemux_parse_moov (demux, data, demux->neededbytes);
7711 qtdemux_node_dump (demux, demux->moov_node);
7712 qtdemux_parse_tree (demux);
7713 qtdemux_prepare_streams (demux);
7714 QTDEMUX_EXPOSE_LOCK (demux);
7715 qtdemux_expose_streams (demux);
7716 QTDEMUX_EXPOSE_UNLOCK (demux);
7718 demux->got_moov = TRUE;
7720 gst_qtdemux_check_send_pending_segment (demux);
7722 if (demux->moov_node_compressed) {
7723 g_node_destroy (demux->moov_node_compressed);
7724 g_free (demux->moov_node->data);
7726 demux->moov_node_compressed = NULL;
7727 g_node_destroy (demux->moov_node);
7728 demux->moov_node = NULL;
7729 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7731 } else if (fourcc == FOURCC_moof) {
7732 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7734 GstClockTime prev_pts;
7735 guint64 prev_offset;
7736 guint64 adapter_discont_offset, adapter_discont_dist;
7738 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7741 * The timestamp of the moof buffer is relevant as some scenarios
7742 * won't have the initial timestamp in the atoms. Whenever a new
7743 * buffer has started, we get that buffer's PTS and use it as a base
7744 * timestamp for the trun entries.
7746 * To keep track of the current buffer timestamp and starting point
7747 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7748 * from the beggining of the buffer, with the distance and demux->offset
7749 * we know if it is still the same buffer or not.
7751 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7752 prev_offset = demux->offset - dist;
7753 if (demux->fragment_start_offset == -1
7754 || prev_offset > demux->fragment_start_offset) {
7755 demux->fragment_start_offset = prev_offset;
7756 demux->fragment_start = prev_pts;
7757 GST_DEBUG_OBJECT (demux,
7758 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7759 GST_TIME_FORMAT, demux->fragment_start_offset,
7760 GST_TIME_ARGS (demux->fragment_start));
7763 /* We can't use prev_offset() here because this would require
7764 * upstream to set consistent and correct offsets on all buffers
7765 * since the discont. Nothing ever did that in the past and we
7766 * would break backwards compatibility here then.
7767 * Instead take the offset we had at the last discont and count
7768 * the bytes from there. This works with old code as there would
7769 * be no discont between moov and moof, and also works with
7770 * adaptivedemux which correctly sets offset and will set the
7771 * DISCONT flag accordingly when needed.
7773 * We also only do this for upstream TIME segments as otherwise
7774 * there are potential backwards compatibility problems with
7775 * seeking in PUSH mode and upstream providing inconsistent
7777 adapter_discont_offset =
7778 gst_adapter_offset_at_discont (demux->adapter);
7779 adapter_discont_dist =
7780 gst_adapter_distance_from_discont (demux->adapter);
7782 GST_DEBUG_OBJECT (demux,
7783 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7784 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7785 demux->offset, adapter_discont_offset, adapter_discont_dist);
7787 if (demux->upstream_format_is_time) {
7788 demux->moof_offset = adapter_discont_offset;
7789 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7790 demux->moof_offset += adapter_discont_dist;
7791 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7792 demux->moof_offset = demux->offset;
7794 demux->moof_offset = demux->offset;
7797 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7798 demux->moof_offset, NULL)) {
7799 gst_adapter_unmap (demux->adapter);
7800 ret = GST_FLOW_ERROR;
7804 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7805 if (demux->mss_mode && !demux->exposed) {
7806 QTDEMUX_EXPOSE_LOCK (demux);
7807 qtdemux_expose_streams (demux);
7808 QTDEMUX_EXPOSE_UNLOCK (demux);
7811 gst_qtdemux_check_send_pending_segment (demux);
7813 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7815 } else if (fourcc == FOURCC_ftyp) {
7816 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7817 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7818 } else if (fourcc == FOURCC_uuid) {
7819 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7820 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7821 } else if (fourcc == FOURCC_sidx) {
7822 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7823 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7827 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7831 /* [free] and [skip] are padding atoms */
7832 GST_DEBUG_OBJECT (demux,
7833 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7834 GST_FOURCC_ARGS (fourcc));
7837 GST_WARNING_OBJECT (demux,
7838 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7839 GST_FOURCC_ARGS (fourcc));
7840 /* Let's jump that one and go back to initial state */
7844 gst_adapter_unmap (demux->adapter);
7847 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7848 gsize remaining_data_size = 0;
7850 /* the mdat was before the header */
7851 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7852 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7853 /* restore our adapter/offset view of things with upstream;
7854 * put preceding buffered data ahead of current moov data.
7855 * This should also handle evil mdat, moov, mdat cases and alike */
7856 gst_adapter_flush (demux->adapter, demux->neededbytes);
7858 /* Store any remaining data after the mdat for later usage */
7859 remaining_data_size = gst_adapter_available (demux->adapter);
7860 if (remaining_data_size > 0) {
7861 g_assert (demux->restoredata_buffer == NULL);
7862 demux->restoredata_buffer =
7863 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7864 demux->restoredata_offset = demux->offset + demux->neededbytes;
7865 GST_DEBUG_OBJECT (demux,
7866 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7867 G_GUINT64_FORMAT, remaining_data_size,
7868 demux->restoredata_offset);
7871 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7872 demux->mdatbuffer = NULL;
7873 demux->offset = demux->mdatoffset;
7874 demux->neededbytes = next_entry_size (demux);
7875 demux->state = QTDEMUX_STATE_MOVIE;
7876 demux->mdatleft = gst_adapter_available (demux->adapter);
7877 demux->mdatsize = demux->mdatleft;
7879 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7880 gst_adapter_flush (demux->adapter, demux->neededbytes);
7882 /* only go back to the mdat if there are samples to play */
7883 if (demux->got_moov && demux->first_mdat != -1
7884 && has_next_entry (demux)) {
7887 /* we need to seek back */
7888 res = qtdemux_seek_offset (demux, demux->first_mdat);
7890 demux->offset = demux->first_mdat;
7892 GST_DEBUG_OBJECT (demux, "Seek back failed");
7895 demux->offset += demux->neededbytes;
7897 demux->neededbytes = 16;
7898 demux->state = QTDEMUX_STATE_INITIAL;
7903 case QTDEMUX_STATE_BUFFER_MDAT:{
7907 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7909 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7910 gst_buffer_extract (buf, 0, fourcc, 4);
7911 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7912 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7913 if (demux->mdatbuffer)
7914 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7916 demux->mdatbuffer = buf;
7917 demux->offset += demux->neededbytes;
7918 demux->neededbytes = 16;
7919 demux->state = QTDEMUX_STATE_INITIAL;
7920 gst_qtdemux_post_progress (demux, 1, 1);
7924 case QTDEMUX_STATE_MOVIE:{
7925 QtDemuxStream *stream = NULL;
7926 QtDemuxSample *sample;
7927 GstClockTime dts, pts, duration;
7931 GST_DEBUG_OBJECT (demux,
7932 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7934 if (demux->fragmented) {
7935 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7937 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7938 /* if needed data starts within this atom,
7939 * then it should not exceed this atom */
7940 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7941 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7942 (_("This file is invalid and cannot be played.")),
7943 ("sample data crosses atom boundary"));
7944 ret = GST_FLOW_ERROR;
7947 demux->mdatleft -= demux->neededbytes;
7949 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7950 /* so we are dropping more than left in this atom */
7951 gst_qtdemux_drop_data (demux, demux->mdatleft);
7952 demux->mdatleft = 0;
7954 /* need to resume atom parsing so we do not miss any other pieces */
7955 demux->state = QTDEMUX_STATE_INITIAL;
7956 demux->neededbytes = 16;
7958 /* check if there was any stored post mdat data from previous buffers */
7959 if (demux->restoredata_buffer) {
7960 g_assert (gst_adapter_available (demux->adapter) == 0);
7962 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7963 demux->restoredata_buffer = NULL;
7964 demux->offset = demux->restoredata_offset;
7971 if (demux->todrop) {
7972 if (demux->cenc_aux_info_offset > 0) {
7976 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7977 data = gst_adapter_map (demux->adapter, demux->todrop);
7978 gst_byte_reader_init (&br, data + 8, demux->todrop);
7979 if (!qtdemux_parse_cenc_aux_info (demux,
7980 QTDEMUX_NTH_STREAM (demux, 0), &br,
7981 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7982 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7983 ret = GST_FLOW_ERROR;
7984 gst_adapter_unmap (demux->adapter);
7985 g_free (demux->cenc_aux_info_sizes);
7986 demux->cenc_aux_info_sizes = NULL;
7989 demux->cenc_aux_info_offset = 0;
7990 g_free (demux->cenc_aux_info_sizes);
7991 demux->cenc_aux_info_sizes = NULL;
7992 gst_adapter_unmap (demux->adapter);
7994 gst_qtdemux_drop_data (demux, demux->todrop);
7998 /* initial newsegment sent here after having added pads,
7999 * possible others in sink_event */
8000 gst_qtdemux_check_send_pending_segment (demux);
8002 /* Figure out which stream this packet belongs to */
8003 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8004 stream = QTDEMUX_NTH_STREAM (demux, i);
8005 if (stream->sample_index >= stream->n_samples) {
8006 /* reset to be checked below G_UNLIKELY (stream == NULL) */
8010 GST_LOG_OBJECT (demux,
8011 "Checking track-id %u (sample_index:%d / offset:%"
8012 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
8013 stream->sample_index,
8014 stream->samples[stream->sample_index].offset,
8015 stream->samples[stream->sample_index].size);
8017 if (stream->samples[stream->sample_index].offset == demux->offset)
8021 if (G_UNLIKELY (stream == NULL))
8022 goto unknown_stream;
8024 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
8026 if (stream->new_caps) {
8027 gst_qtdemux_configure_stream (demux, stream);
8030 /* Put data in a buffer, set timestamps, caps, ... */
8031 sample = &stream->samples[stream->sample_index];
8033 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
8034 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
8035 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
8037 dts = QTSAMPLE_DTS (stream, sample);
8038 pts = QTSAMPLE_PTS (stream, sample);
8039 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
8040 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
8042 /* check for segment end */
8043 if (G_UNLIKELY (demux->segment.stop != -1
8044 && demux->segment.stop <= pts && stream->on_keyframe)
8045 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
8046 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
8047 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
8049 /* skip this data, stream is EOS */
8050 gst_adapter_flush (demux->adapter, demux->neededbytes);
8051 demux->offset += demux->neededbytes;
8053 /* check if all streams are eos */
8055 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8056 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
8065 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8067 /* FIXME: should either be an assert or a plain check */
8068 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8070 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
8071 dts, pts, duration, keyframe, dts, demux->offset);
8075 ret = gst_qtdemux_combine_flows (demux, stream, ret);
8077 /* skip this data, stream is EOS */
8078 gst_adapter_flush (demux->adapter, demux->neededbytes);
8081 stream->sample_index++;
8082 stream->offset_in_sample = 0;
8084 /* update current offset and figure out size of next buffer */
8085 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8086 demux->offset, demux->neededbytes);
8087 demux->offset += demux->neededbytes;
8088 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8092 if (ret == GST_FLOW_EOS) {
8093 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8094 demux->neededbytes = -1;
8098 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
8099 if (demux->fragmented) {
8100 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8101 /* there may be more to follow, only finish this atom */
8102 demux->todrop = demux->mdatleft;
8103 demux->neededbytes = demux->todrop;
8108 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8109 goto non_ok_unlinked_flow;
8118 /* when buffering movie data, at least show user something is happening */
8119 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
8120 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
8121 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
8122 demux->neededbytes);
8129 non_ok_unlinked_flow:
8131 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8132 gst_flow_get_name (ret));
8137 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8138 ret = GST_FLOW_ERROR;
8143 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8149 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8150 (NULL), ("qtdemuxer invalid state %d", demux->state));
8151 ret = GST_FLOW_ERROR;
8156 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8157 (NULL), ("no 'moov' atom within the first 10 MB"));
8158 ret = GST_FLOW_ERROR;
8164 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
8169 query = gst_query_new_scheduling ();
8171 if (!gst_pad_peer_query (sinkpad, query)) {
8172 gst_query_unref (query);
8176 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8177 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8178 gst_query_unref (query);
8183 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8184 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8188 GST_DEBUG_OBJECT (sinkpad, "activating push");
8189 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8194 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
8195 GstPadMode mode, gboolean active)
8198 GstQTDemux *demux = GST_QTDEMUX (parent);
8201 case GST_PAD_MODE_PUSH:
8202 demux->pullbased = FALSE;
8205 case GST_PAD_MODE_PULL:
8207 demux->pullbased = TRUE;
8208 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
8211 res = gst_pad_stop_task (sinkpad);
8223 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8229 memset (&z, 0, sizeof (z));
8234 if ((ret = inflateInit (&z)) != Z_OK) {
8235 GST_ERROR ("inflateInit() returned %d", ret);
8239 z.next_in = z_buffer;
8240 z.avail_in = z_length;
8242 buffer = (guint8 *) g_malloc (*length);
8243 z.avail_out = *length;
8244 z.next_out = (Bytef *) buffer;
8246 ret = inflate (&z, Z_NO_FLUSH);
8247 if (ret == Z_STREAM_END) {
8249 } else if (ret != Z_OK) {
8250 GST_WARNING ("inflate() returned %d", ret);
8255 buffer = (guint8 *) g_realloc (buffer, *length);
8256 z.next_out = (Bytef *) (buffer + z.total_out);
8257 z.avail_out += 4096;
8258 } while (z.avail_in > 0);
8260 if (ret != Z_STREAM_END) {
8265 *length = z.total_out;
8272 #endif /* HAVE_ZLIB */
8275 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
8279 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8281 /* counts as header data */
8282 qtdemux->header_size += length;
8284 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
8285 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
8287 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
8294 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
8295 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
8296 if (dcom == NULL || cmvd == NULL)
8297 goto invalid_compression;
8299 dcom_len = QT_UINT32 (dcom->data);
8301 goto invalid_compression;
8303 method = QT_FOURCC ((guint8 *) dcom->data + 8);
8307 guint uncompressed_length;
8308 guint compressed_length;
8312 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
8314 goto invalid_compression;
8316 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8317 compressed_length = cmvd_len - 12;
8318 GST_LOG ("length = %u", uncompressed_length);
8321 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8322 compressed_length, &uncompressed_length);
8325 qtdemux->moov_node_compressed = qtdemux->moov_node;
8326 qtdemux->moov_node = g_node_new (buf);
8328 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8329 uncompressed_length);
8333 #endif /* HAVE_ZLIB */
8335 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8336 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8343 invalid_compression:
8345 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8351 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8354 while (G_UNLIKELY (buf < end)) {
8358 if (G_UNLIKELY (buf + 4 > end)) {
8359 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8362 len = QT_UINT32 (buf);
8363 if (G_UNLIKELY (len == 0)) {
8364 GST_LOG_OBJECT (qtdemux, "empty container");
8367 if (G_UNLIKELY (len < 8)) {
8368 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8371 if (G_UNLIKELY (len > (end - buf))) {
8372 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8373 (gint) (end - buf));
8377 child = g_node_new ((guint8 *) buf);
8378 g_node_append (node, child);
8379 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8380 qtdemux_parse_node (qtdemux, child, buf, len);
8388 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8391 int len = QT_UINT32 (xdxt->data);
8392 guint8 *buf = xdxt->data;
8393 guint8 *end = buf + len;
8396 /* skip size and type */
8404 size = QT_UINT32 (buf);
8405 type = QT_FOURCC (buf + 4);
8407 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8409 if (buf + size > end || size <= 0)
8415 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8416 GST_FOURCC_ARGS (type));
8420 buffer = gst_buffer_new_and_alloc (size);
8421 gst_buffer_fill (buffer, 0, buf, size);
8422 stream->buffers = g_slist_append (stream->buffers, buffer);
8423 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8426 buffer = gst_buffer_new_and_alloc (size);
8427 gst_buffer_fill (buffer, 0, buf, size);
8428 stream->buffers = g_slist_append (stream->buffers, buffer);
8429 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8432 buffer = gst_buffer_new_and_alloc (size);
8433 gst_buffer_fill (buffer, 0, buf, size);
8434 stream->buffers = g_slist_append (stream->buffers, buffer);
8435 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8438 GST_WARNING_OBJECT (qtdemux,
8439 "unknown theora cookie %" GST_FOURCC_FORMAT,
8440 GST_FOURCC_ARGS (type));
8449 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8453 guint32 node_length = 0;
8454 const QtNodeType *type;
8457 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8459 if (G_UNLIKELY (length < 8))
8460 goto not_enough_data;
8462 node_length = QT_UINT32 (buffer);
8463 fourcc = QT_FOURCC (buffer + 4);
8465 /* ignore empty nodes */
8466 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8469 type = qtdemux_type_get (fourcc);
8471 end = buffer + length;
8473 GST_LOG_OBJECT (qtdemux,
8474 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8475 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8477 if (node_length > length)
8478 goto broken_atom_size;
8480 if (type->flags & QT_FLAG_CONTAINER) {
8481 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8486 if (node_length < 20) {
8487 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8490 GST_DEBUG_OBJECT (qtdemux,
8491 "parsing stsd (sample table, sample description) atom");
8492 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8493 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8504 /* also read alac (or whatever) in stead of mp4a in the following,
8505 * since a similar layout is used in other cases as well */
8506 if (fourcc == FOURCC_mp4a)
8508 else if (fourcc == FOURCC_fLaC)
8513 /* There are two things we might encounter here: a true mp4a atom, and
8514 an mp4a entry in an stsd atom. The latter is what we're interested
8515 in, and it looks like an atom, but isn't really one. The true mp4a
8516 atom is short, so we detect it based on length here. */
8517 if (length < min_size) {
8518 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8519 GST_FOURCC_ARGS (fourcc));
8523 /* 'version' here is the sound sample description version. Types 0 and
8524 1 are documented in the QTFF reference, but type 2 is not: it's
8525 described in Apple header files instead (struct SoundDescriptionV2
8527 version = QT_UINT16 (buffer + 16);
8529 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8530 GST_FOURCC_ARGS (fourcc), version);
8532 /* parse any esds descriptors */
8544 GST_WARNING_OBJECT (qtdemux,
8545 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8546 GST_FOURCC_ARGS (fourcc), version);
8551 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8577 /* codec_data is contained inside these atoms, which all have
8578 * the same format. */
8579 /* video sample description size is 86 bytes without extension.
8580 * node_length have to be bigger than 86 bytes because video sample
8581 * description can include extenstions such as esds, fiel, glbl, etc. */
8582 if (node_length < 86) {
8583 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8584 " sample description length too short (%u < 86)",
8585 GST_FOURCC_ARGS (fourcc), node_length);
8589 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8590 GST_FOURCC_ARGS (fourcc));
8592 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8594 * revision level (2 bytes) : must be set to 0. */
8595 version = QT_UINT32 (buffer + 16);
8596 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8598 /* compressor name : PASCAL string and informative purposes
8599 * first byte : the number of bytes to be displayed.
8600 * it has to be less than 32 because it is reserved
8601 * space of 32 bytes total including itself. */
8602 str_len = QT_UINT8 (buffer + 50);
8604 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8605 (char *) buffer + 51);
8607 GST_WARNING_OBJECT (qtdemux,
8608 "compressorname length too big (%u > 31)", str_len);
8610 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8612 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8617 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8619 /* You are reading this correctly. QTFF specifies that the
8620 * metadata atom is a short atom, whereas ISO BMFF specifies
8621 * it's a full atom. But since so many people are doing things
8622 * differently, we actually peek into the atom to see which
8625 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8626 GST_FOURCC_ARGS (fourcc));
8629 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8630 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8631 * starts with a 'hdlr' atom */
8632 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8633 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8634 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8635 * with version/flags both set to zero */
8636 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8638 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8643 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8644 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8645 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8654 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8655 GST_FOURCC_ARGS (fourcc));
8659 version = QT_UINT32 (buffer + 12);
8660 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8667 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8672 if (length < offset) {
8673 GST_WARNING_OBJECT (qtdemux,
8674 "skipping too small %" GST_FOURCC_FORMAT " box",
8675 GST_FOURCC_ARGS (fourcc));
8678 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8684 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8689 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8694 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8697 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
8700 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
8703 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
8705 if (!strcmp (type->name, "unknown"))
8706 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8710 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8711 GST_FOURCC_ARGS (fourcc));
8717 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8718 (_("This file is corrupt and cannot be played.")),
8719 ("Not enough data for an atom header, got only %u bytes", length));
8724 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8725 (_("This file is corrupt and cannot be played.")),
8726 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8727 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8734 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8738 guint32 child_fourcc;
8740 for (child = g_node_first_child (node); child;
8741 child = g_node_next_sibling (child)) {
8742 buffer = (guint8 *) child->data;
8744 child_fourcc = QT_FOURCC (buffer + 4);
8746 if (G_UNLIKELY (child_fourcc == fourcc)) {
8754 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8755 GstByteReader * parser)
8759 guint32 child_fourcc, child_len;
8761 for (child = g_node_first_child (node); child;
8762 child = g_node_next_sibling (child)) {
8763 buffer = (guint8 *) child->data;
8765 child_len = QT_UINT32 (buffer);
8766 child_fourcc = QT_FOURCC (buffer + 4);
8768 if (G_UNLIKELY (child_fourcc == fourcc)) {
8769 if (G_UNLIKELY (child_len < (4 + 4)))
8771 /* FIXME: must verify if atom length < parent atom length */
8772 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8780 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8782 return g_node_nth_child (node, index);
8786 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8787 GstByteReader * parser)
8791 guint32 child_fourcc, child_len;
8793 for (child = g_node_next_sibling (node); child;
8794 child = g_node_next_sibling (child)) {
8795 buffer = (guint8 *) child->data;
8797 child_fourcc = QT_FOURCC (buffer + 4);
8799 if (child_fourcc == fourcc) {
8801 child_len = QT_UINT32 (buffer);
8802 if (G_UNLIKELY (child_len < (4 + 4)))
8804 /* FIXME: must verify if atom length < parent atom length */
8805 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8814 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8816 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8820 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8822 /* FIXME: This can only reliably work if demuxers have a
8823 * separate streaming thread per srcpad. This should be
8824 * done in a demuxer base class, which integrates parts
8827 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8832 query = gst_query_new_allocation (stream->caps, FALSE);
8834 if (!gst_pad_peer_query (stream->pad, query)) {
8835 /* not a problem, just debug a little */
8836 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8839 if (stream->allocator)
8840 gst_object_unref (stream->allocator);
8842 if (gst_query_get_n_allocation_params (query) > 0) {
8843 /* try the allocator */
8844 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8846 stream->use_allocator = TRUE;
8848 stream->allocator = NULL;
8849 gst_allocation_params_init (&stream->params);
8850 stream->use_allocator = FALSE;
8852 gst_query_unref (query);
8857 pad_query (const GValue * item, GValue * value, gpointer user_data)
8859 GstPad *pad = g_value_get_object (item);
8860 GstQuery *query = user_data;
8863 res = gst_pad_peer_query (pad, query);
8866 g_value_set_boolean (value, TRUE);
8870 GST_INFO_OBJECT (pad, "pad peer query failed");
8875 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8876 GstPadDirection direction)
8879 GstIteratorFoldFunction func = pad_query;
8880 GValue res = { 0, };
8882 g_value_init (&res, G_TYPE_BOOLEAN);
8883 g_value_set_boolean (&res, FALSE);
8886 if (direction == GST_PAD_SRC)
8887 it = gst_element_iterate_src_pads (element);
8889 it = gst_element_iterate_sink_pads (element);
8891 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8892 gst_iterator_resync (it);
8894 gst_iterator_free (it);
8896 return g_value_get_boolean (&res);
8900 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8901 QtDemuxStream * stream)
8905 GstElement *element = GST_ELEMENT (qtdemux);
8907 gchar **filtered_sys_ids;
8908 GValue event_list = G_VALUE_INIT;
8911 /* 1. Check if we already have the context. */
8912 if (qtdemux->preferred_protection_system_id != NULL) {
8913 GST_LOG_OBJECT (element,
8914 "already have the protection context, no need to request it again");
8918 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8919 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8920 (const gchar **) qtdemux->protection_system_ids->pdata);
8922 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8923 qtdemux->protection_system_ids->len - 1);
8924 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8925 "decryptors for %u of them, running context request",
8926 qtdemux->protection_system_ids->len,
8927 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8930 if (stream->protection_scheme_event_queue.length) {
8931 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8932 stream->protection_scheme_event_queue.length);
8933 walk = stream->protection_scheme_event_queue.tail;
8935 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8936 qtdemux->protection_event_queue.length);
8937 walk = qtdemux->protection_event_queue.tail;
8940 g_value_init (&event_list, GST_TYPE_LIST);
8941 for (; walk; walk = g_list_previous (walk)) {
8942 GValue *event_value = g_new0 (GValue, 1);
8943 g_value_init (event_value, GST_TYPE_EVENT);
8944 g_value_set_boxed (event_value, walk->data);
8945 gst_value_list_append_and_take_value (&event_list, event_value);
8948 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8949 * check if downstream already has a context of the specific type
8950 * 2b) Query upstream as above.
8952 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8953 st = gst_query_writable_structure (query);
8954 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8955 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8957 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8958 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8959 gst_query_parse_context (query, &ctxt);
8960 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8961 gst_element_set_context (element, ctxt);
8962 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8963 gst_query_parse_context (query, &ctxt);
8964 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8965 gst_element_set_context (element, ctxt);
8967 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8968 * the required context type and afterwards check if a
8969 * usable context was set now as in 1). The message could
8970 * be handled by the parent bins of the element and the
8975 GST_INFO_OBJECT (element, "posting need context message");
8976 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8977 "drm-preferred-decryption-system-id");
8978 st = (GstStructure *) gst_message_get_structure (msg);
8979 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8980 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8983 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8984 gst_element_post_message (element, msg);
8987 g_strfreev (filtered_sys_ids);
8988 g_value_unset (&event_list);
8989 gst_query_unref (query);
8993 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8994 QtDemuxStream * stream)
8997 const gchar *selected_system = NULL;
8999 g_return_val_if_fail (qtdemux != NULL, FALSE);
9000 g_return_val_if_fail (stream != NULL, FALSE);
9001 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
9004 if (stream->protection_scheme_type != FOURCC_cenc) {
9005 GST_ERROR_OBJECT (qtdemux,
9006 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
9007 GST_FOURCC_ARGS (stream->protection_scheme_type));
9010 if (qtdemux->protection_system_ids == NULL) {
9011 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
9012 "cenc protection system information has been found");
9016 gst_qtdemux_request_protection_context (qtdemux, stream);
9017 if (qtdemux->preferred_protection_system_id != NULL) {
9018 const gchar *preferred_system_array[] =
9019 { qtdemux->preferred_protection_system_id, NULL };
9021 selected_system = gst_protection_select_system (preferred_system_array);
9023 if (selected_system) {
9024 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
9025 qtdemux->preferred_protection_system_id);
9027 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
9028 "because there is no available decryptor",
9029 qtdemux->preferred_protection_system_id);
9033 if (!selected_system) {
9034 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9035 selected_system = gst_protection_select_system ((const gchar **)
9036 qtdemux->protection_system_ids->pdata);
9037 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9038 qtdemux->protection_system_ids->len - 1);
9041 if (!selected_system) {
9042 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
9043 "suitable decryptor element has been found");
9047 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
9050 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9051 if (!gst_structure_has_name (s, "application/x-cenc")) {
9052 gst_structure_set (s,
9053 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
9054 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
9056 gst_structure_set_name (s, "application/x-cenc");
9062 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
9064 /* fps is calculated base on the duration of the average framerate since
9065 * qt does not have a fixed framerate. */
9066 gboolean fps_available = TRUE;
9067 guint32 first_duration = 0;
9069 if (stream->n_samples > 0)
9070 first_duration = stream->samples[0].duration;
9072 if ((stream->n_samples == 1 && first_duration == 0)
9073 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
9075 CUR_STREAM (stream)->fps_n = 0;
9076 CUR_STREAM (stream)->fps_d = 1;
9078 if (stream->duration == 0 || stream->n_samples < 2) {
9079 CUR_STREAM (stream)->fps_n = stream->timescale;
9080 CUR_STREAM (stream)->fps_d = 1;
9081 fps_available = FALSE;
9083 GstClockTime avg_duration;
9087 /* duration and n_samples can be updated for fragmented format
9088 * so, framerate of fragmented format is calculated using data in a moof */
9089 if (qtdemux->fragmented && stream->n_samples_moof > 0
9090 && stream->duration_moof > 0) {
9091 n_samples = stream->n_samples_moof;
9092 duration = stream->duration_moof;
9094 n_samples = stream->n_samples;
9095 duration = stream->duration;
9098 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
9099 /* stream->duration is guint64, timescale, n_samples are guint32 */
9101 gst_util_uint64_scale_round (duration -
9102 first_duration, GST_SECOND,
9103 (guint64) (stream->timescale) * (n_samples - 1));
9105 GST_LOG_OBJECT (qtdemux,
9106 "Calculating avg sample duration based on stream (or moof) duration %"
9108 " minus first sample %u, leaving %d samples gives %"
9109 GST_TIME_FORMAT, duration, first_duration,
9110 n_samples - 1, GST_TIME_ARGS (avg_duration));
9112 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9113 gst_video_guess_framerate (avg_duration,
9114 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9115 if (CUR_STREAM (stream)->fps_d == 0)
9116 fps_available = FALSE;
9119 gst_video_guess_framerate (avg_duration,
9120 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9123 GST_DEBUG_OBJECT (qtdemux,
9124 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
9125 stream->timescale, CUR_STREAM (stream)->fps_n,
9126 CUR_STREAM (stream)->fps_d);
9130 return fps_available;
9134 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
9136 if (stream->subtype == FOURCC_vide) {
9137 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9139 if (CUR_STREAM (stream)->caps) {
9140 CUR_STREAM (stream)->caps =
9141 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9143 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
9144 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9145 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
9146 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
9148 /* set framerate if calculated framerate is reliable */
9149 if (fps_available) {
9150 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9151 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9152 CUR_STREAM (stream)->fps_d, NULL);
9155 /* calculate pixel-aspect-ratio using display width and height */
9156 GST_DEBUG_OBJECT (qtdemux,
9157 "video size %dx%d, target display size %dx%d",
9158 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
9159 stream->display_width, stream->display_height);
9160 /* qt file might have pasp atom */
9161 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9162 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
9163 CUR_STREAM (stream)->par_h);
9164 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9165 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9166 CUR_STREAM (stream)->par_h, NULL);
9167 } else if (stream->display_width > 0 && stream->display_height > 0
9168 && CUR_STREAM (stream)->width > 0
9169 && CUR_STREAM (stream)->height > 0) {
9172 /* calculate the pixel aspect ratio using the display and pixel w/h */
9173 n = stream->display_width * CUR_STREAM (stream)->height;
9174 d = stream->display_height * CUR_STREAM (stream)->width;
9177 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
9178 CUR_STREAM (stream)->par_w = n;
9179 CUR_STREAM (stream)->par_h = d;
9180 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9181 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9182 CUR_STREAM (stream)->par_h, NULL);
9185 if (CUR_STREAM (stream)->interlace_mode > 0) {
9186 if (CUR_STREAM (stream)->interlace_mode == 1) {
9187 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9188 G_TYPE_STRING, "progressive", NULL);
9189 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
9190 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9191 G_TYPE_STRING, "interleaved", NULL);
9192 if (CUR_STREAM (stream)->field_order == 9) {
9193 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9194 G_TYPE_STRING, "top-field-first", NULL);
9195 } else if (CUR_STREAM (stream)->field_order == 14) {
9196 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9197 G_TYPE_STRING, "bottom-field-first", NULL);
9202 /* Create incomplete colorimetry here if needed */
9203 if (CUR_STREAM (stream)->colorimetry.range ||
9204 CUR_STREAM (stream)->colorimetry.matrix ||
9205 CUR_STREAM (stream)->colorimetry.transfer
9206 || CUR_STREAM (stream)->colorimetry.primaries) {
9207 gchar *colorimetry =
9208 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
9209 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
9210 G_TYPE_STRING, colorimetry, NULL);
9211 g_free (colorimetry);
9214 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9215 guint par_w = 1, par_h = 1;
9217 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9218 par_w = CUR_STREAM (stream)->par_w;
9219 par_h = CUR_STREAM (stream)->par_h;
9222 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
9223 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
9225 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9228 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9229 "multiview-mode", G_TYPE_STRING,
9230 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9231 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9232 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9237 else if (stream->subtype == FOURCC_soun) {
9238 if (CUR_STREAM (stream)->caps) {
9239 CUR_STREAM (stream)->caps =
9240 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9241 if (CUR_STREAM (stream)->rate > 0)
9242 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9243 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
9244 if (CUR_STREAM (stream)->n_channels > 0)
9245 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9246 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
9247 if (CUR_STREAM (stream)->n_channels > 2) {
9248 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9249 * correctly; this is just the minimum we can do - assume
9250 * we don't actually have any channel positions. */
9251 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9252 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9257 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
9258 const GstStructure *s;
9259 QtDemuxStream *fps_stream = NULL;
9260 gboolean fps_available = FALSE;
9262 /* CEA608 closed caption tracks are a bit special in that each sample
9263 * can contain CCs for multiple frames, and CCs can be omitted and have to
9264 * be inferred from the duration of the sample then.
9266 * As such we take the framerate from the (first) video track here for
9267 * CEA608 as there must be one CC byte pair for every video frame
9268 * according to the spec.
9270 * For CEA708 all is fine and there is one sample per frame.
9273 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9274 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9277 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
9278 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
9280 if (tmp->subtype == FOURCC_vide) {
9287 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
9288 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
9289 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
9292 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9293 fps_stream = stream;
9296 CUR_STREAM (stream)->caps =
9297 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9299 /* set framerate if calculated framerate is reliable */
9300 if (fps_available) {
9301 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9302 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9303 CUR_STREAM (stream)->fps_d, NULL);
9308 GstCaps *prev_caps = NULL;
9310 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
9311 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
9312 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
9313 gst_pad_set_active (stream->pad, TRUE);
9315 gst_pad_use_fixed_caps (stream->pad);
9317 if (stream->protected) {
9318 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
9319 GST_ERROR_OBJECT (qtdemux,
9320 "Failed to configure protected stream caps.");
9325 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9326 CUR_STREAM (stream)->caps);
9327 if (stream->new_stream) {
9329 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9332 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9335 gst_event_parse_stream_flags (event, &stream_flags);
9336 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9337 qtdemux->have_group_id = TRUE;
9339 qtdemux->have_group_id = FALSE;
9340 gst_event_unref (event);
9341 } else if (!qtdemux->have_group_id) {
9342 qtdemux->have_group_id = TRUE;
9343 qtdemux->group_id = gst_util_group_id_next ();
9346 stream->new_stream = FALSE;
9347 event = gst_event_new_stream_start (stream->stream_id);
9348 if (qtdemux->have_group_id)
9349 gst_event_set_group_id (event, qtdemux->group_id);
9350 if (stream->disabled)
9351 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9352 if (CUR_STREAM (stream)->sparse) {
9353 stream_flags |= GST_STREAM_FLAG_SPARSE;
9355 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9357 gst_event_set_stream_flags (event, stream_flags);
9358 gst_pad_push_event (stream->pad, event);
9361 prev_caps = gst_pad_get_current_caps (stream->pad);
9363 if (CUR_STREAM (stream)->caps) {
9365 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9366 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9367 CUR_STREAM (stream)->caps);
9368 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9370 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9373 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9377 gst_caps_unref (prev_caps);
9378 stream->new_caps = FALSE;
9384 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9385 QtDemuxStream * stream)
9387 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9390 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9391 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9392 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9393 stream->stsd_entries_length)) {
9394 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9395 (_("This file is invalid and cannot be played.")),
9396 ("New sample description id is out of bounds (%d >= %d)",
9397 stream->stsd_sample_description_id, stream->stsd_entries_length));
9399 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9400 stream->new_caps = TRUE;
9405 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9406 QtDemuxStream * stream, GstTagList * list)
9408 gboolean ret = TRUE;
9410 if (stream->subtype == FOURCC_vide) {
9411 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9414 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9417 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9418 gst_object_unref (stream->pad);
9424 qtdemux->n_video_streams++;
9425 } else if (stream->subtype == FOURCC_soun) {
9426 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9429 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9431 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9432 gst_object_unref (stream->pad);
9437 qtdemux->n_audio_streams++;
9438 } else if (stream->subtype == FOURCC_strm) {
9439 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9440 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9441 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9442 || stream->subtype == FOURCC_clcp) {
9443 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9446 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9448 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9449 gst_object_unref (stream->pad);
9454 qtdemux->n_sub_streams++;
9455 } else if (CUR_STREAM (stream)->caps) {
9456 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9459 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9461 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9462 gst_object_unref (stream->pad);
9467 qtdemux->n_video_streams++;
9469 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9476 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9477 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9478 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9479 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9481 if (stream->stream_tags)
9482 gst_tag_list_unref (stream->stream_tags);
9483 stream->stream_tags = list;
9485 /* global tags go on each pad anyway */
9486 stream->send_global_tags = TRUE;
9487 /* send upstream GST_EVENT_PROTECTION events that were received before
9488 this source pad was created */
9489 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9490 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9494 gst_tag_list_unref (list);
9498 /* find next atom with @fourcc starting at @offset */
9499 static GstFlowReturn
9500 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9501 guint64 * length, guint32 fourcc)
9507 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9508 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9514 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9515 if (G_UNLIKELY (ret != GST_FLOW_OK))
9517 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9520 gst_buffer_unref (buf);
9523 gst_buffer_map (buf, &map, GST_MAP_READ);
9524 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9525 gst_buffer_unmap (buf, &map);
9526 gst_buffer_unref (buf);
9528 if (G_UNLIKELY (*length == 0)) {
9529 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9530 ret = GST_FLOW_ERROR;
9534 if (lfourcc == fourcc) {
9535 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
9539 GST_LOG_OBJECT (qtdemux,
9540 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9541 GST_FOURCC_ARGS (fourcc), *offset);
9550 /* might simply have had last one */
9551 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9556 /* should only do something in pull mode */
9557 /* call with OBJECT lock */
9558 static GstFlowReturn
9559 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9561 guint64 length, offset;
9562 GstBuffer *buf = NULL;
9563 GstFlowReturn ret = GST_FLOW_OK;
9564 GstFlowReturn res = GST_FLOW_OK;
9567 offset = qtdemux->moof_offset;
9568 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9571 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9572 return GST_FLOW_EOS;
9575 /* best not do pull etc with lock held */
9576 GST_OBJECT_UNLOCK (qtdemux);
9578 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9579 if (ret != GST_FLOW_OK)
9582 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9583 if (G_UNLIKELY (ret != GST_FLOW_OK))
9585 gst_buffer_map (buf, &map, GST_MAP_READ);
9586 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9587 gst_buffer_unmap (buf, &map);
9588 gst_buffer_unref (buf);
9593 gst_buffer_unmap (buf, &map);
9594 gst_buffer_unref (buf);
9598 /* look for next moof */
9599 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9600 if (G_UNLIKELY (ret != GST_FLOW_OK))
9604 GST_OBJECT_LOCK (qtdemux);
9606 qtdemux->moof_offset = offset;
9612 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9614 res = GST_FLOW_ERROR;
9619 /* maybe upstream temporarily flushing */
9620 if (ret != GST_FLOW_FLUSHING) {
9621 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9624 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9625 /* resume at current position next time */
9632 /* initialise bytereaders for stbl sub-atoms */
9634 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9636 stream->stbl_index = -1; /* no samples have yet been parsed */
9637 stream->sample_index = -1;
9639 /* time-to-sample atom */
9640 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9643 /* copy atom data into a new buffer for later use */
9644 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
9646 /* skip version + flags */
9647 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9648 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9650 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9652 /* make sure there's enough data */
9653 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9654 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9655 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9656 stream->n_sample_times);
9657 if (!stream->n_sample_times)
9661 /* sync sample atom */
9662 stream->stps_present = FALSE;
9663 if ((stream->stss_present =
9664 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9665 &stream->stss) ? TRUE : FALSE) == TRUE) {
9666 /* copy atom data into a new buffer for later use */
9667 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
9669 /* skip version + flags */
9670 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9671 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9674 if (stream->n_sample_syncs) {
9675 /* make sure there's enough data */
9676 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9680 /* partial sync sample atom */
9681 if ((stream->stps_present =
9682 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9683 &stream->stps) ? TRUE : FALSE) == TRUE) {
9684 /* copy atom data into a new buffer for later use */
9685 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
9687 /* skip version + flags */
9688 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9689 !gst_byte_reader_get_uint32_be (&stream->stps,
9690 &stream->n_sample_partial_syncs))
9693 /* if there are no entries, the stss table contains the real
9695 if (stream->n_sample_partial_syncs) {
9696 /* make sure there's enough data */
9697 if (!qt_atom_parser_has_chunks (&stream->stps,
9698 stream->n_sample_partial_syncs, 4))
9705 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9708 /* copy atom data into a new buffer for later use */
9709 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
9711 /* skip version + flags */
9712 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9713 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9716 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9719 if (!stream->n_samples)
9722 /* sample-to-chunk atom */
9723 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9726 /* copy atom data into a new buffer for later use */
9727 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
9729 /* skip version + flags */
9730 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9731 !gst_byte_reader_get_uint32_be (&stream->stsc,
9732 &stream->n_samples_per_chunk))
9735 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9736 stream->n_samples_per_chunk);
9738 /* make sure there's enough data */
9739 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9745 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9746 stream->co_size = sizeof (guint32);
9747 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9749 stream->co_size = sizeof (guint64);
9753 /* copy atom data into a new buffer for later use */
9754 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
9756 /* skip version + flags */
9757 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9760 /* chunks_are_samples == TRUE means treat chunks as samples */
9761 stream->chunks_are_samples = stream->sample_size
9762 && !CUR_STREAM (stream)->sampled;
9763 if (stream->chunks_are_samples) {
9764 /* treat chunks as samples */
9765 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9768 /* skip number of entries */
9769 if (!gst_byte_reader_skip (&stream->stco, 4))
9772 /* make sure there are enough data in the stsz atom */
9773 if (!stream->sample_size) {
9774 /* different sizes for each sample */
9775 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9780 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9781 stream->n_samples, (guint) sizeof (QtDemuxSample),
9782 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9784 if (stream->n_samples >=
9785 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9786 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9787 "be larger than %uMB (broken file?)", stream->n_samples,
9788 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9792 g_assert (stream->samples == NULL);
9793 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9794 if (!stream->samples) {
9795 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9800 /* composition time-to-sample */
9801 if ((stream->ctts_present =
9802 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9803 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9804 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9806 /* copy atom data into a new buffer for later use */
9807 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
9809 /* skip version + flags */
9810 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9811 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9812 &stream->n_composition_times))
9815 /* make sure there's enough data */
9816 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9820 /* This is optional, if missing we iterate the ctts */
9821 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9822 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9823 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9824 g_free ((gpointer) cslg.data);
9828 gint32 cslg_least = 0;
9829 guint num_entries, pos;
9832 pos = gst_byte_reader_get_pos (&stream->ctts);
9833 num_entries = stream->n_composition_times;
9835 stream->cslg_shift = 0;
9837 for (i = 0; i < num_entries; i++) {
9840 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9841 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9842 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9843 * slightly inaccurate PTS could be more usable than corrupted one */
9844 if (G_UNLIKELY ((ABS (offset) / 2) > stream->duration)) {
9845 GST_WARNING_OBJECT (qtdemux,
9846 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9847 " larger than duration %" G_GUINT64_FORMAT,
9848 offset, stream->duration);
9850 stream->cslg_shift = 0;
9851 stream->ctts_present = FALSE;
9855 if (offset < cslg_least)
9856 cslg_least = offset;
9860 stream->cslg_shift = ABS (cslg_least);
9862 stream->cslg_shift = 0;
9864 /* reset the reader so we can generate sample table */
9865 gst_byte_reader_set_pos (&stream->ctts, pos);
9868 /* Ensure the cslg_shift value is consistent so we can use it
9869 * unconditionnally to produce TS and Segment */
9870 stream->cslg_shift = 0;
9877 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9878 (_("This file is corrupt and cannot be played.")), (NULL));
9883 gst_qtdemux_stbl_free (stream);
9884 if (!qtdemux->fragmented) {
9885 /* not quite good */
9886 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9889 /* may pick up samples elsewhere */
9895 /* collect samples from the next sample to be parsed up to sample @n for @stream
9896 * by reading the info from @stbl
9898 * This code can be executed from both the streaming thread and the seeking
9899 * thread so it takes the object lock to protect itself
9902 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9905 QtDemuxSample *samples, *first, *cur, *last;
9906 guint32 n_samples_per_chunk;
9909 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9910 GST_FOURCC_FORMAT ", pad %s",
9911 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9912 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9914 n_samples = stream->n_samples;
9917 goto out_of_samples;
9919 GST_OBJECT_LOCK (qtdemux);
9920 if (n <= stream->stbl_index)
9921 goto already_parsed;
9923 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9925 if (!stream->stsz.data) {
9926 /* so we already parsed and passed all the moov samples;
9927 * onto fragmented ones */
9928 g_assert (qtdemux->fragmented);
9932 /* pointer to the sample table */
9933 samples = stream->samples;
9935 /* starts from -1, moves to the next sample index to parse */
9936 stream->stbl_index++;
9938 /* keep track of the first and last sample to fill */
9939 first = &samples[stream->stbl_index];
9942 if (!stream->chunks_are_samples) {
9943 /* set the sample sizes */
9944 if (stream->sample_size == 0) {
9945 /* different sizes for each sample */
9946 for (cur = first; cur <= last; cur++) {
9947 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9948 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9949 (guint) (cur - samples), cur->size);
9952 /* samples have the same size */
9953 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9954 for (cur = first; cur <= last; cur++)
9955 cur->size = stream->sample_size;
9959 n_samples_per_chunk = stream->n_samples_per_chunk;
9962 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9965 if (stream->stsc_chunk_index >= stream->last_chunk
9966 || stream->stsc_chunk_index < stream->first_chunk) {
9967 stream->first_chunk =
9968 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9969 stream->samples_per_chunk =
9970 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9972 stream->stsd_sample_description_id =
9973 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9975 /* chunk numbers are counted from 1 it seems */
9976 if (G_UNLIKELY (stream->first_chunk == 0))
9979 --stream->first_chunk;
9981 /* the last chunk of each entry is calculated by taking the first chunk
9982 * of the next entry; except if there is no next, where we fake it with
9984 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9985 stream->last_chunk = G_MAXUINT32;
9987 stream->last_chunk =
9988 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9989 if (G_UNLIKELY (stream->last_chunk == 0))
9992 --stream->last_chunk;
9995 GST_LOG_OBJECT (qtdemux,
9996 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9997 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9998 stream->samples_per_chunk, stream->stsd_sample_description_id);
10000 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
10003 if (stream->last_chunk != G_MAXUINT32) {
10004 if (!qt_atom_parser_peek_sub (&stream->stco,
10005 stream->first_chunk * stream->co_size,
10006 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10007 &stream->co_chunk))
10011 stream->co_chunk = stream->stco;
10012 if (!gst_byte_reader_skip (&stream->co_chunk,
10013 stream->first_chunk * stream->co_size))
10017 stream->stsc_chunk_index = stream->first_chunk;
10020 last_chunk = stream->last_chunk;
10022 if (stream->chunks_are_samples) {
10023 cur = &samples[stream->stsc_chunk_index];
10025 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10028 stream->stsc_chunk_index = j;
10033 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
10036 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10037 "%" G_GUINT64_FORMAT, j, cur->offset);
10039 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
10040 CUR_STREAM (stream)->bytes_per_frame > 0) {
10042 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
10043 CUR_STREAM (stream)->samples_per_frame *
10044 CUR_STREAM (stream)->bytes_per_frame;
10046 cur->size = stream->samples_per_chunk;
10049 GST_DEBUG_OBJECT (qtdemux,
10050 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
10051 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
10052 stream->stco_sample_index)), cur->size);
10054 cur->timestamp = stream->stco_sample_index;
10055 cur->duration = stream->samples_per_chunk;
10056 cur->keyframe = TRUE;
10059 stream->stco_sample_index += stream->samples_per_chunk;
10061 stream->stsc_chunk_index = j;
10063 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10064 guint32 samples_per_chunk;
10065 guint64 chunk_offset;
10067 if (!stream->stsc_sample_index
10068 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
10069 &stream->chunk_offset))
10072 samples_per_chunk = stream->samples_per_chunk;
10073 chunk_offset = stream->chunk_offset;
10075 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10076 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10077 G_GUINT64_FORMAT " and size %d",
10078 (guint) (cur - samples), chunk_offset, cur->size);
10080 cur->offset = chunk_offset;
10081 chunk_offset += cur->size;
10084 if (G_UNLIKELY (cur > last)) {
10086 stream->stsc_sample_index = k + 1;
10087 stream->chunk_offset = chunk_offset;
10088 stream->stsc_chunk_index = j;
10092 stream->stsc_sample_index = 0;
10094 stream->stsc_chunk_index = j;
10096 stream->stsc_index++;
10099 if (stream->chunks_are_samples)
10103 guint32 n_sample_times;
10105 n_sample_times = stream->n_sample_times;
10108 for (i = stream->stts_index; i < n_sample_times; i++) {
10109 guint32 stts_samples;
10110 gint32 stts_duration;
10113 if (stream->stts_sample_index >= stream->stts_samples
10114 || !stream->stts_sample_index) {
10116 stream->stts_samples =
10117 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10118 stream->stts_duration =
10119 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10121 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10122 i, stream->stts_samples, stream->stts_duration);
10124 stream->stts_sample_index = 0;
10127 stts_samples = stream->stts_samples;
10128 stts_duration = stream->stts_duration;
10129 stts_time = stream->stts_time;
10131 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10132 GST_DEBUG_OBJECT (qtdemux,
10133 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10134 (guint) (cur - samples), j,
10135 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10137 cur->timestamp = stts_time;
10138 cur->duration = stts_duration;
10140 /* avoid 32-bit wrap-around,
10141 * but still mind possible 'negative' duration */
10142 stts_time += (gint64) stts_duration;
10145 if (G_UNLIKELY (cur > last)) {
10147 stream->stts_time = stts_time;
10148 stream->stts_sample_index = j + 1;
10149 if (stream->stts_sample_index >= stream->stts_samples)
10150 stream->stts_index++;
10154 stream->stts_sample_index = 0;
10155 stream->stts_time = stts_time;
10156 stream->stts_index++;
10158 /* fill up empty timestamps with the last timestamp, this can happen when
10159 * the last samples do not decode and so we don't have timestamps for them.
10160 * We however look at the last timestamp to estimate the track length so we
10161 * need something in here. */
10162 for (; cur < last; cur++) {
10163 GST_DEBUG_OBJECT (qtdemux,
10164 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10165 (guint) (cur - samples),
10166 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10167 cur->timestamp = stream->stts_time;
10168 cur->duration = -1;
10173 /* sample sync, can be NULL */
10174 if (stream->stss_present == TRUE) {
10175 guint32 n_sample_syncs;
10177 n_sample_syncs = stream->n_sample_syncs;
10179 if (!n_sample_syncs) {
10180 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10181 stream->all_keyframe = TRUE;
10183 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10184 /* note that the first sample is index 1, not 0 */
10187 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10189 if (G_LIKELY (index > 0 && index <= n_samples)) {
10191 samples[index].keyframe = TRUE;
10192 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10193 /* and exit if we have enough samples */
10194 if (G_UNLIKELY (index >= n)) {
10201 stream->stss_index = i;
10204 /* stps marks partial sync frames like open GOP I-Frames */
10205 if (stream->stps_present == TRUE) {
10206 guint32 n_sample_partial_syncs;
10208 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10210 /* if there are no entries, the stss table contains the real
10212 if (n_sample_partial_syncs) {
10213 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10214 /* note that the first sample is index 1, not 0 */
10217 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10219 if (G_LIKELY (index > 0 && index <= n_samples)) {
10221 samples[index].keyframe = TRUE;
10222 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10223 /* and exit if we have enough samples */
10224 if (G_UNLIKELY (index >= n)) {
10231 stream->stps_index = i;
10235 /* no stss, all samples are keyframes */
10236 stream->all_keyframe = TRUE;
10237 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10242 /* composition time to sample */
10243 if (stream->ctts_present == TRUE) {
10244 guint32 n_composition_times;
10245 guint32 ctts_count;
10246 gint32 ctts_soffset;
10248 /* Fill in the pts_offsets */
10250 n_composition_times = stream->n_composition_times;
10252 for (i = stream->ctts_index; i < n_composition_times; i++) {
10253 if (stream->ctts_sample_index >= stream->ctts_count
10254 || !stream->ctts_sample_index) {
10255 stream->ctts_count =
10256 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10257 stream->ctts_soffset =
10258 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10259 stream->ctts_sample_index = 0;
10262 ctts_count = stream->ctts_count;
10263 ctts_soffset = stream->ctts_soffset;
10265 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10266 cur->pts_offset = ctts_soffset;
10269 if (G_UNLIKELY (cur > last)) {
10271 stream->ctts_sample_index = j + 1;
10275 stream->ctts_sample_index = 0;
10276 stream->ctts_index++;
10280 stream->stbl_index = n;
10281 /* if index has been completely parsed, free data that is no-longer needed */
10282 if (n + 1 == stream->n_samples) {
10283 gst_qtdemux_stbl_free (stream);
10284 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10285 if (qtdemux->pullbased) {
10286 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10287 while (n + 1 == stream->n_samples)
10288 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10292 GST_OBJECT_UNLOCK (qtdemux);
10299 GST_LOG_OBJECT (qtdemux,
10300 "Tried to parse up to sample %u but this sample has already been parsed",
10302 /* if fragmented, there may be more */
10303 if (qtdemux->fragmented && n == stream->stbl_index)
10305 GST_OBJECT_UNLOCK (qtdemux);
10311 GST_LOG_OBJECT (qtdemux,
10312 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10313 stream->n_samples);
10314 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10315 (_("This file is corrupt and cannot be played.")), (NULL));
10320 GST_OBJECT_UNLOCK (qtdemux);
10321 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10322 (_("This file is corrupt and cannot be played.")), (NULL));
10327 /* collect all segment info for @stream.
10330 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10334 /* accept edts if they contain gaps at start and there is only
10335 * one media segment */
10336 gboolean allow_pushbased_edts = TRUE;
10337 gint media_segments_count = 0;
10339 /* parse and prepare segment info from the edit list */
10340 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10341 stream->n_segments = 0;
10342 stream->segments = NULL;
10343 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10346 gint segment_number, entry_size;
10348 GstClockTime stime;
10349 const guint8 *buffer;
10353 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10354 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10357 buffer = elst->data;
10359 size = QT_UINT32 (buffer);
10360 /* version, flags, n_segments */
10362 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10365 version = QT_UINT8 (buffer + 8);
10366 entry_size = (version == 1) ? 20 : 12;
10368 n_segments = QT_UINT32 (buffer + 12);
10370 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10371 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10375 /* we might allocate a bit too much, at least allocate 1 segment */
10376 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10378 /* segments always start from 0 */
10382 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10384 guint64 media_time;
10385 gboolean empty_edit = FALSE;
10386 QtDemuxSegment *segment;
10388 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10390 if (version == 1) {
10391 media_time = QT_UINT64 (buffer + 8);
10392 duration = QT_UINT64 (buffer);
10393 if (media_time == G_MAXUINT64)
10396 media_time = QT_UINT32 (buffer + 4);
10397 duration = QT_UINT32 (buffer);
10398 if (media_time == G_MAXUINT32)
10403 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
10405 segment = &stream->segments[segment_number];
10407 /* time and duration expressed in global timescale */
10408 segment->time = stime;
10409 if (duration != 0 || empty_edit) {
10410 /* edge case: empty edits with duration=zero are treated here.
10411 * (files should not have these anyway). */
10413 /* add non scaled values so we don't cause roundoff errors */
10415 stime = QTTIME_TO_GSTTIME (qtdemux, time);
10416 segment->duration = stime - segment->time;
10418 /* zero duration does not imply media_start == media_stop
10419 * but, only specify media_start. The edit ends with the track. */
10420 stime = segment->duration = GST_CLOCK_TIME_NONE;
10421 /* Don't allow more edits after this one. */
10422 n_segments = segment_number + 1;
10424 segment->stop_time = stime;
10426 segment->trak_media_start = media_time;
10427 /* media_time expressed in stream timescale */
10429 segment->media_start = media_start;
10430 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10431 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10432 media_segments_count++;
10434 segment->media_start = GST_CLOCK_TIME_NONE;
10435 segment->media_stop = GST_CLOCK_TIME_NONE;
10437 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
10439 if (rate_int <= 1) {
10440 /* 0 is not allowed, some programs write 1 instead of the floating point
10442 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10446 segment->rate = rate_int / 65536.0;
10449 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10450 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10451 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10452 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10453 segment_number, GST_TIME_ARGS (segment->time),
10454 GST_TIME_ARGS (segment->duration),
10455 GST_TIME_ARGS (segment->media_start), media_time,
10456 GST_TIME_ARGS (segment->media_stop),
10457 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10458 stream->timescale);
10459 if (segment->stop_time > qtdemux->segment.stop &&
10460 !qtdemux->upstream_format_is_time) {
10461 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10462 " extends to %" GST_TIME_FORMAT
10463 " past the end of the declared movie duration %" GST_TIME_FORMAT
10464 " movie segment will be extended", segment_number,
10465 GST_TIME_ARGS (segment->stop_time),
10466 GST_TIME_ARGS (qtdemux->segment.stop));
10467 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10470 buffer += entry_size;
10472 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10473 stream->n_segments = n_segments;
10474 if (media_segments_count != 1)
10475 allow_pushbased_edts = FALSE;
10479 /* push based does not handle segments, so act accordingly here,
10480 * and warn if applicable */
10481 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10482 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10483 /* remove and use default one below, we stream like it anyway */
10484 g_free (stream->segments);
10485 stream->segments = NULL;
10486 stream->n_segments = 0;
10489 /* no segments, create one to play the complete trak */
10490 if (stream->n_segments == 0) {
10491 GstClockTime stream_duration =
10492 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
10494 if (stream->segments == NULL)
10495 stream->segments = g_new (QtDemuxSegment, 1);
10497 /* represent unknown our way */
10498 if (stream_duration == 0)
10499 stream_duration = GST_CLOCK_TIME_NONE;
10501 stream->segments[0].time = 0;
10502 stream->segments[0].stop_time = stream_duration;
10503 stream->segments[0].duration = stream_duration;
10504 stream->segments[0].media_start = 0;
10505 stream->segments[0].media_stop = stream_duration;
10506 stream->segments[0].rate = 1.0;
10507 stream->segments[0].trak_media_start = 0;
10509 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10510 GST_TIME_ARGS (stream_duration));
10511 stream->n_segments = 1;
10512 stream->dummy_segment = TRUE;
10514 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10520 * Parses the stsd atom of a svq3 trak looking for
10521 * the SMI and gama atoms.
10524 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
10525 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10527 const guint8 *_gamma = NULL;
10528 GstBuffer *_seqh = NULL;
10529 const guint8 *stsd_data = stsd_entry_data;
10530 guint32 length = QT_UINT32 (stsd_data);
10534 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10540 version = QT_UINT16 (stsd_data);
10541 if (version == 3) {
10542 if (length >= 70) {
10545 while (length > 8) {
10546 guint32 fourcc, size;
10547 const guint8 *data;
10548 size = QT_UINT32 (stsd_data);
10549 fourcc = QT_FOURCC (stsd_data + 4);
10550 data = stsd_data + 8;
10553 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10554 "svq3 atom parsing");
10563 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10564 " for gama atom, expected 12", size);
10569 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
10571 if (_seqh != NULL) {
10572 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10573 " found, ignoring");
10575 seqh_size = QT_UINT32 (data + 4);
10576 if (seqh_size > 0) {
10577 _seqh = gst_buffer_new_and_alloc (seqh_size);
10578 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10585 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10586 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10590 if (size <= length) {
10596 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10599 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10600 G_GUINT16_FORMAT, version);
10610 } else if (_seqh) {
10611 gst_buffer_unref (_seqh);
10616 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10619 GstByteReader dref;
10623 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10624 * atom that might contain a 'data' atom with the rtsp uri.
10625 * This case was reported in bug #597497, some info about
10626 * the hndl atom can be found in TN1195
10628 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10629 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10632 guint32 dref_num_entries = 0;
10633 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10634 gst_byte_reader_skip (&dref, 4) &&
10635 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10638 /* search dref entries for hndl atom */
10639 for (i = 0; i < dref_num_entries; i++) {
10640 guint32 size = 0, type;
10641 guint8 string_len = 0;
10642 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10643 qt_atom_parser_get_fourcc (&dref, &type)) {
10644 if (type == FOURCC_hndl) {
10645 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10647 /* skip data reference handle bytes and the
10648 * following pascal string and some extra 4
10649 * bytes I have no idea what are */
10650 if (!gst_byte_reader_skip (&dref, 4) ||
10651 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10652 !gst_byte_reader_skip (&dref, string_len + 4)) {
10653 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10657 /* iterate over the atoms to find the data atom */
10658 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10662 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10663 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10664 if (atom_type == FOURCC_data) {
10665 const guint8 *uri_aux = NULL;
10667 /* found the data atom that might contain the rtsp uri */
10668 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10669 "hndl atom, interpreting it as an URI");
10670 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10672 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10673 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10675 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10676 "didn't contain a rtsp address");
10678 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10683 /* skipping to the next entry */
10684 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10687 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10694 /* skip to the next entry */
10695 if (!gst_byte_reader_skip (&dref, size - 8))
10698 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10701 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10707 #define AMR_NB_ALL_MODES 0x81ff
10708 #define AMR_WB_ALL_MODES 0x83ff
10710 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10712 /* The 'damr' atom is of the form:
10714 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10715 * 32 b 8 b 16 b 8 b 8 b
10717 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10718 * represents the highest mode used in the stream (and thus the maximum
10719 * bitrate), with a couple of special cases as seen below.
10722 /* Map of frame type ID -> bitrate */
10723 static const guint nb_bitrates[] = {
10724 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10726 static const guint wb_bitrates[] = {
10727 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10733 gst_buffer_map (buf, &map, GST_MAP_READ);
10735 if (map.size != 0x11) {
10736 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10740 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10741 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10742 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10746 mode_set = QT_UINT16 (map.data + 13);
10748 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10749 max_mode = 7 + (wb ? 1 : 0);
10751 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10752 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10754 if (max_mode == -1) {
10755 GST_DEBUG ("No mode indication was found (mode set) = %x",
10760 gst_buffer_unmap (buf, &map);
10761 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10764 gst_buffer_unmap (buf, &map);
10769 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10770 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10773 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10779 if (gst_byte_reader_get_remaining (reader) < 36)
10782 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10783 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10784 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10785 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10786 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10787 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10788 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10789 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10790 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10792 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10793 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10794 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10796 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10797 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10799 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10800 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10807 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10808 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10815 * This macro will only compare value abdegh, it expects cfi to have already
10818 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10819 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10821 /* only handle the cases where the last column has standard values */
10822 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10823 const gchar *rotation_tag = NULL;
10825 /* no rotation needed */
10826 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10828 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10829 rotation_tag = "rotate-90";
10830 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10831 rotation_tag = "rotate-180";
10832 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10833 rotation_tag = "rotate-270";
10835 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10838 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10840 if (rotation_tag != NULL) {
10841 if (*taglist == NULL)
10842 *taglist = gst_tag_list_new_empty ();
10843 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10844 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10847 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10851 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10852 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10853 * Common Encryption (cenc), the function will also parse the tenc box (defined
10854 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10855 * (typically an enc[v|a|t|s] sample entry); the function will set
10856 * @original_fmt to the fourcc of the original unencrypted stream format.
10857 * Returns TRUE if successful; FALSE otherwise. */
10859 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10860 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10866 QtDemuxCencSampleSetInfo *info;
10868 const guint8 *tenc_data;
10870 g_return_val_if_fail (qtdemux != NULL, FALSE);
10871 g_return_val_if_fail (stream != NULL, FALSE);
10872 g_return_val_if_fail (container != NULL, FALSE);
10873 g_return_val_if_fail (original_fmt != NULL, FALSE);
10875 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10876 if (G_UNLIKELY (!sinf)) {
10877 if (stream->protection_scheme_type == FOURCC_cenc) {
10878 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10879 "mandatory for Common Encryption");
10885 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10886 if (G_UNLIKELY (!frma)) {
10887 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10891 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10892 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10893 GST_FOURCC_ARGS (*original_fmt));
10895 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10897 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10900 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10901 stream->protection_scheme_version =
10902 QT_UINT32 ((const guint8 *) schm->data + 16);
10904 GST_DEBUG_OBJECT (qtdemux,
10905 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10906 "protection_scheme_version: %#010x",
10907 GST_FOURCC_ARGS (stream->protection_scheme_type),
10908 stream->protection_scheme_version);
10910 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10912 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10915 if (stream->protection_scheme_type != FOURCC_cenc &&
10916 stream->protection_scheme_type != FOURCC_piff) {
10917 GST_ERROR_OBJECT (qtdemux,
10918 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10919 GST_FOURCC_ARGS (stream->protection_scheme_type));
10923 if (G_UNLIKELY (!stream->protection_scheme_info))
10924 stream->protection_scheme_info =
10925 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10927 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10929 if (stream->protection_scheme_type == FOURCC_cenc) {
10930 guint32 is_encrypted;
10932 const guint8 *default_kid;
10934 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10936 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10937 "which is mandatory for Common Encryption");
10940 tenc_data = (const guint8 *) tenc->data + 12;
10941 is_encrypted = QT_UINT24 (tenc_data);
10942 iv_size = QT_UINT8 (tenc_data + 3);
10943 default_kid = (tenc_data + 4);
10944 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
10945 is_encrypted, iv_size, default_kid);
10946 } else if (stream->protection_scheme_type == FOURCC_piff) {
10948 static const guint8 piff_track_encryption_uuid[] = {
10949 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10950 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10953 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10955 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10956 "which is mandatory for Common Encryption");
10960 tenc_data = (const guint8 *) tenc->data + 8;
10961 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10962 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10963 GST_ERROR_OBJECT (qtdemux,
10964 "Unsupported track encryption box with uuid: %s", box_uuid);
10968 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10969 gst_byte_reader_init (&br, tenc_data, 20);
10970 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10971 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10974 stream->protection_scheme_type = FOURCC_cenc;
10981 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10982 QtDemuxStream ** stream2)
10984 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10987 /* parse the traks.
10988 * With each track we associate a new QtDemuxStream that contains all the info
10990 * traks that do not decode to something (like strm traks) will not have a pad.
10993 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10995 GstByteReader tkhd;
11010 QtDemuxStream *stream = NULL;
11011 const guint8 *stsd_data;
11012 const guint8 *stsd_entry_data;
11013 guint remaining_stsd_len;
11014 guint stsd_entry_count;
11016 guint16 lang_code; /* quicktime lang code or packed iso code */
11018 guint32 tkhd_flags = 0;
11019 guint8 tkhd_version = 0;
11020 guint32 w = 0, h = 0;
11021 guint value_size, stsd_len, len;
11025 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11027 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11028 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11029 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11032 /* pick between 64 or 32 bits */
11033 value_size = tkhd_version == 1 ? 8 : 4;
11034 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11035 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11038 /* Check if current moov has duplicated track_id */
11039 if (qtdemux_find_stream (qtdemux, track_id))
11040 goto existing_stream;
11042 stream = _create_stream (qtdemux, track_id);
11043 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11045 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11046 if (!gst_byte_reader_skip (&tkhd, 4))
11049 if (tkhd_version == 1) {
11050 if (!gst_byte_reader_get_uint64_be (&tkhd, &stream->tkhd_duration))
11054 if (!gst_byte_reader_get_uint32_be (&tkhd, &dur))
11056 stream->tkhd_duration = dur;
11058 GST_INFO_OBJECT (qtdemux, "tkhd duration: %" G_GUINT64_FORMAT,
11059 stream->tkhd_duration);
11061 /* need defaults for fragments */
11062 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11064 if ((tkhd_flags & 1) == 0)
11065 stream->disabled = TRUE;
11067 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11068 tkhd_version, tkhd_flags, stream->track_id);
11070 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11073 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11074 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11075 if (qtdemux->major_brand != FOURCC_mjp2 ||
11076 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11080 len = QT_UINT32 ((guint8 *) mdhd->data);
11081 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11082 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11083 if (version == 0x01000000) {
11086 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11087 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11088 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11092 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11093 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11094 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11097 if (lang_code < 0x400) {
11098 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11099 } else if (lang_code == 0x7fff) {
11100 stream->lang_id[0] = 0; /* unspecified */
11102 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11103 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11104 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11105 stream->lang_id[3] = 0;
11108 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11109 stream->timescale);
11110 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11112 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11113 lang_code, stream->lang_id);
11115 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11118 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11119 /* chapters track reference */
11120 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11122 gsize length = GST_READ_UINT32_BE (chap->data);
11123 if (qtdemux->chapters_track_id)
11124 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11126 if (length >= 12) {
11127 qtdemux->chapters_track_id =
11128 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11133 /* fragmented files may have bogus duration in moov */
11134 if (!qtdemux->fragmented &&
11135 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11136 guint64 tdur1, tdur2;
11138 /* don't overflow */
11139 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11140 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11143 * some of those trailers, nowadays, have prologue images that are
11144 * themselves video tracks as well. I haven't really found a way to
11145 * identify those yet, except for just looking at their duration. */
11146 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11147 GST_WARNING_OBJECT (qtdemux,
11148 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11149 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11150 "found, assuming preview image or something; skipping track",
11151 stream->duration, stream->timescale, qtdemux->duration,
11152 qtdemux->timescale);
11153 gst_qtdemux_stream_unref (stream);
11158 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11161 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11162 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11164 len = QT_UINT32 ((guint8 *) hdlr->data);
11166 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11167 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11168 GST_FOURCC_ARGS (stream->subtype));
11170 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11173 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11176 /*parse svmi header if existing */
11177 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11179 len = QT_UINT32 ((guint8 *) svmi->data);
11180 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11182 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11183 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11184 guint8 frame_type, frame_layout;
11186 /* MPEG-A stereo video */
11187 if (qtdemux->major_brand == FOURCC_ss02)
11188 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11190 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11191 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11192 switch (frame_type) {
11194 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11197 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11200 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11203 /* mode 3 is primary/secondary view sequence, ie
11204 * left/right views in separate tracks. See section 7.2
11205 * of ISO/IEC 23000-11:2009 */
11206 GST_FIXME_OBJECT (qtdemux,
11207 "Implement stereo video in separate streams");
11210 if ((frame_layout & 0x1) == 0)
11211 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11213 GST_LOG_OBJECT (qtdemux,
11214 "StereoVideo: composition type: %u, is_left_first: %u",
11215 frame_type, frame_layout);
11216 stream->multiview_mode = mode;
11217 stream->multiview_flags = flags;
11221 /* parse rest of tkhd */
11222 if (stream->subtype == FOURCC_vide) {
11225 /* version 1 uses some 64-bit ints */
11226 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11227 if (!gst_byte_reader_skip (&tkhd, 16))
11229 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11233 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11236 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11237 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11240 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11241 &stream->stream_tags);
11245 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11247 stsd_data = (const guint8 *) stsd->data;
11249 /* stsd should at least have one entry */
11250 stsd_len = QT_UINT32 (stsd_data);
11251 if (stsd_len < 24) {
11252 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11253 if (stream->subtype == FOURCC_vivo) {
11254 gst_qtdemux_stream_unref (stream);
11261 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11262 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11263 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11264 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11266 stsd_entry_data = stsd_data + 16;
11267 remaining_stsd_len = stsd_len - 16;
11268 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11270 gchar *codec = NULL;
11271 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11273 /* and that entry should fit within stsd */
11274 len = QT_UINT32 (stsd_entry_data);
11275 if (len > remaining_stsd_len)
11278 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11279 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11280 GST_FOURCC_ARGS (entry->fourcc));
11281 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11283 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11284 goto error_encrypted;
11286 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11287 /* FIXME this looks wrong, there might be multiple children
11288 * with the same type */
11289 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11290 stream->protected = TRUE;
11291 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11292 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11295 if (stream->subtype == FOURCC_vide) {
11300 gint depth, palette_size, palette_count;
11301 guint32 *palette_data = NULL;
11303 entry->sampled = TRUE;
11305 stream->display_width = w >> 16;
11306 stream->display_height = h >> 16;
11309 if (len < 86) /* TODO verify */
11312 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
11313 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
11314 entry->fps_n = 0; /* this is filled in later */
11315 entry->fps_d = 0; /* this is filled in later */
11316 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
11317 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
11319 /* if color_table_id is 0, ctab atom must follow; however some files
11320 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11321 * if color table is not present we'll correct the value */
11322 if (entry->color_table_id == 0 &&
11324 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
11325 entry->color_table_id = -1;
11328 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11329 entry->width, entry->height, entry->bits_per_sample,
11330 entry->color_table_id);
11332 depth = entry->bits_per_sample;
11334 /* more than 32 bits means grayscale */
11335 gray = (depth > 32);
11336 /* low 32 bits specify the depth */
11339 /* different number of palette entries is determined by depth. */
11341 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11342 palette_count = (1 << depth);
11343 palette_size = palette_count * 4;
11345 if (entry->color_table_id) {
11346 switch (palette_count) {
11350 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
11353 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
11358 g_memdup (ff_qt_grayscale_palette_16, palette_size);
11360 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
11365 g_memdup (ff_qt_grayscale_palette_256, palette_size);
11367 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
11370 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11371 (_("The video in this file might not play correctly.")),
11372 ("unsupported palette depth %d", depth));
11376 gint i, j, start, end;
11382 start = QT_UINT32 (stsd_entry_data + offset + 70);
11383 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
11384 end = QT_UINT16 (stsd_entry_data + offset + 76);
11386 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11387 start, end, palette_count);
11394 if (len < 94 + (end - start) * 8)
11397 /* palette is always the same size */
11398 palette_data = g_malloc0 (256 * 4);
11399 palette_size = 256 * 4;
11401 for (j = 0, i = start; i <= end; j++, i++) {
11402 guint32 a, r, g, b;
11404 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11405 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11406 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11407 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
11409 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11410 (g & 0xff00) | (b >> 8);
11415 gst_caps_unref (entry->caps);
11418 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
11420 if (G_UNLIKELY (!entry->caps)) {
11421 g_free (palette_data);
11422 goto unknown_stream;
11426 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11427 GST_TAG_VIDEO_CODEC, codec, NULL);
11432 if (palette_data) {
11435 if (entry->rgb8_palette)
11436 gst_memory_unref (entry->rgb8_palette);
11437 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
11438 palette_data, palette_size, 0, palette_size, palette_data, g_free);
11440 s = gst_caps_get_structure (entry->caps, 0);
11442 /* non-raw video has a palette_data property. raw video has the palette as
11443 * an extra plane that we append to the output buffers before we push
11445 if (!gst_structure_has_name (s, "video/x-raw")) {
11446 GstBuffer *palette;
11448 palette = gst_buffer_new ();
11449 gst_buffer_append_memory (palette, entry->rgb8_palette);
11450 entry->rgb8_palette = NULL;
11452 gst_caps_set_simple (entry->caps, "palette_data",
11453 GST_TYPE_BUFFER, palette, NULL);
11454 gst_buffer_unref (palette);
11456 } else if (palette_count != 0) {
11457 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
11458 (NULL), ("Unsupported palette depth %d", depth));
11461 GST_LOG_OBJECT (qtdemux, "frame count: %u",
11462 QT_UINT16 (stsd_entry_data + offset + 32));
11468 /* pick 'the' stsd child */
11469 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11470 // We should skip parsing the stsd for non-protected streams if
11471 // the entry doesn't match the fourcc, since they don't change
11472 // format. However, for protected streams we can have partial
11473 // encryption, where parts of the stream are encrypted and parts
11474 // not. For both parts of such streams, we should ensure the
11475 // esds overrides are parsed for both from the stsd.
11476 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
11477 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
11479 else if (!stream->protected)
11484 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
11485 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
11486 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
11487 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
11491 const guint8 *pasp_data = (const guint8 *) pasp->data;
11492 gint len = QT_UINT32 (pasp_data);
11495 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
11496 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
11498 CUR_STREAM (stream)->par_w = 0;
11499 CUR_STREAM (stream)->par_h = 0;
11502 CUR_STREAM (stream)->par_w = 0;
11503 CUR_STREAM (stream)->par_h = 0;
11507 const guint8 *fiel_data = (const guint8 *) fiel->data;
11508 gint len = QT_UINT32 (fiel_data);
11511 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
11512 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
11517 const guint8 *colr_data = (const guint8 *) colr->data;
11518 gint len = QT_UINT32 (colr_data);
11520 if (len == 19 || len == 18) {
11521 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
11523 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
11524 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
11525 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
11526 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
11527 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
11529 switch (primaries) {
11531 CUR_STREAM (stream)->colorimetry.primaries =
11532 GST_VIDEO_COLOR_PRIMARIES_BT709;
11535 CUR_STREAM (stream)->colorimetry.primaries =
11536 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
11539 CUR_STREAM (stream)->colorimetry.primaries =
11540 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
11543 CUR_STREAM (stream)->colorimetry.primaries =
11544 GST_VIDEO_COLOR_PRIMARIES_BT2020;
11550 switch (transfer_function) {
11552 CUR_STREAM (stream)->colorimetry.transfer =
11553 GST_VIDEO_TRANSFER_BT709;
11556 CUR_STREAM (stream)->colorimetry.transfer =
11557 GST_VIDEO_TRANSFER_SMPTE240M;
11565 CUR_STREAM (stream)->colorimetry.matrix =
11566 GST_VIDEO_COLOR_MATRIX_BT709;
11569 CUR_STREAM (stream)->colorimetry.matrix =
11570 GST_VIDEO_COLOR_MATRIX_BT601;
11573 CUR_STREAM (stream)->colorimetry.matrix =
11574 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
11577 CUR_STREAM (stream)->colorimetry.matrix =
11578 GST_VIDEO_COLOR_MATRIX_BT2020;
11584 CUR_STREAM (stream)->colorimetry.range =
11585 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
11586 GST_VIDEO_COLOR_RANGE_16_235;
11588 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
11591 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
11596 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11597 stream->stream_tags);
11604 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11605 const guint8 *avc_data = stsd_entry_data + 0x56;
11608 while (len >= 0x8) {
11611 if (QT_UINT32 (avc_data) <= len)
11612 size = QT_UINT32 (avc_data) - 0x8;
11617 /* No real data, so break out */
11620 switch (QT_FOURCC (avc_data + 0x4)) {
11623 /* parse, if found */
11626 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11628 /* First 4 bytes are the length of the atom, the next 4 bytes
11629 * are the fourcc, the next 1 byte is the version, and the
11630 * subsequent bytes are profile_tier_level structure like data. */
11631 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11632 avc_data + 8 + 1, size - 1);
11633 buf = gst_buffer_new_and_alloc (size);
11634 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11635 gst_caps_set_simple (entry->caps,
11636 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11637 gst_buffer_unref (buf);
11645 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11647 /* First 4 bytes are the length of the atom, the next 4 bytes
11648 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11649 * next 1 byte is the version, and the
11650 * subsequent bytes are sequence parameter set like data. */
11652 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11654 gst_codec_utils_h264_caps_set_level_and_profile
11655 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11657 buf = gst_buffer_new_and_alloc (size);
11658 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11659 gst_caps_set_simple (entry->caps,
11660 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11661 gst_buffer_unref (buf);
11667 guint avg_bitrate, max_bitrate;
11669 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11673 max_bitrate = QT_UINT32 (avc_data + 0xc);
11674 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11676 if (!max_bitrate && !avg_bitrate)
11679 /* Some muxers seem to swap the average and maximum bitrates
11680 * (I'm looking at you, YouTube), so we swap for sanity. */
11681 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11682 guint temp = avg_bitrate;
11684 avg_bitrate = max_bitrate;
11685 max_bitrate = temp;
11688 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11689 gst_tag_list_add (stream->stream_tags,
11690 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11691 max_bitrate, NULL);
11693 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11694 gst_tag_list_add (stream->stream_tags,
11695 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11707 avc_data += size + 8;
11716 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11717 const guint8 *hevc_data = stsd_entry_data + 0x56;
11720 while (len >= 0x8) {
11723 if (QT_UINT32 (hevc_data) <= len)
11724 size = QT_UINT32 (hevc_data) - 0x8;
11729 /* No real data, so break out */
11732 switch (QT_FOURCC (hevc_data + 0x4)) {
11735 /* parse, if found */
11738 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11740 /* First 4 bytes are the length of the atom, the next 4 bytes
11741 * are the fourcc, the next 1 byte is the version, and the
11742 * subsequent bytes are sequence parameter set like data. */
11743 gst_codec_utils_h265_caps_set_level_tier_and_profile
11744 (entry->caps, hevc_data + 8 + 1, size - 1);
11746 buf = gst_buffer_new_and_alloc (size);
11747 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11748 gst_caps_set_simple (entry->caps,
11749 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11750 gst_buffer_unref (buf);
11757 hevc_data += size + 8;
11770 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11771 GST_FOURCC_ARGS (fourcc));
11773 /* codec data might be in glbl extension atom */
11775 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11781 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11783 len = QT_UINT32 (data);
11786 buf = gst_buffer_new_and_alloc (len);
11787 gst_buffer_fill (buf, 0, data + 8, len);
11788 gst_caps_set_simple (entry->caps,
11789 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11790 gst_buffer_unref (buf);
11797 /* see annex I of the jpeg2000 spec */
11798 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11799 const guint8 *data;
11800 const gchar *colorspace = NULL;
11802 guint32 ncomp_map = 0;
11803 gint32 *comp_map = NULL;
11804 guint32 nchan_def = 0;
11805 gint32 *chan_def = NULL;
11807 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11808 /* some required atoms */
11809 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11812 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11816 /* number of components; redundant with info in codestream, but useful
11818 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11819 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11821 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11823 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11826 GST_DEBUG_OBJECT (qtdemux, "found colr");
11827 /* extract colour space info */
11828 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11829 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11831 colorspace = "sRGB";
11834 colorspace = "GRAY";
11837 colorspace = "sYUV";
11845 /* colr is required, and only values 16, 17, and 18 are specified,
11846 so error if we have no colorspace */
11849 /* extract component mapping */
11850 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11852 guint32 cmap_len = 0;
11854 cmap_len = QT_UINT32 (cmap->data);
11855 if (cmap_len >= 8) {
11856 /* normal box, subtract off header */
11858 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11859 if (cmap_len % 4 == 0) {
11860 ncomp_map = (cmap_len / 4);
11861 comp_map = g_new0 (gint32, ncomp_map);
11862 for (i = 0; i < ncomp_map; i++) {
11865 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11866 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11867 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11868 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11873 /* extract channel definitions */
11874 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11876 guint32 cdef_len = 0;
11878 cdef_len = QT_UINT32 (cdef->data);
11879 if (cdef_len >= 10) {
11880 /* normal box, subtract off header and len */
11882 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11883 if (cdef_len % 6 == 0) {
11884 nchan_def = (cdef_len / 6);
11885 chan_def = g_new0 (gint32, nchan_def);
11886 for (i = 0; i < nchan_def; i++)
11888 for (i = 0; i < nchan_def; i++) {
11889 guint16 cn, typ, asoc;
11890 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11891 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11892 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11893 if (cn < nchan_def) {
11896 chan_def[cn] = asoc;
11899 chan_def[cn] = 0; /* alpha */
11902 chan_def[cn] = -typ;
11910 gst_caps_set_simple (entry->caps,
11911 "num-components", G_TYPE_INT, ncomp, NULL);
11912 gst_caps_set_simple (entry->caps,
11913 "colorspace", G_TYPE_STRING, colorspace, NULL);
11916 GValue arr = { 0, };
11917 GValue elt = { 0, };
11919 g_value_init (&arr, GST_TYPE_ARRAY);
11920 g_value_init (&elt, G_TYPE_INT);
11921 for (i = 0; i < ncomp_map; i++) {
11922 g_value_set_int (&elt, comp_map[i]);
11923 gst_value_array_append_value (&arr, &elt);
11925 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11926 "component-map", &arr);
11927 g_value_unset (&elt);
11928 g_value_unset (&arr);
11933 GValue arr = { 0, };
11934 GValue elt = { 0, };
11936 g_value_init (&arr, GST_TYPE_ARRAY);
11937 g_value_init (&elt, G_TYPE_INT);
11938 for (i = 0; i < nchan_def; i++) {
11939 g_value_set_int (&elt, chan_def[i]);
11940 gst_value_array_append_value (&arr, &elt);
11942 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11943 "channel-definitions", &arr);
11944 g_value_unset (&elt);
11945 g_value_unset (&arr);
11949 /* some optional atoms */
11950 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11951 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11953 /* indicate possible fields in caps */
11955 data = (guint8 *) field->data + 8;
11957 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11958 (gint) * data, NULL);
11960 /* add codec_data if provided */
11965 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11966 data = prefix->data;
11967 len = QT_UINT32 (data);
11970 buf = gst_buffer_new_and_alloc (len);
11971 gst_buffer_fill (buf, 0, data + 8, len);
11972 gst_caps_set_simple (entry->caps,
11973 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11974 gst_buffer_unref (buf);
11983 GstBuffer *seqh = NULL;
11984 const guint8 *gamma_data = NULL;
11985 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11987 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11990 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11991 QT_FP32 (gamma_data), NULL);
11994 /* sorry for the bad name, but we don't know what this is, other
11995 * than its own fourcc */
11996 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11998 gst_buffer_unref (seqh);
12001 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12002 buf = gst_buffer_new_and_alloc (len);
12003 gst_buffer_fill (buf, 0, stsd_data, len);
12004 gst_caps_set_simple (entry->caps,
12005 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12006 gst_buffer_unref (buf);
12011 /* https://developer.apple.com/standards/qtff-2001.pdf,
12012 * page 92, "Video Sample Description", under table 3.1 */
12015 const gint compressor_offset =
12016 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12017 const gint min_size = compressor_offset + 32 + 2 + 2;
12020 guint16 color_table_id = 0;
12023 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12025 /* recover information on interlaced/progressive */
12026 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
12030 len = QT_UINT32 (jpeg->data);
12031 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12033 if (len >= min_size) {
12034 gst_byte_reader_init (&br, jpeg->data, len);
12036 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12037 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12038 if (color_table_id != 0) {
12039 /* the spec says there can be concatenated chunks in the data, and we want
12040 * to find one called field. Walk through them. */
12041 gint offset = min_size;
12042 while (offset + 8 < len) {
12043 guint32 size = 0, tag;
12044 ok = gst_byte_reader_get_uint32_le (&br, &size);
12045 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12046 if (!ok || size < 8) {
12047 GST_WARNING_OBJECT (qtdemux,
12048 "Failed to walk optional chunk list");
12051 GST_DEBUG_OBJECT (qtdemux,
12052 "Found optional %4.4s chunk, size %u",
12053 (const char *) &tag, size);
12054 if (tag == FOURCC_fiel) {
12055 guint8 n_fields = 0, ordering = 0;
12056 gst_byte_reader_get_uint8 (&br, &n_fields);
12057 gst_byte_reader_get_uint8 (&br, &ordering);
12058 if (n_fields == 1 || n_fields == 2) {
12059 GST_DEBUG_OBJECT (qtdemux,
12060 "Found fiel tag with %u fields, ordering %u",
12061 n_fields, ordering);
12063 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12064 "interlace-mode", G_TYPE_STRING, "interleaved",
12067 GST_WARNING_OBJECT (qtdemux,
12068 "Found fiel tag with invalid fields (%u)", n_fields);
12074 GST_DEBUG_OBJECT (qtdemux,
12075 "Color table ID is 0, not trying to get interlacedness");
12078 GST_WARNING_OBJECT (qtdemux,
12079 "Length of jpeg chunk is too small, not trying to get interlacedness");
12087 gst_caps_set_simple (entry->caps,
12088 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12094 GNode *xith, *xdxt;
12096 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12097 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12101 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12105 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12106 /* collect the headers and store them in a stream list so that we can
12107 * send them out first */
12108 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12118 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12119 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12122 ovc1_data = ovc1->data;
12123 ovc1_len = QT_UINT32 (ovc1_data);
12124 if (ovc1_len <= 198) {
12125 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12128 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12129 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12130 gst_caps_set_simple (entry->caps,
12131 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12132 gst_buffer_unref (buf);
12137 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12138 const guint8 *vc1_data = stsd_entry_data + 0x56;
12144 if (QT_UINT32 (vc1_data) <= len)
12145 size = QT_UINT32 (vc1_data) - 8;
12150 /* No real data, so break out */
12153 switch (QT_FOURCC (vc1_data + 0x4)) {
12154 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12158 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12159 buf = gst_buffer_new_and_alloc (size);
12160 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12161 gst_caps_set_simple (entry->caps,
12162 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12163 gst_buffer_unref (buf);
12170 vc1_data += size + 8;
12176 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12177 const guint8 *av1_data = stsd_entry_data + 0x56;
12180 while (len >= 0x8) {
12183 if (QT_UINT32 (av1_data) <= len)
12184 size = QT_UINT32 (av1_data) - 0x8;
12189 /* No real data, so break out */
12192 switch (QT_FOURCC (av1_data + 0x4)) {
12195 /* parse, if found */
12197 guint8 pres_delay_field;
12199 GST_DEBUG_OBJECT (qtdemux,
12200 "found av1C codec_data in stsd of size %d", size);
12202 /* not enough data, just ignore and hope for the best */
12207 * 4 bytes: atom length
12212 * 1 bits: initial_presentation_delay_present
12213 * 4 bits: initial_presentation_delay (if present else reserved
12217 if (av1_data[9] != 0) {
12218 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
12222 /* We skip initial_presentation_delay* for now */
12223 pres_delay_field = *(av1_data + 12);
12224 if (pres_delay_field & (1 << 5)) {
12225 gst_caps_set_simple (entry->caps,
12226 "presentation-delay", G_TYPE_INT,
12227 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12230 buf = gst_buffer_new_and_alloc (size - 5);
12231 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12232 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
12233 gst_caps_set_simple (entry->caps,
12234 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12235 gst_buffer_unref (buf);
12244 av1_data += size + 8;
12254 GST_INFO_OBJECT (qtdemux,
12255 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12256 GST_FOURCC_ARGS (fourcc), entry->caps);
12258 } else if (stream->subtype == FOURCC_soun) {
12260 int version, samplesize;
12261 guint16 compression_id;
12262 gboolean amrwb = FALSE;
12265 /* sample description entry (16) + sound sample description v0 (20) */
12269 version = QT_UINT32 (stsd_entry_data + offset);
12270 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
12271 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
12272 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
12273 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
12275 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
12276 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
12277 QT_UINT32 (stsd_entry_data + offset + 4));
12278 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12279 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
12280 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
12281 GST_LOG_OBJECT (qtdemux, "packet size: %d",
12282 QT_UINT16 (stsd_entry_data + offset + 14));
12283 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12285 if (compression_id == 0xfffe)
12286 entry->sampled = TRUE;
12288 /* first assume uncompressed audio */
12289 entry->bytes_per_sample = samplesize / 8;
12290 entry->samples_per_frame = entry->n_channels;
12291 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12292 entry->samples_per_packet = entry->samples_per_frame;
12293 entry->bytes_per_packet = entry->bytes_per_sample;
12297 /* Yes, these have to be hard-coded */
12300 entry->samples_per_packet = 6;
12301 entry->bytes_per_packet = 1;
12302 entry->bytes_per_frame = 1 * entry->n_channels;
12303 entry->bytes_per_sample = 1;
12304 entry->samples_per_frame = 6 * entry->n_channels;
12309 entry->samples_per_packet = 3;
12310 entry->bytes_per_packet = 1;
12311 entry->bytes_per_frame = 1 * entry->n_channels;
12312 entry->bytes_per_sample = 1;
12313 entry->samples_per_frame = 3 * entry->n_channels;
12318 entry->samples_per_packet = 64;
12319 entry->bytes_per_packet = 34;
12320 entry->bytes_per_frame = 34 * entry->n_channels;
12321 entry->bytes_per_sample = 2;
12322 entry->samples_per_frame = 64 * entry->n_channels;
12328 entry->samples_per_packet = 1;
12329 entry->bytes_per_packet = 1;
12330 entry->bytes_per_frame = 1 * entry->n_channels;
12331 entry->bytes_per_sample = 1;
12332 entry->samples_per_frame = 1 * entry->n_channels;
12337 entry->samples_per_packet = 160;
12338 entry->bytes_per_packet = 33;
12339 entry->bytes_per_frame = 33 * entry->n_channels;
12340 entry->bytes_per_sample = 2;
12341 entry->samples_per_frame = 160 * entry->n_channels;
12348 if (version == 0x00010000) {
12349 /* sample description entry (16) + sound sample description v1 (20+16) */
12361 /* only parse extra decoding config for non-pcm audio */
12362 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
12363 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
12364 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
12365 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
12367 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
12368 entry->samples_per_packet);
12369 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12370 entry->bytes_per_packet);
12371 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
12372 entry->bytes_per_frame);
12373 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
12374 entry->bytes_per_sample);
12376 if (!entry->sampled && entry->bytes_per_packet) {
12377 entry->samples_per_frame = (entry->bytes_per_frame /
12378 entry->bytes_per_packet) * entry->samples_per_packet;
12379 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
12380 entry->samples_per_frame);
12385 } else if (version == 0x00020000) {
12392 /* sample description entry (16) + sound sample description v2 (56) */
12396 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
12397 entry->rate = qtfp.fp;
12398 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
12400 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
12401 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12402 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12403 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
12404 QT_UINT32 (stsd_entry_data + offset + 20));
12405 GST_LOG_OBJECT (qtdemux, "format flags: %X",
12406 QT_UINT32 (stsd_entry_data + offset + 24));
12407 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12408 QT_UINT32 (stsd_entry_data + offset + 28));
12409 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
12410 QT_UINT32 (stsd_entry_data + offset + 32));
12411 } else if (version != 0x00000) {
12412 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
12417 gst_caps_unref (entry->caps);
12419 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
12420 stsd_entry_data + 32, len - 16, &codec);
12428 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
12430 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
12432 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
12434 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
12437 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
12438 gst_caps_set_simple (entry->caps,
12439 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
12446 const guint8 *owma_data;
12447 const gchar *codec_name = NULL;
12451 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12452 /* FIXME this should also be gst_riff_strf_auds,
12453 * but the latter one is actually missing bits-per-sample :( */
12458 gint32 nSamplesPerSec;
12459 gint32 nAvgBytesPerSec;
12460 gint16 nBlockAlign;
12461 gint16 wBitsPerSample;
12464 WAVEFORMATEX *wfex;
12466 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12467 owma_data = stsd_entry_data;
12468 owma_len = QT_UINT32 (owma_data);
12469 if (owma_len <= 54) {
12470 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12473 wfex = (WAVEFORMATEX *) (owma_data + 36);
12474 buf = gst_buffer_new_and_alloc (owma_len - 54);
12475 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12476 if (wfex->wFormatTag == 0x0161) {
12477 codec_name = "Windows Media Audio";
12479 } else if (wfex->wFormatTag == 0x0162) {
12480 codec_name = "Windows Media Audio 9 Pro";
12482 } else if (wfex->wFormatTag == 0x0163) {
12483 codec_name = "Windows Media Audio 9 Lossless";
12484 /* is that correct? gstffmpegcodecmap.c is missing it, but
12485 * fluendo codec seems to support it */
12489 gst_caps_set_simple (entry->caps,
12490 "codec_data", GST_TYPE_BUFFER, buf,
12491 "wmaversion", G_TYPE_INT, version,
12492 "block_align", G_TYPE_INT,
12493 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12494 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
12495 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
12496 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
12497 gst_buffer_unref (buf);
12501 codec = g_strdup (codec_name);
12507 gint len = QT_UINT32 (stsd_entry_data) - offset;
12508 const guint8 *wfex_data = stsd_entry_data + offset;
12509 const gchar *codec_name = NULL;
12511 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12512 /* FIXME this should also be gst_riff_strf_auds,
12513 * but the latter one is actually missing bits-per-sample :( */
12518 gint32 nSamplesPerSec;
12519 gint32 nAvgBytesPerSec;
12520 gint16 nBlockAlign;
12521 gint16 wBitsPerSample;
12526 /* FIXME: unify with similar wavformatex parsing code above */
12527 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
12533 if (QT_UINT32 (wfex_data) <= len)
12534 size = QT_UINT32 (wfex_data) - 8;
12539 /* No real data, so break out */
12542 switch (QT_FOURCC (wfex_data + 4)) {
12543 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
12545 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
12550 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
12551 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
12552 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
12553 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
12554 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
12555 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
12556 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
12558 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
12559 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
12560 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
12561 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
12562 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
12563 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
12565 if (wfex.wFormatTag == 0x0161) {
12566 codec_name = "Windows Media Audio";
12568 } else if (wfex.wFormatTag == 0x0162) {
12569 codec_name = "Windows Media Audio 9 Pro";
12571 } else if (wfex.wFormatTag == 0x0163) {
12572 codec_name = "Windows Media Audio 9 Lossless";
12573 /* is that correct? gstffmpegcodecmap.c is missing it, but
12574 * fluendo codec seems to support it */
12578 gst_caps_set_simple (entry->caps,
12579 "wmaversion", G_TYPE_INT, version,
12580 "block_align", G_TYPE_INT, wfex.nBlockAlign,
12581 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
12582 "width", G_TYPE_INT, wfex.wBitsPerSample,
12583 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
12585 if (size > wfex.cbSize) {
12588 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
12589 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
12590 size - wfex.cbSize);
12591 gst_caps_set_simple (entry->caps,
12592 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12593 gst_buffer_unref (buf);
12595 GST_WARNING_OBJECT (qtdemux, "no codec data");
12600 codec = g_strdup (codec_name);
12608 wfex_data += size + 8;
12614 const guint8 *opus_data;
12615 guint8 *channel_mapping = NULL;
12618 guint8 channel_mapping_family;
12619 guint8 stream_count;
12620 guint8 coupled_count;
12623 opus_data = stsd_entry_data;
12625 channels = GST_READ_UINT8 (opus_data + 45);
12626 rate = GST_READ_UINT32_LE (opus_data + 48);
12627 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12628 stream_count = GST_READ_UINT8 (opus_data + 55);
12629 coupled_count = GST_READ_UINT8 (opus_data + 56);
12631 if (channels > 0) {
12632 channel_mapping = g_malloc (channels * sizeof (guint8));
12633 for (i = 0; i < channels; i++)
12634 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12637 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12638 channel_mapping_family, stream_count, coupled_count,
12650 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12651 GST_TAG_AUDIO_CODEC, codec, NULL);
12655 /* some bitrate info may have ended up in caps */
12656 s = gst_caps_get_structure (entry->caps, 0);
12657 gst_structure_get_int (s, "bitrate", &bitrate);
12659 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12660 GST_TAG_BITRATE, bitrate, NULL);
12663 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12664 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12665 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12667 else if (!stream->protected)
12674 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12676 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12678 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12682 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12683 16 bits is a byte-swapped wave-style codec identifier,
12684 and we can find a WAVE header internally to a 'wave' atom here.
12685 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12686 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12689 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12690 if (len < offset + 20) {
12691 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12693 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12694 const guint8 *data = stsd_entry_data + offset + 16;
12696 GNode *waveheadernode;
12698 wavenode = g_node_new ((guint8 *) data);
12699 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12700 const guint8 *waveheader;
12703 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12704 if (waveheadernode) {
12705 waveheader = (const guint8 *) waveheadernode->data;
12706 headerlen = QT_UINT32 (waveheader);
12708 if (headerlen > 8) {
12709 gst_riff_strf_auds *header = NULL;
12710 GstBuffer *headerbuf;
12716 headerbuf = gst_buffer_new_and_alloc (headerlen);
12717 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12719 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12720 headerbuf, &header, &extra)) {
12721 gst_caps_unref (entry->caps);
12722 /* FIXME: Need to do something with the channel reorder map */
12724 gst_riff_create_audio_caps (header->format, NULL, header,
12725 extra, NULL, NULL, NULL);
12728 gst_buffer_unref (extra);
12733 GST_DEBUG ("Didn't find waveheadernode for this codec");
12735 g_node_destroy (wavenode);
12738 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12739 stream->stream_tags);
12743 /* FIXME: what is in the chunk? */
12746 gint len = QT_UINT32 (stsd_data);
12748 /* seems to be always = 116 = 0x74 */
12754 gint len = QT_UINT32 (stsd_entry_data);
12757 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12759 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12760 gst_caps_set_simple (entry->caps,
12761 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12762 gst_buffer_unref (buf);
12764 gst_caps_set_simple (entry->caps,
12765 "samplesize", G_TYPE_INT, samplesize, NULL);
12770 GNode *alac, *wave = NULL;
12772 /* apparently, m4a has this atom appended directly in the stsd entry,
12773 * while mov has it in a wave atom */
12774 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12776 /* alac now refers to stsd entry atom */
12777 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12779 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12781 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12784 const guint8 *alac_data = alac->data;
12785 gint len = QT_UINT32 (alac->data);
12789 GST_DEBUG_OBJECT (qtdemux,
12790 "discarding alac atom with unexpected len %d", len);
12792 /* codec-data contains alac atom size and prefix,
12793 * ffmpeg likes it that way, not quite gst-ish though ...*/
12794 buf = gst_buffer_new_and_alloc (len);
12795 gst_buffer_fill (buf, 0, alac->data, len);
12796 gst_caps_set_simple (entry->caps,
12797 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12798 gst_buffer_unref (buf);
12800 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12801 entry->n_channels = QT_UINT8 (alac_data + 21);
12802 entry->rate = QT_UINT32 (alac_data + 32);
12805 gst_caps_set_simple (entry->caps,
12806 "samplesize", G_TYPE_INT, samplesize, NULL);
12811 /* The codingname of the sample entry is 'fLaC' */
12812 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12815 /* The 'dfLa' box is added to the sample entry to convey
12816 initializing information for the decoder. */
12817 const GNode *dfla =
12818 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12821 const guint32 len = QT_UINT32 (dfla->data);
12823 /* Must contain at least dfLa box header (12),
12824 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12826 GST_DEBUG_OBJECT (qtdemux,
12827 "discarding dfla atom with unexpected len %d", len);
12829 /* skip dfLa header to get the METADATA_BLOCKs */
12830 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12831 const guint32 metadata_blocks_len = len - 12;
12833 gchar *stream_marker = g_strdup ("fLaC");
12834 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12835 strlen (stream_marker));
12838 guint32 remainder = 0;
12839 guint32 block_size = 0;
12840 gboolean is_last = FALSE;
12842 GValue array = G_VALUE_INIT;
12843 GValue value = G_VALUE_INIT;
12845 g_value_init (&array, GST_TYPE_ARRAY);
12846 g_value_init (&value, GST_TYPE_BUFFER);
12848 gst_value_set_buffer (&value, block);
12849 gst_value_array_append_value (&array, &value);
12850 g_value_reset (&value);
12852 gst_buffer_unref (block);
12854 /* check there's at least one METADATA_BLOCK_HEADER's worth
12855 * of data, and we haven't already finished parsing */
12856 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12857 remainder = metadata_blocks_len - index;
12859 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12861 (metadata_blocks[index + 1] << 16) +
12862 (metadata_blocks[index + 2] << 8) +
12863 metadata_blocks[index + 3];
12865 /* be careful not to read off end of box */
12866 if (block_size > remainder) {
12870 is_last = metadata_blocks[index] >> 7;
12872 block = gst_buffer_new_and_alloc (block_size);
12874 gst_buffer_fill (block, 0, &metadata_blocks[index],
12877 gst_value_set_buffer (&value, block);
12878 gst_value_array_append_value (&array, &value);
12879 g_value_reset (&value);
12881 gst_buffer_unref (block);
12883 index += block_size;
12886 /* only append the metadata if we successfully read all of it */
12888 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12889 (stream)->caps, 0), "streamheader", &array);
12891 GST_WARNING_OBJECT (qtdemux,
12892 "discarding all METADATA_BLOCKs due to invalid "
12893 "block_size %d at idx %d, rem %d", block_size, index,
12897 g_value_unset (&value);
12898 g_value_unset (&array);
12900 /* The sample rate obtained from the stsd may not be accurate
12901 * since it cannot represent rates greater than 65535Hz, so
12902 * override that value with the sample rate from the
12903 * METADATA_BLOCK_STREAMINFO block */
12904 CUR_STREAM (stream)->rate =
12905 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12916 gint len = QT_UINT32 (stsd_entry_data);
12919 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12922 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12924 /* If we have enough data, let's try to get the 'damr' atom. See
12925 * the 3GPP container spec (26.244) for more details. */
12926 if ((len - 0x34) > 8 &&
12927 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12928 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12929 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12932 gst_caps_set_simple (entry->caps,
12933 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12934 gst_buffer_unref (buf);
12940 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12941 gint len = QT_UINT32 (stsd_entry_data);
12944 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12946 if (sound_version == 1) {
12947 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12948 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12949 guint8 codec_data[2];
12951 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12953 gint sample_rate_index =
12954 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12956 /* build AAC codec data */
12957 codec_data[0] = profile << 3;
12958 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12959 codec_data[1] = (sample_rate_index & 0x01) << 7;
12960 codec_data[1] |= (channels & 0xF) << 3;
12962 buf = gst_buffer_new_and_alloc (2);
12963 gst_buffer_fill (buf, 0, codec_data, 2);
12964 gst_caps_set_simple (entry->caps,
12965 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12966 gst_buffer_unref (buf);
12972 /* Fully handled elsewhere */
12975 GST_INFO_OBJECT (qtdemux,
12976 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12980 GST_INFO_OBJECT (qtdemux,
12981 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12982 GST_FOURCC_ARGS (fourcc), entry->caps);
12984 } else if (stream->subtype == FOURCC_strm) {
12985 if (fourcc == FOURCC_rtsp) {
12986 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12988 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12989 GST_FOURCC_ARGS (fourcc));
12990 goto unknown_stream;
12992 entry->sampled = TRUE;
12993 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12994 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12995 || stream->subtype == FOURCC_clcp) {
12997 entry->sampled = TRUE;
12998 entry->sparse = TRUE;
13001 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13004 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13005 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13010 /* hunt for sort-of codec data */
13014 GNode *mp4s = NULL;
13015 GNode *esds = NULL;
13017 /* look for palette in a stsd->mp4s->esds sub-atom */
13018 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
13020 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
13021 if (esds == NULL) {
13023 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13027 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13028 stream->stream_tags);
13032 GST_INFO_OBJECT (qtdemux,
13033 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13036 GST_INFO_OBJECT (qtdemux,
13037 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13038 GST_FOURCC_ARGS (fourcc), entry->caps);
13040 /* everything in 1 sample */
13041 entry->sampled = TRUE;
13044 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13047 if (entry->caps == NULL)
13048 goto unknown_stream;
13051 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13052 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13058 /* promote to sampled format */
13059 if (entry->fourcc == FOURCC_samr) {
13060 /* force mono 8000 Hz for AMR */
13061 entry->sampled = TRUE;
13062 entry->n_channels = 1;
13063 entry->rate = 8000;
13064 } else if (entry->fourcc == FOURCC_sawb) {
13065 /* force mono 16000 Hz for AMR-WB */
13066 entry->sampled = TRUE;
13067 entry->n_channels = 1;
13068 entry->rate = 16000;
13069 } else if (entry->fourcc == FOURCC_mp4a) {
13070 entry->sampled = TRUE;
13074 stsd_entry_data += len;
13075 remaining_stsd_len -= len;
13079 /* collect sample information */
13080 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
13081 goto samples_failed;
13083 if (qtdemux->fragmented) {
13086 /* need all moov samples as basis; probably not many if any at all */
13087 /* prevent moof parsing taking of at this time */
13088 offset = qtdemux->moof_offset;
13089 qtdemux->moof_offset = 0;
13090 if (stream->n_samples &&
13091 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
13092 qtdemux->moof_offset = offset;
13093 goto samples_failed;
13095 qtdemux->moof_offset = 0;
13096 /* movie duration more reliable in this case (e.g. mehd) */
13097 if (qtdemux->segment.duration &&
13098 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13100 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
13103 /* configure segments */
13104 if (!qtdemux_parse_segments (qtdemux, stream, trak))
13105 goto segments_failed;
13107 /* add some language tag, if useful */
13108 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13109 strcmp (stream->lang_id, "und")) {
13110 const gchar *lang_code;
13112 /* convert ISO 639-2 code to ISO 639-1 */
13113 lang_code = gst_tag_get_language_code (stream->lang_id);
13114 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13115 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13118 /* Check for UDTA tags */
13119 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
13120 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
13123 /* Insert and sort new stream in track-id order.
13124 * This will help in comparing old/new streams during stream update check */
13125 g_ptr_array_add (qtdemux->active_streams, stream);
13126 g_ptr_array_sort (qtdemux->active_streams,
13127 (GCompareFunc) qtdemux_track_id_compare_func);
13128 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
13129 QTDEMUX_N_STREAMS (qtdemux));
13136 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
13137 (_("This file is corrupt and cannot be played.")), (NULL));
13139 gst_qtdemux_stream_unref (stream);
13144 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
13145 gst_qtdemux_stream_unref (stream);
13151 /* we posted an error already */
13152 /* free stbl sub-atoms */
13153 gst_qtdemux_stbl_free (stream);
13154 gst_qtdemux_stream_unref (stream);
13159 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
13165 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
13166 GST_FOURCC_ARGS (stream->subtype));
13167 gst_qtdemux_stream_unref (stream);
13172 /* If we can estimate the overall bitrate, and don't have information about the
13173 * stream bitrate for exactly one stream, this guesses the stream bitrate as
13174 * the overall bitrate minus the sum of the bitrates of all other streams. This
13175 * should be useful for the common case where we have one audio and one video
13176 * stream and can estimate the bitrate of one, but not the other. */
13178 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
13180 QtDemuxStream *stream = NULL;
13181 gint64 size, sys_bitrate, sum_bitrate = 0;
13182 GstClockTime duration;
13186 if (qtdemux->fragmented)
13189 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
13191 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
13193 GST_DEBUG_OBJECT (qtdemux,
13194 "Size in bytes of the stream not known - bailing");
13198 /* Subtract the header size */
13199 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
13200 size, qtdemux->header_size);
13202 if (size < qtdemux->header_size)
13205 size = size - qtdemux->header_size;
13207 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
13208 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
13212 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13213 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
13214 switch (str->subtype) {
13217 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
13218 CUR_STREAM (str)->caps);
13219 /* retrieve bitrate, prefer avg then max */
13221 if (str->stream_tags) {
13222 if (gst_tag_list_get_uint (str->stream_tags,
13223 GST_TAG_MAXIMUM_BITRATE, &bitrate))
13224 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
13225 if (gst_tag_list_get_uint (str->stream_tags,
13226 GST_TAG_NOMINAL_BITRATE, &bitrate))
13227 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
13228 if (gst_tag_list_get_uint (str->stream_tags,
13229 GST_TAG_BITRATE, &bitrate))
13230 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
13233 sum_bitrate += bitrate;
13236 GST_DEBUG_OBJECT (qtdemux,
13237 ">1 stream with unknown bitrate - bailing");
13244 /* For other subtypes, we assume no significant impact on bitrate */
13250 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
13254 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
13256 if (sys_bitrate < sum_bitrate) {
13257 /* This can happen, since sum_bitrate might be derived from maximum
13258 * bitrates and not average bitrates */
13259 GST_DEBUG_OBJECT (qtdemux,
13260 "System bitrate less than sum bitrate - bailing");
13264 bitrate = sys_bitrate - sum_bitrate;
13265 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
13266 ", Stream bitrate = %u", sys_bitrate, bitrate);
13268 if (!stream->stream_tags)
13269 stream->stream_tags = gst_tag_list_new_empty ();
13271 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
13273 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13274 GST_TAG_BITRATE, bitrate, NULL);
13277 static GstFlowReturn
13278 qtdemux_prepare_streams (GstQTDemux * qtdemux)
13280 GstFlowReturn ret = GST_FLOW_OK;
13281 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13282 guint64 tkhd_max_duration = 0;
13286 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
13288 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13289 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13290 guint32 sample_num = 0;
13292 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13293 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13295 if (qtdemux->fragmented) {
13296 /* need all moov samples first */
13297 GST_OBJECT_LOCK (qtdemux);
13298 while (stream->n_samples == 0)
13299 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
13301 GST_OBJECT_UNLOCK (qtdemux);
13303 /* discard any stray moof */
13304 qtdemux->moof_offset = 0;
13305 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13306 if (tkhd_max_duration < stream->tkhd_duration)
13307 tkhd_max_duration = stream->tkhd_duration;
13311 /* prepare braking */
13312 if (ret != GST_FLOW_ERROR)
13315 /* in pull mode, we should have parsed some sample info by now;
13316 * and quite some code will not handle no samples.
13317 * in push mode, we'll just have to deal with it */
13318 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
13319 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
13320 g_ptr_array_remove_index (qtdemux->active_streams, i);
13323 } else if (stream->track_id == qtdemux->chapters_track_id &&
13324 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
13325 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
13326 so that it doesn't look like a subtitle track */
13327 g_ptr_array_remove_index (qtdemux->active_streams, i);
13332 /* parse the initial sample for use in setting the frame rate cap */
13333 while (sample_num == 0 && sample_num < stream->n_samples) {
13334 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
13340 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13341 if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
13342 GST_INFO_OBJECT (qtdemux,
13343 "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
13344 qtdemux->duration, tkhd_max_duration);
13345 qtdemux->duration = tkhd_max_duration;
13353 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
13355 return g_strcmp0 (stream->stream_id, stream_id) == 0;
13359 qtdemux_is_streams_update (GstQTDemux * qtdemux)
13363 /* Different length, updated */
13364 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
13367 /* streams in list are sorted in track-id order */
13368 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13369 /* Different stream-id, updated */
13370 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
13371 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
13379 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
13380 QtDemuxStream * oldstream, QtDemuxStream * newstream)
13382 /* Connect old stream's srcpad to new stream */
13383 newstream->pad = oldstream->pad;
13384 oldstream->pad = NULL;
13386 /* unset new_stream to prevent stream-start event */
13387 newstream->new_stream = FALSE;
13389 return gst_qtdemux_configure_stream (qtdemux, newstream);
13392 /* g_ptr_array_find_with_equal_func is available since 2.54,
13393 * replacement until we can depend unconditionally on the real one in GLib */
13394 #if !GLIB_CHECK_VERSION(2,54,0)
13395 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
13397 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
13398 gconstpointer needle, GEqualFunc equal_func, guint * index_)
13402 g_return_val_if_fail (haystack != NULL, FALSE);
13404 if (equal_func == NULL)
13405 equal_func = g_direct_equal;
13407 for (i = 0; i < haystack->len; i++) {
13408 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
13409 if (index_ != NULL)
13420 qtdemux_update_streams (GstQTDemux * qtdemux)
13423 g_assert (qtdemux->streams_aware);
13425 /* At below, figure out which stream in active_streams has identical stream-id
13426 * with that of in old_streams. If there is matching stream-id,
13427 * corresponding newstream will not be exposed again,
13428 * but demux will reuse srcpad of matched old stream
13430 * active_streams : newly created streams from the latest moov
13431 * old_streams : existing streams (belong to previous moov)
13434 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13435 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13436 QtDemuxStream *oldstream = NULL;
13439 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13440 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13442 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
13443 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
13444 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
13446 /* null pad stream cannot be reused */
13447 if (oldstream->pad == NULL)
13452 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13454 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13457 /* we don't need to preserve order of old streams */
13458 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13462 /* now we have all info and can expose */
13463 list = stream->stream_tags;
13464 stream->stream_tags = NULL;
13465 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13473 /* Must be called with expose lock */
13474 static GstFlowReturn
13475 qtdemux_expose_streams (GstQTDemux * qtdemux)
13479 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13481 if (!qtdemux_is_streams_update (qtdemux)) {
13482 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13483 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13484 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13485 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13486 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13487 return GST_FLOW_ERROR;
13490 g_ptr_array_set_size (qtdemux->old_streams, 0);
13491 qtdemux->need_segment = TRUE;
13493 return GST_FLOW_OK;
13496 if (qtdemux->streams_aware) {
13497 if (!qtdemux_update_streams (qtdemux))
13498 return GST_FLOW_ERROR;
13500 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13501 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13504 /* now we have all info and can expose */
13505 list = stream->stream_tags;
13506 stream->stream_tags = NULL;
13507 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13508 return GST_FLOW_ERROR;
13513 gst_qtdemux_guess_bitrate (qtdemux);
13515 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
13517 /* If we have still old_streams, it's no more used stream */
13518 for (i = 0; i < qtdemux->old_streams->len; i++) {
13519 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13524 event = gst_event_new_eos ();
13525 if (qtdemux->segment_seqnum)
13526 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
13528 gst_pad_push_event (stream->pad, event);
13532 g_ptr_array_set_size (qtdemux->old_streams, 0);
13534 /* check if we should post a redirect in case there is a single trak
13535 * and it is a redirecting trak */
13536 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
13537 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
13540 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
13541 "an external content");
13542 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
13543 gst_structure_new ("redirect",
13544 "new-location", G_TYPE_STRING,
13545 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
13546 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
13547 qtdemux->posted_redirect = TRUE;
13550 g_ptr_array_foreach (qtdemux->active_streams,
13551 (GFunc) qtdemux_do_allocation, qtdemux);
13553 qtdemux->need_segment = TRUE;
13555 qtdemux->exposed = TRUE;
13556 return GST_FLOW_OK;
13559 /* check if major or compatible brand is 3GP */
13560 static inline gboolean
13561 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
13564 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
13566 } else if (qtdemux->comp_brands != NULL) {
13570 gboolean res = FALSE;
13572 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
13575 while (size >= 4) {
13576 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
13581 gst_buffer_unmap (qtdemux->comp_brands, &map);
13588 /* check if tag is a spec'ed 3GP tag keyword storing a string */
13589 static inline gboolean
13590 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
13592 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
13593 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
13594 || fourcc == FOURCC_albm;
13598 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
13599 const char *tag, const char *dummy, GNode * node)
13601 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13605 gdouble longitude, latitude, altitude;
13608 len = QT_UINT32 (node->data);
13615 /* TODO: language code skipped */
13617 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
13620 /* do not alarm in trivial case, but bail out otherwise */
13621 if (*(data + offset) != 0) {
13622 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
13626 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13627 GST_TAG_GEO_LOCATION_NAME, name, NULL);
13628 offset += strlen (name);
13632 if (len < offset + 2 + 4 + 4 + 4)
13635 /* +1 +1 = skip null-terminator and location role byte */
13637 /* table in spec says unsigned, semantics say negative has meaning ... */
13638 longitude = QT_SFP32 (data + offset);
13641 latitude = QT_SFP32 (data + offset);
13644 altitude = QT_SFP32 (data + offset);
13646 /* one invalid means all are invalid */
13647 if (longitude >= -180.0 && longitude <= 180.0 &&
13648 latitude >= -90.0 && latitude <= 90.0) {
13649 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13650 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
13651 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
13652 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
13655 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
13662 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
13669 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
13670 const char *tag, const char *dummy, GNode * node)
13676 len = QT_UINT32 (node->data);
13680 y = QT_UINT16 ((guint8 *) node->data + 12);
13682 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
13685 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
13687 date = g_date_new_dmy (1, 1, y);
13688 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13689 g_date_free (date);
13693 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
13694 const char *tag, const char *dummy, GNode * node)
13697 char *tag_str = NULL;
13702 len = QT_UINT32 (node->data);
13707 entity = (guint8 *) node->data + offset;
13708 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
13709 GST_DEBUG_OBJECT (qtdemux,
13710 "classification info: %c%c%c%c invalid classification entity",
13711 entity[0], entity[1], entity[2], entity[3]);
13716 table = QT_UINT16 ((guint8 *) node->data + offset);
13718 /* Language code skipped */
13722 /* Tag format: "XXXX://Y[YYYY]/classification info string"
13723 * XXXX: classification entity, fixed length 4 chars.
13724 * Y[YYYY]: classification table, max 5 chars.
13726 tag_str = g_strdup_printf ("----://%u/%s",
13727 table, (char *) node->data + offset);
13729 /* memcpy To be sure we're preserving byte order */
13730 memcpy (tag_str, entity, 4);
13731 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
13733 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
13742 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
13748 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
13749 const char *tag, const char *dummy, GNode * node)
13751 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13757 gboolean ret = TRUE;
13758 const gchar *charset = NULL;
13760 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13762 len = QT_UINT32 (data->data);
13763 type = QT_UINT32 ((guint8 *) data->data + 8);
13764 if (type == 0x00000001 && len > 16) {
13765 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
13768 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13769 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13772 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13776 len = QT_UINT32 (node->data);
13777 type = QT_UINT32 ((guint8 *) node->data + 4);
13778 if ((type >> 24) == 0xa9 && len > 8 + 4) {
13782 /* Type starts with the (C) symbol, so the next data is a list
13783 * of (string size(16), language code(16), string) */
13785 str_len = QT_UINT16 ((guint8 *) node->data + 8);
13786 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
13788 /* the string + fourcc + size + 2 16bit fields,
13789 * means that there are more tags in this atom */
13790 if (len > str_len + 8 + 4) {
13791 /* TODO how to represent the same tag in different languages? */
13792 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
13793 "text alternatives, reading only first one");
13797 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
13798 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
13800 if (lang_code < 0x800) { /* MAC encoded string */
13803 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
13804 QT_FOURCC ((guint8 *) node->data + 4))) {
13805 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
13807 /* we go for 3GP style encoding if major brands claims so,
13808 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
13809 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13810 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
13811 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
13813 /* 16-bit Language code is ignored here as well */
13814 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
13821 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
13822 ret = FALSE; /* may have to fallback */
13825 GError *err = NULL;
13827 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
13828 charset, NULL, NULL, &err);
13830 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
13831 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
13833 g_error_free (err);
13836 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13837 len - offset, env_vars);
13840 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13841 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13845 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13852 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
13853 const char *tag, const char *dummy, GNode * node)
13855 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
13859 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
13860 const char *tag, const char *dummy, GNode * node)
13862 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13864 char *s, *t, *k = NULL;
13869 /* first try normal string tag if major brand not 3GP */
13870 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
13871 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
13872 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
13873 * let's try it 3gpp way after minor safety check */
13875 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
13881 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
13885 len = QT_UINT32 (data);
13889 count = QT_UINT8 (data + 14);
13891 for (; count; count--) {
13894 if (offset + 1 > len)
13896 slen = QT_UINT8 (data + offset);
13898 if (offset + slen > len)
13900 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13903 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
13905 t = g_strjoin (",", k, s, NULL);
13913 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
13920 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
13921 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
13930 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
13936 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
13937 const char *tag1, const char *tag2, GNode * node)
13944 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13946 len = QT_UINT32 (data->data);
13947 type = QT_UINT32 ((guint8 *) data->data + 8);
13948 if (type == 0x00000000 && len >= 22) {
13949 n1 = QT_UINT16 ((guint8 *) data->data + 18);
13950 n2 = QT_UINT16 ((guint8 *) data->data + 20);
13952 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
13953 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
13956 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
13957 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
13964 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
13965 const char *tag1, const char *dummy, GNode * node)
13972 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13974 len = QT_UINT32 (data->data);
13975 type = QT_UINT32 ((guint8 *) data->data + 8);
13976 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
13977 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13978 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
13979 n1 = QT_UINT16 ((guint8 *) data->data + 16);
13981 /* do not add bpm=0 */
13982 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
13983 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
13991 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
13992 const char *tag1, const char *dummy, GNode * node)
13999 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14001 len = QT_UINT32 (data->data);
14002 type = QT_UINT32 ((guint8 *) data->data + 8);
14003 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
14004 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
14005 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
14006 num = QT_UINT32 ((guint8 *) data->data + 16);
14008 /* do not add num=0 */
14009 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
14010 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
14017 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
14018 const char *tag1, const char *dummy, GNode * node)
14025 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14027 len = QT_UINT32 (data->data);
14028 type = QT_UINT32 ((guint8 *) data->data + 8);
14029 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
14030 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
14031 GstTagImageType image_type;
14033 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
14034 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
14036 image_type = GST_TAG_IMAGE_TYPE_NONE;
14039 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
14040 len - 16, image_type))) {
14041 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
14042 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
14043 gst_sample_unref (sample);
14050 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
14051 const char *tag, const char *dummy, GNode * node)
14054 GstDateTime *datetime = NULL;
14059 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14061 len = QT_UINT32 (data->data);
14062 type = QT_UINT32 ((guint8 *) data->data + 8);
14063 if (type == 0x00000001 && len > 16) {
14064 guint y, m = 1, d = 1;
14067 s = g_strndup ((char *) data->data + 16, len - 16);
14068 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
14069 datetime = gst_date_time_new_from_iso8601_string (s);
14070 if (datetime != NULL) {
14071 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
14073 gst_date_time_unref (datetime);
14076 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
14077 if (ret >= 1 && y > 1500 && y < 3000) {
14080 date = g_date_new_dmy (d, m, y);
14081 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
14082 g_date_free (date);
14084 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
14092 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
14093 const char *tag, const char *dummy, GNode * node)
14097 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14099 /* re-route to normal string tag if major brand says so
14100 * or no data atom and compatible brand suggests so */
14101 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
14102 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
14103 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
14108 guint len, type, n;
14110 len = QT_UINT32 (data->data);
14111 type = QT_UINT32 ((guint8 *) data->data + 8);
14112 if (type == 0x00000000 && len >= 18) {
14113 n = QT_UINT16 ((guint8 *) data->data + 16);
14115 const gchar *genre;
14117 genre = gst_tag_id3_genre_get (n - 1);
14118 if (genre != NULL) {
14119 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
14120 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
14128 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
14129 const gchar * tag, guint8 * data, guint32 datasize)
14134 /* make a copy to have \0 at the end */
14135 datacopy = g_strndup ((gchar *) data, datasize);
14137 /* convert the str to double */
14138 if (sscanf (datacopy, "%lf", &value) == 1) {
14139 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
14140 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
14142 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
14150 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
14151 const char *tag, const char *tag_bis, GNode * node)
14160 const gchar *meanstr;
14161 const gchar *namestr;
14163 /* checking the whole ---- atom size for consistency */
14164 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
14165 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
14169 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
14171 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
14175 meansize = QT_UINT32 (mean->data);
14176 if (meansize <= 12) {
14177 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
14180 meanstr = ((gchar *) mean->data) + 12;
14183 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
14185 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
14189 namesize = QT_UINT32 (name->data);
14190 if (namesize <= 12) {
14191 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
14194 namestr = ((gchar *) name->data) + 12;
14202 * uint24 - data type
14206 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
14208 GST_WARNING_OBJECT (demux, "No data atom in this tag");
14211 datasize = QT_UINT32 (data->data);
14212 if (datasize <= 16) {
14213 GST_WARNING_OBJECT (demux, "Data atom too small");
14216 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
14218 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
14219 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
14220 static const struct
14222 const gchar name[28];
14223 const gchar tag[28];
14226 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
14227 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
14228 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
14229 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
14230 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
14231 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
14232 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
14233 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
14237 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
14238 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
14239 switch (gst_tag_get_type (tags[i].tag)) {
14240 case G_TYPE_DOUBLE:
14241 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
14242 ((guint8 *) data->data) + 16, datasize - 16);
14244 case G_TYPE_STRING:
14245 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
14254 if (i == G_N_ELEMENTS (tags))
14264 #ifndef GST_DISABLE_GST_DEBUG
14266 gchar *namestr_dbg;
14267 gchar *meanstr_dbg;
14269 meanstr_dbg = g_strndup (meanstr, meansize);
14270 namestr_dbg = g_strndup (namestr, namesize);
14272 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
14273 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
14275 g_free (namestr_dbg);
14276 g_free (meanstr_dbg);
14283 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
14284 const char *tag_bis, GNode * node)
14289 GstTagList *id32_taglist = NULL;
14291 GST_LOG_OBJECT (demux, "parsing ID32");
14294 len = GST_READ_UINT32_BE (data);
14296 /* need at least full box and language tag */
14300 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
14301 gst_buffer_fill (buf, 0, data + 14, len - 14);
14303 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
14304 if (id32_taglist) {
14305 GST_LOG_OBJECT (demux, "parsing ok");
14306 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
14307 gst_tag_list_unref (id32_taglist);
14309 GST_LOG_OBJECT (demux, "parsing failed");
14312 gst_buffer_unref (buf);
14315 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
14316 const char *tag, const char *tag_bis, GNode * node);
14319 FOURCC_pcst -> if media is a podcast -> bool
14320 FOURCC_cpil -> if media is part of a compilation -> bool
14321 FOURCC_pgap -> if media is part of a gapless context -> bool
14322 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
14325 static const struct
14328 const gchar *gst_tag;
14329 const gchar *gst_tag_bis;
14330 const GstQTDemuxAddTagFunc func;
14333 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
14334 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
14335 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
14336 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
14337 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
14338 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
14339 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
14340 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
14341 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
14342 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
14343 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
14344 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
14345 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
14346 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14347 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14348 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
14349 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
14350 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
14351 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
14352 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
14353 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
14354 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
14355 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
14356 qtdemux_tag_add_num}, {
14357 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
14358 qtdemux_tag_add_num}, {
14359 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
14360 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
14361 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
14362 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
14363 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
14364 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
14365 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
14366 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
14367 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
14368 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
14369 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
14370 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
14371 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
14372 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
14373 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
14374 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
14375 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
14376 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
14377 qtdemux_tag_add_classification}, {
14378 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
14379 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
14380 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
14382 /* This is a special case, some tags are stored in this
14383 * 'reverse dns naming', according to:
14384 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
14387 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
14388 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
14389 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
14392 struct _GstQtDemuxTagList
14395 GstTagList *taglist;
14397 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
14400 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
14406 const gchar *style;
14411 GstQTDemux *demux = qtdemuxtaglist->demux;
14412 GstTagList *taglist = qtdemuxtaglist->taglist;
14415 len = QT_UINT32 (data);
14416 buf = gst_buffer_new_and_alloc (len);
14417 gst_buffer_fill (buf, 0, data, len);
14419 /* heuristic to determine style of tag */
14420 if (QT_FOURCC (data + 4) == FOURCC_____ ||
14421 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
14423 else if (demux->major_brand == FOURCC_qt__)
14424 style = "quicktime";
14425 /* fall back to assuming iso/3gp tag style */
14429 /* santize the name for the caps. */
14430 for (i = 0; i < 4; i++) {
14431 guint8 d = data[4 + i];
14432 if (g_ascii_isalnum (d))
14433 ndata[i] = g_ascii_tolower (d);
14438 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
14439 ndata[0], ndata[1], ndata[2], ndata[3]);
14440 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
14442 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
14443 sample = gst_sample_new (buf, NULL, NULL, s);
14444 gst_buffer_unref (buf);
14445 g_free (media_type);
14447 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
14450 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
14451 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
14453 gst_sample_unref (sample);
14457 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
14464 GstQtDemuxTagList demuxtaglist;
14466 demuxtaglist.demux = qtdemux;
14467 demuxtaglist.taglist = taglist;
14469 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
14470 if (meta != NULL) {
14471 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
14472 if (ilst == NULL) {
14473 GST_LOG_OBJECT (qtdemux, "no ilst");
14478 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
14482 while (i < G_N_ELEMENTS (add_funcs)) {
14483 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
14487 len = QT_UINT32 (node->data);
14489 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
14490 GST_FOURCC_ARGS (add_funcs[i].fourcc));
14492 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
14493 add_funcs[i].gst_tag_bis, node);
14495 g_node_destroy (node);
14501 /* parsed nodes have been removed, pass along remainder as blob */
14502 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
14503 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
14505 /* parse up XMP_ node if existing */
14506 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
14507 if (xmp_ != NULL) {
14509 GstTagList *xmptaglist;
14511 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
14512 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
14513 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
14514 gst_buffer_unref (buf);
14516 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
14518 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
14524 GstStructure *structure; /* helper for sort function */
14526 guint min_req_bitrate;
14527 guint min_req_qt_version;
14531 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14533 GstQtReference *ref_a = (GstQtReference *) a;
14534 GstQtReference *ref_b = (GstQtReference *) b;
14536 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14537 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14539 /* known bitrates go before unknown; higher bitrates go first */
14540 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14543 /* sort the redirects and post a message for the application.
14546 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14548 GstQtReference *best;
14551 GValue list_val = { 0, };
14554 g_assert (references != NULL);
14556 references = g_list_sort (references, qtdemux_redirects_sort_func);
14558 best = (GstQtReference *) references->data;
14560 g_value_init (&list_val, GST_TYPE_LIST);
14562 for (l = references; l != NULL; l = l->next) {
14563 GstQtReference *ref = (GstQtReference *) l->data;
14564 GValue struct_val = { 0, };
14566 ref->structure = gst_structure_new ("redirect",
14567 "new-location", G_TYPE_STRING, ref->location, NULL);
14569 if (ref->min_req_bitrate > 0) {
14570 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14571 ref->min_req_bitrate, NULL);
14574 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14575 g_value_set_boxed (&struct_val, ref->structure);
14576 gst_value_list_append_value (&list_val, &struct_val);
14577 g_value_unset (&struct_val);
14578 /* don't free anything here yet, since we need best->structure below */
14581 g_assert (best != NULL);
14582 s = gst_structure_copy (best->structure);
14584 if (g_list_length (references) > 1) {
14585 gst_structure_set_value (s, "locations", &list_val);
14588 g_value_unset (&list_val);
14590 for (l = references; l != NULL; l = l->next) {
14591 GstQtReference *ref = (GstQtReference *) l->data;
14593 gst_structure_free (ref->structure);
14594 g_free (ref->location);
14597 g_list_free (references);
14599 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14600 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14601 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14602 qtdemux->posted_redirect = TRUE;
14605 /* look for redirect nodes, collect all redirect information and
14609 qtdemux_parse_redirects (GstQTDemux * qtdemux)
14611 GNode *rmra, *rmda, *rdrf;
14613 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14615 GList *redirects = NULL;
14617 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14619 GstQtReference ref = { NULL, NULL, 0, 0 };
14620 GNode *rmdr, *rmvc;
14622 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14623 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14624 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14625 ref.min_req_bitrate);
14628 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14629 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14630 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14632 #ifndef GST_DISABLE_GST_DEBUG
14633 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14635 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14637 GST_LOG_OBJECT (qtdemux,
14638 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14639 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14640 bitmask, check_type);
14641 if (package == FOURCC_qtim && check_type == 0) {
14642 ref.min_req_qt_version = version;
14646 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14652 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14653 if (ref_len > 20) {
14654 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14655 ref_data = (guint8 *) rdrf->data + 20;
14656 if (ref_type == FOURCC_alis) {
14657 guint record_len, record_version, fn_len;
14659 if (ref_len > 70) {
14660 /* MacOSX alias record, google for alias-layout.txt */
14661 record_len = QT_UINT16 (ref_data + 4);
14662 record_version = QT_UINT16 (ref_data + 4 + 2);
14663 fn_len = QT_UINT8 (ref_data + 50);
14664 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14665 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14668 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14671 } else if (ref_type == FOURCC_url_) {
14672 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14674 GST_DEBUG_OBJECT (qtdemux,
14675 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14676 GST_FOURCC_ARGS (ref_type));
14678 if (ref.location != NULL) {
14679 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14681 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
14683 GST_WARNING_OBJECT (qtdemux,
14684 "Failed to extract redirect location from rdrf atom");
14687 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14691 /* look for others */
14692 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14695 if (redirects != NULL) {
14696 qtdemux_process_redirects (qtdemux, redirects);
14702 static GstTagList *
14703 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14707 if (tags == NULL) {
14708 tags = gst_tag_list_new_empty ();
14709 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14712 if (qtdemux->major_brand == FOURCC_mjp2)
14713 fmt = "Motion JPEG 2000";
14714 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14716 else if (qtdemux->major_brand == FOURCC_qt__)
14718 else if (qtdemux->fragmented)
14721 fmt = "ISO MP4/M4A";
14723 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14724 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14726 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14732 /* we have read the complete moov node now.
14733 * This function parses all of the relevant info, creates the traks and
14734 * prepares all data structures for playback
14737 qtdemux_parse_tree (GstQTDemux * qtdemux)
14744 guint64 creation_time;
14745 GstDateTime *datetime = NULL;
14748 /* make sure we have a usable taglist */
14749 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14751 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14752 if (mvhd == NULL) {
14753 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14754 return qtdemux_parse_redirects (qtdemux);
14757 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14758 if (version == 1) {
14759 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14760 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14761 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14762 } else if (version == 0) {
14763 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14764 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14765 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14767 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14771 /* Moving qt creation time (secs since 1904) to unix time */
14772 if (creation_time != 0) {
14773 /* Try to use epoch first as it should be faster and more commonly found */
14774 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14777 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14778 /* some data cleansing sanity */
14779 g_get_current_time (&now);
14780 if (now.tv_sec + 24 * 3600 < creation_time) {
14781 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14783 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14786 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14787 GDateTime *dt, *dt_local;
14789 dt = g_date_time_add_seconds (base_dt, creation_time);
14790 dt_local = g_date_time_to_local (dt);
14791 datetime = gst_date_time_new_from_g_date_time (dt_local);
14793 g_date_time_unref (base_dt);
14794 g_date_time_unref (dt);
14798 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14799 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14801 gst_date_time_unref (datetime);
14804 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14805 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14807 /* check for fragmented file and get some (default) data */
14808 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14811 GstByteReader mehd_data;
14813 /* let track parsing or anyone know weird stuff might happen ... */
14814 qtdemux->fragmented = TRUE;
14816 /* compensate for total duration */
14817 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14819 qtdemux_parse_mehd (qtdemux, &mehd_data);
14822 /* Update the movie segment duration, unless it was directly given to us
14823 * by upstream. Otherwise let it as is, as we don't want to mangle the
14824 * duration provided by upstream that may come e.g. from a MPD file. */
14825 if (!qtdemux->upstream_format_is_time) {
14826 GstClockTime duration;
14827 /* set duration in the segment info */
14828 gst_qtdemux_get_duration (qtdemux, &duration);
14829 qtdemux->segment.duration = duration;
14830 /* also do not exceed duration; stop is set that way post seek anyway,
14831 * and segment activation falls back to duration,
14832 * whereas loop only checks stop, so let's align this here as well */
14833 qtdemux->segment.stop = duration;
14836 /* parse all traks */
14837 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14839 qtdemux_parse_trak (qtdemux, trak);
14840 /* iterate all siblings */
14841 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14844 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14847 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14849 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14851 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14854 /* maybe also some tags in meta box */
14855 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14857 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14858 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14860 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14863 /* parse any protection system info */
14864 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14866 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14867 qtdemux_parse_pssh (qtdemux, pssh);
14868 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14871 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14876 /* taken from ffmpeg */
14878 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14890 len = (len << 7) | (c & 0x7f);
14899 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14900 gsize codec_data_size)
14902 GList *list = NULL;
14903 guint8 *p = codec_data;
14904 gint i, offset, num_packets;
14905 guint *length, last;
14907 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14909 if (codec_data == NULL || codec_data_size == 0)
14912 /* start of the stream and vorbis audio or theora video, need to
14913 * send the codec_priv data as first three packets */
14914 num_packets = p[0] + 1;
14915 GST_DEBUG_OBJECT (qtdemux,
14916 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14917 (guint) num_packets, codec_data_size);
14919 /* Let's put some limits, Don't think there even is a xiph codec
14920 * with more than 3-4 headers */
14921 if (G_UNLIKELY (num_packets > 16)) {
14922 GST_WARNING_OBJECT (qtdemux,
14923 "Unlikely number of xiph headers, most likely not valid");
14927 length = g_alloca (num_packets * sizeof (guint));
14931 /* first packets, read length values */
14932 for (i = 0; i < num_packets - 1; i++) {
14934 while (offset < codec_data_size) {
14935 length[i] += p[offset];
14936 if (p[offset++] != 0xff)
14941 if (offset + last > codec_data_size)
14944 /* last packet is the remaining size */
14945 length[i] = codec_data_size - offset - last;
14947 for (i = 0; i < num_packets; i++) {
14950 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14952 if (offset + length[i] > codec_data_size)
14955 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
14956 list = g_list_append (list, hdr);
14958 offset += length[i];
14967 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14973 /* this can change the codec originally present in @list */
14975 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14976 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14978 int len = QT_UINT32 (esds->data);
14979 guint8 *ptr = esds->data;
14980 guint8 *end = ptr + len;
14982 guint8 *data_ptr = NULL;
14984 guint8 object_type_id = 0;
14985 guint8 stream_type = 0;
14986 const char *codec_name = NULL;
14987 GstCaps *caps = NULL;
14989 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14991 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14993 while (ptr + 1 < end) {
14994 tag = QT_UINT8 (ptr);
14995 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14997 len = read_descr_size (ptr, end, &ptr);
14998 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
15000 /* Check the stated amount of data is available for reading */
15001 if (len < 0 || ptr + len > end)
15005 case ES_DESCRIPTOR_TAG:
15006 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
15007 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
15010 case DECODER_CONFIG_DESC_TAG:{
15011 guint max_bitrate, avg_bitrate;
15013 object_type_id = QT_UINT8 (ptr);
15014 stream_type = QT_UINT8 (ptr + 1) >> 2;
15015 max_bitrate = QT_UINT32 (ptr + 5);
15016 avg_bitrate = QT_UINT32 (ptr + 9);
15017 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
15018 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
15019 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
15020 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
15021 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
15022 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
15023 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15024 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
15026 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
15027 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
15028 avg_bitrate, NULL);
15033 case DECODER_SPECIFIC_INFO_TAG:
15034 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
15035 if (object_type_id == 0xe0 && len == 0x40) {
15041 GST_DEBUG_OBJECT (qtdemux,
15042 "Have VOBSUB palette. Creating palette event");
15043 /* move to decConfigDescr data and read palette */
15045 for (i = 0; i < 16; i++) {
15046 clut[i] = QT_UINT32 (data);
15050 s = gst_structure_new ("application/x-gst-dvd", "event",
15051 G_TYPE_STRING, "dvd-spu-clut-change",
15052 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
15053 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
15054 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
15055 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
15056 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
15057 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
15058 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
15059 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
15062 /* store event and trigger custom processing */
15063 stream->pending_event =
15064 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
15066 /* Generic codec_data handler puts it on the caps */
15073 case SL_CONFIG_DESC_TAG:
15074 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
15078 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
15080 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
15086 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
15087 * in use, and should also be used to override some other parameters for some
15089 switch (object_type_id) {
15090 case 0x20: /* MPEG-4 */
15091 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
15092 * profile_and_level_indication */
15093 if (data_ptr != NULL && data_len >= 5 &&
15094 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
15095 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
15096 data_ptr + 4, data_len - 4);
15098 break; /* Nothing special needed here */
15099 case 0x21: /* H.264 */
15100 codec_name = "H.264 / AVC";
15101 caps = gst_caps_new_simple ("video/x-h264",
15102 "stream-format", G_TYPE_STRING, "avc",
15103 "alignment", G_TYPE_STRING, "au", NULL);
15105 case 0x40: /* AAC (any) */
15106 case 0x66: /* AAC Main */
15107 case 0x67: /* AAC LC */
15108 case 0x68: /* AAC SSR */
15109 /* Override channels and rate based on the codec_data, as it's often
15111 /* Only do so for basic setup without HE-AAC extension */
15112 if (data_ptr && data_len == 2) {
15113 guint channels, rate;
15115 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
15117 entry->n_channels = channels;
15119 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15121 entry->rate = rate;
15124 /* Set level and profile if possible */
15125 if (data_ptr != NULL && data_len >= 2) {
15126 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15127 data_ptr, data_len);
15129 const gchar *profile_str = NULL;
15132 guint8 *codec_data;
15133 gint rate_idx, profile;
15135 /* No codec_data, let's invent something.
15136 * FIXME: This is wrong for SBR! */
15138 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15140 buffer = gst_buffer_new_and_alloc (2);
15141 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15142 codec_data = map.data;
15145 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
15148 switch (object_type_id) {
15150 profile_str = "main";
15154 profile_str = "lc";
15158 profile_str = "ssr";
15166 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15168 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
15170 gst_buffer_unmap (buffer, &map);
15171 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
15172 GST_TYPE_BUFFER, buffer, NULL);
15173 gst_buffer_unref (buffer);
15176 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
15177 G_TYPE_STRING, profile_str, NULL);
15181 case 0x60: /* MPEG-2, various profiles */
15187 codec_name = "MPEG-2 video";
15188 caps = gst_caps_new_simple ("video/mpeg",
15189 "mpegversion", G_TYPE_INT, 2,
15190 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15192 case 0x69: /* MPEG-2 BC audio */
15193 case 0x6B: /* MPEG-1 audio */
15194 caps = gst_caps_new_simple ("audio/mpeg",
15195 "mpegversion", G_TYPE_INT, 1, NULL);
15196 codec_name = "MPEG-1 audio";
15198 case 0x6A: /* MPEG-1 */
15199 codec_name = "MPEG-1 video";
15200 caps = gst_caps_new_simple ("video/mpeg",
15201 "mpegversion", G_TYPE_INT, 1,
15202 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15204 case 0x6C: /* MJPEG */
15206 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15208 codec_name = "Motion-JPEG";
15210 case 0x6D: /* PNG */
15212 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15214 codec_name = "PNG still images";
15216 case 0x6E: /* JPEG2000 */
15217 codec_name = "JPEG-2000";
15218 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15220 case 0xA4: /* Dirac */
15221 codec_name = "Dirac";
15222 caps = gst_caps_new_empty_simple ("video/x-dirac");
15224 case 0xA5: /* AC3 */
15225 codec_name = "AC-3 audio";
15226 caps = gst_caps_new_simple ("audio/x-ac3",
15227 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15229 case 0xA9: /* AC3 */
15230 codec_name = "DTS audio";
15231 caps = gst_caps_new_simple ("audio/x-dts",
15232 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15235 if (stream_type == 0x05 && data_ptr) {
15237 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
15240 GValue arr_val = G_VALUE_INIT;
15241 GValue buf_val = G_VALUE_INIT;
15244 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15245 codec_name = "Vorbis";
15246 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15247 g_value_init (&arr_val, GST_TYPE_ARRAY);
15248 g_value_init (&buf_val, GST_TYPE_BUFFER);
15249 for (tmp = headers; tmp; tmp = tmp->next) {
15250 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15251 gst_value_array_append_value (&arr_val, &buf_val);
15253 s = gst_caps_get_structure (caps, 0);
15254 gst_structure_take_value (s, "streamheader", &arr_val);
15255 g_value_unset (&buf_val);
15256 g_list_free (headers);
15263 case 0xE1: /* QCELP */
15264 /* QCELP, the codec_data is a riff tag (little endian) with
15265 * 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). */
15266 caps = gst_caps_new_empty_simple ("audio/qcelp");
15267 codec_name = "QCELP";
15273 /* If we have a replacement caps, then change our caps for this stream */
15275 gst_caps_unref (entry->caps);
15276 entry->caps = caps;
15279 if (codec_name && list)
15280 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15281 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15283 /* Add the codec_data attribute to caps, if we have it */
15287 buffer = gst_buffer_new_and_alloc (data_len);
15288 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15290 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15291 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15293 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15295 gst_buffer_unref (buffer);
15300 static inline GstCaps *
15301 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
15305 char *s, fourstr[5];
15307 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15308 for (i = 0; i < 4; i++) {
15309 if (!g_ascii_isalnum (fourstr[i]))
15312 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15313 caps = gst_caps_new_empty_simple (s);
15318 #define _codec(name) \
15320 if (codec_name) { \
15321 *codec_name = g_strdup (name); \
15326 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15327 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15328 const guint8 * stsd_entry_data, gchar ** codec_name)
15330 GstCaps *caps = NULL;
15331 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15335 _codec ("PNG still images");
15336 caps = gst_caps_new_empty_simple ("image/png");
15339 _codec ("JPEG still images");
15341 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15344 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15345 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15346 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15347 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
15348 _codec ("Motion-JPEG");
15350 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15353 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
15354 _codec ("Motion-JPEG format B");
15355 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15358 _codec ("JPEG-2000");
15359 /* override to what it should be according to spec, avoid palette_data */
15360 entry->bits_per_sample = 24;
15361 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15364 _codec ("Sorensen video v.3");
15365 caps = gst_caps_new_simple ("video/x-svq",
15366 "svqversion", G_TYPE_INT, 3, NULL);
15368 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15369 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
15370 _codec ("Sorensen video v.1");
15371 caps = gst_caps_new_simple ("video/x-svq",
15372 "svqversion", G_TYPE_INT, 1, NULL);
15374 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15375 caps = gst_caps_new_empty_simple ("video/x-raw");
15376 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
15377 _codec ("Windows Raw RGB");
15378 stream->alignment = 32;
15384 bps = QT_UINT16 (stsd_entry_data + 82);
15387 format = GST_VIDEO_FORMAT_RGB15;
15390 format = GST_VIDEO_FORMAT_RGB16;
15393 format = GST_VIDEO_FORMAT_RGB;
15396 format = GST_VIDEO_FORMAT_ARGB;
15404 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15405 format = GST_VIDEO_FORMAT_I420;
15407 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15408 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15409 format = GST_VIDEO_FORMAT_I420;
15412 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15413 format = GST_VIDEO_FORMAT_UYVY;
15415 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15416 format = GST_VIDEO_FORMAT_v308;
15418 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15419 format = GST_VIDEO_FORMAT_v216;
15422 format = GST_VIDEO_FORMAT_v210;
15424 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15425 format = GST_VIDEO_FORMAT_r210;
15427 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15428 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15429 format = GST_VIDEO_FORMAT_v410;
15432 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15433 * but different order than AYUV
15434 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15435 format = GST_VIDEO_FORMAT_v408;
15438 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15439 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
15440 _codec ("MPEG-1 video");
15441 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15442 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15444 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15445 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15446 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15447 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15448 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15449 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15450 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15451 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15452 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15453 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15454 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15455 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15456 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15457 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15458 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15459 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15460 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15461 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15462 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15463 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15464 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15465 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15466 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15467 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15468 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15469 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15470 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15471 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15472 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15473 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15474 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15475 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15476 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15477 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15478 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15479 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15480 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15481 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15482 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15483 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15484 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15485 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15486 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15487 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15488 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15489 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15490 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15491 _codec ("MPEG-2 video");
15492 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15493 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15495 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15496 _codec ("GIF still images");
15497 caps = gst_caps_new_empty_simple ("image/gif");
15500 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15502 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15504 /* ffmpeg uses the height/width props, don't know why */
15505 caps = gst_caps_new_simple ("video/x-h263",
15506 "variant", G_TYPE_STRING, "itu", NULL);
15510 _codec ("MPEG-4 video");
15511 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15512 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15514 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15515 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15516 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15517 caps = gst_caps_new_simple ("video/x-msmpeg",
15518 "msmpegversion", G_TYPE_INT, 43, NULL);
15520 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15522 caps = gst_caps_new_simple ("video/x-divx",
15523 "divxversion", G_TYPE_INT, 3, NULL);
15525 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15526 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15528 caps = gst_caps_new_simple ("video/x-divx",
15529 "divxversion", G_TYPE_INT, 4, NULL);
15531 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15533 caps = gst_caps_new_simple ("video/x-divx",
15534 "divxversion", G_TYPE_INT, 5, NULL);
15537 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15539 caps = gst_caps_new_simple ("video/x-ffv",
15540 "ffvversion", G_TYPE_INT, 1, NULL);
15543 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15544 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15549 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15550 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15551 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15555 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15556 _codec ("Cinepak");
15557 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15559 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15560 _codec ("Apple QuickDraw");
15561 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15563 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15564 _codec ("Apple video");
15565 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15569 _codec ("H.264 / AVC");
15570 caps = gst_caps_new_simple ("video/x-h264",
15571 "stream-format", G_TYPE_STRING, "avc",
15572 "alignment", G_TYPE_STRING, "au", NULL);
15575 _codec ("H.264 / AVC");
15576 caps = gst_caps_new_simple ("video/x-h264",
15577 "stream-format", G_TYPE_STRING, "avc3",
15578 "alignment", G_TYPE_STRING, "au", NULL);
15582 _codec ("H.265 / HEVC");
15583 caps = gst_caps_new_simple ("video/x-h265",
15584 "stream-format", G_TYPE_STRING, "hvc1",
15585 "alignment", G_TYPE_STRING, "au", NULL);
15588 _codec ("H.265 / HEVC");
15589 caps = gst_caps_new_simple ("video/x-h265",
15590 "stream-format", G_TYPE_STRING, "hev1",
15591 "alignment", G_TYPE_STRING, "au", NULL);
15594 _codec ("Run-length encoding");
15595 caps = gst_caps_new_simple ("video/x-rle",
15596 "layout", G_TYPE_STRING, "quicktime", NULL);
15599 _codec ("Run-length encoding");
15600 caps = gst_caps_new_simple ("video/x-rle",
15601 "layout", G_TYPE_STRING, "microsoft", NULL);
15603 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15604 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15605 _codec ("Indeo Video 3");
15606 caps = gst_caps_new_simple ("video/x-indeo",
15607 "indeoversion", G_TYPE_INT, 3, NULL);
15609 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15610 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15611 _codec ("Intel Video 4");
15612 caps = gst_caps_new_simple ("video/x-indeo",
15613 "indeoversion", G_TYPE_INT, 4, NULL);
15617 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15618 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15619 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15620 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15621 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15622 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15623 _codec ("DV Video");
15624 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15625 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15627 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15628 case FOURCC_dv5p: /* DVCPRO50 PAL */
15629 _codec ("DVCPro50 Video");
15630 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15631 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15633 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15634 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15635 _codec ("DVCProHD Video");
15636 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15637 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15639 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15640 _codec ("Apple Graphics (SMC)");
15641 caps = gst_caps_new_empty_simple ("video/x-smc");
15643 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15645 caps = gst_caps_new_empty_simple ("video/x-vp3");
15647 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15648 _codec ("VP6 Flash");
15649 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15653 caps = gst_caps_new_empty_simple ("video/x-theora");
15654 /* theora uses one byte of padding in the data stream because it does not
15655 * allow 0 sized packets while theora does */
15656 entry->padding = 1;
15660 caps = gst_caps_new_empty_simple ("video/x-dirac");
15662 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15663 _codec ("TIFF still images");
15664 caps = gst_caps_new_empty_simple ("image/tiff");
15666 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15667 _codec ("Apple Intermediate Codec");
15668 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15670 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15671 _codec ("AVID DNxHD");
15672 caps = gst_caps_from_string ("video/x-dnxhd");
15676 _codec ("On2 VP8");
15677 caps = gst_caps_from_string ("video/x-vp8");
15680 _codec ("Google VP9");
15681 caps = gst_caps_from_string ("video/x-vp9");
15684 _codec ("Apple ProRes LT");
15686 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15690 _codec ("Apple ProRes HQ");
15692 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15696 _codec ("Apple ProRes");
15698 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15702 _codec ("Apple ProRes Proxy");
15704 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15708 _codec ("Apple ProRes 4444");
15710 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15714 _codec ("Apple ProRes 4444 XQ");
15716 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15720 _codec ("GoPro CineForm");
15721 caps = gst_caps_from_string ("video/x-cineform");
15726 caps = gst_caps_new_simple ("video/x-wmv",
15727 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15731 caps = gst_caps_new_empty_simple ("video/x-av1");
15733 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15736 caps = _get_unknown_codec_name ("video", fourcc);
15741 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15744 gst_video_info_init (&info);
15745 gst_video_info_set_format (&info, format, entry->width, entry->height);
15747 caps = gst_video_info_to_caps (&info);
15748 *codec_name = gst_pb_utils_get_codec_description (caps);
15750 /* enable clipping for raw video streams */
15751 stream->need_clip = TRUE;
15752 stream->alignment = 32;
15759 round_up_pow2 (guint n)
15771 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15772 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15773 int len, gchar ** codec_name)
15776 const GstStructure *s;
15779 GstAudioFormat format = 0;
15782 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15784 depth = entry->bytes_per_packet * 8;
15787 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15789 /* 8-bit audio is unsigned */
15791 format = GST_AUDIO_FORMAT_U8;
15792 /* otherwise it's signed and big-endian just like 'twos' */
15794 endian = G_BIG_ENDIAN;
15801 endian = G_LITTLE_ENDIAN;
15804 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15806 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15810 caps = gst_caps_new_simple ("audio/x-raw",
15811 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15812 "layout", G_TYPE_STRING, "interleaved", NULL);
15813 stream->alignment = GST_ROUND_UP_8 (depth);
15814 stream->alignment = round_up_pow2 (stream->alignment);
15817 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
15818 _codec ("Raw 64-bit floating-point audio");
15819 caps = gst_caps_new_simple ("audio/x-raw",
15820 "format", G_TYPE_STRING, "F64BE",
15821 "layout", G_TYPE_STRING, "interleaved", NULL);
15822 stream->alignment = 8;
15824 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
15825 _codec ("Raw 32-bit floating-point audio");
15826 caps = gst_caps_new_simple ("audio/x-raw",
15827 "format", G_TYPE_STRING, "F32BE",
15828 "layout", G_TYPE_STRING, "interleaved", NULL);
15829 stream->alignment = 4;
15832 _codec ("Raw 24-bit PCM audio");
15833 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15835 caps = gst_caps_new_simple ("audio/x-raw",
15836 "format", G_TYPE_STRING, "S24BE",
15837 "layout", G_TYPE_STRING, "interleaved", NULL);
15838 stream->alignment = 4;
15840 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
15841 _codec ("Raw 32-bit PCM audio");
15842 caps = gst_caps_new_simple ("audio/x-raw",
15843 "format", G_TYPE_STRING, "S32BE",
15844 "layout", G_TYPE_STRING, "interleaved", NULL);
15845 stream->alignment = 4;
15847 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
15848 _codec ("Raw 16-bit PCM audio");
15849 caps = gst_caps_new_simple ("audio/x-raw",
15850 "format", G_TYPE_STRING, "S16LE",
15851 "layout", G_TYPE_STRING, "interleaved", NULL);
15852 stream->alignment = 2;
15855 _codec ("Mu-law audio");
15856 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15859 _codec ("A-law audio");
15860 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15864 _codec ("Microsoft ADPCM");
15865 /* Microsoft ADPCM-ACM code 2 */
15866 caps = gst_caps_new_simple ("audio/x-adpcm",
15867 "layout", G_TYPE_STRING, "microsoft", NULL);
15871 _codec ("DVI/IMA ADPCM");
15872 caps = gst_caps_new_simple ("audio/x-adpcm",
15873 "layout", G_TYPE_STRING, "dvi", NULL);
15877 _codec ("DVI/Intel IMA ADPCM");
15878 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15879 caps = gst_caps_new_simple ("audio/x-adpcm",
15880 "layout", G_TYPE_STRING, "quicktime", NULL);
15884 /* MPEG layer 3, CBR only (pre QT4.1) */
15886 _codec ("MPEG-1 layer 3");
15887 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15888 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15889 "mpegversion", G_TYPE_INT, 1, NULL);
15891 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15892 _codec ("MPEG-1 layer 2");
15894 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15895 "mpegversion", G_TYPE_INT, 1, NULL);
15898 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15899 _codec ("EAC-3 audio");
15900 caps = gst_caps_new_simple ("audio/x-eac3",
15901 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15902 entry->sampled = TRUE;
15904 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15906 _codec ("AC-3 audio");
15907 caps = gst_caps_new_simple ("audio/x-ac3",
15908 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15909 entry->sampled = TRUE;
15911 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15912 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15913 _codec ("DTS audio");
15914 caps = gst_caps_new_simple ("audio/x-dts",
15915 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15916 entry->sampled = TRUE;
15918 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15919 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15920 _codec ("DTS-HD audio");
15921 caps = gst_caps_new_simple ("audio/x-dts",
15922 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15923 entry->sampled = TRUE;
15927 caps = gst_caps_new_simple ("audio/x-mace",
15928 "maceversion", G_TYPE_INT, 3, NULL);
15932 caps = gst_caps_new_simple ("audio/x-mace",
15933 "maceversion", G_TYPE_INT, 6, NULL);
15935 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15937 caps = gst_caps_new_empty_simple ("application/ogg");
15939 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15940 _codec ("DV audio");
15941 caps = gst_caps_new_empty_simple ("audio/x-dv");
15944 _codec ("MPEG-4 AAC audio");
15945 caps = gst_caps_new_simple ("audio/mpeg",
15946 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15947 "stream-format", G_TYPE_STRING, "raw", NULL);
15949 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15950 _codec ("QDesign Music");
15951 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15954 _codec ("QDesign Music v.2");
15955 /* FIXME: QDesign music version 2 (no constant) */
15956 if (FALSE && data) {
15957 caps = gst_caps_new_simple ("audio/x-qdm2",
15958 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15959 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15960 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15962 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15966 _codec ("GSM audio");
15967 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15970 _codec ("AMR audio");
15971 caps = gst_caps_new_empty_simple ("audio/AMR");
15974 _codec ("AMR-WB audio");
15975 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15978 _codec ("Quicktime IMA ADPCM");
15979 caps = gst_caps_new_simple ("audio/x-adpcm",
15980 "layout", G_TYPE_STRING, "quicktime", NULL);
15983 _codec ("Apple lossless audio");
15984 caps = gst_caps_new_empty_simple ("audio/x-alac");
15987 _codec ("Free Lossless Audio Codec");
15988 caps = gst_caps_new_simple ("audio/x-flac",
15989 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15991 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15992 _codec ("QualComm PureVoice");
15993 caps = gst_caps_from_string ("audio/qcelp");
15998 caps = gst_caps_new_empty_simple ("audio/x-wma");
16002 caps = gst_caps_new_empty_simple ("audio/x-opus");
16009 GstAudioFormat format;
16012 FLAG_IS_FLOAT = 0x1,
16013 FLAG_IS_BIG_ENDIAN = 0x2,
16014 FLAG_IS_SIGNED = 0x4,
16015 FLAG_IS_PACKED = 0x8,
16016 FLAG_IS_ALIGNED_HIGH = 0x10,
16017 FLAG_IS_NON_INTERLEAVED = 0x20
16019 _codec ("Raw LPCM audio");
16021 if (data && len >= 36) {
16022 depth = QT_UINT32 (data + 24);
16023 flags = QT_UINT32 (data + 28);
16024 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
16026 if ((flags & FLAG_IS_FLOAT) == 0) {
16031 if ((flags & FLAG_IS_ALIGNED_HIGH))
16034 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
16035 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
16036 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
16037 caps = gst_caps_new_simple ("audio/x-raw",
16038 "format", G_TYPE_STRING,
16040 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
16041 "UNKNOWN", "layout", G_TYPE_STRING,
16042 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
16043 "interleaved", NULL);
16044 stream->alignment = GST_ROUND_UP_8 (depth);
16045 stream->alignment = round_up_pow2 (stream->alignment);
16050 if (flags & FLAG_IS_BIG_ENDIAN)
16051 format = GST_AUDIO_FORMAT_F64BE;
16053 format = GST_AUDIO_FORMAT_F64LE;
16055 if (flags & FLAG_IS_BIG_ENDIAN)
16056 format = GST_AUDIO_FORMAT_F32BE;
16058 format = GST_AUDIO_FORMAT_F32LE;
16060 caps = gst_caps_new_simple ("audio/x-raw",
16061 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
16062 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
16063 "non-interleaved" : "interleaved", NULL);
16064 stream->alignment = width / 8;
16068 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
16072 caps = _get_unknown_codec_name ("audio", fourcc);
16078 GstCaps *templ_caps =
16079 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
16080 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16081 gst_caps_unref (caps);
16082 gst_caps_unref (templ_caps);
16083 caps = intersection;
16086 /* enable clipping for raw audio streams */
16087 s = gst_caps_get_structure (caps, 0);
16088 name = gst_structure_get_name (s);
16089 if (g_str_has_prefix (name, "audio/x-raw")) {
16090 stream->need_clip = TRUE;
16091 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
16092 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
16098 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16099 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16100 const guint8 * stsd_entry_data, gchar ** codec_name)
16104 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16108 _codec ("DVD subtitle");
16109 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
16110 stream->need_process = TRUE;
16113 _codec ("Quicktime timed text");
16116 _codec ("3GPP timed text");
16118 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16120 /* actual text piece needs to be extracted */
16121 stream->need_process = TRUE;
16124 _codec ("XML subtitles");
16125 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16128 _codec ("CEA 608 Closed Caption");
16130 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16131 G_TYPE_STRING, "s334-1a", NULL);
16132 stream->need_process = TRUE;
16133 stream->need_split = TRUE;
16136 _codec ("CEA 708 Closed Caption");
16138 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16139 G_TYPE_STRING, "cdp", NULL);
16140 stream->need_process = TRUE;
16145 caps = _get_unknown_codec_name ("text", fourcc);
16153 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16154 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16155 const guint8 * stsd_entry_data, gchar ** codec_name)
16161 _codec ("MPEG 1 video");
16162 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16163 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16173 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
16174 const gchar * system_id)
16178 if (!qtdemux->protection_system_ids)
16179 qtdemux->protection_system_ids =
16180 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16181 /* Check whether we already have an entry for this system ID. */
16182 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16183 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16184 if (g_ascii_strcasecmp (system_id, id) == 0) {
16188 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16189 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,