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 /* Macros for converting to/from timescale */
126 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
127 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
129 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
130 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
132 /* timestamp is the DTS */
133 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
134 /* timestamp + offset + cslg_shift is the outgoing PTS */
135 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
136 /* timestamp + offset is the PTS used for internal seek calcuations */
137 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
138 /* timestamp + duration - dts is the duration */
139 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
141 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
143 #define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
144 #define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
145 GST_TRACE("Locking from thread %p", g_thread_self()); \
146 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
147 GST_TRACE("Locked from thread %p", g_thread_self()); \
150 #define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
151 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
152 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
156 * Quicktime has tracks and segments. A track is a continuous piece of
157 * multimedia content. The track is not always played from start to finish but
158 * instead, pieces of the track are 'cut out' and played in sequence. This is
159 * what the segments do.
161 * Inside the track we have keyframes (K) and delta frames. The track has its
162 * own timing, which starts from 0 and extends to end. The position in the track
163 * is called the media_time.
165 * The segments now describe the pieces that should be played from this track
166 * and are basically tuples of media_time/duration/rate entries. We can have
167 * multiple segments and they are all played after one another. An example:
169 * segment 1: media_time: 1 second, duration: 1 second, rate 1
170 * segment 2: media_time: 3 second, duration: 2 second, rate 2
172 * To correctly play back this track, one must play: 1 second of media starting
173 * from media_time 1 followed by 2 seconds of media starting from media_time 3
176 * Each of the segments will be played at a specific time, the first segment at
177 * time 0, the second one after the duration of the first one, etc.. Note that
178 * the time in resulting playback is not identical to the media_time of the
181 * Visually, assuming the track has 4 second of media_time:
184 * .-----------------------------------------------------------.
185 * track: | K.....K.........K........K.......K.......K...........K... |
186 * '-----------------------------------------------------------'
188 * .------------^ ^ .----------^ ^
189 * / .-------------' / .------------------'
191 * .--------------. .--------------.
192 * | segment 1 | | segment 2 |
193 * '--------------' '--------------'
195 * The challenge here is to cut out the right pieces of the track for each of
196 * the playback segments. This fortunately can easily be done with the SEGMENT
197 * events of GStreamer.
199 * For playback of segment 1, we need to provide the decoder with the keyframe
200 * (a), in the above figure, but we must instruct it only to output the decoded
201 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
202 * position set to the time of the segment: 0.
204 * We then proceed to push data from keyframe (a) to frame (b). The decoder
205 * decodes but clips all before media_time 1.
207 * After finishing a segment, we push out a new SEGMENT event with the clipping
208 * boundaries of the new data.
210 * This is a good usecase for the GStreamer accumulated SEGMENT events.
213 struct _QtDemuxSegment
215 /* global time and duration, all gst time */
217 GstClockTime stop_time;
218 GstClockTime duration;
219 /* media time of trak, all gst time */
220 GstClockTime media_start;
221 GstClockTime media_stop;
223 /* Media start time in trak timescale units */
224 guint32 trak_media_start;
227 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
229 /* Used with fragmented MP4 files (mfra atom) */
234 } QtDemuxRandomAccessEntry;
236 typedef struct _QtDemuxStreamStsdEntry
247 /* Numerator/denominator framerate */
250 GstVideoColorimetry colorimetry;
251 guint16 bits_per_sample;
252 guint16 color_table_id;
253 GstMemory *rgb8_palette;
254 guint interlace_mode;
260 guint samples_per_packet;
261 guint samples_per_frame;
262 guint bytes_per_packet;
263 guint bytes_per_sample;
264 guint bytes_per_frame;
267 /* if we use chunks or samples */
271 } QtDemuxStreamStsdEntry;
273 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
275 struct _QtDemuxStream
282 QtDemuxStreamStsdEntry *stsd_entries;
283 guint stsd_entries_length;
284 guint cur_stsd_entry_index;
289 gboolean new_caps; /* If TRUE, caps need to be generated (by
290 * calling _configure_stream()) This happens
291 * for MSS and fragmented streams */
293 gboolean new_stream; /* signals that a stream_start is required */
294 gboolean on_keyframe; /* if this stream last pushed buffer was a
295 * keyframe. This is important to identify
296 * where to stop pushing buffers after a
297 * segment stop time */
299 /* if the stream has a redirect URI in its headers, we store it here */
306 guint64 duration; /* in timescale units */
310 gchar lang_id[4]; /* ISO 639-2T language code */
314 QtDemuxSample *samples;
315 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
316 guint32 n_samples_moof; /* sample count in a moof */
317 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
318 * the framerate of fragmented format stream */
319 guint64 duration_last_moof;
321 guint32 offset_in_sample; /* Offset in the current sample, used for
322 * streams which have got exceedingly big
323 * sample size (such as 24s of raw audio).
324 * Only used when max_buffer_size is non-NULL */
325 guint32 max_buffer_size; /* Maximum allowed size for output buffers.
326 * Currently only set for raw audio streams*/
334 gboolean use_allocator;
335 GstAllocator *allocator;
336 GstAllocationParams params;
340 /* when a discontinuity is pending */
343 /* list of buffers to push first */
346 /* if we need to clip this buffer. This is only needed for uncompressed
350 /* buffer needs some custom processing, e.g. subtitles */
351 gboolean need_process;
352 /* buffer needs potentially be split, e.g. CEA608 subtitles */
355 /* current position */
356 guint32 segment_index;
357 guint32 sample_index;
358 GstClockTime time_position; /* in gst time */
359 guint64 accumulated_base;
361 /* the Gst segment we are processing out, used for clipping */
364 /* quicktime segments */
366 QtDemuxSegment *segments;
367 gboolean dummy_segment;
372 GstTagList *stream_tags;
373 gboolean send_global_tags;
375 GstEvent *pending_event;
385 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
389 GstByteReader co_chunk;
391 guint32 current_chunk;
393 guint32 samples_per_chunk;
394 guint32 stsd_sample_description_id;
395 guint32 stco_sample_index;
397 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
400 guint32 n_samples_per_chunk;
401 guint32 stsc_chunk_index;
402 guint32 stsc_sample_index;
403 guint64 chunk_offset;
406 guint32 stts_samples;
407 guint32 n_sample_times;
408 guint32 stts_sample_index;
410 guint32 stts_duration;
412 gboolean stss_present;
413 guint32 n_sample_syncs;
416 gboolean stps_present;
417 guint32 n_sample_partial_syncs;
419 QtDemuxRandomAccessEntry *ra_entries;
422 const QtDemuxRandomAccessEntry *pending_seek;
425 gboolean ctts_present;
426 guint32 n_composition_times;
428 guint32 ctts_sample_index;
436 gboolean parsed_trex;
437 guint32 def_sample_description_index; /* index is 1-based */
438 guint32 def_sample_duration;
439 guint32 def_sample_size;
440 guint32 def_sample_flags;
444 /* stereoscopic video streams */
445 GstVideoMultiviewMode multiview_mode;
446 GstVideoMultiviewFlags multiview_flags;
448 /* protected streams */
450 guint32 protection_scheme_type;
451 guint32 protection_scheme_version;
452 gpointer protection_scheme_info; /* specific to the protection scheme */
453 GQueue protection_scheme_event_queue;
455 gint ref_count; /* atomic */
458 /* Contains properties and cryptographic info for a set of samples from a
459 * track protected using Common Encryption (cenc) */
460 struct _QtDemuxCencSampleSetInfo
462 GstStructure *default_properties;
464 /* @crypto_info holds one GstStructure per sample */
465 GPtrArray *crypto_info;
469 qt_demux_state_string (enum QtDemuxState state)
472 case QTDEMUX_STATE_INITIAL:
474 case QTDEMUX_STATE_HEADER:
476 case QTDEMUX_STATE_MOVIE:
478 case QTDEMUX_STATE_BUFFER_MDAT:
479 return "<BUFFER_MDAT>";
485 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
486 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
487 guint32 fourcc, GstByteReader * parser);
488 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
489 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
490 guint32 fourcc, GstByteReader * parser);
492 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
494 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
496 static GstStaticPadTemplate gst_qtdemux_sink_template =
497 GST_STATIC_PAD_TEMPLATE ("sink",
500 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
504 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
505 GST_STATIC_PAD_TEMPLATE ("video_%u",
508 GST_STATIC_CAPS_ANY);
510 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
511 GST_STATIC_PAD_TEMPLATE ("audio_%u",
514 GST_STATIC_CAPS_ANY);
516 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
517 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
520 GST_STATIC_CAPS_ANY);
522 #define gst_qtdemux_parent_class parent_class
523 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
525 static void gst_qtdemux_dispose (GObject * object);
528 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
529 GstClockTime media_time);
531 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
532 QtDemuxStream * str, gint64 media_offset);
535 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
536 static GstIndex *gst_qtdemux_get_index (GstElement * element);
538 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
539 GstStateChange transition);
540 static void gst_qtdemux_set_context (GstElement * element,
541 GstContext * context);
542 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
543 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
544 GstObject * parent, GstPadMode mode, gboolean active);
546 static void gst_qtdemux_loop (GstPad * pad);
547 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
549 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
551 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
553 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
554 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
555 QtDemuxStream * stream);
556 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
557 QtDemuxStream * stream);
558 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
561 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
563 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
564 const guint8 * buffer, guint length);
565 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
566 const guint8 * buffer, guint length);
567 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
568 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
571 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
572 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
574 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
575 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
576 const guint8 * stsd_entry_data, gchar ** codec_name);
577 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
578 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
579 const guint8 * data, int len, gchar ** codec_name);
580 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
581 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
582 gchar ** codec_name);
583 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
584 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
585 const guint8 * stsd_entry_data, gchar ** codec_name);
587 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
588 QtDemuxStream * stream, guint32 n);
589 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
590 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
591 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
592 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
593 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
594 static void qtdemux_do_allocation (QtDemuxStream * stream,
595 GstQTDemux * qtdemux);
596 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
597 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
598 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
599 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
600 GstClockTime * _start, GstClockTime * _stop);
601 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
602 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
604 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
605 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
607 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
609 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
610 QtDemuxStream * stream, guint sample_index);
611 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
613 static void qtdemux_gst_structure_free (GstStructure * gststructure);
614 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
617 gst_qtdemux_class_init (GstQTDemuxClass * klass)
619 GObjectClass *gobject_class;
620 GstElementClass *gstelement_class;
622 gobject_class = (GObjectClass *) klass;
623 gstelement_class = (GstElementClass *) klass;
625 parent_class = g_type_class_peek_parent (klass);
627 gobject_class->dispose = gst_qtdemux_dispose;
629 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
631 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
632 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
634 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
636 gst_tag_register_musicbrainz_tags ();
638 gst_element_class_add_static_pad_template (gstelement_class,
639 &gst_qtdemux_sink_template);
640 gst_element_class_add_static_pad_template (gstelement_class,
641 &gst_qtdemux_videosrc_template);
642 gst_element_class_add_static_pad_template (gstelement_class,
643 &gst_qtdemux_audiosrc_template);
644 gst_element_class_add_static_pad_template (gstelement_class,
645 &gst_qtdemux_subsrc_template);
646 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
648 "Demultiplex a QuickTime file into audio and video streams",
649 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
651 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
656 gst_qtdemux_init (GstQTDemux * qtdemux)
659 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
660 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
661 gst_pad_set_activatemode_function (qtdemux->sinkpad,
662 qtdemux_sink_activate_mode);
663 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
664 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
665 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
666 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
668 qtdemux->adapter = gst_adapter_new ();
669 g_queue_init (&qtdemux->protection_event_queue);
670 qtdemux->flowcombiner = gst_flow_combiner_new ();
671 g_mutex_init (&qtdemux->expose_lock);
673 qtdemux->active_streams = g_ptr_array_new_with_free_func
674 ((GDestroyNotify) gst_qtdemux_stream_unref);
675 qtdemux->old_streams = g_ptr_array_new_with_free_func
676 ((GDestroyNotify) gst_qtdemux_stream_unref);
678 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
680 gst_qtdemux_reset (qtdemux, TRUE);
684 gst_qtdemux_dispose (GObject * object)
686 GstQTDemux *qtdemux = GST_QTDEMUX (object);
688 if (qtdemux->adapter) {
689 g_object_unref (G_OBJECT (qtdemux->adapter));
690 qtdemux->adapter = NULL;
692 gst_tag_list_unref (qtdemux->tag_list);
693 gst_flow_combiner_free (qtdemux->flowcombiner);
694 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
696 g_queue_clear (&qtdemux->protection_event_queue);
698 g_free (qtdemux->cenc_aux_info_sizes);
699 qtdemux->cenc_aux_info_sizes = NULL;
700 g_mutex_clear (&qtdemux->expose_lock);
702 g_ptr_array_free (qtdemux->active_streams, TRUE);
703 g_ptr_array_free (qtdemux->old_streams, TRUE);
705 G_OBJECT_CLASS (parent_class)->dispose (object);
709 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
711 if (qtdemux->posted_redirect) {
712 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
713 (_("This file contains no playable streams.")),
714 ("no known streams found, a redirect message has been posted"));
716 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
717 (_("This file contains no playable streams.")),
718 ("no known streams found"));
723 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
725 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
726 mem, size, 0, size, mem, free_func);
730 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
737 if (G_UNLIKELY (size == 0)) {
739 GstBuffer *tmp = NULL;
741 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
742 if (ret != GST_FLOW_OK)
745 gst_buffer_map (tmp, &map, GST_MAP_READ);
746 size = QT_UINT32 (map.data);
747 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
749 gst_buffer_unmap (tmp, &map);
750 gst_buffer_unref (tmp);
753 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
754 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
755 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
756 /* we're pulling header but already got most interesting bits,
757 * so never mind the rest (e.g. tags) (that much) */
758 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
762 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
763 (_("This file is invalid and cannot be played.")),
764 ("atom has bogus size %" G_GUINT64_FORMAT, size));
765 return GST_FLOW_ERROR;
769 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
771 if (G_UNLIKELY (flow != GST_FLOW_OK))
774 bsize = gst_buffer_get_size (*buf);
775 /* Catch short reads - we don't want any partial atoms */
776 if (G_UNLIKELY (bsize < size)) {
777 GST_WARNING_OBJECT (qtdemux,
778 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
779 gst_buffer_unref (*buf);
789 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
790 GstFormat src_format, gint64 src_value, GstFormat dest_format,
794 QtDemuxStream *stream = gst_pad_get_element_private (pad);
797 if (stream->subtype != FOURCC_vide) {
802 switch (src_format) {
803 case GST_FORMAT_TIME:
804 switch (dest_format) {
805 case GST_FORMAT_BYTES:{
806 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
812 *dest_value = stream->samples[index].offset;
814 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
815 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
816 GST_TIME_ARGS (src_value), *dest_value);
824 case GST_FORMAT_BYTES:
825 switch (dest_format) {
826 case GST_FORMAT_TIME:{
828 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
837 QTSTREAMTIME_TO_GSTTIME (stream,
838 stream->samples[index].timestamp);
839 GST_DEBUG_OBJECT (qtdemux,
840 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
841 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
860 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
862 gboolean res = FALSE;
864 *duration = GST_CLOCK_TIME_NONE;
866 if (qtdemux->duration != 0 &&
867 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
868 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
871 *duration = GST_CLOCK_TIME_NONE;
878 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
881 gboolean res = FALSE;
882 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
884 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
886 switch (GST_QUERY_TYPE (query)) {
887 case GST_QUERY_POSITION:{
890 gst_query_parse_position (query, &fmt, NULL);
891 if (fmt == GST_FORMAT_TIME
892 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
893 gst_query_set_position (query, GST_FORMAT_TIME,
894 qtdemux->segment.position);
899 case GST_QUERY_DURATION:{
902 gst_query_parse_duration (query, &fmt, NULL);
903 if (fmt == GST_FORMAT_TIME) {
904 /* First try to query upstream */
905 res = gst_pad_query_default (pad, parent, query);
907 GstClockTime duration;
908 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
909 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
916 case GST_QUERY_CONVERT:{
917 GstFormat src_fmt, dest_fmt;
918 gint64 src_value, dest_value = 0;
920 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
922 res = gst_qtdemux_src_convert (qtdemux, pad,
923 src_fmt, src_value, dest_fmt, &dest_value);
925 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
929 case GST_QUERY_FORMATS:
930 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
933 case GST_QUERY_SEEKING:{
937 /* try upstream first */
938 res = gst_pad_query_default (pad, parent, query);
941 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
942 if (fmt == GST_FORMAT_TIME) {
943 GstClockTime duration;
945 gst_qtdemux_get_duration (qtdemux, &duration);
947 if (!qtdemux->pullbased) {
950 /* we might be able with help from upstream */
952 q = gst_query_new_seeking (GST_FORMAT_BYTES);
953 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
954 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
955 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
959 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
965 case GST_QUERY_SEGMENT:
970 format = qtdemux->segment.format;
973 gst_segment_to_stream_time (&qtdemux->segment, format,
974 qtdemux->segment.start);
975 if ((stop = qtdemux->segment.stop) == -1)
976 stop = qtdemux->segment.duration;
978 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
980 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
985 res = gst_pad_query_default (pad, parent, query);
993 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
995 if (G_LIKELY (stream->pad)) {
996 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
997 GST_DEBUG_PAD_NAME (stream->pad));
999 if (!gst_tag_list_is_empty (stream->stream_tags)) {
1000 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
1001 stream->stream_tags);
1002 gst_pad_push_event (stream->pad,
1003 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
1006 if (G_UNLIKELY (stream->send_global_tags)) {
1007 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1009 gst_pad_push_event (stream->pad,
1010 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1011 stream->send_global_tags = FALSE;
1016 /* push event on all source pads; takes ownership of the event */
1018 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1020 gboolean has_valid_stream = FALSE;
1021 GstEventType etype = GST_EVENT_TYPE (event);
1024 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1025 GST_EVENT_TYPE_NAME (event));
1027 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1029 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1030 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
1032 if ((pad = stream->pad)) {
1033 has_valid_stream = TRUE;
1035 if (etype == GST_EVENT_EOS) {
1036 /* let's not send twice */
1037 if (stream->sent_eos)
1039 stream->sent_eos = TRUE;
1042 gst_pad_push_event (pad, gst_event_ref (event));
1046 gst_event_unref (event);
1048 /* if it is EOS and there are no pads, post an error */
1049 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1050 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1060 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1062 if ((gint64) s1->timestamp > *media_time)
1064 if ((gint64) s1->timestamp == *media_time)
1070 /* find the index of the sample that includes the data for @media_time using a
1071 * binary search. Only to be called in optimized cases of linear search below.
1073 * Returns the index of the sample with the corresponding *DTS*.
1076 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1079 QtDemuxSample *result;
1082 /* convert media_time to mov format */
1084 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1086 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1087 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1088 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1090 if (G_LIKELY (result))
1091 index = result - str->samples;
1100 /* find the index of the sample that includes the data for @media_offset using a
1103 * Returns the index of the sample.
1106 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1107 QtDemuxStream * str, gint64 media_offset)
1109 QtDemuxSample *result = str->samples;
1112 if (result == NULL || str->n_samples == 0)
1115 if (media_offset == result->offset)
1119 while (index < str->n_samples - 1) {
1120 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1123 if (media_offset < result->offset)
1134 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1139 /* find the index of the sample that includes the data for @media_time using a
1140 * linear search, and keeping in mind that not all samples may have been parsed
1141 * yet. If possible, it will delegate to binary search.
1143 * Returns the index of the sample.
1146 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1147 GstClockTime media_time)
1151 QtDemuxSample *sample;
1153 /* convert media_time to mov format */
1155 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1157 sample = str->samples;
1158 if (mov_time == sample->timestamp + sample->pts_offset)
1161 /* use faster search if requested time in already parsed range */
1162 sample = str->samples + str->stbl_index;
1163 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
1164 index = gst_qtdemux_find_index (qtdemux, str, media_time);
1165 sample = str->samples + index;
1167 while (index < str->n_samples - 1) {
1168 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1171 sample = str->samples + index + 1;
1172 if (mov_time < sample->timestamp) {
1173 sample = str->samples + index;
1181 /* sample->timestamp is now <= media_time, need to find the corresponding
1182 * PTS now by looking backwards */
1183 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
1185 sample = str->samples + index;
1193 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1198 /* find the index of the keyframe needed to decode the sample at @index
1199 * of stream @str, or of a subsequent keyframe (depending on @next)
1201 * Returns the index of the keyframe.
1204 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1205 guint32 index, gboolean next)
1207 guint32 new_index = index;
1209 if (index >= str->n_samples) {
1210 new_index = str->n_samples;
1214 /* all keyframes, return index */
1215 if (str->all_keyframe) {
1220 /* else search until we have a keyframe */
1221 while (new_index < str->n_samples) {
1222 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1225 if (str->samples[new_index].keyframe)
1237 if (new_index == str->n_samples) {
1238 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1243 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1244 "gave %u", next ? "after" : "before", index, new_index);
1251 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1256 /* find the segment for @time_position for @stream
1258 * Returns the index of the segment containing @time_position.
1259 * Returns the last segment and sets the @eos variable to TRUE
1260 * if the time is beyond the end. @eos may be NULL
1263 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1264 GstClockTime time_position)
1269 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1270 GST_TIME_ARGS (time_position));
1273 for (i = 0; i < stream->n_segments; i++) {
1274 QtDemuxSegment *segment = &stream->segments[i];
1276 GST_LOG_OBJECT (stream->pad,
1277 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1278 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1280 /* For the last segment we include stop_time in the last segment */
1281 if (i < stream->n_segments - 1) {
1282 if (segment->time <= time_position && time_position < segment->stop_time) {
1283 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1288 /* Last segment always matches */
1296 /* move the stream @str to the sample position @index.
1298 * Updates @str->sample_index and marks discontinuity if needed.
1301 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1304 /* no change needed */
1305 if (index == str->sample_index)
1308 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1311 /* position changed, we have a discont */
1312 str->sample_index = index;
1313 str->offset_in_sample = 0;
1314 /* Each time we move in the stream we store the position where we are
1316 str->from_sample = index;
1317 str->discont = TRUE;
1321 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1322 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1325 gint64 min_byte_offset = -1;
1328 min_offset = desired_time;
1330 /* for each stream, find the index of the sample in the segment
1331 * and move back to the previous keyframe. */
1332 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1334 guint32 index, kindex;
1336 GstClockTime media_start;
1337 GstClockTime media_time;
1338 GstClockTime seg_time;
1339 QtDemuxSegment *seg;
1340 gboolean empty_segment = FALSE;
1342 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1344 if (CUR_STREAM (str)->sparse && !use_sparse)
1347 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1348 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1350 /* get segment and time in the segment */
1351 seg = &str->segments[seg_idx];
1352 seg_time = (desired_time - seg->time) * seg->rate;
1354 while (QTSEGMENT_IS_EMPTY (seg)) {
1356 empty_segment = TRUE;
1357 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1360 if (seg_idx == str->n_segments)
1362 seg = &str->segments[seg_idx];
1365 if (seg_idx == str->n_segments) {
1366 /* FIXME track shouldn't have the last segment as empty, but if it
1367 * happens we better handle it */
1371 /* get the media time in the segment */
1372 media_start = seg->media_start + seg_time;
1374 /* get the index of the sample with media time */
1375 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1376 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1377 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1378 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1381 /* shift to next frame if we are looking for next keyframe */
1382 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1383 && index < str->stbl_index)
1386 if (!empty_segment) {
1387 /* find previous keyframe */
1388 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1390 /* we will settle for one before if none found after */
1391 if (next && kindex == -1)
1392 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1394 /* if the keyframe is at a different position, we need to update the
1395 * requested seek time */
1396 if (index != kindex) {
1399 /* get timestamp of keyframe */
1400 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1401 GST_DEBUG_OBJECT (qtdemux,
1402 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1403 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1404 str->samples[kindex].offset);
1406 /* keyframes in the segment get a chance to change the
1407 * desired_offset. keyframes out of the segment are
1409 if (media_time >= seg->media_start) {
1410 GstClockTime seg_time;
1412 /* this keyframe is inside the segment, convert back to
1414 seg_time = (media_time - seg->media_start) + seg->time;
1415 if ((!next && (seg_time < min_offset)) ||
1416 (next && (seg_time > min_offset)))
1417 min_offset = seg_time;
1422 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1423 min_byte_offset = str->samples[index].offset;
1427 *key_time = min_offset;
1429 *key_offset = min_byte_offset;
1433 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1434 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1438 g_return_val_if_fail (format != NULL, FALSE);
1439 g_return_val_if_fail (cur != NULL, FALSE);
1440 g_return_val_if_fail (stop != NULL, FALSE);
1442 if (*format == GST_FORMAT_TIME)
1446 if (cur_type != GST_SEEK_TYPE_NONE)
1447 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1448 if (res && stop_type != GST_SEEK_TYPE_NONE)
1449 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1452 *format = GST_FORMAT_TIME;
1457 /* perform seek in push based mode:
1458 find BYTE position to move to based on time and delegate to upstream
1461 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1466 GstSeekType cur_type, stop_type;
1467 gint64 cur, stop, key_cur;
1470 gint64 original_stop;
1473 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1475 gst_event_parse_seek (event, &rate, &format, &flags,
1476 &cur_type, &cur, &stop_type, &stop);
1477 seqnum = gst_event_get_seqnum (event);
1479 /* only forward streaming and seeking is possible */
1481 goto unsupported_seek;
1483 /* convert to TIME if needed and possible */
1484 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1488 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1489 * the original stop position to use when upstream pushes the new segment
1491 original_stop = stop;
1494 /* find reasonable corresponding BYTE position,
1495 * also try to mind about keyframes, since we can not go back a bit for them
1497 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1498 * mostly just work, but let's not yet boldly go there ... */
1499 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1504 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1505 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1508 GST_OBJECT_LOCK (qtdemux);
1509 qtdemux->seek_offset = byte_cur;
1510 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1511 qtdemux->push_seek_start = cur;
1513 qtdemux->push_seek_start = key_cur;
1516 if (stop_type == GST_SEEK_TYPE_NONE) {
1517 qtdemux->push_seek_stop = qtdemux->segment.stop;
1519 qtdemux->push_seek_stop = original_stop;
1521 GST_OBJECT_UNLOCK (qtdemux);
1523 qtdemux->segment_seqnum = seqnum;
1524 /* BYTE seek event */
1525 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1527 gst_event_set_seqnum (event, seqnum);
1528 res = gst_pad_push_event (qtdemux->sinkpad, event);
1535 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1541 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1546 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1551 /* perform the seek.
1553 * We set all segment_indexes in the streams to unknown and
1554 * adjust the time_position to the desired position. this is enough
1555 * to trigger a segment switch in the streaming thread to start
1556 * streaming from the desired position.
1558 * Keyframe seeking is a little more complicated when dealing with
1559 * segments. Ideally we want to move to the previous keyframe in
1560 * the segment but there might not be a keyframe in the segment. In
1561 * fact, none of the segments could contain a keyframe. We take a
1562 * practical approach: seek to the previous keyframe in the segment,
1563 * if there is none, seek to the beginning of the segment.
1565 * Called with STREAM_LOCK
1568 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1569 guint32 seqnum, GstSeekFlags flags)
1571 gint64 desired_offset;
1574 desired_offset = segment->position;
1576 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1577 GST_TIME_ARGS (desired_offset));
1579 /* may not have enough fragmented info to do this adjustment,
1580 * and we can't scan (and probably should not) at this time with
1581 * possibly flushing upstream */
1582 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1584 gboolean next, before, after;
1586 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1587 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1588 next = after && !before;
1589 if (segment->rate < 0)
1592 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1594 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1595 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1596 desired_offset = min_offset;
1599 /* and set all streams to the final position */
1600 gst_flow_combiner_reset (qtdemux->flowcombiner);
1601 qtdemux->segment_seqnum = seqnum;
1602 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1603 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1605 stream->time_position = desired_offset;
1606 stream->accumulated_base = 0;
1607 stream->sample_index = -1;
1608 stream->offset_in_sample = 0;
1609 stream->segment_index = -1;
1610 stream->sent_eos = FALSE;
1612 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1613 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1615 segment->position = desired_offset;
1616 segment->time = desired_offset;
1617 if (segment->rate >= 0) {
1618 segment->start = desired_offset;
1620 /* we stop at the end */
1621 if (segment->stop == -1)
1622 segment->stop = segment->duration;
1624 segment->stop = desired_offset;
1627 if (qtdemux->fragmented)
1628 qtdemux->fragmented_seek_pending = TRUE;
1633 /* do a seek in pull based mode */
1635 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1640 GstSeekType cur_type, stop_type;
1644 GstSegment seeksegment;
1645 guint32 seqnum = GST_SEQNUM_INVALID;
1646 GstEvent *flush_event;
1650 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1652 gst_event_parse_seek (event, &rate, &format, &flags,
1653 &cur_type, &cur, &stop_type, &stop);
1654 seqnum = gst_event_get_seqnum (event);
1656 /* we have to have a format as the segment format. Try to convert
1658 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1662 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1664 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1668 flush = flags & GST_SEEK_FLAG_FLUSH;
1670 /* stop streaming, either by flushing or by pausing the task */
1672 flush_event = gst_event_new_flush_start ();
1673 if (seqnum != GST_SEQNUM_INVALID)
1674 gst_event_set_seqnum (flush_event, seqnum);
1675 /* unlock upstream pull_range */
1676 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1677 /* make sure out loop function exits */
1678 gst_qtdemux_push_event (qtdemux, flush_event);
1680 /* non flushing seek, pause the task */
1681 gst_pad_pause_task (qtdemux->sinkpad);
1684 /* wait for streaming to finish */
1685 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1687 /* copy segment, we need this because we still need the old
1688 * segment when we close the current segment. */
1689 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1692 /* configure the segment with the seek variables */
1693 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1694 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1695 cur_type, cur, stop_type, stop, &update)) {
1697 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1699 /* now do the seek */
1700 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1703 /* now do the seek */
1704 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1707 /* prepare for streaming again */
1709 flush_event = gst_event_new_flush_stop (TRUE);
1710 if (seqnum != GST_SEQNUM_INVALID)
1711 gst_event_set_seqnum (flush_event, seqnum);
1713 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1714 gst_qtdemux_push_event (qtdemux, flush_event);
1717 /* commit the new segment */
1718 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1720 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1721 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1722 qtdemux->segment.format, qtdemux->segment.position);
1723 if (seqnum != GST_SEQNUM_INVALID)
1724 gst_message_set_seqnum (msg, seqnum);
1725 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1728 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1729 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1730 qtdemux->sinkpad, NULL);
1732 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1739 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1745 qtdemux_ensure_index (GstQTDemux * qtdemux)
1749 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1751 /* Build complete index */
1752 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1753 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1755 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1756 GST_LOG_OBJECT (qtdemux,
1757 "Building complete index of track-id %u for seeking failed!",
1767 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1770 gboolean res = TRUE;
1771 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1773 switch (GST_EVENT_TYPE (event)) {
1774 case GST_EVENT_SEEK:
1776 #ifndef GST_DISABLE_GST_DEBUG
1777 GstClockTime ts = gst_util_get_timestamp ();
1779 guint32 seqnum = gst_event_get_seqnum (event);
1781 qtdemux->received_seek = TRUE;
1783 if (seqnum == qtdemux->segment_seqnum) {
1784 GST_LOG_OBJECT (pad,
1785 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1786 gst_event_unref (event);
1790 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1791 /* seek should be handled by upstream, we might need to re-download fragments */
1792 GST_DEBUG_OBJECT (qtdemux,
1793 "let upstream handle seek for fragmented playback");
1797 /* Build complete index for seeking;
1798 * if not a fragmented file at least */
1799 if (!qtdemux->fragmented)
1800 if (!qtdemux_ensure_index (qtdemux))
1802 #ifndef GST_DISABLE_GST_DEBUG
1803 ts = gst_util_get_timestamp () - ts;
1804 GST_INFO_OBJECT (qtdemux,
1805 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1808 if (qtdemux->pullbased) {
1809 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1810 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1811 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1813 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1814 && QTDEMUX_N_STREAMS (qtdemux)
1815 && !qtdemux->fragmented) {
1816 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1818 GST_DEBUG_OBJECT (qtdemux,
1819 "ignoring seek in push mode in current state");
1822 gst_event_unref (event);
1826 res = gst_pad_event_default (pad, parent, event);
1836 GST_ERROR_OBJECT (qtdemux, "Index failed");
1837 gst_event_unref (event);
1843 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1845 * If @fw is false, the coding order is explored backwards.
1847 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1848 * sample is found for that track.
1850 * The stream and sample index of the sample with the minimum offset in the direction explored
1851 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1853 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1854 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1855 * @_stream and @_index. */
1857 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1858 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1861 gint64 time, min_time;
1862 QtDemuxStream *stream;
1869 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1872 gboolean set_sample;
1874 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1881 i = str->n_samples - 1;
1885 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1886 if (str->samples[i].size == 0)
1889 if (fw && (str->samples[i].offset < byte_pos))
1892 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1895 /* move stream to first available sample */
1897 gst_qtdemux_move_stream (qtdemux, str, i);
1901 /* avoid index from sparse streams since they might be far away */
1902 if (!CUR_STREAM (str)->sparse) {
1903 /* determine min/max time */
1904 time = QTSAMPLE_PTS (str, &str->samples[i]);
1905 if (min_time == -1 || (!fw && time > min_time) ||
1906 (fw && time < min_time)) {
1910 /* determine stream with leading sample, to get its position */
1912 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1913 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1921 /* no sample for this stream, mark eos */
1923 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1934 /* Copied from mpegtsbase code */
1935 /* FIXME: replace this function when we add new util function for stream-id creation */
1937 _get_upstream_id (GstQTDemux * demux)
1939 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1942 /* Try to create one from the upstream URI, else use a randome number */
1946 /* Try to generate one from the URI query and
1947 * if it fails take a random number instead */
1948 query = gst_query_new_uri ();
1949 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1950 gst_query_parse_uri (query, &uri);
1956 /* And then generate an SHA256 sum of the URI */
1957 cs = g_checksum_new (G_CHECKSUM_SHA256);
1958 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1960 upstream_id = g_strdup (g_checksum_get_string (cs));
1961 g_checksum_free (cs);
1963 /* Just get some random number if the URI query fails */
1964 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1965 "implementing a deterministic way of creating a stream-id");
1967 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1968 g_random_int (), g_random_int ());
1971 gst_query_unref (query);
1976 static QtDemuxStream *
1977 _create_stream (GstQTDemux * demux, guint32 track_id)
1979 QtDemuxStream *stream;
1982 stream = g_new0 (QtDemuxStream, 1);
1983 stream->demux = demux;
1984 stream->track_id = track_id;
1985 upstream_id = _get_upstream_id (demux);
1986 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1987 g_free (upstream_id);
1988 /* new streams always need a discont */
1989 stream->discont = TRUE;
1990 /* we enable clipping for raw audio/video streams */
1991 stream->need_clip = FALSE;
1992 stream->need_process = FALSE;
1993 stream->segment_index = -1;
1994 stream->time_position = 0;
1995 stream->sample_index = -1;
1996 stream->offset_in_sample = 0;
1997 stream->new_stream = TRUE;
1998 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1999 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2000 stream->protected = FALSE;
2001 stream->protection_scheme_type = 0;
2002 stream->protection_scheme_version = 0;
2003 stream->protection_scheme_info = NULL;
2004 stream->n_samples_moof = 0;
2005 stream->duration_moof = 0;
2006 stream->duration_last_moof = 0;
2007 stream->alignment = 1;
2008 stream->stream_tags = gst_tag_list_new_empty ();
2009 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2010 g_queue_init (&stream->protection_scheme_event_queue);
2011 stream->ref_count = 1;
2012 /* consistent default for push based mode */
2013 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
2018 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
2020 GstStructure *structure;
2021 const gchar *variant;
2022 const GstCaps *mediacaps = NULL;
2024 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
2026 structure = gst_caps_get_structure (caps, 0);
2027 variant = gst_structure_get_string (structure, "variant");
2029 if (variant && strcmp (variant, "mss-fragmented") == 0) {
2030 QtDemuxStream *stream;
2031 const GValue *value;
2033 demux->fragmented = TRUE;
2034 demux->mss_mode = TRUE;
2036 if (QTDEMUX_N_STREAMS (demux) > 1) {
2037 /* can't do this, we can only renegotiate for another mss format */
2041 value = gst_structure_get_value (structure, "media-caps");
2044 const GValue *timescale_v;
2046 /* TODO update when stream changes during playback */
2048 if (QTDEMUX_N_STREAMS (demux) == 0) {
2049 stream = _create_stream (demux, 1);
2050 g_ptr_array_add (demux->active_streams, stream);
2051 /* mss has no stsd/stsd entry, use id 0 as default */
2052 stream->stsd_entries_length = 1;
2053 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2054 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2056 stream = QTDEMUX_NTH_STREAM (demux, 0);
2059 timescale_v = gst_structure_get_value (structure, "timescale");
2061 stream->timescale = g_value_get_uint64 (timescale_v);
2063 /* default mss timescale */
2064 stream->timescale = 10000000;
2066 demux->timescale = stream->timescale;
2068 mediacaps = gst_value_get_caps (value);
2069 if (!CUR_STREAM (stream)->caps
2070 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2071 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2073 stream->new_caps = TRUE;
2075 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2076 structure = gst_caps_get_structure (mediacaps, 0);
2077 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2078 stream->subtype = FOURCC_vide;
2080 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2081 gst_structure_get_int (structure, "height",
2082 &CUR_STREAM (stream)->height);
2083 gst_structure_get_fraction (structure, "framerate",
2084 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2085 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2087 stream->subtype = FOURCC_soun;
2088 gst_structure_get_int (structure, "channels",
2089 &CUR_STREAM (stream)->n_channels);
2090 gst_structure_get_int (structure, "rate", &rate);
2091 CUR_STREAM (stream)->rate = rate;
2094 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2096 demux->mss_mode = FALSE;
2103 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2107 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2108 gst_pad_stop_task (qtdemux->sinkpad);
2110 if (hard || qtdemux->upstream_format_is_time) {
2111 qtdemux->state = QTDEMUX_STATE_INITIAL;
2112 qtdemux->neededbytes = 16;
2113 qtdemux->todrop = 0;
2114 qtdemux->pullbased = FALSE;
2115 qtdemux->posted_redirect = FALSE;
2116 qtdemux->first_mdat = -1;
2117 qtdemux->header_size = 0;
2118 qtdemux->mdatoffset = -1;
2119 qtdemux->restoredata_offset = -1;
2120 if (qtdemux->mdatbuffer)
2121 gst_buffer_unref (qtdemux->mdatbuffer);
2122 if (qtdemux->restoredata_buffer)
2123 gst_buffer_unref (qtdemux->restoredata_buffer);
2124 qtdemux->mdatbuffer = NULL;
2125 qtdemux->restoredata_buffer = NULL;
2126 qtdemux->mdatleft = 0;
2127 qtdemux->mdatsize = 0;
2128 if (qtdemux->comp_brands)
2129 gst_buffer_unref (qtdemux->comp_brands);
2130 qtdemux->comp_brands = NULL;
2131 qtdemux->last_moov_offset = -1;
2132 if (qtdemux->moov_node_compressed) {
2133 g_node_destroy (qtdemux->moov_node_compressed);
2134 if (qtdemux->moov_node)
2135 g_free (qtdemux->moov_node->data);
2137 qtdemux->moov_node_compressed = NULL;
2138 if (qtdemux->moov_node)
2139 g_node_destroy (qtdemux->moov_node);
2140 qtdemux->moov_node = NULL;
2141 if (qtdemux->tag_list)
2142 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2143 qtdemux->tag_list = gst_tag_list_new_empty ();
2144 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2146 if (qtdemux->element_index)
2147 gst_object_unref (qtdemux->element_index);
2148 qtdemux->element_index = NULL;
2150 qtdemux->major_brand = 0;
2151 qtdemux->upstream_format_is_time = FALSE;
2152 qtdemux->upstream_seekable = FALSE;
2153 qtdemux->upstream_size = 0;
2155 qtdemux->fragment_start = -1;
2156 qtdemux->fragment_start_offset = -1;
2157 qtdemux->duration = 0;
2158 qtdemux->moof_offset = 0;
2159 qtdemux->chapters_track_id = 0;
2160 qtdemux->have_group_id = FALSE;
2161 qtdemux->group_id = G_MAXUINT;
2163 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2165 g_queue_clear (&qtdemux->protection_event_queue);
2167 qtdemux->received_seek = FALSE;
2168 qtdemux->first_moof_already_parsed = FALSE;
2170 qtdemux->offset = 0;
2171 gst_adapter_clear (qtdemux->adapter);
2172 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2173 qtdemux->need_segment = TRUE;
2176 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2177 g_ptr_array_set_size (qtdemux->active_streams, 0);
2178 g_ptr_array_set_size (qtdemux->old_streams, 0);
2179 qtdemux->n_video_streams = 0;
2180 qtdemux->n_audio_streams = 0;
2181 qtdemux->n_sub_streams = 0;
2182 qtdemux->exposed = FALSE;
2183 qtdemux->fragmented = FALSE;
2184 qtdemux->mss_mode = FALSE;
2185 gst_caps_replace (&qtdemux->media_caps, NULL);
2186 qtdemux->timescale = 0;
2187 qtdemux->got_moov = FALSE;
2188 qtdemux->cenc_aux_info_offset = 0;
2189 qtdemux->cenc_aux_info_sizes = NULL;
2190 qtdemux->cenc_aux_sample_count = 0;
2191 if (qtdemux->protection_system_ids) {
2192 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2193 qtdemux->protection_system_ids = NULL;
2195 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2196 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2197 GST_BIN_FLAG_STREAMS_AWARE);
2199 if (qtdemux->preferred_protection_system_id) {
2200 g_free (qtdemux->preferred_protection_system_id);
2201 qtdemux->preferred_protection_system_id = NULL;
2203 } else if (qtdemux->mss_mode) {
2204 gst_flow_combiner_reset (qtdemux->flowcombiner);
2205 g_ptr_array_foreach (qtdemux->active_streams,
2206 (GFunc) gst_qtdemux_stream_clear, NULL);
2208 gst_flow_combiner_reset (qtdemux->flowcombiner);
2209 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2210 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2211 stream->sent_eos = FALSE;
2212 stream->time_position = 0;
2213 stream->accumulated_base = 0;
2219 /* Maps the @segment to the qt edts internal segments and pushes
2220 * the correspnding segment event.
2222 * If it ends up being at a empty segment, a gap will be pushed and the next
2223 * edts segment will be activated in sequence.
2225 * To be used in push-mode only */
2227 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2231 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2232 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2234 stream->time_position = segment->start;
2236 /* in push mode we should be guaranteed that we will have empty segments
2237 * at the beginning and then one segment after, other scenarios are not
2238 * supported and are discarded when parsing the edts */
2239 for (i = 0; i < stream->n_segments; i++) {
2240 if (stream->segments[i].stop_time > segment->start) {
2241 /* push the empty segment and move to the next one */
2242 gst_qtdemux_activate_segment (qtdemux, stream, i,
2243 stream->time_position);
2244 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2245 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2246 stream->time_position);
2248 /* accumulate previous segments */
2249 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2250 stream->accumulated_base +=
2251 (stream->segment.stop -
2252 stream->segment.start) / ABS (stream->segment.rate);
2256 g_assert (i == stream->n_segments - 1);
2263 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2274 for (i = 0; i < len; i++) {
2275 QtDemuxStream *stream = g_ptr_array_index (src, i);
2277 #ifndef GST_DISABLE_GST_DEBUG
2278 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2279 stream, GST_STR_NULL (stream->stream_id), dest);
2281 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2284 g_ptr_array_set_size (src, 0);
2288 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2291 GstQTDemux *demux = GST_QTDEMUX (parent);
2292 gboolean res = TRUE;
2294 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2296 switch (GST_EVENT_TYPE (event)) {
2297 case GST_EVENT_SEGMENT:
2300 QtDemuxStream *stream;
2304 /* some debug output */
2305 gst_event_copy_segment (event, &segment);
2306 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2309 if (segment.format == GST_FORMAT_TIME) {
2310 demux->upstream_format_is_time = TRUE;
2311 demux->segment_seqnum = gst_event_get_seqnum (event);
2313 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2314 "not in time format");
2316 /* chain will send initial newsegment after pads have been added */
2317 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2318 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2323 /* check if this matches a time seek we received previously
2324 * FIXME for backwards compatibility reasons we use the
2325 * seek_offset here to compare. In the future we might want to
2326 * change this to use the seqnum as it uniquely should identify
2327 * the segment that corresponds to the seek. */
2328 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2329 ", received segment offset %" G_GINT64_FORMAT,
2330 demux->seek_offset, segment.start);
2331 if (segment.format == GST_FORMAT_BYTES
2332 && demux->seek_offset == segment.start) {
2333 GST_OBJECT_LOCK (demux);
2334 offset = segment.start;
2336 segment.format = GST_FORMAT_TIME;
2337 segment.start = demux->push_seek_start;
2338 segment.stop = demux->push_seek_stop;
2339 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2340 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2341 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2342 GST_OBJECT_UNLOCK (demux);
2345 /* we only expect a BYTE segment, e.g. following a seek */
2346 if (segment.format == GST_FORMAT_BYTES) {
2347 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2348 offset = segment.start;
2350 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2351 NULL, (gint64 *) & segment.start);
2352 if ((gint64) segment.start < 0)
2355 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2356 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2357 NULL, (gint64 *) & segment.stop);
2358 /* keyframe seeking should already arrange for start >= stop,
2359 * but make sure in other rare cases */
2360 segment.stop = MAX (segment.stop, segment.start);
2362 } else if (segment.format == GST_FORMAT_TIME) {
2363 /* push all data on the adapter before starting this
2365 gst_qtdemux_process_adapter (demux, TRUE);
2367 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2371 /* We shouldn't modify upstream driven TIME FORMAT segment */
2372 if (!demux->upstream_format_is_time) {
2373 /* accept upstream's notion of segment and distribute along */
2374 segment.format = GST_FORMAT_TIME;
2375 segment.position = segment.time = segment.start;
2376 segment.duration = demux->segment.duration;
2377 segment.base = gst_segment_to_running_time (&demux->segment,
2378 GST_FORMAT_TIME, demux->segment.position);
2381 gst_segment_copy_into (&segment, &demux->segment);
2382 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2384 /* map segment to internal qt segments and push on each stream */
2385 if (QTDEMUX_N_STREAMS (demux)) {
2386 demux->need_segment = TRUE;
2387 gst_qtdemux_check_send_pending_segment (demux);
2390 /* clear leftover in current segment, if any */
2391 gst_adapter_clear (demux->adapter);
2393 /* set up streaming thread */
2394 demux->offset = offset;
2395 if (demux->upstream_format_is_time) {
2396 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2397 "set values to restart reading from a new atom");
2398 demux->neededbytes = 16;
2401 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2404 demux->todrop = stream->samples[idx].offset - offset;
2405 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2407 /* set up for EOS */
2408 demux->neededbytes = -1;
2413 gst_event_unref (event);
2417 case GST_EVENT_FLUSH_START:
2419 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2420 gst_event_unref (event);
2423 QTDEMUX_EXPOSE_LOCK (demux);
2424 res = gst_pad_event_default (demux->sinkpad, parent, event);
2425 QTDEMUX_EXPOSE_UNLOCK (demux);
2428 case GST_EVENT_FLUSH_STOP:
2432 dur = demux->segment.duration;
2433 gst_qtdemux_reset (demux, FALSE);
2434 demux->segment.duration = dur;
2436 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2437 gst_event_unref (event);
2443 /* If we are in push mode, and get an EOS before we've seen any streams,
2444 * then error out - we have nowhere to send the EOS */
2445 if (!demux->pullbased) {
2447 gboolean has_valid_stream = FALSE;
2448 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2449 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2450 has_valid_stream = TRUE;
2454 if (!has_valid_stream)
2455 gst_qtdemux_post_no_playable_stream_error (demux);
2457 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2458 (guint) gst_adapter_available (demux->adapter));
2459 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2465 case GST_EVENT_CAPS:{
2466 GstCaps *caps = NULL;
2468 gst_event_parse_caps (event, &caps);
2469 gst_qtdemux_setcaps (demux, caps);
2471 gst_event_unref (event);
2474 case GST_EVENT_PROTECTION:
2476 const gchar *system_id = NULL;
2478 gst_event_parse_protection (event, &system_id, NULL, NULL);
2479 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2481 gst_qtdemux_append_protection_system_id (demux, system_id);
2482 /* save the event for later, for source pads that have not been created */
2483 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2484 /* send it to all pads that already exist */
2485 gst_qtdemux_push_event (demux, event);
2489 case GST_EVENT_STREAM_START:
2492 gst_event_unref (event);
2494 /* Drain all the buffers */
2495 gst_qtdemux_process_adapter (demux, TRUE);
2496 gst_qtdemux_reset (demux, FALSE);
2497 /* We expect new moov box after new stream-start event */
2498 if (demux->exposed) {
2499 gst_qtdemux_stream_concat (demux,
2500 demux->old_streams, demux->active_streams);
2509 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2516 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2519 GstQTDemux *demux = GST_QTDEMUX (parent);
2520 gboolean res = FALSE;
2522 switch (GST_QUERY_TYPE (query)) {
2523 case GST_QUERY_BITRATE:
2525 GstClockTime duration;
2527 /* populate demux->upstream_size if not done yet */
2528 gst_qtdemux_check_seekability (demux);
2530 if (demux->upstream_size != -1
2531 && gst_qtdemux_get_duration (demux, &duration)) {
2533 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2536 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2537 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2538 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2540 /* TODO: better results based on ranges/index tables */
2541 gst_query_set_bitrate (query, bitrate);
2547 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2557 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2559 GstQTDemux *demux = GST_QTDEMUX (element);
2561 GST_OBJECT_LOCK (demux);
2562 if (demux->element_index)
2563 gst_object_unref (demux->element_index);
2565 demux->element_index = gst_object_ref (index);
2567 demux->element_index = NULL;
2569 GST_OBJECT_UNLOCK (demux);
2570 /* object lock might be taken again */
2572 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2573 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2574 demux->element_index, demux->index_id);
2578 gst_qtdemux_get_index (GstElement * element)
2580 GstIndex *result = NULL;
2581 GstQTDemux *demux = GST_QTDEMUX (element);
2583 GST_OBJECT_LOCK (demux);
2584 if (demux->element_index)
2585 result = gst_object_ref (demux->element_index);
2586 GST_OBJECT_UNLOCK (demux);
2588 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2595 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2597 g_free ((gpointer) stream->stco.data);
2598 stream->stco.data = NULL;
2599 g_free ((gpointer) stream->stsz.data);
2600 stream->stsz.data = NULL;
2601 g_free ((gpointer) stream->stsc.data);
2602 stream->stsc.data = NULL;
2603 g_free ((gpointer) stream->stts.data);
2604 stream->stts.data = NULL;
2605 g_free ((gpointer) stream->stss.data);
2606 stream->stss.data = NULL;
2607 g_free ((gpointer) stream->stps.data);
2608 stream->stps.data = NULL;
2609 g_free ((gpointer) stream->ctts.data);
2610 stream->ctts.data = NULL;
2614 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2616 g_free (stream->segments);
2617 stream->segments = NULL;
2618 stream->segment_index = -1;
2619 stream->accumulated_base = 0;
2623 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2625 g_free (stream->samples);
2626 stream->samples = NULL;
2627 gst_qtdemux_stbl_free (stream);
2630 g_free (stream->ra_entries);
2631 stream->ra_entries = NULL;
2632 stream->n_ra_entries = 0;
2634 stream->sample_index = -1;
2635 stream->stbl_index = -1;
2636 stream->n_samples = 0;
2637 stream->time_position = 0;
2639 stream->n_samples_moof = 0;
2640 stream->duration_moof = 0;
2641 stream->duration_last_moof = 0;
2645 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2648 if (stream->allocator)
2649 gst_object_unref (stream->allocator);
2650 while (stream->buffers) {
2651 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2652 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2654 for (i = 0; i < stream->stsd_entries_length; i++) {
2655 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2656 if (entry->rgb8_palette) {
2657 gst_memory_unref (entry->rgb8_palette);
2658 entry->rgb8_palette = NULL;
2660 entry->sparse = FALSE;
2663 if (stream->stream_tags)
2664 gst_tag_list_unref (stream->stream_tags);
2666 stream->stream_tags = gst_tag_list_new_empty ();
2667 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2668 g_free (stream->redirect_uri);
2669 stream->redirect_uri = NULL;
2670 stream->sent_eos = FALSE;
2671 stream->protected = FALSE;
2672 if (stream->protection_scheme_info) {
2673 if (stream->protection_scheme_type == FOURCC_cenc) {
2674 QtDemuxCencSampleSetInfo *info =
2675 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2676 if (info->default_properties)
2677 gst_structure_free (info->default_properties);
2678 if (info->crypto_info)
2679 g_ptr_array_free (info->crypto_info, TRUE);
2681 g_free (stream->protection_scheme_info);
2682 stream->protection_scheme_info = NULL;
2684 stream->protection_scheme_type = 0;
2685 stream->protection_scheme_version = 0;
2686 g_queue_foreach (&stream->protection_scheme_event_queue,
2687 (GFunc) gst_event_unref, NULL);
2688 g_queue_clear (&stream->protection_scheme_event_queue);
2689 gst_qtdemux_stream_flush_segments_data (stream);
2690 gst_qtdemux_stream_flush_samples_data (stream);
2694 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2697 gst_qtdemux_stream_clear (stream);
2698 for (i = 0; i < stream->stsd_entries_length; i++) {
2699 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2701 gst_caps_unref (entry->caps);
2705 g_free (stream->stsd_entries);
2706 stream->stsd_entries = NULL;
2707 stream->stsd_entries_length = 0;
2710 static QtDemuxStream *
2711 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2713 g_atomic_int_add (&stream->ref_count, 1);
2719 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2721 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2722 gst_qtdemux_stream_reset (stream);
2723 gst_tag_list_unref (stream->stream_tags);
2725 GstQTDemux *demux = stream->demux;
2726 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2727 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2729 g_free (stream->stream_id);
2734 static GstStateChangeReturn
2735 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2737 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2738 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2740 switch (transition) {
2741 case GST_STATE_CHANGE_READY_TO_PAUSED:
2742 gst_qtdemux_reset (qtdemux, TRUE);
2748 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2750 switch (transition) {
2751 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2752 gst_qtdemux_reset (qtdemux, TRUE);
2763 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2765 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2767 g_return_if_fail (GST_IS_CONTEXT (context));
2769 if (gst_context_has_context_type (context,
2770 "drm-preferred-decryption-system-id")) {
2771 const GstStructure *s;
2773 s = gst_context_get_structure (context);
2774 g_free (qtdemux->preferred_protection_system_id);
2775 qtdemux->preferred_protection_system_id =
2776 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2777 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2778 qtdemux->preferred_protection_system_id);
2781 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2785 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2787 /* counts as header data */
2788 qtdemux->header_size += length;
2790 /* only consider at least a sufficiently complete ftyp atom */
2794 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2795 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2796 GST_FOURCC_ARGS (qtdemux->major_brand));
2797 if (qtdemux->comp_brands)
2798 gst_buffer_unref (qtdemux->comp_brands);
2799 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2800 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2805 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2806 GstTagList * xmptaglist)
2808 /* Strip out bogus fields */
2810 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2811 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2812 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2814 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2817 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2819 /* prioritize native tags using _KEEP mode */
2820 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2821 gst_tag_list_unref (xmptaglist);
2826 qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
2827 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
2830 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2831 gst_buffer_fill (kid_buf, 0, kid, 16);
2832 if (info->default_properties)
2833 gst_structure_free (info->default_properties);
2834 info->default_properties =
2835 gst_structure_new ("application/x-cenc",
2836 "iv_size", G_TYPE_UINT, iv_size,
2837 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2838 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2839 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
2840 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
2841 gst_buffer_unref (kid_buf);
2845 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
2846 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
2848 guint32 algorithm_id = 0;
2850 gboolean is_encrypted = TRUE;
2853 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2854 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2859 if (algorithm_id == 0) {
2860 is_encrypted = FALSE;
2861 } else if (algorithm_id == 1) {
2862 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2863 } else if (algorithm_id == 2) {
2864 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2867 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2870 if (!gst_byte_reader_get_data (br, 16, &kid))
2873 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
2874 is_encrypted, iv_size, kid);
2875 gst_structure_set (info->default_properties, "piff_algorithm_id",
2876 G_TYPE_UINT, algorithm_id, NULL);
2882 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
2890 QtDemuxStream *stream;
2891 GstStructure *structure;
2892 QtDemuxCencSampleSetInfo *ss_info = NULL;
2893 const gchar *system_id;
2894 gboolean uses_sub_sample_encryption = FALSE;
2895 guint32 sample_count;
2897 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
2900 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
2902 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
2903 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2904 GST_WARNING_OBJECT (qtdemux,
2905 "Attempting PIFF box parsing on an unencrypted stream.");
2909 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2910 G_TYPE_STRING, &system_id, NULL);
2911 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
2913 stream->protected = TRUE;
2914 stream->protection_scheme_type = FOURCC_cenc;
2916 if (!stream->protection_scheme_info)
2917 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
2919 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2920 if (!ss_info->default_properties) {
2921 ss_info->default_properties =
2922 gst_structure_new ("application/x-cenc",
2923 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2928 if (ss_info->crypto_info) {
2929 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2930 g_ptr_array_free (ss_info->crypto_info, TRUE);
2931 ss_info->crypto_info = NULL;
2935 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2937 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2938 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2942 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2943 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2947 if ((flags & 0x000001)) {
2948 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
2951 } else if ((flags & 0x000002)) {
2952 uses_sub_sample_encryption = TRUE;
2955 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2957 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2961 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2962 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2966 ss_info->crypto_info =
2967 g_ptr_array_new_full (sample_count,
2968 (GDestroyNotify) qtdemux_gst_structure_free);
2970 for (i = 0; i < sample_count; ++i) {
2971 GstStructure *properties;
2975 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
2976 if (properties == NULL) {
2977 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2978 qtdemux->cenc_aux_sample_count = i;
2982 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2983 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2984 gst_structure_free (properties);
2985 qtdemux->cenc_aux_sample_count = i;
2988 buf = gst_buffer_new_wrapped (data, iv_size);
2989 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2990 gst_buffer_unref (buf);
2992 if (uses_sub_sample_encryption) {
2993 guint16 n_subsamples;
2994 const GValue *kid_buf_value;
2996 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2997 || n_subsamples == 0) {
2998 GST_ERROR_OBJECT (qtdemux,
2999 "failed to get subsample count for sample %u", i);
3000 gst_structure_free (properties);
3001 qtdemux->cenc_aux_sample_count = i;
3004 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3005 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3006 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3008 gst_structure_free (properties);
3009 qtdemux->cenc_aux_sample_count = i;
3012 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3015 gst_structure_get_value (ss_info->default_properties, "kid");
3017 gst_structure_set (properties,
3018 "subsample_count", G_TYPE_UINT, n_subsamples,
3019 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3020 gst_structure_set_value (properties, "kid", kid_buf_value);
3021 gst_buffer_unref (buf);
3023 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3026 g_ptr_array_add (ss_info->crypto_info, properties);
3029 qtdemux->cenc_aux_sample_count = sample_count;
3033 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3035 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3036 0x97, 0xA9, 0x42, 0xE8,
3037 0x9C, 0x71, 0x99, 0x94,
3038 0x91, 0xE3, 0xAF, 0xAC
3040 static const guint8 playready_uuid[] = {
3041 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3042 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3045 static const guint8 piff_sample_encryption_uuid[] = {
3046 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3047 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3052 /* counts as header data */
3053 qtdemux->header_size += length;
3055 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3057 if (length <= offset + 16) {
3058 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3062 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3064 GstTagList *taglist;
3066 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3067 length - offset - 16, NULL);
3068 taglist = gst_tag_list_from_xmp_buffer (buf);
3069 gst_buffer_unref (buf);
3071 /* make sure we have a usable taglist */
3072 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3074 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3076 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3078 const gunichar2 *s_utf16;
3081 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3082 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3083 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3084 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3088 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3089 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3091 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3092 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3094 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3095 GST_READ_UINT32_LE (buffer + offset),
3096 GST_READ_UINT32_LE (buffer + offset + 4),
3097 GST_READ_UINT32_LE (buffer + offset + 8),
3098 GST_READ_UINT32_LE (buffer + offset + 12));
3103 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3105 GstSidxParser sidx_parser;
3106 GstIsoffParserResult res;
3109 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3112 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3114 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3115 if (res == GST_ISOFF_QT_PARSER_DONE) {
3116 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3118 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3121 /* caller verifies at least 8 bytes in buf */
3123 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3124 guint64 * plength, guint32 * pfourcc)
3129 length = QT_UINT32 (data);
3130 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3131 fourcc = QT_FOURCC (data + 4);
3132 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3135 length = G_MAXUINT64;
3136 } else if (length == 1 && size >= 16) {
3137 /* this means we have an extended size, which is the 64 bit value of
3138 * the next 8 bytes */
3139 length = QT_UINT64 (data + 8);
3140 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3150 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3152 guint32 version = 0;
3153 GstClockTime duration = 0;
3155 if (!gst_byte_reader_get_uint32_be (br, &version))
3160 if (!gst_byte_reader_get_uint64_be (br, &duration))
3165 if (!gst_byte_reader_get_uint32_be (br, &dur))
3170 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3171 qtdemux->duration = duration;
3177 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3183 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3184 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3186 if (!stream->parsed_trex && qtdemux->moov_node) {
3188 GstByteReader trex_data;
3190 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3192 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3195 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3197 /* skip version/flags */
3198 if (!gst_byte_reader_skip (&trex_data, 4))
3200 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3202 if (id != stream->track_id)
3204 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3206 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3208 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3210 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3213 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3214 "duration %d, size %d, flags 0x%x", stream->track_id,
3217 stream->parsed_trex = TRUE;
3218 stream->def_sample_description_index = sdi;
3219 stream->def_sample_duration = dur;
3220 stream->def_sample_size = size;
3221 stream->def_sample_flags = flags;
3224 /* iterate all siblings */
3225 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3231 *ds_duration = stream->def_sample_duration;
3232 *ds_size = stream->def_sample_size;
3233 *ds_flags = stream->def_sample_flags;
3235 /* even then, above values are better than random ... */
3236 if (G_UNLIKELY (!stream->parsed_trex)) {
3237 GST_WARNING_OBJECT (qtdemux,
3238 "failed to find fragment defaults for stream %d", stream->track_id);
3245 /* This method should be called whenever a more accurate duration might
3246 * have been found. It will update all relevant variables if/where needed
3249 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3253 GstClockTime prevdur;
3255 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3257 if (movdur > qtdemux->duration) {
3258 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3259 GST_DEBUG_OBJECT (qtdemux,
3260 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3261 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3262 qtdemux->duration = movdur;
3263 GST_DEBUG_OBJECT (qtdemux,
3264 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3265 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3266 GST_TIME_ARGS (qtdemux->segment.stop));
3267 if (qtdemux->segment.duration == prevdur) {
3268 /* If the current segment has duration/stop identical to previous duration
3269 * update them also (because they were set at that point in time with
3270 * the wrong duration */
3271 /* We convert the value *from* the timescale version to avoid rounding errors */
3272 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3273 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3274 qtdemux->segment.duration = fixeddur;
3275 qtdemux->segment.stop = fixeddur;
3279 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3280 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3282 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3283 if (movdur > stream->duration) {
3284 GST_DEBUG_OBJECT (qtdemux,
3285 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3286 GST_TIME_ARGS (duration));
3287 stream->duration = movdur;
3288 /* internal duration tracking state has been updated above, so */
3289 /* preserve an open-ended dummy segment rather than repeatedly updating
3290 * it and spamming downstream accordingly with segment events */
3291 if (stream->dummy_segment &&
3292 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3293 /* Update all dummy values to new duration */
3294 stream->segments[0].stop_time = duration;
3295 stream->segments[0].duration = duration;
3296 stream->segments[0].media_stop = duration;
3298 /* let downstream know we possibly have a new stop time */
3299 if (stream->segment_index != -1) {
3302 if (qtdemux->segment.rate >= 0) {
3303 pos = stream->segment.start;
3305 pos = stream->segment.stop;
3308 gst_qtdemux_stream_update_segment (qtdemux, stream,
3309 stream->segment_index, pos, NULL, NULL);
3317 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3318 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3319 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3320 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3323 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3325 gint32 data_offset = 0;
3326 guint32 flags = 0, first_flags = 0, samples_count = 0;
3329 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3330 QtDemuxSample *sample;
3331 gboolean ismv = FALSE;
3332 gint64 initial_offset;
3334 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3335 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3336 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3337 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3339 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3340 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3344 /* presence of stss or not can't really tell us much,
3345 * and flags and so on tend to be marginally reliable in these files */
3346 if (stream->subtype == FOURCC_soun) {
3347 GST_DEBUG_OBJECT (qtdemux,
3348 "sound track in fragmented file; marking all keyframes");
3349 stream->all_keyframe = TRUE;
3352 if (!gst_byte_reader_skip (trun, 1) ||
3353 !gst_byte_reader_get_uint24_be (trun, &flags))
3356 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3359 if (flags & TR_DATA_OFFSET) {
3360 /* note this is really signed */
3361 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3363 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3364 /* default base offset = first byte of moof */
3365 if (*base_offset == -1) {
3366 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3367 *base_offset = moof_offset;
3369 *running_offset = *base_offset + data_offset;
3371 /* if no offset at all, that would mean data starts at moof start,
3372 * which is a bit wrong and is ismv crappy way, so compensate
3373 * assuming data is in mdat following moof */
3374 if (*base_offset == -1) {
3375 *base_offset = moof_offset + moof_length + 8;
3376 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3379 if (*running_offset == -1)
3380 *running_offset = *base_offset;
3383 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3385 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3386 data_offset, flags, samples_count);
3388 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3389 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3390 GST_DEBUG_OBJECT (qtdemux,
3391 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3392 flags ^= TR_FIRST_SAMPLE_FLAGS;
3394 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3396 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3400 /* FIXME ? spec says other bits should also be checked to determine
3401 * entry size (and prefix size for that matter) */
3403 dur_offset = size_offset = 0;
3404 if (flags & TR_SAMPLE_DURATION) {
3405 GST_LOG_OBJECT (qtdemux, "entry duration present");
3406 dur_offset = entry_size;
3409 if (flags & TR_SAMPLE_SIZE) {
3410 GST_LOG_OBJECT (qtdemux, "entry size present");
3411 size_offset = entry_size;
3414 if (flags & TR_SAMPLE_FLAGS) {
3415 GST_LOG_OBJECT (qtdemux, "entry flags present");
3416 flags_offset = entry_size;
3419 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3420 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3421 ct_offset = entry_size;
3425 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3427 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3429 if (stream->n_samples + samples_count >=
3430 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3433 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3434 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3435 (stream->n_samples + samples_count) *
3436 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3438 /* create a new array of samples if it's the first sample parsed */
3439 if (stream->n_samples == 0) {
3440 g_assert (stream->samples == NULL);
3441 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3442 /* or try to reallocate it with space enough to insert the new samples */
3444 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3445 stream->n_samples + samples_count);
3446 if (stream->samples == NULL)
3449 if (qtdemux->fragment_start != -1) {
3450 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3451 qtdemux->fragment_start = -1;
3453 if (stream->n_samples == 0) {
3454 if (decode_ts > 0) {
3455 timestamp = decode_ts;
3456 } else if (stream->pending_seek != NULL) {
3457 /* if we don't have a timestamp from a tfdt box, we'll use the one
3458 * from the mfra seek table */
3459 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3460 GST_TIME_ARGS (stream->pending_seek->ts));
3462 /* FIXME: this is not fully correct, the timestamp refers to the random
3463 * access sample refered to in the tfra entry, which may not necessarily
3464 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3465 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3470 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3471 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3472 GST_TIME_ARGS (gst_ts));
3474 /* subsequent fragments extend stream */
3476 stream->samples[stream->n_samples - 1].timestamp +
3477 stream->samples[stream->n_samples - 1].duration;
3479 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3480 * difference (1 sec.) between decode_ts and timestamp, prefer the
3482 if (has_tfdt && !qtdemux->upstream_format_is_time
3483 && ABSDIFF (decode_ts, timestamp) >
3484 MAX (stream->duration_last_moof / 2,
3485 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3486 GST_INFO_OBJECT (qtdemux,
3487 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3488 ") are significantly different (more than %" GST_TIME_FORMAT
3489 "), using decode_ts",
3490 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3491 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3492 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3493 MAX (stream->duration_last_moof / 2,
3494 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3495 timestamp = decode_ts;
3498 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3499 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3500 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3504 initial_offset = *running_offset;
3506 sample = stream->samples + stream->n_samples;
3507 for (i = 0; i < samples_count; i++) {
3508 guint32 dur, size, sflags, ct;
3510 /* first read sample data */
3511 if (flags & TR_SAMPLE_DURATION) {
3512 dur = QT_UINT32 (data + dur_offset);
3514 dur = d_sample_duration;
3516 if (flags & TR_SAMPLE_SIZE) {
3517 size = QT_UINT32 (data + size_offset);
3519 size = d_sample_size;
3521 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3523 sflags = first_flags;
3525 sflags = d_sample_flags;
3527 } else if (flags & TR_SAMPLE_FLAGS) {
3528 sflags = QT_UINT32 (data + flags_offset);
3530 sflags = d_sample_flags;
3532 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3533 ct = QT_UINT32 (data + ct_offset);
3539 /* fill the sample information */
3540 sample->offset = *running_offset;
3541 sample->pts_offset = ct;
3542 sample->size = size;
3543 sample->timestamp = timestamp;
3544 sample->duration = dur;
3545 /* sample-is-difference-sample */
3546 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3547 * now idea how it relates to bitfield other than massive LE/BE confusion */
3548 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3549 *running_offset += size;
3551 stream->duration_moof += dur;
3555 /* Update total duration if needed */
3556 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3558 /* Pre-emptively figure out size of mdat based on trun information.
3559 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3560 * size, else we will still be able to use this when dealing with gap'ed
3562 qtdemux->mdatleft = *running_offset - initial_offset;
3563 qtdemux->mdatoffset = initial_offset;
3564 qtdemux->mdatsize = qtdemux->mdatleft;
3566 stream->n_samples += samples_count;
3567 stream->n_samples_moof += samples_count;
3569 if (stream->pending_seek != NULL)
3570 stream->pending_seek = NULL;
3576 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3581 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3587 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3588 "be larger than %uMB (broken file?)", stream->n_samples,
3589 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3594 /* find stream with @id */
3595 static inline QtDemuxStream *
3596 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3598 QtDemuxStream *stream;
3602 if (G_UNLIKELY (!id)) {
3603 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3607 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3608 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3609 if (stream->track_id == id)
3612 if (qtdemux->mss_mode) {
3613 /* mss should have only 1 stream anyway */
3614 return QTDEMUX_NTH_STREAM (qtdemux, 0);
3621 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3622 guint32 * fragment_number)
3624 if (!gst_byte_reader_skip (mfhd, 4))
3626 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3631 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3637 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3638 QtDemuxStream ** stream, guint32 * default_sample_duration,
3639 guint32 * default_sample_size, guint32 * default_sample_flags,
3640 gint64 * base_offset)
3643 guint32 track_id = 0;
3645 if (!gst_byte_reader_skip (tfhd, 1) ||
3646 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3649 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3652 *stream = qtdemux_find_stream (qtdemux, track_id);
3653 if (G_UNLIKELY (!*stream))
3654 goto unknown_stream;
3656 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3657 *base_offset = qtdemux->moof_offset;
3659 if (flags & TF_BASE_DATA_OFFSET)
3660 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3663 /* obtain stream defaults */
3664 qtdemux_parse_trex (qtdemux, *stream,
3665 default_sample_duration, default_sample_size, default_sample_flags);
3667 (*stream)->stsd_sample_description_id =
3668 (*stream)->def_sample_description_index - 1;
3670 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
3671 guint32 sample_description_index;
3672 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3674 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3677 if (qtdemux->mss_mode) {
3678 /* mss has no stsd entry */
3679 (*stream)->stsd_sample_description_id = 0;
3682 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3683 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3686 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3687 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3690 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3691 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3698 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3703 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
3709 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3710 guint64 * decode_time)
3712 guint32 version = 0;
3714 if (!gst_byte_reader_get_uint32_be (br, &version))
3719 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3722 guint32 dec_time = 0;
3723 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3725 *decode_time = dec_time;
3728 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3735 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3740 /* Returns a pointer to a GstStructure containing the properties of
3741 * the stream sample identified by @sample_index. The caller must unref
3742 * the returned object after use. Returns NULL if unsuccessful. */
3743 static GstStructure *
3744 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3745 QtDemuxStream * stream, guint sample_index)
3747 QtDemuxCencSampleSetInfo *info = NULL;
3749 g_return_val_if_fail (stream != NULL, NULL);
3750 g_return_val_if_fail (stream->protected, NULL);
3751 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3753 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3755 /* Currently, cenc properties for groups of samples are not supported, so
3756 * simply return a copy of the default sample properties */
3757 return gst_structure_copy (info->default_properties);
3760 /* Parses the sizes of sample auxiliary information contained within a stream,
3761 * as given in a saiz box. Returns array of sample_count guint8 size values,
3762 * or NULL on failure */
3764 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3765 GstByteReader * br, guint32 * sample_count)
3769 guint8 default_info_size;
3771 g_return_val_if_fail (qtdemux != NULL, NULL);
3772 g_return_val_if_fail (stream != NULL, NULL);
3773 g_return_val_if_fail (br != NULL, NULL);
3774 g_return_val_if_fail (sample_count != NULL, NULL);
3776 if (!gst_byte_reader_get_uint32_be (br, &flags))
3780 /* aux_info_type and aux_info_type_parameter are ignored */
3781 if (!gst_byte_reader_skip (br, 8))
3785 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3787 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3789 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3791 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3794 if (default_info_size == 0) {
3795 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3799 info_sizes = g_new (guint8, *sample_count);
3800 memset (info_sizes, default_info_size, *sample_count);
3806 /* Parses the offset of sample auxiliary information contained within a stream,
3807 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3809 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3810 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3815 guint32 aux_info_type = 0;
3816 guint32 aux_info_type_parameter = 0;
3817 guint32 entry_count;
3820 const guint8 *aux_info_type_data = NULL;
3822 g_return_val_if_fail (qtdemux != NULL, FALSE);
3823 g_return_val_if_fail (stream != NULL, FALSE);
3824 g_return_val_if_fail (br != NULL, FALSE);
3825 g_return_val_if_fail (offset != NULL, FALSE);
3827 if (!gst_byte_reader_get_uint8 (br, &version))
3830 if (!gst_byte_reader_get_uint24_be (br, &flags))
3835 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3837 aux_info_type = QT_FOURCC (aux_info_type_data);
3839 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3841 } else if (stream->protected) {
3842 aux_info_type = stream->protection_scheme_type;
3844 aux_info_type = CUR_STREAM (stream)->fourcc;
3848 *info_type = aux_info_type;
3849 if (info_type_parameter)
3850 *info_type_parameter = aux_info_type_parameter;
3852 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3853 "aux_info_type_parameter: %#06x",
3854 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3856 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3859 if (entry_count != 1) {
3860 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3865 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3867 *offset = (guint64) off_32;
3869 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3874 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3879 qtdemux_gst_structure_free (GstStructure * gststructure)
3882 gst_structure_free (gststructure);
3886 /* Parses auxiliary information relating to samples protected using Common
3887 * Encryption (cenc); the format of this information is defined in
3888 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
3890 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3891 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3893 QtDemuxCencSampleSetInfo *ss_info = NULL;
3896 GPtrArray *old_crypto_info = NULL;
3897 guint old_entries = 0;
3899 g_return_val_if_fail (qtdemux != NULL, FALSE);
3900 g_return_val_if_fail (stream != NULL, FALSE);
3901 g_return_val_if_fail (br != NULL, FALSE);
3902 g_return_val_if_fail (stream->protected, FALSE);
3903 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3905 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3907 if (ss_info->crypto_info) {
3908 old_crypto_info = ss_info->crypto_info;
3909 /* Count number of non-null entries remaining at the tail end */
3910 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3911 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3917 ss_info->crypto_info =
3918 g_ptr_array_new_full (sample_count + old_entries,
3919 (GDestroyNotify) qtdemux_gst_structure_free);
3921 /* We preserve old entries because we parse the next moof in advance
3922 * of consuming all samples from the previous moof, and otherwise
3923 * we'd discard the corresponding crypto info for the samples
3924 * from the previous fragment. */
3926 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3928 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3929 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3931 g_ptr_array_index (old_crypto_info, i) = NULL;
3935 if (old_crypto_info) {
3936 /* Everything now belongs to the new array */
3937 g_ptr_array_free (old_crypto_info, TRUE);
3940 for (i = 0; i < sample_count; ++i) {
3941 GstStructure *properties;
3942 guint16 n_subsamples = 0;
3947 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3948 if (properties == NULL) {
3949 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3952 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3953 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3954 gst_structure_free (properties);
3957 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
3958 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3959 gst_structure_free (properties);
3962 buf = gst_buffer_new_wrapped (data, iv_size);
3963 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3964 gst_buffer_unref (buf);
3965 size = info_sizes[i];
3966 if (size > iv_size) {
3967 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3968 || !(n_subsamples > 0)) {
3969 gst_structure_free (properties);
3970 GST_ERROR_OBJECT (qtdemux,
3971 "failed to get subsample count for sample %u", i);
3974 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3975 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3976 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3978 gst_structure_free (properties);
3981 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3983 gst_structure_free (properties);
3986 gst_structure_set (properties,
3987 "subsample_count", G_TYPE_UINT, n_subsamples,
3988 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3989 gst_buffer_unref (buf);
3991 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3993 g_ptr_array_add (ss_info->crypto_info, properties);
3998 /* Converts a UUID in raw byte form to a string representation, as defined in
3999 * RFC 4122. The caller takes ownership of the returned string and is
4000 * responsible for freeing it after use. */
4002 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4004 const guint8 *uuid = (const guint8 *) uuid_bytes;
4006 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4007 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4008 uuid[0], uuid[1], uuid[2], uuid[3],
4009 uuid[4], uuid[5], uuid[6], uuid[7],
4010 uuid[8], uuid[9], uuid[10], uuid[11],
4011 uuid[12], uuid[13], uuid[14], uuid[15]);
4014 /* Parses a Protection System Specific Header box (pssh), as defined in the
4015 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4016 * information needed by a specific content protection system in order to
4017 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4020 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4022 gchar *sysid_string;
4023 guint32 pssh_size = QT_UINT32 (node->data);
4024 GstBuffer *pssh = NULL;
4025 GstEvent *event = NULL;
4026 guint32 parent_box_type;
4029 if (G_UNLIKELY (pssh_size < 32U)) {
4030 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4035 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4037 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4039 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4040 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4041 gst_buffer_get_size (pssh));
4043 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4045 /* Push an event containing the pssh box onto the queues of all streams. */
4046 event = gst_event_new_protection (sysid_string, pssh,
4047 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4048 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4049 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4050 GST_TRACE_OBJECT (qtdemux,
4051 "adding protection event for stream %s and system %s",
4052 stream->stream_id, sysid_string);
4053 g_queue_push_tail (&stream->protection_scheme_event_queue,
4054 gst_event_ref (event));
4056 g_free (sysid_string);
4057 gst_event_unref (event);
4058 gst_buffer_unref (pssh);
4063 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4064 guint64 moof_offset, QtDemuxStream * stream)
4066 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4068 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4069 GNode *saiz_node, *saio_node, *pssh_node;
4070 GstByteReader saiz_data, saio_data;
4071 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4072 gint64 base_offset, running_offset;
4074 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4076 /* NOTE @stream ignored */
4078 moof_node = g_node_new ((guint8 *) buffer);
4079 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4080 qtdemux_node_dump (qtdemux, moof_node);
4082 /* Get fragment number from mfhd and check it's valid */
4084 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4085 if (mfhd_node == NULL)
4087 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4089 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4091 /* unknown base_offset to start with */
4092 base_offset = running_offset = -1;
4093 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4095 guint64 decode_time = 0;
4097 /* Fragment Header node */
4099 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4103 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4104 &ds_size, &ds_flags, &base_offset))
4107 /* The following code assumes at most a single set of sample auxiliary
4108 * data in the fragment (consisting of a saiz box and a corresponding saio
4109 * box); in theory, however, there could be multiple sets of sample
4110 * auxiliary data in a fragment. */
4112 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4115 guint32 info_type = 0;
4117 guint32 info_type_parameter = 0;
4119 g_free (qtdemux->cenc_aux_info_sizes);
4121 qtdemux->cenc_aux_info_sizes =
4122 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4123 &qtdemux->cenc_aux_sample_count);
4124 if (qtdemux->cenc_aux_info_sizes == NULL) {
4125 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4129 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4132 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4133 g_free (qtdemux->cenc_aux_info_sizes);
4134 qtdemux->cenc_aux_info_sizes = NULL;
4138 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4139 &info_type, &info_type_parameter, &offset))) {
4140 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4141 g_free (qtdemux->cenc_aux_info_sizes);
4142 qtdemux->cenc_aux_info_sizes = NULL;
4145 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4146 offset += (guint64) (base_offset - qtdemux->moof_offset);
4147 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4149 if (offset > length) {
4150 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4151 qtdemux->cenc_aux_info_offset = offset;
4153 gst_byte_reader_init (&br, buffer + offset, length - offset);
4154 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4155 qtdemux->cenc_aux_info_sizes,
4156 qtdemux->cenc_aux_sample_count)) {
4157 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4158 g_free (qtdemux->cenc_aux_info_sizes);
4159 qtdemux->cenc_aux_info_sizes = NULL;
4167 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4170 /* We'll use decode_time to interpolate timestamps
4171 * in case the input timestamps are missing */
4172 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4174 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4175 " (%" GST_TIME_FORMAT ")", decode_time,
4176 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4177 decode_time) : GST_CLOCK_TIME_NONE));
4179 /* Discard the fragment buffer timestamp info to avoid using it.
4180 * Rely on tfdt instead as it is more accurate than the timestamp
4181 * that is fetched from a manifest/playlist and is usually
4183 qtdemux->fragment_start = -1;
4186 if (G_UNLIKELY (!stream)) {
4187 /* we lost track of offset, we'll need to regain it,
4188 * but can delay complaining until later or avoid doing so altogether */
4192 if (G_UNLIKELY (base_offset < -1))
4195 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4197 if (!qtdemux->pullbased) {
4198 /* Sample tables can grow enough to be problematic if the system memory
4199 * is very low (e.g. embedded devices) and the videos very long
4200 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4201 * Fortunately, we can easily discard them for each new fragment when
4202 * we know qtdemux will not receive seeks outside of the current fragment.
4203 * adaptivedemux honors this assumption.
4204 * This optimization is also useful for applications that use qtdemux as
4205 * a push-based simple demuxer, like Media Source Extensions. */
4206 gst_qtdemux_stream_flush_samples_data (stream);
4209 /* initialise moof sample data */
4210 stream->n_samples_moof = 0;
4211 stream->duration_last_moof = stream->duration_moof;
4212 stream->duration_moof = 0;
4214 /* Track Run node */
4216 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4219 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4220 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4221 &running_offset, decode_time, (tfdt_node != NULL));
4222 /* iterate all siblings */
4223 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4227 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4229 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4230 guint32 box_length = QT_UINT32 (uuid_buffer);
4232 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4235 /* if no new base_offset provided for next traf,
4236 * base is end of current traf */
4237 base_offset = running_offset;
4238 running_offset = -1;
4240 if (stream->n_samples_moof && stream->duration_moof)
4241 stream->new_caps = TRUE;
4244 /* iterate all siblings */
4245 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4248 /* parse any protection system info */
4249 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4251 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4252 qtdemux_parse_pssh (qtdemux, pssh_node);
4253 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4256 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4257 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4259 /* Unless the user has explictly requested another seek, perform an
4260 * internal seek to the time specified in the tfdt.
4262 * This way if the user opens a file where the first tfdt is 1 hour
4263 * into the presentation, they will not have to wait 1 hour for run
4264 * time to catch up and actual playback to start. */
4267 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4268 "performing an internal seek to %" GST_TIME_FORMAT,
4269 GST_TIME_ARGS (min_dts));
4271 qtdemux->segment.start = min_dts;
4272 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4274 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4275 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4276 stream->time_position = min_dts;
4279 /* Before this code was run a segment was already sent when the moov was
4280 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4281 * be emitted after a moov, and we can emit a second segment anyway for
4282 * special cases like this. */
4283 qtdemux->need_segment = TRUE;
4286 qtdemux->first_moof_already_parsed = TRUE;
4288 g_node_destroy (moof_node);
4293 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4298 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4303 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4308 g_node_destroy (moof_node);
4309 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4310 (_("This file is corrupt and cannot be played.")), (NULL));
4316 /* might be used if some day we actually use mfra & co
4317 * for random access to fragments,
4318 * but that will require quite some modifications and much less relying
4319 * on a sample array */
4323 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4325 QtDemuxStream *stream;
4326 guint32 ver_flags, track_id, len, num_entries, i;
4327 guint value_size, traf_size, trun_size, sample_size;
4328 guint64 time = 0, moof_offset = 0;
4330 GstBuffer *buf = NULL;
4335 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4337 if (!gst_byte_reader_skip (&tfra, 8))
4340 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4343 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4344 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4345 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4348 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4350 stream = qtdemux_find_stream (qtdemux, track_id);
4352 goto unknown_trackid;
4354 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4355 sample_size = (len & 3) + 1;
4356 trun_size = ((len & 12) >> 2) + 1;
4357 traf_size = ((len & 48) >> 4) + 1;
4359 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4360 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4362 if (num_entries == 0)
4365 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4366 value_size + value_size + traf_size + trun_size + sample_size))
4369 g_free (stream->ra_entries);
4370 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4371 stream->n_ra_entries = num_entries;
4373 for (i = 0; i < num_entries; i++) {
4374 qt_atom_parser_get_offset (&tfra, value_size, &time);
4375 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4376 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4377 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4378 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4380 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4382 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4383 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4385 stream->ra_entries[i].ts = time;
4386 stream->ra_entries[i].moof_offset = moof_offset;
4388 /* don't want to go through the entire file and read all moofs at startup */
4390 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4391 if (ret != GST_FLOW_OK)
4393 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4394 moof_offset, stream);
4395 gst_buffer_unref (buf);
4399 check_update_duration (qtdemux, time);
4406 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4411 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4416 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4422 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4424 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4425 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4426 GstBuffer *mfro = NULL, *mfra = NULL;
4428 gboolean ret = FALSE;
4429 GNode *mfra_node, *tfra_node;
4430 guint64 mfra_offset = 0;
4431 guint32 fourcc, mfra_size;
4434 /* query upstream size in bytes */
4435 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4436 goto size_query_failed;
4438 /* mfro box should be at the very end of the file */
4439 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4440 if (flow != GST_FLOW_OK)
4443 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4445 fourcc = QT_FOURCC (mfro_map.data + 4);
4446 if (fourcc != FOURCC_mfro)
4449 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4450 if (mfro_map.size < 16)
4451 goto invalid_mfro_size;
4453 mfra_size = QT_UINT32 (mfro_map.data + 12);
4454 if (mfra_size >= len)
4455 goto invalid_mfra_size;
4457 mfra_offset = len - mfra_size;
4459 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4460 mfra_offset, mfra_size);
4462 /* now get and parse mfra box */
4463 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4464 if (flow != GST_FLOW_OK)
4467 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4469 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4470 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4472 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4475 qtdemux_parse_tfra (qtdemux, tfra_node);
4476 /* iterate all siblings */
4477 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4479 g_node_destroy (mfra_node);
4481 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4487 if (mfro_map.memory != NULL)
4488 gst_buffer_unmap (mfro, &mfro_map);
4489 gst_buffer_unref (mfro);
4492 if (mfra_map.memory != NULL)
4493 gst_buffer_unmap (mfra, &mfra_map);
4494 gst_buffer_unref (mfra);
4501 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4506 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4511 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4516 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4522 add_offset (guint64 offset, guint64 advance)
4524 /* Avoid 64-bit overflow by clamping */
4525 if (offset > G_MAXUINT64 - advance)
4527 return offset + advance;
4530 static GstFlowReturn
4531 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4535 GstBuffer *buf = NULL;
4536 GstFlowReturn ret = GST_FLOW_OK;
4537 guint64 cur_offset = qtdemux->offset;
4540 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4541 if (G_UNLIKELY (ret != GST_FLOW_OK))
4543 gst_buffer_map (buf, &map, GST_MAP_READ);
4544 if (G_LIKELY (map.size >= 8))
4545 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4546 gst_buffer_unmap (buf, &map);
4547 gst_buffer_unref (buf);
4549 /* maybe we already got most we needed, so only consider this eof */
4550 if (G_UNLIKELY (length == 0)) {
4551 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4552 (_("Invalid atom size.")),
4553 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4554 GST_FOURCC_ARGS (fourcc)));
4561 /* record for later parsing when needed */
4562 if (!qtdemux->moof_offset) {
4563 qtdemux->moof_offset = qtdemux->offset;
4565 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4568 qtdemux->offset += length; /* skip moof and keep going */
4570 if (qtdemux->got_moov) {
4571 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4583 GST_LOG_OBJECT (qtdemux,
4584 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4585 GST_FOURCC_ARGS (fourcc), cur_offset);
4586 qtdemux->offset = add_offset (qtdemux->offset, length);
4591 GstBuffer *moov = NULL;
4593 if (qtdemux->got_moov) {
4594 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4595 qtdemux->offset = add_offset (qtdemux->offset, length);
4599 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4600 if (ret != GST_FLOW_OK)
4602 gst_buffer_map (moov, &map, GST_MAP_READ);
4604 if (length != map.size) {
4605 /* Some files have a 'moov' atom at the end of the file which contains
4606 * a terminal 'free' atom where the body of the atom is missing.
4607 * Check for, and permit, this special case.
4609 if (map.size >= 8) {
4610 guint8 *final_data = map.data + (map.size - 8);
4611 guint32 final_length = QT_UINT32 (final_data);
4612 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4614 if (final_fourcc == FOURCC_free
4615 && map.size + final_length - 8 == length) {
4616 /* Ok, we've found that special case. Allocate a new buffer with
4617 * that free atom actually present. */
4618 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4619 gst_buffer_fill (newmoov, 0, map.data, map.size);
4620 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4621 gst_buffer_unmap (moov, &map);
4622 gst_buffer_unref (moov);
4624 gst_buffer_map (moov, &map, GST_MAP_READ);
4629 if (length != map.size) {
4630 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4631 (_("This file is incomplete and cannot be played.")),
4632 ("We got less than expected (received %" G_GSIZE_FORMAT
4633 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4634 (guint) length, cur_offset));
4635 gst_buffer_unmap (moov, &map);
4636 gst_buffer_unref (moov);
4637 ret = GST_FLOW_ERROR;
4640 qtdemux->offset += length;
4642 qtdemux_parse_moov (qtdemux, map.data, length);
4643 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4645 qtdemux_parse_tree (qtdemux);
4646 if (qtdemux->moov_node_compressed) {
4647 g_node_destroy (qtdemux->moov_node_compressed);
4648 g_free (qtdemux->moov_node->data);
4650 qtdemux->moov_node_compressed = NULL;
4651 g_node_destroy (qtdemux->moov_node);
4652 qtdemux->moov_node = NULL;
4653 gst_buffer_unmap (moov, &map);
4654 gst_buffer_unref (moov);
4655 qtdemux->got_moov = TRUE;
4661 GstBuffer *ftyp = NULL;
4663 /* extract major brand; might come in handy for ISO vs QT issues */
4664 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4665 if (ret != GST_FLOW_OK)
4667 qtdemux->offset += length;
4668 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4669 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4670 gst_buffer_unmap (ftyp, &map);
4671 gst_buffer_unref (ftyp);
4676 GstBuffer *uuid = NULL;
4678 /* uuid are extension atoms */
4679 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4680 if (ret != GST_FLOW_OK)
4682 qtdemux->offset += length;
4683 gst_buffer_map (uuid, &map, GST_MAP_READ);
4684 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4685 gst_buffer_unmap (uuid, &map);
4686 gst_buffer_unref (uuid);
4691 GstBuffer *sidx = NULL;
4692 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4693 if (ret != GST_FLOW_OK)
4695 qtdemux->offset += length;
4696 gst_buffer_map (sidx, &map, GST_MAP_READ);
4697 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4698 gst_buffer_unmap (sidx, &map);
4699 gst_buffer_unref (sidx);
4704 GstBuffer *unknown = NULL;
4706 GST_LOG_OBJECT (qtdemux,
4707 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4708 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4710 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4711 if (ret != GST_FLOW_OK)
4713 gst_buffer_map (unknown, &map, GST_MAP_READ);
4714 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4715 gst_buffer_unmap (unknown, &map);
4716 gst_buffer_unref (unknown);
4717 qtdemux->offset += length;
4723 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4724 /* digested all data, show what we have */
4725 qtdemux_prepare_streams (qtdemux);
4726 QTDEMUX_EXPOSE_LOCK (qtdemux);
4727 ret = qtdemux_expose_streams (qtdemux);
4728 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
4730 qtdemux->state = QTDEMUX_STATE_MOVIE;
4731 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4738 /* Seeks to the previous keyframe of the indexed stream and
4739 * aligns other streams with respect to the keyframe timestamp
4740 * of indexed stream. Only called in case of Reverse Playback
4742 static GstFlowReturn
4743 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4745 guint32 seg_idx = 0, k_index = 0;
4746 guint32 ref_seg_idx, ref_k_index;
4747 GstClockTime k_pos = 0, last_stop = 0;
4748 QtDemuxSegment *seg = NULL;
4749 QtDemuxStream *ref_str = NULL;
4750 guint64 seg_media_start_mov; /* segment media start time in mov format */
4754 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4755 * and finally align all the other streams on that timestamp with their
4756 * respective keyframes */
4757 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4758 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4760 /* No candidate yet, take the first stream */
4766 /* So that stream has a segment, we prefer video streams */
4767 if (str->subtype == FOURCC_vide) {
4773 if (G_UNLIKELY (!ref_str)) {
4774 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4778 if (G_UNLIKELY (!ref_str->from_sample)) {
4779 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4783 /* So that stream has been playing from from_sample to to_sample. We will
4784 * get the timestamp of the previous sample and search for a keyframe before
4785 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4786 if (ref_str->subtype == FOURCC_vide) {
4787 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4788 ref_str->from_sample - 1, FALSE);
4790 if (ref_str->from_sample >= 10)
4791 k_index = ref_str->from_sample - 10;
4797 ref_str->samples[k_index].timestamp +
4798 ref_str->samples[k_index].pts_offset;
4800 /* get current segment for that stream */
4801 seg = &ref_str->segments[ref_str->segment_index];
4802 /* Use segment start in original timescale for comparisons */
4803 seg_media_start_mov = seg->trak_media_start;
4805 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4806 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4807 k_index, target_ts, seg_media_start_mov,
4808 GST_TIME_ARGS (seg->media_start));
4810 /* Crawl back through segments to find the one containing this I frame */
4811 while (target_ts < seg_media_start_mov) {
4812 GST_DEBUG_OBJECT (qtdemux,
4813 "keyframe position (sample %u) is out of segment %u " " target %"
4814 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4815 ref_str->segment_index, target_ts, seg_media_start_mov);
4817 if (G_UNLIKELY (!ref_str->segment_index)) {
4818 /* Reached first segment, let's consider it's EOS */
4821 ref_str->segment_index--;
4822 seg = &ref_str->segments[ref_str->segment_index];
4823 /* Use segment start in original timescale for comparisons */
4824 seg_media_start_mov = seg->trak_media_start;
4826 /* Calculate time position of the keyframe and where we should stop */
4828 QTSTREAMTIME_TO_GSTTIME (ref_str,
4829 target_ts - seg->trak_media_start) + seg->time;
4831 QTSTREAMTIME_TO_GSTTIME (ref_str,
4832 ref_str->samples[ref_str->from_sample].timestamp -
4833 seg->trak_media_start) + seg->time;
4835 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4836 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4837 k_index, GST_TIME_ARGS (k_pos));
4839 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4840 qtdemux->segment.position = last_stop;
4841 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4842 GST_TIME_ARGS (last_stop));
4844 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4845 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4849 ref_seg_idx = ref_str->segment_index;
4850 ref_k_index = k_index;
4852 /* Align them all on this */
4853 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4855 GstClockTime seg_time = 0;
4856 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4858 /* aligning reference stream again might lead to backing up to yet another
4859 * keyframe (due to timestamp rounding issues),
4860 * potentially putting more load on downstream; so let's try to avoid */
4861 if (str == ref_str) {
4862 seg_idx = ref_seg_idx;
4863 seg = &str->segments[seg_idx];
4864 k_index = ref_k_index;
4865 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
4866 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
4868 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4869 GST_DEBUG_OBJECT (qtdemux,
4870 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
4871 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
4873 /* get segment and time in the segment */
4874 seg = &str->segments[seg_idx];
4875 seg_time = k_pos - seg->time;
4877 /* get the media time in the segment.
4878 * No adjustment for empty "filler" segments */
4879 if (seg->media_start != GST_CLOCK_TIME_NONE)
4880 seg_time += seg->media_start;
4882 /* get the index of the sample with media time */
4883 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4884 GST_DEBUG_OBJECT (qtdemux,
4885 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
4886 GST_TIME_ARGS (seg_time), index);
4888 /* find previous keyframe */
4889 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
4892 /* Remember until where we want to go */
4893 str->to_sample = str->from_sample - 1;
4894 /* Define our time position */
4896 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4897 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4898 if (seg->media_start != GST_CLOCK_TIME_NONE)
4899 str->time_position -= seg->media_start;
4901 /* Now seek back in time */
4902 gst_qtdemux_move_stream (qtdemux, str, k_index);
4903 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
4904 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
4905 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4911 return GST_FLOW_EOS;
4915 * Gets the current qt segment start, stop and position for the
4916 * given time offset. This is used in update_segment()
4919 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
4920 QtDemuxStream * stream, GstClockTime offset,
4921 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
4923 GstClockTime seg_time;
4924 GstClockTime start, stop, time;
4925 QtDemuxSegment *segment;
4927 segment = &stream->segments[stream->segment_index];
4929 /* get time in this segment */
4930 seg_time = (offset - segment->time) * segment->rate;
4932 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4933 GST_TIME_ARGS (seg_time));
4935 if (G_UNLIKELY (seg_time > segment->duration)) {
4936 GST_LOG_OBJECT (stream->pad,
4937 "seg_time > segment->duration %" GST_TIME_FORMAT,
4938 GST_TIME_ARGS (segment->duration));
4939 seg_time = segment->duration;
4942 /* qtdemux->segment.stop is in outside-time-realm, whereas
4943 * segment->media_stop is in track-time-realm.
4945 * In order to compare the two, we need to bring segment.stop
4946 * into the track-time-realm
4948 * FIXME - does this comment still hold? Don't see any conversion here */
4950 stop = qtdemux->segment.stop;
4951 if (stop == GST_CLOCK_TIME_NONE)
4952 stop = qtdemux->segment.duration;
4953 if (stop == GST_CLOCK_TIME_NONE)
4954 stop = segment->media_stop;
4957 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4959 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4960 start = segment->time + seg_time;
4962 stop = start - seg_time + segment->duration;
4963 } else if (qtdemux->segment.rate >= 0) {
4964 start = MIN (segment->media_start + seg_time, stop);
4967 if (segment->media_start >= qtdemux->segment.start) {
4968 time = segment->time;
4970 time = segment->time + (qtdemux->segment.start - segment->media_start);
4973 start = MAX (segment->media_start, qtdemux->segment.start);
4974 stop = MIN (segment->media_start + seg_time, stop);
4983 * Updates the qt segment used for the stream and pushes a new segment event
4984 * downstream on this stream's pad.
4987 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4988 gint seg_idx, GstClockTime offset, GstClockTime * _start,
4989 GstClockTime * _stop)
4991 QtDemuxSegment *segment;
4992 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
4996 /* update the current segment */
4997 stream->segment_index = seg_idx;
4999 /* get the segment */
5000 segment = &stream->segments[seg_idx];
5002 if (G_UNLIKELY (offset < segment->time)) {
5003 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5004 GST_TIME_ARGS (segment->time));
5008 /* segment lies beyond total indicated duration */
5009 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5010 segment->time > qtdemux->segment.duration)) {
5011 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5012 " < segment->time %" GST_TIME_FORMAT,
5013 GST_TIME_ARGS (qtdemux->segment.duration),
5014 GST_TIME_ARGS (segment->time));
5018 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5019 &start, &stop, &time);
5021 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5022 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5023 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5025 /* combine global rate with that of the segment */
5026 rate = segment->rate * qtdemux->segment.rate;
5028 /* Copy flags from main segment */
5029 stream->segment.flags = qtdemux->segment.flags;
5031 /* update the segment values used for clipping */
5032 stream->segment.offset = qtdemux->segment.offset;
5033 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5034 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5035 stream->segment.rate = rate;
5036 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5037 stream->cslg_shift);
5038 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5039 stream->cslg_shift);
5040 stream->segment.time = time;
5041 stream->segment.position = stream->segment.start;
5043 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5046 /* now prepare and send the segment */
5048 event = gst_event_new_segment (&stream->segment);
5049 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5050 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5052 gst_pad_push_event (stream->pad, event);
5053 /* assume we can send more data now */
5054 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5055 /* clear to send tags on this pad now */
5056 gst_qtdemux_push_tags (qtdemux, stream);
5067 /* activate the given segment number @seg_idx of @stream at time @offset.
5068 * @offset is an absolute global position over all the segments.
5070 * This will push out a NEWSEGMENT event with the right values and
5071 * position the stream index to the first decodable sample before
5075 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5076 guint32 seg_idx, GstClockTime offset)
5078 QtDemuxSegment *segment;
5079 guint32 index, kf_index;
5080 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5082 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5083 seg_idx, GST_TIME_ARGS (offset));
5085 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5089 segment = &stream->segments[stream->segment_index];
5091 /* in the fragmented case, we pick a fragment that starts before our
5092 * desired position and rely on downstream to wait for a keyframe
5093 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5094 * tfra entries tells us which trun/sample the key unit is in, but we don't
5095 * make use of this additional information at the moment) */
5096 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5097 stream->to_sample = G_MAXUINT32;
5100 /* well, it will be taken care of below */
5101 qtdemux->fragmented_seek_pending = FALSE;
5102 /* FIXME ideally the do_fragmented_seek can be done right here,
5103 * rather than at loop level
5104 * (which might even allow handling edit lists in a fragmented file) */
5107 /* We don't need to look for a sample in push-based */
5108 if (!qtdemux->pullbased)
5111 /* and move to the keyframe before the indicated media time of the
5113 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5114 if (qtdemux->segment.rate >= 0) {
5115 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5116 stream->to_sample = G_MAXUINT32;
5117 GST_DEBUG_OBJECT (stream->pad,
5118 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5119 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5120 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5122 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5123 stream->to_sample = index;
5124 GST_DEBUG_OBJECT (stream->pad,
5125 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5126 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5127 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5130 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5131 "this is an empty segment");
5135 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5136 * encountered an error and printed a message so we return appropriately */
5140 /* we're at the right spot */
5141 if (index == stream->sample_index) {
5142 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5146 /* find keyframe of the target index */
5147 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5150 /* indent does stupid stuff with stream->samples[].timestamp */
5152 /* if we move forwards, we don't have to go back to the previous
5153 * keyframe since we already sent that. We can also just jump to
5154 * the keyframe right before the target index if there is one. */
5155 if (index > stream->sample_index) {
5156 /* moving forwards check if we move past a keyframe */
5157 if (kf_index > stream->sample_index) {
5158 GST_DEBUG_OBJECT (stream->pad,
5159 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5160 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5161 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5162 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5164 GST_DEBUG_OBJECT (stream->pad,
5165 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5166 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5167 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5170 GST_DEBUG_OBJECT (stream->pad,
5171 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5172 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5173 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5174 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5182 /* prepare to get the current sample of @stream, getting essential values.
5184 * This function will also prepare and send the segment when needed.
5186 * Return FALSE if the stream is EOS.
5191 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5192 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5193 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5194 gboolean * keyframe)
5196 QtDemuxSample *sample;
5197 GstClockTime time_position;
5200 g_return_val_if_fail (stream != NULL, FALSE);
5202 time_position = stream->time_position;
5203 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5206 seg_idx = stream->segment_index;
5207 if (G_UNLIKELY (seg_idx == -1)) {
5208 /* find segment corresponding to time_position if we are looking
5210 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5213 /* different segment, activate it, sample_index will be set. */
5214 if (G_UNLIKELY (stream->segment_index != seg_idx))
5215 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5217 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5218 segments[stream->segment_index]))) {
5219 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5221 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5222 " prepare empty sample");
5225 *pts = *dts = time_position;
5226 *duration = seg->duration - (time_position - seg->time);
5233 if (stream->sample_index == -1)
5234 stream->sample_index = 0;
5236 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5237 stream->sample_index, stream->n_samples);
5239 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5240 if (!qtdemux->fragmented)
5243 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5247 GST_OBJECT_LOCK (qtdemux);
5248 flow = qtdemux_add_fragmented_samples (qtdemux);
5249 GST_OBJECT_UNLOCK (qtdemux);
5251 if (flow != GST_FLOW_OK)
5254 while (stream->sample_index >= stream->n_samples);
5257 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5258 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5259 stream->sample_index);
5263 /* now get the info for the sample we're at */
5264 sample = &stream->samples[stream->sample_index];
5266 *dts = QTSAMPLE_DTS (stream, sample);
5267 *pts = QTSAMPLE_PTS (stream, sample);
5268 *offset = sample->offset;
5269 *size = sample->size;
5270 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5271 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5278 stream->time_position = GST_CLOCK_TIME_NONE;
5283 /* move to the next sample in @stream.
5285 * Moves to the next segment when needed.
5288 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5290 QtDemuxSample *sample;
5291 QtDemuxSegment *segment;
5293 /* get current segment */
5294 segment = &stream->segments[stream->segment_index];
5296 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5297 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5301 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5302 /* Mark the stream as EOS */
5303 GST_DEBUG_OBJECT (qtdemux,
5304 "reached max allowed sample %u, mark EOS", stream->to_sample);
5305 stream->time_position = GST_CLOCK_TIME_NONE;
5309 /* move to next sample */
5310 stream->sample_index++;
5311 stream->offset_in_sample = 0;
5313 /* reached the last sample, we need the next segment */
5314 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5317 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5318 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5319 stream->sample_index);
5323 /* get next sample */
5324 sample = &stream->samples[stream->sample_index];
5326 /* see if we are past the segment */
5327 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5330 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5331 /* inside the segment, update time_position, looks very familiar to
5332 * GStreamer segments, doesn't it? */
5333 stream->time_position =
5334 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5336 /* not yet in segment, time does not yet increment. This means
5337 * that we are still prerolling keyframes to the decoder so it can
5338 * decode the first sample of the segment. */
5339 stream->time_position = segment->time;
5343 /* move to the next segment */
5346 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5348 if (stream->segment_index == stream->n_segments - 1) {
5349 /* are we at the end of the last segment, we're EOS */
5350 stream->time_position = GST_CLOCK_TIME_NONE;
5352 /* else we're only at the end of the current segment */
5353 stream->time_position = segment->stop_time;
5355 /* make sure we select a new segment */
5357 /* accumulate previous segments */
5358 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5359 stream->accumulated_base +=
5360 (stream->segment.stop -
5361 stream->segment.start) / ABS (stream->segment.rate);
5363 stream->segment_index = -1;
5368 gst_qtdemux_sync_streams (GstQTDemux * demux)
5372 if (QTDEMUX_N_STREAMS (demux) <= 1)
5375 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5376 QtDemuxStream *stream;
5377 GstClockTime end_time;
5379 stream = QTDEMUX_NTH_STREAM (demux, i);
5384 /* TODO advance time on subtitle streams here, if any some day */
5386 /* some clips/trailers may have unbalanced streams at the end,
5387 * so send EOS on shorter stream to prevent stalling others */
5389 /* do not mess with EOS if SEGMENT seeking */
5390 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5393 if (demux->pullbased) {
5394 /* loop mode is sample time based */
5395 if (!STREAM_IS_EOS (stream))
5398 /* push mode is byte position based */
5399 if (stream->n_samples &&
5400 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5404 if (stream->sent_eos)
5407 /* only act if some gap */
5408 end_time = stream->segments[stream->n_segments - 1].stop_time;
5409 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5410 ", stream end: %" GST_TIME_FORMAT,
5411 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5412 if (GST_CLOCK_TIME_IS_VALID (end_time)
5413 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5416 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5417 GST_PAD_NAME (stream->pad));
5418 stream->sent_eos = TRUE;
5419 event = gst_event_new_eos ();
5420 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5421 gst_event_set_seqnum (event, demux->segment_seqnum);
5422 gst_pad_push_event (stream->pad, event);
5427 /* EOS and NOT_LINKED need to be combined. This means that we return:
5429 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5430 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5432 static GstFlowReturn
5433 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5436 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5439 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5442 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5444 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5448 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5449 * completely clipped
5451 * Should be used only with raw buffers */
5453 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5456 guint64 start, stop, cstart, cstop, diff;
5457 GstClockTime pts, duration;
5459 gint num_rate, denom_rate;
5464 osize = size = gst_buffer_get_size (buf);
5467 /* depending on the type, setup the clip parameters */
5468 if (stream->subtype == FOURCC_soun) {
5469 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5470 num_rate = GST_SECOND;
5471 denom_rate = (gint) CUR_STREAM (stream)->rate;
5473 } else if (stream->subtype == FOURCC_vide) {
5475 num_rate = CUR_STREAM (stream)->fps_n;
5476 denom_rate = CUR_STREAM (stream)->fps_d;
5481 if (frame_size <= 0)
5482 goto bad_frame_size;
5484 /* we can only clip if we have a valid pts */
5485 pts = GST_BUFFER_PTS (buf);
5486 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5489 duration = GST_BUFFER_DURATION (buf);
5491 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5493 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5497 stop = start + duration;
5499 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5500 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5503 /* see if some clipping happened */
5504 diff = cstart - start;
5510 /* bring clipped time to samples and to bytes */
5511 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5514 GST_DEBUG_OBJECT (qtdemux,
5515 "clipping start to %" GST_TIME_FORMAT " %"
5516 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5522 diff = stop - cstop;
5527 /* bring clipped time to samples and then to bytes */
5528 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5530 GST_DEBUG_OBJECT (qtdemux,
5531 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5532 " bytes", GST_TIME_ARGS (cstop), diff);
5537 if (offset != 0 || size != osize)
5538 gst_buffer_resize (buf, offset, size);
5540 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5541 GST_BUFFER_PTS (buf) = pts;
5542 GST_BUFFER_DURATION (buf) = duration;
5546 /* dropped buffer */
5549 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5554 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5559 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5564 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5565 gst_buffer_unref (buf);
5571 gst_qtdemux_align_buffer (GstQTDemux * demux,
5572 GstBuffer * buffer, gsize alignment)
5576 gst_buffer_map (buffer, &map, GST_MAP_READ);
5578 if (map.size < sizeof (guintptr)) {
5579 gst_buffer_unmap (buffer, &map);
5583 if (((guintptr) map.data) & (alignment - 1)) {
5584 GstBuffer *new_buffer;
5585 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5587 new_buffer = gst_buffer_new_allocate (NULL,
5588 gst_buffer_get_size (buffer), ¶ms);
5590 /* Copy data "by hand", so ensure alignment is kept: */
5591 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5593 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5594 GST_DEBUG_OBJECT (demux,
5595 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5598 gst_buffer_unmap (buffer, &map);
5599 gst_buffer_unref (buffer);
5604 gst_buffer_unmap (buffer, &map);
5609 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
5615 /* We are converting from pairs to triplets */
5616 *res = ccpair_size / 2 * 3;
5617 storage = g_malloc (*res);
5618 for (i = 0; i * 2 < ccpair_size; i += 1) {
5619 /* FIXME: Use line offset 0 as we simply can't know here */
5621 storage[i * 3] = 0x80 | 0x00;
5623 storage[i * 3] = 0x00 | 0x00;
5624 storage[i * 3 + 1] = ccpair[i * 2];
5625 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5632 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
5636 guint32 atom_length, fourcc;
5637 QtDemuxStreamStsdEntry *stsd_entry;
5639 GST_MEMDUMP ("caption atom", data, size);
5641 /* There might be multiple atoms */
5646 atom_length = QT_UINT32 (data);
5647 fourcc = QT_FOURCC (data + 4);
5648 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5651 GST_DEBUG_OBJECT (stream->pad, "here");
5653 /* Check if we have somethig compatible */
5654 stsd_entry = CUR_STREAM (stream);
5655 switch (stsd_entry->fourcc) {
5657 guint8 *cdat = NULL, *cdt2 = NULL;
5658 gsize cdat_size = 0, cdt2_size = 0;
5659 /* Should be cdat or cdt2 */
5660 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
5661 GST_WARNING_OBJECT (stream->pad,
5662 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5663 GST_FOURCC_ARGS (fourcc));
5667 /* Convert to S334-1 Annex A byte triplet */
5668 if (fourcc == FOURCC_cdat)
5669 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
5671 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
5672 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5675 /* Check for another atom ? */
5676 if (size > atom_length + 8) {
5677 guint32 new_atom_length = QT_UINT32 (data + atom_length);
5678 if (size >= atom_length + new_atom_length) {
5679 fourcc = QT_FOURCC (data + atom_length + 4);
5680 if (fourcc == FOURCC_cdat) {
5683 convert_to_s334_1a (data + atom_length + 8,
5684 new_atom_length - 8, 1, &cdat_size);
5686 GST_WARNING_OBJECT (stream->pad,
5687 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5691 convert_to_s334_1a (data + atom_length + 8,
5692 new_atom_length - 8, 2, &cdt2_size);
5694 GST_WARNING_OBJECT (stream->pad,
5695 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5700 *cclen = cdat_size + cdt2_size;
5701 res = g_malloc (*cclen);
5703 memcpy (res, cdat, cdat_size);
5705 memcpy (res + cdat_size, cdt2, cdt2_size);
5711 if (fourcc != FOURCC_ccdp) {
5712 GST_WARNING_OBJECT (stream->pad,
5713 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5714 GST_FOURCC_ARGS (fourcc));
5717 *cclen = atom_length - 8;
5718 res = g_memdup (data + 8, *cclen);
5721 /* Keep this here in case other closed caption formats are added */
5722 g_assert_not_reached ();
5726 GST_MEMDUMP ("Output", res, *cclen);
5731 GST_WARNING ("[cdat] atom is too small or invalid");
5735 /* the input buffer metadata must be writable,
5736 * but time/duration etc not yet set and need not be preserved */
5738 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5745 /* not many cases for now */
5746 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
5747 /* send a one time dvd clut event */
5748 if (stream->pending_event && stream->pad)
5749 gst_pad_push_event (stream->pad, stream->pending_event);
5750 stream->pending_event = NULL;
5753 if (G_UNLIKELY (stream->subtype != FOURCC_text
5754 && stream->subtype != FOURCC_sbtl &&
5755 stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
5759 gst_buffer_map (buf, &map, GST_MAP_READ);
5761 /* empty buffer is sent to terminate previous subtitle */
5762 if (map.size <= 2) {
5763 gst_buffer_unmap (buf, &map);
5764 gst_buffer_unref (buf);
5767 if (stream->subtype == FOURCC_subp) {
5768 /* That's all the processing needed for subpictures */
5769 gst_buffer_unmap (buf, &map);
5773 if (stream->subtype == FOURCC_clcp) {
5776 /* For closed caption, we need to extract the information from the
5777 * [cdat],[cdt2] or [ccdp] atom */
5778 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
5779 gst_buffer_unmap (buf, &map);
5780 gst_buffer_unref (buf);
5782 buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
5784 /* Conversion failed or there's nothing */
5790 nsize = GST_READ_UINT16_BE (map.data);
5791 nsize = MIN (nsize, map.size - 2);
5793 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5796 /* takes care of UTF-8 validation or UTF-16 recognition,
5797 * no other encoding expected */
5798 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5799 gst_buffer_unmap (buf, &map);
5801 gst_buffer_unref (buf);
5802 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5804 /* this should not really happen unless the subtitle is corrupted */
5805 gst_buffer_unref (buf);
5809 /* FIXME ? convert optional subsequent style info to markup */
5814 static GstFlowReturn
5815 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5818 GstFlowReturn ret = GST_FLOW_OK;
5819 GstClockTime pts, duration;
5821 if (stream->need_clip)
5822 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5824 if (G_UNLIKELY (buf == NULL))
5827 if (G_UNLIKELY (stream->discont)) {
5828 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5829 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5830 stream->discont = FALSE;
5832 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5835 GST_LOG_OBJECT (qtdemux,
5836 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
5837 ", duration %" GST_TIME_FORMAT " on pad %s",
5838 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
5839 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
5840 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
5842 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
5843 GstStructure *crypto_info;
5844 QtDemuxCencSampleSetInfo *info =
5845 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5849 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5850 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
5851 GST_PTR_FORMAT, event);
5852 gst_pad_push_event (stream->pad, event);
5855 if (info->crypto_info == NULL) {
5856 GST_DEBUG_OBJECT (qtdemux,
5857 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
5859 /* The end of the crypto_info array matches our n_samples position,
5860 * so count backward from there */
5861 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
5862 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5863 /* steal structure from array */
5864 crypto_info = g_ptr_array_index (info->crypto_info, index);
5865 g_ptr_array_index (info->crypto_info, index) = NULL;
5866 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
5867 info->crypto_info->len);
5868 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5869 GST_ERROR_OBJECT (qtdemux,
5870 "failed to attach cenc metadata to buffer");
5872 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
5873 index, stream->sample_index);
5878 if (stream->alignment > 1)
5879 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
5881 pts = GST_BUFFER_PTS (buf);
5882 duration = GST_BUFFER_DURATION (buf);
5884 ret = gst_pad_push (stream->pad, buf);
5886 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
5887 /* mark position in stream, we'll need this to know when to send GAP event */
5888 stream->segment.position = pts + duration;
5896 static GstFlowReturn
5897 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5900 GstFlowReturn ret = GST_FLOW_OK;
5902 if (stream->subtype == FOURCC_clcp
5903 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
5905 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
5906 guint n_triplets, i;
5907 guint field1_off = 0, field2_off = 0;
5909 /* We have to split CEA608 buffers so that each outgoing buffer contains
5910 * one byte pair per field according to the framerate of the video track.
5912 * If there is only a single byte pair per field we don't have to do
5916 gst_buffer_map (buf, &map, GST_MAP_READ);
5918 n_triplets = map.size / 3;
5919 for (i = 0; i < n_triplets; i++) {
5920 if (map.data[3 * i] & 0x80)
5926 g_assert (n_field1 || n_field2);
5928 /* If there's more than 1 frame we have to split, otherwise we can just
5930 if (n_field1 > 1 || n_field2 > 1) {
5932 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
5933 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
5935 for (i = 0; i < n_output_buffers; i++) {
5937 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
5941 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
5942 outptr = outmap.data;
5945 gboolean found = FALSE;
5947 while (map.data + field1_off < map.data + map.size) {
5948 if (map.data[field1_off] & 0x80) {
5949 memcpy (outptr, &map.data[field1_off], 3);
5958 const guint8 empty[] = { 0x80, 0x80, 0x80 };
5960 memcpy (outptr, empty, 3);
5967 gboolean found = FALSE;
5969 while (map.data + field2_off < map.data + map.size) {
5970 if ((map.data[field2_off] & 0x80) == 0) {
5971 memcpy (outptr, &map.data[field2_off], 3);
5980 const guint8 empty[] = { 0x00, 0x80, 0x80 };
5982 memcpy (outptr, empty, 3);
5988 gst_buffer_unmap (outbuf, &outmap);
5990 GST_BUFFER_PTS (outbuf) =
5991 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
5992 GST_SECOND * CUR_STREAM (stream)->fps_d,
5993 CUR_STREAM (stream)->fps_n);
5994 GST_BUFFER_DURATION (outbuf) =
5995 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
5996 CUR_STREAM (stream)->fps_n);
5997 GST_BUFFER_OFFSET (outbuf) = -1;
5998 GST_BUFFER_OFFSET_END (outbuf) = -1;
6000 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6002 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6005 gst_buffer_unmap (buf, &map);
6006 gst_buffer_unref (buf);
6008 gst_buffer_unmap (buf, &map);
6009 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6012 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6018 /* Sets a buffer's attributes properly and pushes it downstream.
6019 * Also checks for additional actions and custom processing that may
6020 * need to be done first.
6022 static GstFlowReturn
6023 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6024 QtDemuxStream * stream, GstBuffer * buf,
6025 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6026 gboolean keyframe, GstClockTime position, guint64 byte_position)
6028 GstFlowReturn ret = GST_FLOW_OK;
6030 /* offset the timestamps according to the edit list */
6032 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6036 gst_buffer_map (buf, &map, GST_MAP_READ);
6037 url = g_strndup ((gchar *) map.data, map.size);
6038 gst_buffer_unmap (buf, &map);
6039 if (url != NULL && strlen (url) != 0) {
6040 /* we have RTSP redirect now */
6041 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6042 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6043 gst_structure_new ("redirect",
6044 "new-location", G_TYPE_STRING, url, NULL)));
6045 qtdemux->posted_redirect = TRUE;
6047 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6053 /* position reporting */
6054 if (qtdemux->segment.rate >= 0) {
6055 qtdemux->segment.position = position;
6056 gst_qtdemux_sync_streams (qtdemux);
6059 if (G_UNLIKELY (!stream->pad)) {
6060 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6061 gst_buffer_unref (buf);
6065 /* send out pending buffers */
6066 while (stream->buffers) {
6067 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6069 if (G_UNLIKELY (stream->discont)) {
6070 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6071 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6072 stream->discont = FALSE;
6074 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6077 if (stream->alignment > 1)
6078 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6079 gst_pad_push (stream->pad, buffer);
6081 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6084 /* we're going to modify the metadata */
6085 buf = gst_buffer_make_writable (buf);
6087 if (G_UNLIKELY (stream->need_process))
6088 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6094 GST_BUFFER_DTS (buf) = dts;
6095 GST_BUFFER_PTS (buf) = pts;
6096 GST_BUFFER_DURATION (buf) = duration;
6097 GST_BUFFER_OFFSET (buf) = -1;
6098 GST_BUFFER_OFFSET_END (buf) = -1;
6101 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6102 stream->on_keyframe = FALSE;
6104 stream->on_keyframe = TRUE;
6107 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6108 gst_buffer_append_memory (buf,
6109 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6111 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6112 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6115 if (G_UNLIKELY (qtdemux->element_index)) {
6116 GstClockTime stream_time;
6119 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6121 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6122 GST_LOG_OBJECT (qtdemux,
6123 "adding association %" GST_TIME_FORMAT "-> %"
6124 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6125 gst_index_add_association (qtdemux->element_index,
6127 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6128 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6129 GST_FORMAT_BYTES, byte_position, NULL);
6134 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6140 static const QtDemuxRandomAccessEntry *
6141 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6142 GstClockTime pos, gboolean after)
6144 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6145 guint n_entries = stream->n_ra_entries;
6148 /* we assume the table is sorted */
6149 for (i = 0; i < n_entries; ++i) {
6150 if (entries[i].ts > pos)
6154 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6155 * probably okay to assume that the index lists the very first fragment */
6162 return &entries[i - 1];
6166 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6168 const QtDemuxRandomAccessEntry *best_entry = NULL;
6171 GST_OBJECT_LOCK (qtdemux);
6173 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6175 /* first see if we can determine where to go to using mfra,
6176 * before we start clearing things */
6177 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6178 const QtDemuxRandomAccessEntry *entry;
6179 QtDemuxStream *stream;
6180 gboolean is_audio_or_video;
6182 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6184 if (stream->ra_entries == NULL)
6187 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6188 is_audio_or_video = TRUE;
6190 is_audio_or_video = FALSE;
6193 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6194 stream->time_position, !is_audio_or_video);
6196 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6197 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6199 stream->pending_seek = entry;
6201 /* decide position to jump to just based on audio/video tracks, not subs */
6202 if (!is_audio_or_video)
6205 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6209 /* no luck, will handle seek otherwise */
6210 if (best_entry == NULL) {
6211 GST_OBJECT_UNLOCK (qtdemux);
6215 /* ok, now we can prepare for processing as of located moof */
6216 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6217 QtDemuxStream *stream;
6219 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6221 g_free (stream->samples);
6222 stream->samples = NULL;
6223 stream->n_samples = 0;
6224 stream->stbl_index = -1; /* no samples have yet been parsed */
6225 stream->sample_index = -1;
6227 if (stream->protection_scheme_info) {
6228 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6229 if (stream->protection_scheme_type == FOURCC_cenc) {
6230 QtDemuxCencSampleSetInfo *info =
6231 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6232 if (info->crypto_info) {
6233 g_ptr_array_free (info->crypto_info, TRUE);
6234 info->crypto_info = NULL;
6240 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6241 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6242 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6243 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6245 qtdemux->moof_offset = best_entry->moof_offset;
6247 qtdemux_add_fragmented_samples (qtdemux);
6249 GST_OBJECT_UNLOCK (qtdemux);
6253 static GstFlowReturn
6254 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6256 GstFlowReturn ret = GST_FLOW_OK;
6257 GstBuffer *buf = NULL;
6258 QtDemuxStream *stream, *target_stream = NULL;
6259 GstClockTime min_time;
6261 GstClockTime dts = GST_CLOCK_TIME_NONE;
6262 GstClockTime pts = GST_CLOCK_TIME_NONE;
6263 GstClockTime duration = 0;
6264 gboolean keyframe = FALSE;
6265 guint sample_size = 0;
6270 if (qtdemux->fragmented_seek_pending) {
6271 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6272 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6273 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6274 qtdemux->fragmented_seek_pending = FALSE;
6276 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6280 /* Figure out the next stream sample to output, min_time is expressed in
6281 * global time and runs over the edit list segments. */
6282 min_time = G_MAXUINT64;
6283 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6284 GstClockTime position;
6286 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6287 position = stream->time_position;
6289 /* position of -1 is EOS */
6290 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6291 min_time = position;
6292 target_stream = stream;
6296 if (G_UNLIKELY (target_stream == NULL)) {
6297 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6301 /* check for segment end */
6302 if (G_UNLIKELY (qtdemux->segment.stop != -1
6303 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6304 || (qtdemux->segment.rate < 0
6305 && qtdemux->segment.start > min_time))
6306 && target_stream->on_keyframe)) {
6307 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6308 target_stream->time_position = GST_CLOCK_TIME_NONE;
6312 /* gap events for subtitle streams */
6313 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6314 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6315 if (stream->pad && (stream->subtype == FOURCC_subp
6316 || stream->subtype == FOURCC_text
6317 || stream->subtype == FOURCC_sbtl)) {
6318 /* send one second gap events until the stream catches up */
6319 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6320 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6321 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6322 stream->segment.position + GST_SECOND < min_time) {
6324 gst_event_new_gap (stream->segment.position, GST_SECOND);
6325 gst_pad_push_event (stream->pad, gap);
6326 stream->segment.position += GST_SECOND;
6331 stream = target_stream;
6332 /* fetch info for the current sample of this stream */
6333 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6334 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6337 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6338 if (stream->new_caps) {
6339 gst_qtdemux_configure_stream (qtdemux, stream);
6340 qtdemux_do_allocation (stream, qtdemux);
6343 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6344 if (G_UNLIKELY (qtdemux->segment.
6345 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6346 if (stream->subtype == FOURCC_vide && !keyframe) {
6347 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6353 GST_DEBUG_OBJECT (qtdemux,
6354 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6355 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6356 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6357 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6358 GST_TIME_ARGS (duration));
6360 if (G_UNLIKELY (empty)) {
6361 /* empty segment, push a gap if there's a second or more
6362 * difference and move to the next one */
6363 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6364 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6365 stream->segment.position = pts + duration;
6369 /* hmm, empty sample, skip and move to next sample */
6370 if (G_UNLIKELY (sample_size <= 0))
6373 /* last pushed sample was out of boundary, goto next sample */
6374 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6377 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6380 GST_DEBUG_OBJECT (qtdemux,
6381 "size %d larger than stream max_buffer_size %d, trimming",
6382 sample_size, stream->max_buffer_size);
6384 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6387 if (qtdemux->cenc_aux_info_offset > 0) {
6390 GstBuffer *aux_info = NULL;
6392 /* pull the data stored before the sample */
6394 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6395 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6396 if (G_UNLIKELY (ret != GST_FLOW_OK))
6398 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6399 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6400 gst_byte_reader_init (&br, map.data + 8, map.size);
6401 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6402 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6403 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6404 gst_buffer_unmap (aux_info, &map);
6405 gst_buffer_unref (aux_info);
6406 ret = GST_FLOW_ERROR;
6409 gst_buffer_unmap (aux_info, &map);
6410 gst_buffer_unref (aux_info);
6413 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6416 if (stream->use_allocator) {
6417 /* if we have a per-stream allocator, use it */
6418 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6421 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6423 if (G_UNLIKELY (ret != GST_FLOW_OK))
6426 if (size != sample_size) {
6427 pts += gst_util_uint64_scale_int (GST_SECOND,
6428 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6431 gst_util_uint64_scale_int (GST_SECOND,
6432 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6435 gst_util_uint64_scale_int (GST_SECOND,
6436 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6439 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6440 dts, pts, duration, keyframe, min_time, offset);
6442 if (size != sample_size) {
6443 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6444 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6446 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6448 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6449 if (time_position >= segment->media_start) {
6450 /* inside the segment, update time_position, looks very familiar to
6451 * GStreamer segments, doesn't it? */
6452 stream->time_position = (time_position - segment->media_start) +
6455 /* not yet in segment, time does not yet increment. This means
6456 * that we are still prerolling keyframes to the decoder so it can
6457 * decode the first sample of the segment. */
6458 stream->time_position = segment->time;
6463 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6464 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6465 * we have no more data for the pad to push */
6466 if (ret == GST_FLOW_EOS)
6469 stream->offset_in_sample += size;
6470 if (stream->offset_in_sample >= sample_size) {
6471 gst_qtdemux_advance_sample (qtdemux, stream);
6476 gst_qtdemux_advance_sample (qtdemux, stream);
6484 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6490 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6491 /* EOS will be raised if all are EOS */
6498 gst_qtdemux_loop (GstPad * pad)
6500 GstQTDemux *qtdemux;
6504 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6506 cur_offset = qtdemux->offset;
6507 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6508 cur_offset, qt_demux_state_string (qtdemux->state));
6510 switch (qtdemux->state) {
6511 case QTDEMUX_STATE_INITIAL:
6512 case QTDEMUX_STATE_HEADER:
6513 ret = gst_qtdemux_loop_state_header (qtdemux);
6515 case QTDEMUX_STATE_MOVIE:
6516 ret = gst_qtdemux_loop_state_movie (qtdemux);
6517 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6518 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6526 /* if something went wrong, pause */
6527 if (ret != GST_FLOW_OK)
6531 gst_object_unref (qtdemux);
6537 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6538 (NULL), ("streaming stopped, invalid state"));
6539 gst_pad_pause_task (pad);
6540 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6545 const gchar *reason = gst_flow_get_name (ret);
6547 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6549 gst_pad_pause_task (pad);
6551 /* fatal errors need special actions */
6553 if (ret == GST_FLOW_EOS) {
6554 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
6555 /* we have no streams, post an error */
6556 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6558 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6561 if ((stop = qtdemux->segment.stop) == -1)
6562 stop = qtdemux->segment.duration;
6564 if (qtdemux->segment.rate >= 0) {
6565 GstMessage *message;
6568 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6569 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6570 GST_FORMAT_TIME, stop);
6571 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6572 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6573 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6574 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6576 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6577 gst_qtdemux_push_event (qtdemux, event);
6579 GstMessage *message;
6582 /* For Reverse Playback */
6583 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6584 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6585 GST_FORMAT_TIME, qtdemux->segment.start);
6586 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6587 qtdemux->segment.start);
6588 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6589 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6590 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6592 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6593 gst_qtdemux_push_event (qtdemux, event);
6598 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6599 event = gst_event_new_eos ();
6600 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
6601 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6602 gst_qtdemux_push_event (qtdemux, event);
6604 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6605 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6606 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6615 * Returns if there are samples to be played.
6618 has_next_entry (GstQTDemux * demux)
6620 QtDemuxStream *stream;
6623 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6625 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6626 stream = QTDEMUX_NTH_STREAM (demux, i);
6628 if (stream->sample_index == -1) {
6629 stream->sample_index = 0;
6630 stream->offset_in_sample = 0;
6633 if (stream->sample_index >= stream->n_samples) {
6634 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6637 GST_DEBUG_OBJECT (demux, "Found a sample");
6641 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6648 * Returns the size of the first entry at the current offset.
6649 * If -1, there are none (which means EOS or empty file).
6652 next_entry_size (GstQTDemux * demux)
6654 QtDemuxStream *stream, *target_stream = NULL;
6655 guint64 smalloffs = (guint64) - 1;
6656 QtDemuxSample *sample;
6659 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6662 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6663 stream = QTDEMUX_NTH_STREAM (demux, i);
6665 if (stream->sample_index == -1) {
6666 stream->sample_index = 0;
6667 stream->offset_in_sample = 0;
6670 if (stream->sample_index >= stream->n_samples) {
6671 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6675 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6676 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6677 stream->sample_index);
6681 sample = &stream->samples[stream->sample_index];
6683 GST_LOG_OBJECT (demux,
6684 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
6685 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
6686 stream->sample_index, sample->offset, sample->size);
6688 if (((smalloffs == -1)
6689 || (sample->offset < smalloffs)) && (sample->size)) {
6690 smalloffs = sample->offset;
6691 target_stream = stream;
6698 GST_LOG_OBJECT (demux,
6699 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
6700 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
6702 stream = target_stream;
6703 sample = &stream->samples[stream->sample_index];
6705 if (sample->offset >= demux->offset) {
6706 demux->todrop = sample->offset - demux->offset;
6707 return sample->size + demux->todrop;
6710 GST_DEBUG_OBJECT (demux,
6711 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6716 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6718 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6720 gst_element_post_message (GST_ELEMENT_CAST (demux),
6721 gst_message_new_element (GST_OBJECT_CAST (demux),
6722 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6726 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6731 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6734 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6735 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6736 GST_SEEK_TYPE_NONE, -1);
6738 /* store seqnum to drop flush events, they don't need to reach downstream */
6739 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6740 res = gst_pad_push_event (demux->sinkpad, event);
6741 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
6746 /* check for seekable upstream, above and beyond a mere query */
6748 gst_qtdemux_check_seekability (GstQTDemux * demux)
6751 gboolean seekable = FALSE;
6752 gint64 start = -1, stop = -1;
6754 if (demux->upstream_size)
6757 if (demux->upstream_format_is_time)
6760 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6761 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6762 GST_DEBUG_OBJECT (demux, "seeking query failed");
6766 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6768 /* try harder to query upstream size if we didn't get it the first time */
6769 if (seekable && stop == -1) {
6770 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6771 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6774 /* if upstream doesn't know the size, it's likely that it's not seekable in
6775 * practice even if it technically may be seekable */
6776 if (seekable && (start != 0 || stop <= start)) {
6777 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6782 gst_query_unref (query);
6784 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6785 G_GUINT64_FORMAT ")", seekable, start, stop);
6786 demux->upstream_seekable = seekable;
6787 demux->upstream_size = seekable ? stop : -1;
6791 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6793 g_return_if_fail (bytes <= demux->todrop);
6795 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6796 gst_adapter_flush (demux->adapter, bytes);
6797 demux->neededbytes -= bytes;
6798 demux->offset += bytes;
6799 demux->todrop -= bytes;
6802 /* PUSH-MODE only: Send a segment, if not done already. */
6804 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
6806 if (G_UNLIKELY (demux->need_segment)) {
6809 if (!demux->upstream_format_is_time) {
6810 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
6812 GstEvent *segment_event;
6813 segment_event = gst_event_new_segment (&demux->segment);
6814 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6815 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
6816 gst_qtdemux_push_event (demux, segment_event);
6819 demux->need_segment = FALSE;
6821 /* clear to send tags on all streams */
6822 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6823 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6824 gst_qtdemux_push_tags (demux, stream);
6825 if (CUR_STREAM (stream)->sparse) {
6826 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6827 gst_pad_push_event (stream->pad,
6828 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
6834 /* Used for push mode only. */
6836 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
6837 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
6839 GstClockTime ts, dur;
6843 stream->segments[segment_index].duration - (pos -
6844 stream->segments[segment_index].time);
6845 stream->time_position += dur;
6847 /* Only gaps with a duration of at least one second are propagated.
6848 * Same workaround as in pull mode.
6849 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
6850 if (dur >= GST_SECOND) {
6852 gap = gst_event_new_gap (ts, dur);
6854 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
6855 "segment: %" GST_PTR_FORMAT, gap);
6856 gst_pad_push_event (stream->pad, gap);
6860 static GstFlowReturn
6861 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6865 demux = GST_QTDEMUX (parent);
6867 GST_DEBUG_OBJECT (demux,
6868 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
6869 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
6870 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
6871 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
6872 gst_buffer_get_size (inbuf), demux->offset);
6874 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6875 gboolean is_gap_input = FALSE;
6878 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6880 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6881 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
6884 /* Check if we can land back on our feet in the case where upstream is
6885 * handling the seeking/pushing of samples with gaps in between (like
6886 * in the case of trick-mode DASH for example) */
6887 if (demux->upstream_format_is_time
6888 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
6889 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6891 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6892 GST_LOG_OBJECT (demux,
6893 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
6894 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
6896 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
6897 stream, GST_BUFFER_OFFSET (inbuf));
6899 QtDemuxSample *sample = &stream->samples[res];
6900 GST_LOG_OBJECT (demux,
6901 "Checking if sample %d from track-id %u is valid (offset:%"
6902 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
6903 stream->track_id, sample->offset, sample->size);
6904 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
6905 GST_LOG_OBJECT (demux,
6906 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
6908 is_gap_input = TRUE;
6909 /* We can go back to standard playback mode */
6910 demux->state = QTDEMUX_STATE_MOVIE;
6911 /* Remember which sample this stream is at */
6912 stream->sample_index = res;
6913 /* Finally update all push-based values to the expected values */
6914 demux->neededbytes = stream->samples[res].size;
6915 demux->offset = GST_BUFFER_OFFSET (inbuf);
6917 demux->mdatsize - demux->offset + demux->mdatoffset;
6922 if (!is_gap_input) {
6923 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
6924 /* Reset state if it's a real discont */
6925 demux->neededbytes = 16;
6926 demux->state = QTDEMUX_STATE_INITIAL;
6927 demux->offset = GST_BUFFER_OFFSET (inbuf);
6928 gst_adapter_clear (demux->adapter);
6931 /* Reverse fragmented playback, need to flush all we have before
6932 * consuming a new fragment.
6933 * The samples array have the timestamps calculated by accumulating the
6934 * durations but this won't work for reverse playback of fragments as
6935 * the timestamps of a subsequent fragment should be smaller than the
6936 * previously received one. */
6937 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
6938 gst_qtdemux_process_adapter (demux, TRUE);
6939 g_ptr_array_foreach (demux->active_streams,
6940 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
6944 gst_adapter_push (demux->adapter, inbuf);
6946 GST_DEBUG_OBJECT (demux,
6947 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6948 demux->neededbytes, gst_adapter_available (demux->adapter));
6950 return gst_qtdemux_process_adapter (demux, FALSE);
6953 static GstFlowReturn
6954 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6956 GstFlowReturn ret = GST_FLOW_OK;
6958 /* we never really mean to buffer that much */
6959 if (demux->neededbytes == -1) {
6963 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6964 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6966 #ifndef GST_DISABLE_GST_DEBUG
6968 guint64 discont_offset, distance_from_discont;
6970 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
6971 distance_from_discont =
6972 gst_adapter_distance_from_discont (demux->adapter);
6974 GST_DEBUG_OBJECT (demux,
6975 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
6976 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
6977 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
6978 demux->offset, discont_offset, distance_from_discont);
6982 switch (demux->state) {
6983 case QTDEMUX_STATE_INITIAL:{
6988 gst_qtdemux_check_seekability (demux);
6990 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6992 /* get fourcc/length, set neededbytes */
6993 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6995 gst_adapter_unmap (demux->adapter);
6997 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
6998 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7000 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7001 (_("This file is invalid and cannot be played.")),
7002 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7003 GST_FOURCC_ARGS (fourcc)));
7004 ret = GST_FLOW_ERROR;
7007 if (fourcc == FOURCC_mdat) {
7008 gint next_entry = next_entry_size (demux);
7009 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7010 || !demux->fragmented)) {
7011 /* we have the headers, start playback */
7012 demux->state = QTDEMUX_STATE_MOVIE;
7013 demux->neededbytes = next_entry;
7014 demux->mdatleft = size;
7015 demux->mdatsize = demux->mdatleft;
7017 /* no headers yet, try to get them */
7020 guint64 old, target;
7023 old = demux->offset;
7024 target = old + size;
7026 /* try to jump over the atom with a seek */
7027 /* only bother if it seems worth doing so,
7028 * and avoids possible upstream/server problems */
7029 if (demux->upstream_seekable &&
7030 demux->upstream_size > 4 * (1 << 20)) {
7031 res = qtdemux_seek_offset (demux, target);
7033 GST_DEBUG_OBJECT (demux, "skipping seek");
7038 GST_DEBUG_OBJECT (demux, "seek success");
7039 /* remember the offset fo the first mdat so we can seek back to it
7040 * after we have the headers */
7041 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7042 demux->first_mdat = old;
7043 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7046 /* seek worked, continue reading */
7047 demux->offset = target;
7048 demux->neededbytes = 16;
7049 demux->state = QTDEMUX_STATE_INITIAL;
7051 /* seek failed, need to buffer */
7052 demux->offset = old;
7053 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7054 /* there may be multiple mdat (or alike) buffers */
7056 if (demux->mdatbuffer)
7057 bs = gst_buffer_get_size (demux->mdatbuffer);
7060 if (size + bs > 10 * (1 << 20))
7062 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7063 demux->neededbytes = size;
7064 if (!demux->mdatbuffer)
7065 demux->mdatoffset = demux->offset;
7068 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7069 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7070 (_("This file is invalid and cannot be played.")),
7071 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7072 GST_FOURCC_ARGS (fourcc), size));
7073 ret = GST_FLOW_ERROR;
7076 /* this means we already started buffering and still no moov header,
7077 * let's continue buffering everything till we get moov */
7078 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7079 || fourcc == FOURCC_moof))
7081 demux->neededbytes = size;
7082 demux->state = QTDEMUX_STATE_HEADER;
7086 case QTDEMUX_STATE_HEADER:{
7090 GST_DEBUG_OBJECT (demux, "In header");
7092 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7094 /* parse the header */
7095 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7097 if (fourcc == FOURCC_moov) {
7098 /* in usual fragmented setup we could try to scan for more
7099 * and end up at the the moov (after mdat) again */
7100 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7102 || demux->last_moov_offset == demux->offset)) {
7103 GST_DEBUG_OBJECT (demux,
7104 "Skipping moov atom as we have (this) one already");
7106 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7108 if (demux->got_moov && demux->fragmented) {
7109 GST_DEBUG_OBJECT (demux,
7110 "Got a second moov, clean up data from old one");
7111 if (demux->moov_node_compressed) {
7112 g_node_destroy (demux->moov_node_compressed);
7113 if (demux->moov_node)
7114 g_free (demux->moov_node->data);
7116 demux->moov_node_compressed = NULL;
7117 if (demux->moov_node)
7118 g_node_destroy (demux->moov_node);
7119 demux->moov_node = NULL;
7122 demux->last_moov_offset = demux->offset;
7124 /* Update streams with new moov */
7125 gst_qtdemux_stream_concat (demux,
7126 demux->old_streams, demux->active_streams);
7128 qtdemux_parse_moov (demux, data, demux->neededbytes);
7129 qtdemux_node_dump (demux, demux->moov_node);
7130 qtdemux_parse_tree (demux);
7131 qtdemux_prepare_streams (demux);
7132 QTDEMUX_EXPOSE_LOCK (demux);
7133 qtdemux_expose_streams (demux);
7134 QTDEMUX_EXPOSE_UNLOCK (demux);
7136 demux->got_moov = TRUE;
7138 gst_qtdemux_check_send_pending_segment (demux);
7140 if (demux->moov_node_compressed) {
7141 g_node_destroy (demux->moov_node_compressed);
7142 g_free (demux->moov_node->data);
7144 demux->moov_node_compressed = NULL;
7145 g_node_destroy (demux->moov_node);
7146 demux->moov_node = NULL;
7147 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7149 } else if (fourcc == FOURCC_moof) {
7150 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7152 GstClockTime prev_pts;
7153 guint64 prev_offset;
7154 guint64 adapter_discont_offset, adapter_discont_dist;
7156 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7159 * The timestamp of the moof buffer is relevant as some scenarios
7160 * won't have the initial timestamp in the atoms. Whenever a new
7161 * buffer has started, we get that buffer's PTS and use it as a base
7162 * timestamp for the trun entries.
7164 * To keep track of the current buffer timestamp and starting point
7165 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7166 * from the beggining of the buffer, with the distance and demux->offset
7167 * we know if it is still the same buffer or not.
7169 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7170 prev_offset = demux->offset - dist;
7171 if (demux->fragment_start_offset == -1
7172 || prev_offset > demux->fragment_start_offset) {
7173 demux->fragment_start_offset = prev_offset;
7174 demux->fragment_start = prev_pts;
7175 GST_DEBUG_OBJECT (demux,
7176 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7177 GST_TIME_FORMAT, demux->fragment_start_offset,
7178 GST_TIME_ARGS (demux->fragment_start));
7181 /* We can't use prev_offset() here because this would require
7182 * upstream to set consistent and correct offsets on all buffers
7183 * since the discont. Nothing ever did that in the past and we
7184 * would break backwards compatibility here then.
7185 * Instead take the offset we had at the last discont and count
7186 * the bytes from there. This works with old code as there would
7187 * be no discont between moov and moof, and also works with
7188 * adaptivedemux which correctly sets offset and will set the
7189 * DISCONT flag accordingly when needed.
7191 * We also only do this for upstream TIME segments as otherwise
7192 * there are potential backwards compatibility problems with
7193 * seeking in PUSH mode and upstream providing inconsistent
7195 adapter_discont_offset =
7196 gst_adapter_offset_at_discont (demux->adapter);
7197 adapter_discont_dist =
7198 gst_adapter_distance_from_discont (demux->adapter);
7200 GST_DEBUG_OBJECT (demux,
7201 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7202 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7203 demux->offset, adapter_discont_offset, adapter_discont_dist);
7205 if (demux->upstream_format_is_time) {
7206 demux->moof_offset = adapter_discont_offset;
7207 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7208 demux->moof_offset += adapter_discont_dist;
7209 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7210 demux->moof_offset = demux->offset;
7212 demux->moof_offset = demux->offset;
7215 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7216 demux->moof_offset, NULL)) {
7217 gst_adapter_unmap (demux->adapter);
7218 ret = GST_FLOW_ERROR;
7222 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7223 if (demux->mss_mode && !demux->exposed) {
7224 QTDEMUX_EXPOSE_LOCK (demux);
7225 qtdemux_expose_streams (demux);
7226 QTDEMUX_EXPOSE_UNLOCK (demux);
7229 gst_qtdemux_check_send_pending_segment (demux);
7231 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7233 } else if (fourcc == FOURCC_ftyp) {
7234 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7235 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7236 } else if (fourcc == FOURCC_uuid) {
7237 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7238 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7239 } else if (fourcc == FOURCC_sidx) {
7240 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7241 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7245 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7249 /* [free] and [skip] are padding atoms */
7250 GST_DEBUG_OBJECT (demux,
7251 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7252 GST_FOURCC_ARGS (fourcc));
7255 GST_WARNING_OBJECT (demux,
7256 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7257 GST_FOURCC_ARGS (fourcc));
7258 /* Let's jump that one and go back to initial state */
7262 gst_adapter_unmap (demux->adapter);
7265 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7266 gsize remaining_data_size = 0;
7268 /* the mdat was before the header */
7269 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7270 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7271 /* restore our adapter/offset view of things with upstream;
7272 * put preceding buffered data ahead of current moov data.
7273 * This should also handle evil mdat, moov, mdat cases and alike */
7274 gst_adapter_flush (demux->adapter, demux->neededbytes);
7276 /* Store any remaining data after the mdat for later usage */
7277 remaining_data_size = gst_adapter_available (demux->adapter);
7278 if (remaining_data_size > 0) {
7279 g_assert (demux->restoredata_buffer == NULL);
7280 demux->restoredata_buffer =
7281 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7282 demux->restoredata_offset = demux->offset + demux->neededbytes;
7283 GST_DEBUG_OBJECT (demux,
7284 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7285 G_GUINT64_FORMAT, remaining_data_size,
7286 demux->restoredata_offset);
7289 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7290 demux->mdatbuffer = NULL;
7291 demux->offset = demux->mdatoffset;
7292 demux->neededbytes = next_entry_size (demux);
7293 demux->state = QTDEMUX_STATE_MOVIE;
7294 demux->mdatleft = gst_adapter_available (demux->adapter);
7295 demux->mdatsize = demux->mdatleft;
7297 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7298 gst_adapter_flush (demux->adapter, demux->neededbytes);
7300 /* only go back to the mdat if there are samples to play */
7301 if (demux->got_moov && demux->first_mdat != -1
7302 && has_next_entry (demux)) {
7305 /* we need to seek back */
7306 res = qtdemux_seek_offset (demux, demux->first_mdat);
7308 demux->offset = demux->first_mdat;
7310 GST_DEBUG_OBJECT (demux, "Seek back failed");
7313 demux->offset += demux->neededbytes;
7315 demux->neededbytes = 16;
7316 demux->state = QTDEMUX_STATE_INITIAL;
7321 case QTDEMUX_STATE_BUFFER_MDAT:{
7325 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7327 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7328 gst_buffer_extract (buf, 0, fourcc, 4);
7329 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7330 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7331 if (demux->mdatbuffer)
7332 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7334 demux->mdatbuffer = buf;
7335 demux->offset += demux->neededbytes;
7336 demux->neededbytes = 16;
7337 demux->state = QTDEMUX_STATE_INITIAL;
7338 gst_qtdemux_post_progress (demux, 1, 1);
7342 case QTDEMUX_STATE_MOVIE:{
7343 QtDemuxStream *stream = NULL;
7344 QtDemuxSample *sample;
7345 GstClockTime dts, pts, duration;
7349 GST_DEBUG_OBJECT (demux,
7350 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7352 if (demux->fragmented) {
7353 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7355 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7356 /* if needed data starts within this atom,
7357 * then it should not exceed this atom */
7358 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7359 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7360 (_("This file is invalid and cannot be played.")),
7361 ("sample data crosses atom boundary"));
7362 ret = GST_FLOW_ERROR;
7365 demux->mdatleft -= demux->neededbytes;
7367 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7368 /* so we are dropping more than left in this atom */
7369 gst_qtdemux_drop_data (demux, demux->mdatleft);
7370 demux->mdatleft = 0;
7372 /* need to resume atom parsing so we do not miss any other pieces */
7373 demux->state = QTDEMUX_STATE_INITIAL;
7374 demux->neededbytes = 16;
7376 /* check if there was any stored post mdat data from previous buffers */
7377 if (demux->restoredata_buffer) {
7378 g_assert (gst_adapter_available (demux->adapter) == 0);
7380 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7381 demux->restoredata_buffer = NULL;
7382 demux->offset = demux->restoredata_offset;
7389 if (demux->todrop) {
7390 if (demux->cenc_aux_info_offset > 0) {
7394 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7395 data = gst_adapter_map (demux->adapter, demux->todrop);
7396 gst_byte_reader_init (&br, data + 8, demux->todrop);
7397 if (!qtdemux_parse_cenc_aux_info (demux,
7398 QTDEMUX_NTH_STREAM (demux, 0), &br,
7399 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7400 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7401 ret = GST_FLOW_ERROR;
7402 gst_adapter_unmap (demux->adapter);
7403 g_free (demux->cenc_aux_info_sizes);
7404 demux->cenc_aux_info_sizes = NULL;
7407 demux->cenc_aux_info_offset = 0;
7408 g_free (demux->cenc_aux_info_sizes);
7409 demux->cenc_aux_info_sizes = NULL;
7410 gst_adapter_unmap (demux->adapter);
7412 gst_qtdemux_drop_data (demux, demux->todrop);
7416 /* initial newsegment sent here after having added pads,
7417 * possible others in sink_event */
7418 gst_qtdemux_check_send_pending_segment (demux);
7420 /* Figure out which stream this packet belongs to */
7421 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7422 stream = QTDEMUX_NTH_STREAM (demux, i);
7423 if (stream->sample_index >= stream->n_samples) {
7424 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7428 GST_LOG_OBJECT (demux,
7429 "Checking track-id %u (sample_index:%d / offset:%"
7430 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7431 stream->sample_index,
7432 stream->samples[stream->sample_index].offset,
7433 stream->samples[stream->sample_index].size);
7435 if (stream->samples[stream->sample_index].offset == demux->offset)
7439 if (G_UNLIKELY (stream == NULL))
7440 goto unknown_stream;
7442 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7444 if (stream->new_caps) {
7445 gst_qtdemux_configure_stream (demux, stream);
7448 /* Put data in a buffer, set timestamps, caps, ... */
7449 sample = &stream->samples[stream->sample_index];
7451 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7452 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7453 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7455 dts = QTSAMPLE_DTS (stream, sample);
7456 pts = QTSAMPLE_PTS (stream, sample);
7457 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7458 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7460 /* check for segment end */
7461 if (G_UNLIKELY (demux->segment.stop != -1
7462 && demux->segment.stop <= pts && stream->on_keyframe)
7463 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7464 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7465 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7467 /* skip this data, stream is EOS */
7468 gst_adapter_flush (demux->adapter, demux->neededbytes);
7469 demux->offset += demux->neededbytes;
7471 /* check if all streams are eos */
7473 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7474 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
7483 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7485 /* FIXME: should either be an assert or a plain check */
7486 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7488 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7489 dts, pts, duration, keyframe, dts, demux->offset);
7493 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7495 /* skip this data, stream is EOS */
7496 gst_adapter_flush (demux->adapter, demux->neededbytes);
7499 stream->sample_index++;
7500 stream->offset_in_sample = 0;
7502 /* update current offset and figure out size of next buffer */
7503 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7504 demux->offset, demux->neededbytes);
7505 demux->offset += demux->neededbytes;
7506 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7510 if (ret == GST_FLOW_EOS) {
7511 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7512 demux->neededbytes = -1;
7516 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7517 if (demux->fragmented) {
7518 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7519 /* there may be more to follow, only finish this atom */
7520 demux->todrop = demux->mdatleft;
7521 demux->neededbytes = demux->todrop;
7526 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7527 goto non_ok_unlinked_flow;
7536 /* when buffering movie data, at least show user something is happening */
7537 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7538 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7539 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7540 demux->neededbytes);
7547 non_ok_unlinked_flow:
7549 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7550 gst_flow_get_name (ret));
7555 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7556 ret = GST_FLOW_ERROR;
7561 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7567 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7568 (NULL), ("qtdemuxer invalid state %d", demux->state));
7569 ret = GST_FLOW_ERROR;
7574 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7575 (NULL), ("no 'moov' atom within the first 10 MB"));
7576 ret = GST_FLOW_ERROR;
7582 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7587 query = gst_query_new_scheduling ();
7589 if (!gst_pad_peer_query (sinkpad, query)) {
7590 gst_query_unref (query);
7594 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7595 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7596 gst_query_unref (query);
7601 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7602 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7606 GST_DEBUG_OBJECT (sinkpad, "activating push");
7607 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7612 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7613 GstPadMode mode, gboolean active)
7616 GstQTDemux *demux = GST_QTDEMUX (parent);
7619 case GST_PAD_MODE_PUSH:
7620 demux->pullbased = FALSE;
7623 case GST_PAD_MODE_PULL:
7625 demux->pullbased = TRUE;
7626 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7629 res = gst_pad_stop_task (sinkpad);
7641 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7647 memset (&z, 0, sizeof (z));
7652 if ((ret = inflateInit (&z)) != Z_OK) {
7653 GST_ERROR ("inflateInit() returned %d", ret);
7657 z.next_in = z_buffer;
7658 z.avail_in = z_length;
7660 buffer = (guint8 *) g_malloc (*length);
7661 z.avail_out = *length;
7662 z.next_out = (Bytef *) buffer;
7664 ret = inflate (&z, Z_NO_FLUSH);
7665 if (ret == Z_STREAM_END) {
7667 } else if (ret != Z_OK) {
7668 GST_WARNING ("inflate() returned %d", ret);
7673 buffer = (guint8 *) g_realloc (buffer, *length);
7674 z.next_out = (Bytef *) (buffer + z.total_out);
7675 z.avail_out += 4096;
7676 } while (z.avail_in > 0);
7678 if (ret != Z_STREAM_END) {
7683 *length = z.total_out;
7690 #endif /* HAVE_ZLIB */
7693 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7697 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7699 /* counts as header data */
7700 qtdemux->header_size += length;
7702 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7703 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7705 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7712 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7713 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7714 if (dcom == NULL || cmvd == NULL)
7715 goto invalid_compression;
7717 dcom_len = QT_UINT32 (dcom->data);
7719 goto invalid_compression;
7721 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7725 guint uncompressed_length;
7726 guint compressed_length;
7730 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7732 goto invalid_compression;
7734 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7735 compressed_length = cmvd_len - 12;
7736 GST_LOG ("length = %u", uncompressed_length);
7739 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7740 compressed_length, &uncompressed_length);
7743 qtdemux->moov_node_compressed = qtdemux->moov_node;
7744 qtdemux->moov_node = g_node_new (buf);
7746 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7747 uncompressed_length);
7751 #endif /* HAVE_ZLIB */
7753 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7754 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7761 invalid_compression:
7763 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
7769 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
7772 while (G_UNLIKELY (buf < end)) {
7776 if (G_UNLIKELY (buf + 4 > end)) {
7777 GST_LOG_OBJECT (qtdemux, "buffer overrun");
7780 len = QT_UINT32 (buf);
7781 if (G_UNLIKELY (len == 0)) {
7782 GST_LOG_OBJECT (qtdemux, "empty container");
7785 if (G_UNLIKELY (len < 8)) {
7786 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
7789 if (G_UNLIKELY (len > (end - buf))) {
7790 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
7791 (gint) (end - buf));
7795 child = g_node_new ((guint8 *) buf);
7796 g_node_append (node, child);
7797 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
7798 qtdemux_parse_node (qtdemux, child, buf, len);
7806 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
7809 int len = QT_UINT32 (xdxt->data);
7810 guint8 *buf = xdxt->data;
7811 guint8 *end = buf + len;
7814 /* skip size and type */
7822 size = QT_UINT32 (buf);
7823 type = QT_FOURCC (buf + 4);
7825 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
7827 if (buf + size > end || size <= 0)
7833 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
7834 GST_FOURCC_ARGS (type));
7838 buffer = gst_buffer_new_and_alloc (size);
7839 gst_buffer_fill (buffer, 0, buf, size);
7840 stream->buffers = g_slist_append (stream->buffers, buffer);
7841 GST_LOG_OBJECT (qtdemux, "parsing theora header");
7844 buffer = gst_buffer_new_and_alloc (size);
7845 gst_buffer_fill (buffer, 0, buf, size);
7846 stream->buffers = g_slist_append (stream->buffers, buffer);
7847 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
7850 buffer = gst_buffer_new_and_alloc (size);
7851 gst_buffer_fill (buffer, 0, buf, size);
7852 stream->buffers = g_slist_append (stream->buffers, buffer);
7853 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
7856 GST_WARNING_OBJECT (qtdemux,
7857 "unknown theora cookie %" GST_FOURCC_FORMAT,
7858 GST_FOURCC_ARGS (type));
7867 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
7871 guint32 node_length = 0;
7872 const QtNodeType *type;
7875 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
7877 if (G_UNLIKELY (length < 8))
7878 goto not_enough_data;
7880 node_length = QT_UINT32 (buffer);
7881 fourcc = QT_FOURCC (buffer + 4);
7883 /* ignore empty nodes */
7884 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
7887 type = qtdemux_type_get (fourcc);
7889 end = buffer + length;
7891 GST_LOG_OBJECT (qtdemux,
7892 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
7893 GST_FOURCC_ARGS (fourcc), node_length, type->name);
7895 if (node_length > length)
7896 goto broken_atom_size;
7898 if (type->flags & QT_FLAG_CONTAINER) {
7899 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7904 if (node_length < 20) {
7905 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
7908 GST_DEBUG_OBJECT (qtdemux,
7909 "parsing stsd (sample table, sample description) atom");
7910 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
7911 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7922 /* also read alac (or whatever) in stead of mp4a in the following,
7923 * since a similar layout is used in other cases as well */
7924 if (fourcc == FOURCC_mp4a)
7926 else if (fourcc == FOURCC_fLaC)
7931 /* There are two things we might encounter here: a true mp4a atom, and
7932 an mp4a entry in an stsd atom. The latter is what we're interested
7933 in, and it looks like an atom, but isn't really one. The true mp4a
7934 atom is short, so we detect it based on length here. */
7935 if (length < min_size) {
7936 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7937 GST_FOURCC_ARGS (fourcc));
7941 /* 'version' here is the sound sample description version. Types 0 and
7942 1 are documented in the QTFF reference, but type 2 is not: it's
7943 described in Apple header files instead (struct SoundDescriptionV2
7945 version = QT_UINT16 (buffer + 16);
7947 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7948 GST_FOURCC_ARGS (fourcc), version);
7950 /* parse any esds descriptors */
7962 GST_WARNING_OBJECT (qtdemux,
7963 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7964 GST_FOURCC_ARGS (fourcc), version);
7969 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7995 /* codec_data is contained inside these atoms, which all have
7996 * the same format. */
7997 /* video sample description size is 86 bytes without extension.
7998 * node_length have to be bigger than 86 bytes because video sample
7999 * description can include extenstions such as esds, fiel, glbl, etc. */
8000 if (node_length < 86) {
8001 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8002 " sample description length too short (%u < 86)",
8003 GST_FOURCC_ARGS (fourcc), node_length);
8007 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8008 GST_FOURCC_ARGS (fourcc));
8010 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8012 * revision level (2 bytes) : must be set to 0. */
8013 version = QT_UINT32 (buffer + 16);
8014 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8016 /* compressor name : PASCAL string and informative purposes
8017 * first byte : the number of bytes to be displayed.
8018 * it has to be less than 32 because it is reserved
8019 * space of 32 bytes total including itself. */
8020 str_len = QT_UINT8 (buffer + 50);
8022 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8023 (char *) buffer + 51);
8025 GST_WARNING_OBJECT (qtdemux,
8026 "compressorname length too big (%u > 31)", str_len);
8028 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8030 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8035 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8037 /* You are reading this correctly. QTFF specifies that the
8038 * metadata atom is a short atom, whereas ISO BMFF specifies
8039 * it's a full atom. But since so many people are doing things
8040 * differently, we actually peek into the atom to see which
8043 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8044 GST_FOURCC_ARGS (fourcc));
8047 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8048 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8049 * starts with a 'hdlr' atom */
8050 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8051 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8052 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8053 * with version/flags both set to zero */
8054 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8056 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8061 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8062 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8063 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8072 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8073 GST_FOURCC_ARGS (fourcc));
8077 version = QT_UINT32 (buffer + 12);
8078 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8085 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8090 if (length < offset) {
8091 GST_WARNING_OBJECT (qtdemux,
8092 "skipping too small %" GST_FOURCC_FORMAT " box",
8093 GST_FOURCC_ARGS (fourcc));
8096 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8102 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8107 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8112 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8116 if (!strcmp (type->name, "unknown"))
8117 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8121 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8122 GST_FOURCC_ARGS (fourcc));
8128 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8129 (_("This file is corrupt and cannot be played.")),
8130 ("Not enough data for an atom header, got only %u bytes", length));
8135 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8136 (_("This file is corrupt and cannot be played.")),
8137 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8138 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8145 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8149 guint32 child_fourcc;
8151 for (child = g_node_first_child (node); child;
8152 child = g_node_next_sibling (child)) {
8153 buffer = (guint8 *) child->data;
8155 child_fourcc = QT_FOURCC (buffer + 4);
8157 if (G_UNLIKELY (child_fourcc == fourcc)) {
8165 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8166 GstByteReader * parser)
8170 guint32 child_fourcc, child_len;
8172 for (child = g_node_first_child (node); child;
8173 child = g_node_next_sibling (child)) {
8174 buffer = (guint8 *) child->data;
8176 child_len = QT_UINT32 (buffer);
8177 child_fourcc = QT_FOURCC (buffer + 4);
8179 if (G_UNLIKELY (child_fourcc == fourcc)) {
8180 if (G_UNLIKELY (child_len < (4 + 4)))
8182 /* FIXME: must verify if atom length < parent atom length */
8183 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8191 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8193 return g_node_nth_child (node, index);
8197 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8198 GstByteReader * parser)
8202 guint32 child_fourcc, child_len;
8204 for (child = g_node_next_sibling (node); child;
8205 child = g_node_next_sibling (child)) {
8206 buffer = (guint8 *) child->data;
8208 child_fourcc = QT_FOURCC (buffer + 4);
8210 if (child_fourcc == fourcc) {
8212 child_len = QT_UINT32 (buffer);
8213 if (G_UNLIKELY (child_len < (4 + 4)))
8215 /* FIXME: must verify if atom length < parent atom length */
8216 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8225 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8227 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8231 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8233 /* FIXME: This can only reliably work if demuxers have a
8234 * separate streaming thread per srcpad. This should be
8235 * done in a demuxer base class, which integrates parts
8238 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8243 query = gst_query_new_allocation (stream->caps, FALSE);
8245 if (!gst_pad_peer_query (stream->pad, query)) {
8246 /* not a problem, just debug a little */
8247 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8250 if (stream->allocator)
8251 gst_object_unref (stream->allocator);
8253 if (gst_query_get_n_allocation_params (query) > 0) {
8254 /* try the allocator */
8255 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8257 stream->use_allocator = TRUE;
8259 stream->allocator = NULL;
8260 gst_allocation_params_init (&stream->params);
8261 stream->use_allocator = FALSE;
8263 gst_query_unref (query);
8268 pad_query (const GValue * item, GValue * value, gpointer user_data)
8270 GstPad *pad = g_value_get_object (item);
8271 GstQuery *query = user_data;
8274 res = gst_pad_peer_query (pad, query);
8277 g_value_set_boolean (value, TRUE);
8281 GST_INFO_OBJECT (pad, "pad peer query failed");
8286 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8287 GstPadDirection direction)
8290 GstIteratorFoldFunction func = pad_query;
8291 GValue res = { 0, };
8293 g_value_init (&res, G_TYPE_BOOLEAN);
8294 g_value_set_boolean (&res, FALSE);
8297 if (direction == GST_PAD_SRC)
8298 it = gst_element_iterate_src_pads (element);
8300 it = gst_element_iterate_sink_pads (element);
8302 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8303 gst_iterator_resync (it);
8305 gst_iterator_free (it);
8307 return g_value_get_boolean (&res);
8311 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8312 QtDemuxStream * stream)
8316 GstElement *element = GST_ELEMENT (qtdemux);
8318 gchar **filtered_sys_ids;
8319 GValue event_list = G_VALUE_INIT;
8322 /* 1. Check if we already have the context. */
8323 if (qtdemux->preferred_protection_system_id != NULL) {
8324 GST_LOG_OBJECT (element,
8325 "already have the protection context, no need to request it again");
8329 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8330 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8331 (const gchar **) qtdemux->protection_system_ids->pdata);
8333 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8334 qtdemux->protection_system_ids->len - 1);
8335 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8336 "decryptors for %u of them, running context request",
8337 qtdemux->protection_system_ids->len,
8338 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8341 if (stream->protection_scheme_event_queue.length) {
8342 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8343 stream->protection_scheme_event_queue.length);
8344 walk = stream->protection_scheme_event_queue.tail;
8346 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8347 qtdemux->protection_event_queue.length);
8348 walk = qtdemux->protection_event_queue.tail;
8351 g_value_init (&event_list, GST_TYPE_LIST);
8352 for (; walk; walk = g_list_previous (walk)) {
8353 GValue *event_value = g_new0 (GValue, 1);
8354 g_value_init (event_value, GST_TYPE_EVENT);
8355 g_value_set_boxed (event_value, walk->data);
8356 gst_value_list_append_and_take_value (&event_list, event_value);
8359 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8360 * check if downstream already has a context of the specific type
8361 * 2b) Query upstream as above.
8363 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8364 st = gst_query_writable_structure (query);
8365 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8366 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8368 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8369 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8370 gst_query_parse_context (query, &ctxt);
8371 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8372 gst_element_set_context (element, ctxt);
8373 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8374 gst_query_parse_context (query, &ctxt);
8375 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8376 gst_element_set_context (element, ctxt);
8378 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8379 * the required context type and afterwards check if a
8380 * usable context was set now as in 1). The message could
8381 * be handled by the parent bins of the element and the
8386 GST_INFO_OBJECT (element, "posting need context message");
8387 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8388 "drm-preferred-decryption-system-id");
8389 st = (GstStructure *) gst_message_get_structure (msg);
8390 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8391 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8394 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8395 gst_element_post_message (element, msg);
8398 g_strfreev (filtered_sys_ids);
8399 g_value_unset (&event_list);
8400 gst_query_unref (query);
8404 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8405 QtDemuxStream * stream)
8408 const gchar *selected_system = NULL;
8410 g_return_val_if_fail (qtdemux != NULL, FALSE);
8411 g_return_val_if_fail (stream != NULL, FALSE);
8412 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8415 if (stream->protection_scheme_type != FOURCC_cenc) {
8416 GST_ERROR_OBJECT (qtdemux,
8417 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8418 GST_FOURCC_ARGS (stream->protection_scheme_type));
8421 if (qtdemux->protection_system_ids == NULL) {
8422 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
8423 "cenc protection system information has been found");
8427 gst_qtdemux_request_protection_context (qtdemux, stream);
8428 if (qtdemux->preferred_protection_system_id != NULL) {
8429 const gchar *preferred_system_array[] =
8430 { qtdemux->preferred_protection_system_id, NULL };
8432 selected_system = gst_protection_select_system (preferred_system_array);
8434 if (selected_system) {
8435 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8436 qtdemux->preferred_protection_system_id);
8438 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8439 "because there is no available decryptor",
8440 qtdemux->preferred_protection_system_id);
8444 if (!selected_system) {
8445 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8446 selected_system = gst_protection_select_system ((const gchar **)
8447 qtdemux->protection_system_ids->pdata);
8448 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8449 qtdemux->protection_system_ids->len - 1);
8452 if (!selected_system) {
8453 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8454 "suitable decryptor element has been found");
8458 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8461 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8462 if (!gst_structure_has_name (s, "application/x-cenc")) {
8463 gst_structure_set (s,
8464 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8465 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8467 gst_structure_set_name (s, "application/x-cenc");
8473 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
8475 /* fps is calculated base on the duration of the average framerate since
8476 * qt does not have a fixed framerate. */
8477 gboolean fps_available = TRUE;
8478 guint32 first_duration = 0;
8480 if (stream->n_samples > 0)
8481 first_duration = stream->samples[0].duration;
8483 if ((stream->n_samples == 1 && first_duration == 0)
8484 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8486 CUR_STREAM (stream)->fps_n = 0;
8487 CUR_STREAM (stream)->fps_d = 1;
8489 if (stream->duration == 0 || stream->n_samples < 2) {
8490 CUR_STREAM (stream)->fps_n = stream->timescale;
8491 CUR_STREAM (stream)->fps_d = 1;
8492 fps_available = FALSE;
8494 GstClockTime avg_duration;
8498 /* duration and n_samples can be updated for fragmented format
8499 * so, framerate of fragmented format is calculated using data in a moof */
8500 if (qtdemux->fragmented && stream->n_samples_moof > 0
8501 && stream->duration_moof > 0) {
8502 n_samples = stream->n_samples_moof;
8503 duration = stream->duration_moof;
8505 n_samples = stream->n_samples;
8506 duration = stream->duration;
8509 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8510 /* stream->duration is guint64, timescale, n_samples are guint32 */
8512 gst_util_uint64_scale_round (duration -
8513 first_duration, GST_SECOND,
8514 (guint64) (stream->timescale) * (n_samples - 1));
8516 GST_LOG_OBJECT (qtdemux,
8517 "Calculating avg sample duration based on stream (or moof) duration %"
8519 " minus first sample %u, leaving %d samples gives %"
8520 GST_TIME_FORMAT, duration, first_duration,
8521 n_samples - 1, GST_TIME_ARGS (avg_duration));
8524 gst_video_guess_framerate (avg_duration,
8525 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
8527 GST_DEBUG_OBJECT (qtdemux,
8528 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8529 stream->timescale, CUR_STREAM (stream)->fps_n,
8530 CUR_STREAM (stream)->fps_d);
8534 return fps_available;
8538 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8540 if (stream->subtype == FOURCC_vide) {
8541 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8543 if (CUR_STREAM (stream)->caps) {
8544 CUR_STREAM (stream)->caps =
8545 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8547 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
8548 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8549 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8550 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8552 /* set framerate if calculated framerate is reliable */
8553 if (fps_available) {
8554 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8555 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8556 CUR_STREAM (stream)->fps_d, NULL);
8559 /* calculate pixel-aspect-ratio using display width and height */
8560 GST_DEBUG_OBJECT (qtdemux,
8561 "video size %dx%d, target display size %dx%d",
8562 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8563 stream->display_width, stream->display_height);
8564 /* qt file might have pasp atom */
8565 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8566 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8567 CUR_STREAM (stream)->par_h);
8568 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8569 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8570 CUR_STREAM (stream)->par_h, NULL);
8571 } else if (stream->display_width > 0 && stream->display_height > 0
8572 && CUR_STREAM (stream)->width > 0
8573 && CUR_STREAM (stream)->height > 0) {
8576 /* calculate the pixel aspect ratio using the display and pixel w/h */
8577 n = stream->display_width * CUR_STREAM (stream)->height;
8578 d = stream->display_height * CUR_STREAM (stream)->width;
8581 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8582 CUR_STREAM (stream)->par_w = n;
8583 CUR_STREAM (stream)->par_h = d;
8584 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8585 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8586 CUR_STREAM (stream)->par_h, NULL);
8589 if (CUR_STREAM (stream)->interlace_mode > 0) {
8590 if (CUR_STREAM (stream)->interlace_mode == 1) {
8591 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8592 G_TYPE_STRING, "progressive", NULL);
8593 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8594 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8595 G_TYPE_STRING, "interleaved", NULL);
8596 if (CUR_STREAM (stream)->field_order == 9) {
8597 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8598 G_TYPE_STRING, "top-field-first", NULL);
8599 } else if (CUR_STREAM (stream)->field_order == 14) {
8600 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8601 G_TYPE_STRING, "bottom-field-first", NULL);
8606 /* Create incomplete colorimetry here if needed */
8607 if (CUR_STREAM (stream)->colorimetry.range ||
8608 CUR_STREAM (stream)->colorimetry.matrix ||
8609 CUR_STREAM (stream)->colorimetry.transfer
8610 || CUR_STREAM (stream)->colorimetry.primaries) {
8611 gchar *colorimetry =
8612 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8613 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8614 G_TYPE_STRING, colorimetry, NULL);
8615 g_free (colorimetry);
8618 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8619 guint par_w = 1, par_h = 1;
8621 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8622 par_w = CUR_STREAM (stream)->par_w;
8623 par_h = CUR_STREAM (stream)->par_h;
8626 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8627 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8629 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8632 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8633 "multiview-mode", G_TYPE_STRING,
8634 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8635 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8636 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8641 else if (stream->subtype == FOURCC_soun) {
8642 if (CUR_STREAM (stream)->caps) {
8643 CUR_STREAM (stream)->caps =
8644 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8645 if (CUR_STREAM (stream)->rate > 0)
8646 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8647 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8648 if (CUR_STREAM (stream)->n_channels > 0)
8649 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8650 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8651 if (CUR_STREAM (stream)->n_channels > 2) {
8652 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8653 * correctly; this is just the minimum we can do - assume
8654 * we don't actually have any channel positions. */
8655 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8656 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8661 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
8662 const GstStructure *s;
8663 QtDemuxStream *fps_stream = NULL;
8664 gboolean fps_available = FALSE;
8666 /* CEA608 closed caption tracks are a bit special in that each sample
8667 * can contain CCs for multiple frames, and CCs can be omitted and have to
8668 * be inferred from the duration of the sample then.
8670 * As such we take the framerate from the (first) video track here for
8671 * CEA608 as there must be one CC byte pair for every video frame
8672 * according to the spec.
8674 * For CEA708 all is fine and there is one sample per frame.
8677 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8678 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
8681 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
8682 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
8684 if (tmp->subtype == FOURCC_vide) {
8691 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
8692 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
8693 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
8696 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8697 fps_stream = stream;
8700 CUR_STREAM (stream)->caps =
8701 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8703 /* set framerate if calculated framerate is reliable */
8704 if (fps_available) {
8705 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8706 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8707 CUR_STREAM (stream)->fps_d, NULL);
8712 GstCaps *prev_caps = NULL;
8714 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8715 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8716 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8717 gst_pad_set_active (stream->pad, TRUE);
8719 gst_pad_use_fixed_caps (stream->pad);
8721 if (stream->protected) {
8722 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8723 GST_ERROR_OBJECT (qtdemux,
8724 "Failed to configure protected stream caps.");
8729 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8730 CUR_STREAM (stream)->caps);
8731 if (stream->new_stream) {
8733 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8736 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8739 gst_event_parse_stream_flags (event, &stream_flags);
8740 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8741 qtdemux->have_group_id = TRUE;
8743 qtdemux->have_group_id = FALSE;
8744 gst_event_unref (event);
8745 } else if (!qtdemux->have_group_id) {
8746 qtdemux->have_group_id = TRUE;
8747 qtdemux->group_id = gst_util_group_id_next ();
8750 stream->new_stream = FALSE;
8751 event = gst_event_new_stream_start (stream->stream_id);
8752 if (qtdemux->have_group_id)
8753 gst_event_set_group_id (event, qtdemux->group_id);
8754 if (stream->disabled)
8755 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8756 if (CUR_STREAM (stream)->sparse) {
8757 stream_flags |= GST_STREAM_FLAG_SPARSE;
8759 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8761 gst_event_set_stream_flags (event, stream_flags);
8762 gst_pad_push_event (stream->pad, event);
8765 prev_caps = gst_pad_get_current_caps (stream->pad);
8767 if (CUR_STREAM (stream)->caps) {
8769 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8770 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8771 CUR_STREAM (stream)->caps);
8772 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8774 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8777 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8781 gst_caps_unref (prev_caps);
8782 stream->new_caps = FALSE;
8788 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8789 QtDemuxStream * stream)
8791 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8794 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8795 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8796 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8797 stream->stsd_entries_length)) {
8798 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8799 (_("This file is invalid and cannot be played.")),
8800 ("New sample description id is out of bounds (%d >= %d)",
8801 stream->stsd_sample_description_id, stream->stsd_entries_length));
8803 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8804 stream->new_caps = TRUE;
8809 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8810 QtDemuxStream * stream, GstTagList * list)
8812 gboolean ret = TRUE;
8814 if (stream->subtype == FOURCC_vide) {
8815 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8818 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8821 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8822 gst_object_unref (stream->pad);
8828 qtdemux->n_video_streams++;
8829 } else if (stream->subtype == FOURCC_soun) {
8830 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
8833 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
8835 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8836 gst_object_unref (stream->pad);
8841 qtdemux->n_audio_streams++;
8842 } else if (stream->subtype == FOURCC_strm) {
8843 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
8844 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
8845 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
8846 || stream->subtype == FOURCC_clcp) {
8847 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
8850 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
8852 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8853 gst_object_unref (stream->pad);
8858 qtdemux->n_sub_streams++;
8859 } else if (CUR_STREAM (stream)->caps) {
8860 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8863 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8865 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8866 gst_object_unref (stream->pad);
8871 qtdemux->n_video_streams++;
8873 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
8880 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
8881 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
8882 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
8883 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
8885 if (stream->stream_tags)
8886 gst_tag_list_unref (stream->stream_tags);
8887 stream->stream_tags = list;
8889 /* global tags go on each pad anyway */
8890 stream->send_global_tags = TRUE;
8891 /* send upstream GST_EVENT_PROTECTION events that were received before
8892 this source pad was created */
8893 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
8894 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
8898 gst_tag_list_unref (list);
8902 /* find next atom with @fourcc starting at @offset */
8903 static GstFlowReturn
8904 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
8905 guint64 * length, guint32 fourcc)
8911 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
8912 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
8918 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
8919 if (G_UNLIKELY (ret != GST_FLOW_OK))
8921 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
8924 gst_buffer_unref (buf);
8927 gst_buffer_map (buf, &map, GST_MAP_READ);
8928 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
8929 gst_buffer_unmap (buf, &map);
8930 gst_buffer_unref (buf);
8932 if (G_UNLIKELY (*length == 0)) {
8933 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
8934 ret = GST_FLOW_ERROR;
8938 if (lfourcc == fourcc) {
8939 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
8943 GST_LOG_OBJECT (qtdemux,
8944 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
8945 GST_FOURCC_ARGS (fourcc), *offset);
8954 /* might simply have had last one */
8955 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
8960 /* should only do something in pull mode */
8961 /* call with OBJECT lock */
8962 static GstFlowReturn
8963 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
8965 guint64 length, offset;
8966 GstBuffer *buf = NULL;
8967 GstFlowReturn ret = GST_FLOW_OK;
8968 GstFlowReturn res = GST_FLOW_OK;
8971 offset = qtdemux->moof_offset;
8972 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
8975 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8976 return GST_FLOW_EOS;
8979 /* best not do pull etc with lock held */
8980 GST_OBJECT_UNLOCK (qtdemux);
8982 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8983 if (ret != GST_FLOW_OK)
8986 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
8987 if (G_UNLIKELY (ret != GST_FLOW_OK))
8989 gst_buffer_map (buf, &map, GST_MAP_READ);
8990 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
8991 gst_buffer_unmap (buf, &map);
8992 gst_buffer_unref (buf);
8997 gst_buffer_unmap (buf, &map);
8998 gst_buffer_unref (buf);
9002 /* look for next moof */
9003 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9004 if (G_UNLIKELY (ret != GST_FLOW_OK))
9008 GST_OBJECT_LOCK (qtdemux);
9010 qtdemux->moof_offset = offset;
9016 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9018 res = GST_FLOW_ERROR;
9023 /* maybe upstream temporarily flushing */
9024 if (ret != GST_FLOW_FLUSHING) {
9025 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9028 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9029 /* resume at current position next time */
9036 /* initialise bytereaders for stbl sub-atoms */
9038 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9040 stream->stbl_index = -1; /* no samples have yet been parsed */
9041 stream->sample_index = -1;
9043 /* time-to-sample atom */
9044 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9047 /* copy atom data into a new buffer for later use */
9048 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
9050 /* skip version + flags */
9051 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9052 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9054 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9056 /* make sure there's enough data */
9057 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9058 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9059 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9060 stream->n_sample_times);
9061 if (!stream->n_sample_times)
9065 /* sync sample atom */
9066 stream->stps_present = FALSE;
9067 if ((stream->stss_present =
9068 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9069 &stream->stss) ? TRUE : FALSE) == TRUE) {
9070 /* copy atom data into a new buffer for later use */
9071 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
9073 /* skip version + flags */
9074 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9075 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9078 if (stream->n_sample_syncs) {
9079 /* make sure there's enough data */
9080 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9084 /* partial sync sample atom */
9085 if ((stream->stps_present =
9086 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9087 &stream->stps) ? TRUE : FALSE) == TRUE) {
9088 /* copy atom data into a new buffer for later use */
9089 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
9091 /* skip version + flags */
9092 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9093 !gst_byte_reader_get_uint32_be (&stream->stps,
9094 &stream->n_sample_partial_syncs))
9097 /* if there are no entries, the stss table contains the real
9099 if (stream->n_sample_partial_syncs) {
9100 /* make sure there's enough data */
9101 if (!qt_atom_parser_has_chunks (&stream->stps,
9102 stream->n_sample_partial_syncs, 4))
9109 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9112 /* copy atom data into a new buffer for later use */
9113 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
9115 /* skip version + flags */
9116 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9117 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9120 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9123 if (!stream->n_samples)
9126 /* sample-to-chunk atom */
9127 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9130 /* copy atom data into a new buffer for later use */
9131 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
9133 /* skip version + flags */
9134 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9135 !gst_byte_reader_get_uint32_be (&stream->stsc,
9136 &stream->n_samples_per_chunk))
9139 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9140 stream->n_samples_per_chunk);
9142 /* make sure there's enough data */
9143 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9149 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9150 stream->co_size = sizeof (guint32);
9151 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9153 stream->co_size = sizeof (guint64);
9157 /* copy atom data into a new buffer for later use */
9158 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
9160 /* skip version + flags */
9161 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9164 /* chunks_are_samples == TRUE means treat chunks as samples */
9165 stream->chunks_are_samples = stream->sample_size
9166 && !CUR_STREAM (stream)->sampled;
9167 if (stream->chunks_are_samples) {
9168 /* treat chunks as samples */
9169 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9172 /* skip number of entries */
9173 if (!gst_byte_reader_skip (&stream->stco, 4))
9176 /* make sure there are enough data in the stsz atom */
9177 if (!stream->sample_size) {
9178 /* different sizes for each sample */
9179 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9184 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9185 stream->n_samples, (guint) sizeof (QtDemuxSample),
9186 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9188 if (stream->n_samples >=
9189 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9190 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9191 "be larger than %uMB (broken file?)", stream->n_samples,
9192 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9196 g_assert (stream->samples == NULL);
9197 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9198 if (!stream->samples) {
9199 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9204 /* composition time-to-sample */
9205 if ((stream->ctts_present =
9206 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9207 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9208 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9210 /* copy atom data into a new buffer for later use */
9211 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
9213 /* skip version + flags */
9214 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9215 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9216 &stream->n_composition_times))
9219 /* make sure there's enough data */
9220 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9224 /* This is optional, if missing we iterate the ctts */
9225 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9226 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9227 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9228 g_free ((gpointer) cslg.data);
9232 gint32 cslg_least = 0;
9233 guint num_entries, pos;
9236 pos = gst_byte_reader_get_pos (&stream->ctts);
9237 num_entries = stream->n_composition_times;
9239 stream->cslg_shift = 0;
9241 for (i = 0; i < num_entries; i++) {
9244 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9245 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9246 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9247 * slightly inaccurate PTS could be more usable than corrupted one */
9248 if (G_UNLIKELY ((ABS (offset) / 2) > stream->duration)) {
9249 GST_WARNING_OBJECT (qtdemux,
9250 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9251 " larger than duration %" G_GUINT64_FORMAT,
9252 offset, stream->duration);
9254 stream->cslg_shift = 0;
9255 stream->ctts_present = FALSE;
9259 if (offset < cslg_least)
9260 cslg_least = offset;
9264 stream->cslg_shift = ABS (cslg_least);
9266 stream->cslg_shift = 0;
9268 /* reset the reader so we can generate sample table */
9269 gst_byte_reader_set_pos (&stream->ctts, pos);
9272 /* Ensure the cslg_shift value is consistent so we can use it
9273 * unconditionnally to produce TS and Segment */
9274 stream->cslg_shift = 0;
9281 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9282 (_("This file is corrupt and cannot be played.")), (NULL));
9287 gst_qtdemux_stbl_free (stream);
9288 if (!qtdemux->fragmented) {
9289 /* not quite good */
9290 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9293 /* may pick up samples elsewhere */
9299 /* collect samples from the next sample to be parsed up to sample @n for @stream
9300 * by reading the info from @stbl
9302 * This code can be executed from both the streaming thread and the seeking
9303 * thread so it takes the object lock to protect itself
9306 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9309 QtDemuxSample *samples, *first, *cur, *last;
9310 guint32 n_samples_per_chunk;
9313 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9314 GST_FOURCC_FORMAT ", pad %s",
9315 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9316 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9318 n_samples = stream->n_samples;
9321 goto out_of_samples;
9323 GST_OBJECT_LOCK (qtdemux);
9324 if (n <= stream->stbl_index)
9325 goto already_parsed;
9327 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9329 if (!stream->stsz.data) {
9330 /* so we already parsed and passed all the moov samples;
9331 * onto fragmented ones */
9332 g_assert (qtdemux->fragmented);
9336 /* pointer to the sample table */
9337 samples = stream->samples;
9339 /* starts from -1, moves to the next sample index to parse */
9340 stream->stbl_index++;
9342 /* keep track of the first and last sample to fill */
9343 first = &samples[stream->stbl_index];
9346 if (!stream->chunks_are_samples) {
9347 /* set the sample sizes */
9348 if (stream->sample_size == 0) {
9349 /* different sizes for each sample */
9350 for (cur = first; cur <= last; cur++) {
9351 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9352 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9353 (guint) (cur - samples), cur->size);
9356 /* samples have the same size */
9357 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9358 for (cur = first; cur <= last; cur++)
9359 cur->size = stream->sample_size;
9363 n_samples_per_chunk = stream->n_samples_per_chunk;
9366 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9369 if (stream->stsc_chunk_index >= stream->last_chunk
9370 || stream->stsc_chunk_index < stream->first_chunk) {
9371 stream->first_chunk =
9372 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9373 stream->samples_per_chunk =
9374 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9376 stream->stsd_sample_description_id =
9377 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9379 /* chunk numbers are counted from 1 it seems */
9380 if (G_UNLIKELY (stream->first_chunk == 0))
9383 --stream->first_chunk;
9385 /* the last chunk of each entry is calculated by taking the first chunk
9386 * of the next entry; except if there is no next, where we fake it with
9388 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9389 stream->last_chunk = G_MAXUINT32;
9391 stream->last_chunk =
9392 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9393 if (G_UNLIKELY (stream->last_chunk == 0))
9396 --stream->last_chunk;
9399 GST_LOG_OBJECT (qtdemux,
9400 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9401 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9402 stream->samples_per_chunk, stream->stsd_sample_description_id);
9404 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9407 if (stream->last_chunk != G_MAXUINT32) {
9408 if (!qt_atom_parser_peek_sub (&stream->stco,
9409 stream->first_chunk * stream->co_size,
9410 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9415 stream->co_chunk = stream->stco;
9416 if (!gst_byte_reader_skip (&stream->co_chunk,
9417 stream->first_chunk * stream->co_size))
9421 stream->stsc_chunk_index = stream->first_chunk;
9424 last_chunk = stream->last_chunk;
9426 if (stream->chunks_are_samples) {
9427 cur = &samples[stream->stsc_chunk_index];
9429 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9432 stream->stsc_chunk_index = j;
9437 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9440 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9441 "%" G_GUINT64_FORMAT, j, cur->offset);
9443 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9444 CUR_STREAM (stream)->bytes_per_frame > 0) {
9446 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9447 CUR_STREAM (stream)->samples_per_frame *
9448 CUR_STREAM (stream)->bytes_per_frame;
9450 cur->size = stream->samples_per_chunk;
9453 GST_DEBUG_OBJECT (qtdemux,
9454 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9455 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9456 stream->stco_sample_index)), cur->size);
9458 cur->timestamp = stream->stco_sample_index;
9459 cur->duration = stream->samples_per_chunk;
9460 cur->keyframe = TRUE;
9463 stream->stco_sample_index += stream->samples_per_chunk;
9465 stream->stsc_chunk_index = j;
9467 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9468 guint32 samples_per_chunk;
9469 guint64 chunk_offset;
9471 if (!stream->stsc_sample_index
9472 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9473 &stream->chunk_offset))
9476 samples_per_chunk = stream->samples_per_chunk;
9477 chunk_offset = stream->chunk_offset;
9479 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9480 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9481 G_GUINT64_FORMAT " and size %d",
9482 (guint) (cur - samples), chunk_offset, cur->size);
9484 cur->offset = chunk_offset;
9485 chunk_offset += cur->size;
9488 if (G_UNLIKELY (cur > last)) {
9490 stream->stsc_sample_index = k + 1;
9491 stream->chunk_offset = chunk_offset;
9492 stream->stsc_chunk_index = j;
9496 stream->stsc_sample_index = 0;
9498 stream->stsc_chunk_index = j;
9500 stream->stsc_index++;
9503 if (stream->chunks_are_samples)
9507 guint32 n_sample_times;
9509 n_sample_times = stream->n_sample_times;
9512 for (i = stream->stts_index; i < n_sample_times; i++) {
9513 guint32 stts_samples;
9514 gint32 stts_duration;
9517 if (stream->stts_sample_index >= stream->stts_samples
9518 || !stream->stts_sample_index) {
9520 stream->stts_samples =
9521 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9522 stream->stts_duration =
9523 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9525 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9526 i, stream->stts_samples, stream->stts_duration);
9528 stream->stts_sample_index = 0;
9531 stts_samples = stream->stts_samples;
9532 stts_duration = stream->stts_duration;
9533 stts_time = stream->stts_time;
9535 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9536 GST_DEBUG_OBJECT (qtdemux,
9537 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9538 (guint) (cur - samples), j,
9539 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9541 cur->timestamp = stts_time;
9542 cur->duration = stts_duration;
9544 /* avoid 32-bit wrap-around,
9545 * but still mind possible 'negative' duration */
9546 stts_time += (gint64) stts_duration;
9549 if (G_UNLIKELY (cur > last)) {
9551 stream->stts_time = stts_time;
9552 stream->stts_sample_index = j + 1;
9553 if (stream->stts_sample_index >= stream->stts_samples)
9554 stream->stts_index++;
9558 stream->stts_sample_index = 0;
9559 stream->stts_time = stts_time;
9560 stream->stts_index++;
9562 /* fill up empty timestamps with the last timestamp, this can happen when
9563 * the last samples do not decode and so we don't have timestamps for them.
9564 * We however look at the last timestamp to estimate the track length so we
9565 * need something in here. */
9566 for (; cur < last; cur++) {
9567 GST_DEBUG_OBJECT (qtdemux,
9568 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9569 (guint) (cur - samples),
9570 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9571 cur->timestamp = stream->stts_time;
9577 /* sample sync, can be NULL */
9578 if (stream->stss_present == TRUE) {
9579 guint32 n_sample_syncs;
9581 n_sample_syncs = stream->n_sample_syncs;
9583 if (!n_sample_syncs) {
9584 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9585 stream->all_keyframe = TRUE;
9587 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9588 /* note that the first sample is index 1, not 0 */
9591 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9593 if (G_LIKELY (index > 0 && index <= n_samples)) {
9595 samples[index].keyframe = TRUE;
9596 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9597 /* and exit if we have enough samples */
9598 if (G_UNLIKELY (index >= n)) {
9605 stream->stss_index = i;
9608 /* stps marks partial sync frames like open GOP I-Frames */
9609 if (stream->stps_present == TRUE) {
9610 guint32 n_sample_partial_syncs;
9612 n_sample_partial_syncs = stream->n_sample_partial_syncs;
9614 /* if there are no entries, the stss table contains the real
9616 if (n_sample_partial_syncs) {
9617 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
9618 /* note that the first sample is index 1, not 0 */
9621 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
9623 if (G_LIKELY (index > 0 && index <= n_samples)) {
9625 samples[index].keyframe = TRUE;
9626 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9627 /* and exit if we have enough samples */
9628 if (G_UNLIKELY (index >= n)) {
9635 stream->stps_index = i;
9639 /* no stss, all samples are keyframes */
9640 stream->all_keyframe = TRUE;
9641 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
9646 /* composition time to sample */
9647 if (stream->ctts_present == TRUE) {
9648 guint32 n_composition_times;
9650 gint32 ctts_soffset;
9652 /* Fill in the pts_offsets */
9654 n_composition_times = stream->n_composition_times;
9656 for (i = stream->ctts_index; i < n_composition_times; i++) {
9657 if (stream->ctts_sample_index >= stream->ctts_count
9658 || !stream->ctts_sample_index) {
9659 stream->ctts_count =
9660 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
9661 stream->ctts_soffset =
9662 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9663 stream->ctts_sample_index = 0;
9666 ctts_count = stream->ctts_count;
9667 ctts_soffset = stream->ctts_soffset;
9669 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
9670 cur->pts_offset = ctts_soffset;
9673 if (G_UNLIKELY (cur > last)) {
9675 stream->ctts_sample_index = j + 1;
9679 stream->ctts_sample_index = 0;
9680 stream->ctts_index++;
9684 stream->stbl_index = n;
9685 /* if index has been completely parsed, free data that is no-longer needed */
9686 if (n + 1 == stream->n_samples) {
9687 gst_qtdemux_stbl_free (stream);
9688 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
9689 if (qtdemux->pullbased) {
9690 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
9691 while (n + 1 == stream->n_samples)
9692 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
9696 GST_OBJECT_UNLOCK (qtdemux);
9703 GST_LOG_OBJECT (qtdemux,
9704 "Tried to parse up to sample %u but this sample has already been parsed",
9706 /* if fragmented, there may be more */
9707 if (qtdemux->fragmented && n == stream->stbl_index)
9709 GST_OBJECT_UNLOCK (qtdemux);
9715 GST_LOG_OBJECT (qtdemux,
9716 "Tried to parse up to sample %u but there are only %u samples", n + 1,
9718 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9719 (_("This file is corrupt and cannot be played.")), (NULL));
9724 GST_OBJECT_UNLOCK (qtdemux);
9725 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9726 (_("This file is corrupt and cannot be played.")), (NULL));
9731 /* collect all segment info for @stream.
9734 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
9738 /* accept edts if they contain gaps at start and there is only
9739 * one media segment */
9740 gboolean allow_pushbased_edts = TRUE;
9741 gint media_segments_count = 0;
9743 /* parse and prepare segment info from the edit list */
9744 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
9745 stream->n_segments = 0;
9746 stream->segments = NULL;
9747 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
9750 gint segment_number, entry_size;
9753 const guint8 *buffer;
9757 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
9758 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
9761 buffer = elst->data;
9763 size = QT_UINT32 (buffer);
9764 /* version, flags, n_segments */
9766 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9769 version = QT_UINT8 (buffer + 8);
9770 entry_size = (version == 1) ? 20 : 12;
9772 n_segments = QT_UINT32 (buffer + 12);
9774 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
9775 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9779 /* we might allocate a bit too much, at least allocate 1 segment */
9780 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
9782 /* segments always start from 0 */
9786 for (segment_number = 0; segment_number < n_segments; segment_number++) {
9789 gboolean empty_edit = FALSE;
9790 QtDemuxSegment *segment;
9792 GstClockTime media_start = GST_CLOCK_TIME_NONE;
9795 media_time = QT_UINT64 (buffer + 8);
9796 duration = QT_UINT64 (buffer);
9797 if (media_time == G_MAXUINT64)
9800 media_time = QT_UINT32 (buffer + 4);
9801 duration = QT_UINT32 (buffer);
9802 if (media_time == G_MAXUINT32)
9807 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
9809 segment = &stream->segments[segment_number];
9811 /* time and duration expressed in global timescale */
9812 segment->time = stime;
9813 if (duration != 0 || empty_edit) {
9814 /* edge case: empty edits with duration=zero are treated here.
9815 * (files should not have these anyway). */
9817 /* add non scaled values so we don't cause roundoff errors */
9819 stime = QTTIME_TO_GSTTIME (qtdemux, time);
9820 segment->duration = stime - segment->time;
9822 /* zero duration does not imply media_start == media_stop
9823 * but, only specify media_start. The edit ends with the track. */
9824 stime = segment->duration = GST_CLOCK_TIME_NONE;
9825 /* Don't allow more edits after this one. */
9826 n_segments = segment_number + 1;
9828 segment->stop_time = stime;
9830 segment->trak_media_start = media_time;
9831 /* media_time expressed in stream timescale */
9833 segment->media_start = media_start;
9834 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
9835 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
9836 media_segments_count++;
9838 segment->media_start = GST_CLOCK_TIME_NONE;
9839 segment->media_stop = GST_CLOCK_TIME_NONE;
9841 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
9843 if (rate_int <= 1) {
9844 /* 0 is not allowed, some programs write 1 instead of the floating point
9846 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
9850 segment->rate = rate_int / 65536.0;
9853 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
9854 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
9855 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
9856 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
9857 segment_number, GST_TIME_ARGS (segment->time),
9858 GST_TIME_ARGS (segment->duration),
9859 GST_TIME_ARGS (segment->media_start), media_time,
9860 GST_TIME_ARGS (segment->media_stop),
9861 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
9863 if (segment->stop_time > qtdemux->segment.stop &&
9864 !qtdemux->upstream_format_is_time) {
9865 GST_WARNING_OBJECT (qtdemux, "Segment %d "
9866 " extends to %" GST_TIME_FORMAT
9867 " past the end of the declared movie duration %" GST_TIME_FORMAT
9868 " movie segment will be extended", segment_number,
9869 GST_TIME_ARGS (segment->stop_time),
9870 GST_TIME_ARGS (qtdemux->segment.stop));
9871 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
9874 buffer += entry_size;
9876 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
9877 stream->n_segments = n_segments;
9878 if (media_segments_count != 1)
9879 allow_pushbased_edts = FALSE;
9883 /* push based does not handle segments, so act accordingly here,
9884 * and warn if applicable */
9885 if (!qtdemux->pullbased && !allow_pushbased_edts) {
9886 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
9887 /* remove and use default one below, we stream like it anyway */
9888 g_free (stream->segments);
9889 stream->segments = NULL;
9890 stream->n_segments = 0;
9893 /* no segments, create one to play the complete trak */
9894 if (stream->n_segments == 0) {
9895 GstClockTime stream_duration =
9896 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
9898 if (stream->segments == NULL)
9899 stream->segments = g_new (QtDemuxSegment, 1);
9901 /* represent unknown our way */
9902 if (stream_duration == 0)
9903 stream_duration = GST_CLOCK_TIME_NONE;
9905 stream->segments[0].time = 0;
9906 stream->segments[0].stop_time = stream_duration;
9907 stream->segments[0].duration = stream_duration;
9908 stream->segments[0].media_start = 0;
9909 stream->segments[0].media_stop = stream_duration;
9910 stream->segments[0].rate = 1.0;
9911 stream->segments[0].trak_media_start = 0;
9913 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
9914 GST_TIME_ARGS (stream_duration));
9915 stream->n_segments = 1;
9916 stream->dummy_segment = TRUE;
9918 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
9924 * Parses the stsd atom of a svq3 trak looking for
9925 * the SMI and gama atoms.
9928 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
9929 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
9931 const guint8 *_gamma = NULL;
9932 GstBuffer *_seqh = NULL;
9933 const guint8 *stsd_data = stsd_entry_data;
9934 guint32 length = QT_UINT32 (stsd_data);
9938 GST_WARNING_OBJECT (qtdemux, "stsd too short");
9944 version = QT_UINT16 (stsd_data);
9949 while (length > 8) {
9950 guint32 fourcc, size;
9952 size = QT_UINT32 (stsd_data);
9953 fourcc = QT_FOURCC (stsd_data + 4);
9954 data = stsd_data + 8;
9957 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
9958 "svq3 atom parsing");
9967 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
9968 " for gama atom, expected 12", size);
9973 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
9975 if (_seqh != NULL) {
9976 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
9977 " found, ignoring");
9979 seqh_size = QT_UINT32 (data + 4);
9980 if (seqh_size > 0) {
9981 _seqh = gst_buffer_new_and_alloc (seqh_size);
9982 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
9989 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
9990 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
9994 if (size <= length) {
10000 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10003 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10004 G_GUINT16_FORMAT, version);
10014 } else if (_seqh) {
10015 gst_buffer_unref (_seqh);
10020 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10023 GstByteReader dref;
10027 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10028 * atom that might contain a 'data' atom with the rtsp uri.
10029 * This case was reported in bug #597497, some info about
10030 * the hndl atom can be found in TN1195
10032 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10033 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10036 guint32 dref_num_entries = 0;
10037 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10038 gst_byte_reader_skip (&dref, 4) &&
10039 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10042 /* search dref entries for hndl atom */
10043 for (i = 0; i < dref_num_entries; i++) {
10044 guint32 size = 0, type;
10045 guint8 string_len = 0;
10046 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10047 qt_atom_parser_get_fourcc (&dref, &type)) {
10048 if (type == FOURCC_hndl) {
10049 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10051 /* skip data reference handle bytes and the
10052 * following pascal string and some extra 4
10053 * bytes I have no idea what are */
10054 if (!gst_byte_reader_skip (&dref, 4) ||
10055 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10056 !gst_byte_reader_skip (&dref, string_len + 4)) {
10057 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10061 /* iterate over the atoms to find the data atom */
10062 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10066 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10067 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10068 if (atom_type == FOURCC_data) {
10069 const guint8 *uri_aux = NULL;
10071 /* found the data atom that might contain the rtsp uri */
10072 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10073 "hndl atom, interpreting it as an URI");
10074 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10076 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10077 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10079 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10080 "didn't contain a rtsp address");
10082 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10087 /* skipping to the next entry */
10088 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10091 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10098 /* skip to the next entry */
10099 if (!gst_byte_reader_skip (&dref, size - 8))
10102 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10105 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10111 #define AMR_NB_ALL_MODES 0x81ff
10112 #define AMR_WB_ALL_MODES 0x83ff
10114 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10116 /* The 'damr' atom is of the form:
10118 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10119 * 32 b 8 b 16 b 8 b 8 b
10121 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10122 * represents the highest mode used in the stream (and thus the maximum
10123 * bitrate), with a couple of special cases as seen below.
10126 /* Map of frame type ID -> bitrate */
10127 static const guint nb_bitrates[] = {
10128 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10130 static const guint wb_bitrates[] = {
10131 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10137 gst_buffer_map (buf, &map, GST_MAP_READ);
10139 if (map.size != 0x11) {
10140 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10144 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10145 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10146 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10150 mode_set = QT_UINT16 (map.data + 13);
10152 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10153 max_mode = 7 + (wb ? 1 : 0);
10155 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10156 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10158 if (max_mode == -1) {
10159 GST_DEBUG ("No mode indication was found (mode set) = %x",
10164 gst_buffer_unmap (buf, &map);
10165 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10168 gst_buffer_unmap (buf, &map);
10173 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10174 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10177 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10183 if (gst_byte_reader_get_remaining (reader) < 36)
10186 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10187 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10188 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10189 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10190 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10191 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10192 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10193 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10194 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10196 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10197 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10198 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10200 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10201 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10203 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10204 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10211 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10212 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10219 * This macro will only compare value abdegh, it expects cfi to have already
10222 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10223 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10225 /* only handle the cases where the last column has standard values */
10226 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10227 const gchar *rotation_tag = NULL;
10229 /* no rotation needed */
10230 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10232 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10233 rotation_tag = "rotate-90";
10234 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10235 rotation_tag = "rotate-180";
10236 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10237 rotation_tag = "rotate-270";
10239 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10242 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10244 if (rotation_tag != NULL) {
10245 if (*taglist == NULL)
10246 *taglist = gst_tag_list_new_empty ();
10247 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10248 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10251 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10255 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10256 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10257 * Common Encryption (cenc), the function will also parse the tenc box (defined
10258 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10259 * (typically an enc[v|a|t|s] sample entry); the function will set
10260 * @original_fmt to the fourcc of the original unencrypted stream format.
10261 * Returns TRUE if successful; FALSE otherwise. */
10263 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10264 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10270 QtDemuxCencSampleSetInfo *info;
10272 const guint8 *tenc_data;
10274 g_return_val_if_fail (qtdemux != NULL, FALSE);
10275 g_return_val_if_fail (stream != NULL, FALSE);
10276 g_return_val_if_fail (container != NULL, FALSE);
10277 g_return_val_if_fail (original_fmt != NULL, FALSE);
10279 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10280 if (G_UNLIKELY (!sinf)) {
10281 if (stream->protection_scheme_type == FOURCC_cenc) {
10282 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10283 "mandatory for Common Encryption");
10289 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10290 if (G_UNLIKELY (!frma)) {
10291 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10295 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10296 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10297 GST_FOURCC_ARGS (*original_fmt));
10299 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10301 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10304 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10305 stream->protection_scheme_version =
10306 QT_UINT32 ((const guint8 *) schm->data + 16);
10308 GST_DEBUG_OBJECT (qtdemux,
10309 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10310 "protection_scheme_version: %#010x",
10311 GST_FOURCC_ARGS (stream->protection_scheme_type),
10312 stream->protection_scheme_version);
10314 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10316 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10319 if (stream->protection_scheme_type != FOURCC_cenc &&
10320 stream->protection_scheme_type != FOURCC_piff) {
10321 GST_ERROR_OBJECT (qtdemux,
10322 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10323 GST_FOURCC_ARGS (stream->protection_scheme_type));
10327 if (G_UNLIKELY (!stream->protection_scheme_info))
10328 stream->protection_scheme_info =
10329 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10331 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10333 if (stream->protection_scheme_type == FOURCC_cenc) {
10334 guint32 is_encrypted;
10336 const guint8 *default_kid;
10338 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10340 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10341 "which is mandatory for Common Encryption");
10344 tenc_data = (const guint8 *) tenc->data + 12;
10345 is_encrypted = QT_UINT24 (tenc_data);
10346 iv_size = QT_UINT8 (tenc_data + 3);
10347 default_kid = (tenc_data + 4);
10348 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
10349 is_encrypted, iv_size, default_kid);
10350 } else if (stream->protection_scheme_type == FOURCC_piff) {
10352 static const guint8 piff_track_encryption_uuid[] = {
10353 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10354 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10357 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10359 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10360 "which is mandatory for Common Encryption");
10364 tenc_data = (const guint8 *) tenc->data + 8;
10365 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10366 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10367 GST_ERROR_OBJECT (qtdemux,
10368 "Unsupported track encryption box with uuid: %s", box_uuid);
10372 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10373 gst_byte_reader_init (&br, tenc_data, 20);
10374 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10375 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10378 stream->protection_scheme_type = FOURCC_cenc;
10385 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10386 QtDemuxStream ** stream2)
10388 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10391 /* parse the traks.
10392 * With each track we associate a new QtDemuxStream that contains all the info
10394 * traks that do not decode to something (like strm traks) will not have a pad.
10397 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10399 GstByteReader tkhd;
10414 QtDemuxStream *stream = NULL;
10415 const guint8 *stsd_data;
10416 const guint8 *stsd_entry_data;
10417 guint remaining_stsd_len;
10418 guint stsd_entry_count;
10420 guint16 lang_code; /* quicktime lang code or packed iso code */
10422 guint32 tkhd_flags = 0;
10423 guint8 tkhd_version = 0;
10424 guint32 w = 0, h = 0;
10425 guint value_size, stsd_len, len;
10429 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10431 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10432 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10433 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10436 /* pick between 64 or 32 bits */
10437 value_size = tkhd_version == 1 ? 8 : 4;
10438 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10439 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10442 /* Check if current moov has duplicated track_id */
10443 if (qtdemux_find_stream (qtdemux, track_id))
10444 goto existing_stream;
10446 stream = _create_stream (qtdemux, track_id);
10447 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10449 /* need defaults for fragments */
10450 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10452 if ((tkhd_flags & 1) == 0)
10453 stream->disabled = TRUE;
10455 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10456 tkhd_version, tkhd_flags, stream->track_id);
10458 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10461 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10462 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10463 if (qtdemux->major_brand != FOURCC_mjp2 ||
10464 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
10468 len = QT_UINT32 ((guint8 *) mdhd->data);
10469 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
10470 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
10471 if (version == 0x01000000) {
10474 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
10475 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
10476 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
10480 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
10481 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
10482 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
10485 if (lang_code < 0x400) {
10486 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
10487 } else if (lang_code == 0x7fff) {
10488 stream->lang_id[0] = 0; /* unspecified */
10490 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
10491 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
10492 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
10493 stream->lang_id[3] = 0;
10496 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
10497 stream->timescale);
10498 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
10500 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
10501 lang_code, stream->lang_id);
10503 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
10506 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
10507 /* chapters track reference */
10508 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
10510 gsize length = GST_READ_UINT32_BE (chap->data);
10511 if (qtdemux->chapters_track_id)
10512 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
10514 if (length >= 12) {
10515 qtdemux->chapters_track_id =
10516 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
10521 /* fragmented files may have bogus duration in moov */
10522 if (!qtdemux->fragmented &&
10523 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
10524 guint64 tdur1, tdur2;
10526 /* don't overflow */
10527 tdur1 = stream->timescale * (guint64) qtdemux->duration;
10528 tdur2 = qtdemux->timescale * (guint64) stream->duration;
10531 * some of those trailers, nowadays, have prologue images that are
10532 * themselves video tracks as well. I haven't really found a way to
10533 * identify those yet, except for just looking at their duration. */
10534 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
10535 GST_WARNING_OBJECT (qtdemux,
10536 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
10537 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
10538 "found, assuming preview image or something; skipping track",
10539 stream->duration, stream->timescale, qtdemux->duration,
10540 qtdemux->timescale);
10541 gst_qtdemux_stream_unref (stream);
10546 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
10549 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
10550 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
10552 len = QT_UINT32 ((guint8 *) hdlr->data);
10554 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
10555 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
10556 GST_FOURCC_ARGS (stream->subtype));
10558 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
10561 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
10564 /*parse svmi header if existing */
10565 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10567 len = QT_UINT32 ((guint8 *) svmi->data);
10568 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10570 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10571 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10572 guint8 frame_type, frame_layout;
10574 /* MPEG-A stereo video */
10575 if (qtdemux->major_brand == FOURCC_ss02)
10576 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10578 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10579 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10580 switch (frame_type) {
10582 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10585 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10588 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10591 /* mode 3 is primary/secondary view sequence, ie
10592 * left/right views in separate tracks. See section 7.2
10593 * of ISO/IEC 23000-11:2009 */
10594 GST_FIXME_OBJECT (qtdemux,
10595 "Implement stereo video in separate streams");
10598 if ((frame_layout & 0x1) == 0)
10599 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10601 GST_LOG_OBJECT (qtdemux,
10602 "StereoVideo: composition type: %u, is_left_first: %u",
10603 frame_type, frame_layout);
10604 stream->multiview_mode = mode;
10605 stream->multiview_flags = flags;
10609 /* parse rest of tkhd */
10610 if (stream->subtype == FOURCC_vide) {
10613 /* version 1 uses some 64-bit ints */
10614 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
10617 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
10620 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
10621 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
10624 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
10625 &stream->stream_tags);
10629 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
10631 stsd_data = (const guint8 *) stsd->data;
10633 /* stsd should at least have one entry */
10634 stsd_len = QT_UINT32 (stsd_data);
10635 if (stsd_len < 24) {
10636 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
10637 if (stream->subtype == FOURCC_vivo) {
10638 gst_qtdemux_stream_unref (stream);
10645 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
10646 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10647 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10648 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10650 stsd_entry_data = stsd_data + 16;
10651 remaining_stsd_len = stsd_len - 16;
10652 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10654 gchar *codec = NULL;
10655 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10657 /* and that entry should fit within stsd */
10658 len = QT_UINT32 (stsd_entry_data);
10659 if (len > remaining_stsd_len)
10662 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10663 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10664 GST_FOURCC_ARGS (entry->fourcc));
10665 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10667 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10668 goto error_encrypted;
10670 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10671 /* FIXME this looks wrong, there might be multiple children
10672 * with the same type */
10673 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10674 stream->protected = TRUE;
10675 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10676 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10679 if (stream->subtype == FOURCC_vide) {
10684 gint depth, palette_size, palette_count;
10685 guint32 *palette_data = NULL;
10687 entry->sampled = TRUE;
10689 stream->display_width = w >> 16;
10690 stream->display_height = h >> 16;
10693 if (len < 86) /* TODO verify */
10696 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10697 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10698 entry->fps_n = 0; /* this is filled in later */
10699 entry->fps_d = 0; /* this is filled in later */
10700 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10701 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10703 /* if color_table_id is 0, ctab atom must follow; however some files
10704 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10705 * if color table is not present we'll correct the value */
10706 if (entry->color_table_id == 0 &&
10708 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10709 entry->color_table_id = -1;
10712 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10713 entry->width, entry->height, entry->bits_per_sample,
10714 entry->color_table_id);
10716 depth = entry->bits_per_sample;
10718 /* more than 32 bits means grayscale */
10719 gray = (depth > 32);
10720 /* low 32 bits specify the depth */
10723 /* different number of palette entries is determined by depth. */
10725 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10726 palette_count = (1 << depth);
10727 palette_size = palette_count * 4;
10729 if (entry->color_table_id) {
10730 switch (palette_count) {
10734 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10737 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10742 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10744 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10749 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10751 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10754 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10755 (_("The video in this file might not play correctly.")),
10756 ("unsupported palette depth %d", depth));
10760 gint i, j, start, end;
10766 start = QT_UINT32 (stsd_entry_data + offset + 70);
10767 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10768 end = QT_UINT16 (stsd_entry_data + offset + 76);
10770 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10771 start, end, palette_count);
10778 if (len < 94 + (end - start) * 8)
10781 /* palette is always the same size */
10782 palette_data = g_malloc0 (256 * 4);
10783 palette_size = 256 * 4;
10785 for (j = 0, i = start; i <= end; j++, i++) {
10786 guint32 a, r, g, b;
10788 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10789 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10790 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10791 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10793 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10794 (g & 0xff00) | (b >> 8);
10799 gst_caps_unref (entry->caps);
10802 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10804 if (G_UNLIKELY (!entry->caps)) {
10805 g_free (palette_data);
10806 goto unknown_stream;
10810 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10811 GST_TAG_VIDEO_CODEC, codec, NULL);
10816 if (palette_data) {
10819 if (entry->rgb8_palette)
10820 gst_memory_unref (entry->rgb8_palette);
10821 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10822 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10824 s = gst_caps_get_structure (entry->caps, 0);
10826 /* non-raw video has a palette_data property. raw video has the palette as
10827 * an extra plane that we append to the output buffers before we push
10829 if (!gst_structure_has_name (s, "video/x-raw")) {
10830 GstBuffer *palette;
10832 palette = gst_buffer_new ();
10833 gst_buffer_append_memory (palette, entry->rgb8_palette);
10834 entry->rgb8_palette = NULL;
10836 gst_caps_set_simple (entry->caps, "palette_data",
10837 GST_TYPE_BUFFER, palette, NULL);
10838 gst_buffer_unref (palette);
10840 } else if (palette_count != 0) {
10841 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10842 (NULL), ("Unsupported palette depth %d", depth));
10845 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10846 QT_UINT16 (stsd_entry_data + offset + 32));
10852 /* pick 'the' stsd child */
10853 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10854 // We should skip parsing the stsd for non-protected streams if
10855 // the entry doesn't match the fourcc, since they don't change
10856 // format. However, for protected streams we can have partial
10857 // encryption, where parts of the stream are encrypted and parts
10858 // not. For both parts of such streams, we should ensure the
10859 // esds overrides are parsed for both from the stsd.
10860 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10861 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
10863 else if (!stream->protected)
10868 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10869 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10870 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10871 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10875 const guint8 *pasp_data = (const guint8 *) pasp->data;
10876 gint len = QT_UINT32 (pasp_data);
10879 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10880 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10882 CUR_STREAM (stream)->par_w = 0;
10883 CUR_STREAM (stream)->par_h = 0;
10886 CUR_STREAM (stream)->par_w = 0;
10887 CUR_STREAM (stream)->par_h = 0;
10891 const guint8 *fiel_data = (const guint8 *) fiel->data;
10892 gint len = QT_UINT32 (fiel_data);
10895 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10896 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10901 const guint8 *colr_data = (const guint8 *) colr->data;
10902 gint len = QT_UINT32 (colr_data);
10904 if (len == 19 || len == 18) {
10905 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10907 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10908 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10909 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10910 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10911 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10913 switch (primaries) {
10915 CUR_STREAM (stream)->colorimetry.primaries =
10916 GST_VIDEO_COLOR_PRIMARIES_BT709;
10919 CUR_STREAM (stream)->colorimetry.primaries =
10920 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
10923 CUR_STREAM (stream)->colorimetry.primaries =
10924 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
10927 CUR_STREAM (stream)->colorimetry.primaries =
10928 GST_VIDEO_COLOR_PRIMARIES_BT2020;
10934 switch (transfer_function) {
10936 CUR_STREAM (stream)->colorimetry.transfer =
10937 GST_VIDEO_TRANSFER_BT709;
10940 CUR_STREAM (stream)->colorimetry.transfer =
10941 GST_VIDEO_TRANSFER_SMPTE240M;
10949 CUR_STREAM (stream)->colorimetry.matrix =
10950 GST_VIDEO_COLOR_MATRIX_BT709;
10953 CUR_STREAM (stream)->colorimetry.matrix =
10954 GST_VIDEO_COLOR_MATRIX_BT601;
10957 CUR_STREAM (stream)->colorimetry.matrix =
10958 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
10961 CUR_STREAM (stream)->colorimetry.matrix =
10962 GST_VIDEO_COLOR_MATRIX_BT2020;
10968 CUR_STREAM (stream)->colorimetry.range =
10969 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10970 GST_VIDEO_COLOR_RANGE_16_235;
10972 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10975 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10980 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10981 stream->stream_tags);
10988 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10989 const guint8 *avc_data = stsd_entry_data + 0x56;
10992 while (len >= 0x8) {
10995 if (QT_UINT32 (avc_data) <= len)
10996 size = QT_UINT32 (avc_data) - 0x8;
11001 /* No real data, so break out */
11004 switch (QT_FOURCC (avc_data + 0x4)) {
11007 /* parse, if found */
11010 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11012 /* First 4 bytes are the length of the atom, the next 4 bytes
11013 * are the fourcc, the next 1 byte is the version, and the
11014 * subsequent bytes are profile_tier_level structure like data. */
11015 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11016 avc_data + 8 + 1, size - 1);
11017 buf = gst_buffer_new_and_alloc (size);
11018 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11019 gst_caps_set_simple (entry->caps,
11020 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11021 gst_buffer_unref (buf);
11029 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11031 /* First 4 bytes are the length of the atom, the next 4 bytes
11032 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11033 * next 1 byte is the version, and the
11034 * subsequent bytes are sequence parameter set like data. */
11036 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11038 gst_codec_utils_h264_caps_set_level_and_profile
11039 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11041 buf = gst_buffer_new_and_alloc (size);
11042 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11043 gst_caps_set_simple (entry->caps,
11044 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11045 gst_buffer_unref (buf);
11051 guint avg_bitrate, max_bitrate;
11053 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11057 max_bitrate = QT_UINT32 (avc_data + 0xc);
11058 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11060 if (!max_bitrate && !avg_bitrate)
11063 /* Some muxers seem to swap the average and maximum bitrates
11064 * (I'm looking at you, YouTube), so we swap for sanity. */
11065 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11066 guint temp = avg_bitrate;
11068 avg_bitrate = max_bitrate;
11069 max_bitrate = temp;
11072 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11073 gst_tag_list_add (stream->stream_tags,
11074 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11075 max_bitrate, NULL);
11077 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11078 gst_tag_list_add (stream->stream_tags,
11079 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11091 avc_data += size + 8;
11100 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11101 const guint8 *hevc_data = stsd_entry_data + 0x56;
11104 while (len >= 0x8) {
11107 if (QT_UINT32 (hevc_data) <= len)
11108 size = QT_UINT32 (hevc_data) - 0x8;
11113 /* No real data, so break out */
11116 switch (QT_FOURCC (hevc_data + 0x4)) {
11119 /* parse, if found */
11122 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11124 /* First 4 bytes are the length of the atom, the next 4 bytes
11125 * are the fourcc, the next 1 byte is the version, and the
11126 * subsequent bytes are sequence parameter set like data. */
11127 gst_codec_utils_h265_caps_set_level_tier_and_profile
11128 (entry->caps, hevc_data + 8 + 1, size - 1);
11130 buf = gst_buffer_new_and_alloc (size);
11131 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11132 gst_caps_set_simple (entry->caps,
11133 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11134 gst_buffer_unref (buf);
11141 hevc_data += size + 8;
11154 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11155 GST_FOURCC_ARGS (fourcc));
11157 /* codec data might be in glbl extension atom */
11159 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11165 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11167 len = QT_UINT32 (data);
11170 buf = gst_buffer_new_and_alloc (len);
11171 gst_buffer_fill (buf, 0, data + 8, len);
11172 gst_caps_set_simple (entry->caps,
11173 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11174 gst_buffer_unref (buf);
11181 /* see annex I of the jpeg2000 spec */
11182 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11183 const guint8 *data;
11184 const gchar *colorspace = NULL;
11186 guint32 ncomp_map = 0;
11187 gint32 *comp_map = NULL;
11188 guint32 nchan_def = 0;
11189 gint32 *chan_def = NULL;
11191 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11192 /* some required atoms */
11193 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11196 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11200 /* number of components; redundant with info in codestream, but useful
11202 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11203 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11205 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11207 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11210 GST_DEBUG_OBJECT (qtdemux, "found colr");
11211 /* extract colour space info */
11212 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11213 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11215 colorspace = "sRGB";
11218 colorspace = "GRAY";
11221 colorspace = "sYUV";
11229 /* colr is required, and only values 16, 17, and 18 are specified,
11230 so error if we have no colorspace */
11233 /* extract component mapping */
11234 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11236 guint32 cmap_len = 0;
11238 cmap_len = QT_UINT32 (cmap->data);
11239 if (cmap_len >= 8) {
11240 /* normal box, subtract off header */
11242 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11243 if (cmap_len % 4 == 0) {
11244 ncomp_map = (cmap_len / 4);
11245 comp_map = g_new0 (gint32, ncomp_map);
11246 for (i = 0; i < ncomp_map; i++) {
11249 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11250 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11251 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11252 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11257 /* extract channel definitions */
11258 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11260 guint32 cdef_len = 0;
11262 cdef_len = QT_UINT32 (cdef->data);
11263 if (cdef_len >= 10) {
11264 /* normal box, subtract off header and len */
11266 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11267 if (cdef_len % 6 == 0) {
11268 nchan_def = (cdef_len / 6);
11269 chan_def = g_new0 (gint32, nchan_def);
11270 for (i = 0; i < nchan_def; i++)
11272 for (i = 0; i < nchan_def; i++) {
11273 guint16 cn, typ, asoc;
11274 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11275 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11276 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11277 if (cn < nchan_def) {
11280 chan_def[cn] = asoc;
11283 chan_def[cn] = 0; /* alpha */
11286 chan_def[cn] = -typ;
11294 gst_caps_set_simple (entry->caps,
11295 "num-components", G_TYPE_INT, ncomp, NULL);
11296 gst_caps_set_simple (entry->caps,
11297 "colorspace", G_TYPE_STRING, colorspace, NULL);
11300 GValue arr = { 0, };
11301 GValue elt = { 0, };
11303 g_value_init (&arr, GST_TYPE_ARRAY);
11304 g_value_init (&elt, G_TYPE_INT);
11305 for (i = 0; i < ncomp_map; i++) {
11306 g_value_set_int (&elt, comp_map[i]);
11307 gst_value_array_append_value (&arr, &elt);
11309 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11310 "component-map", &arr);
11311 g_value_unset (&elt);
11312 g_value_unset (&arr);
11317 GValue arr = { 0, };
11318 GValue elt = { 0, };
11320 g_value_init (&arr, GST_TYPE_ARRAY);
11321 g_value_init (&elt, G_TYPE_INT);
11322 for (i = 0; i < nchan_def; i++) {
11323 g_value_set_int (&elt, chan_def[i]);
11324 gst_value_array_append_value (&arr, &elt);
11326 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11327 "channel-definitions", &arr);
11328 g_value_unset (&elt);
11329 g_value_unset (&arr);
11333 /* some optional atoms */
11334 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11335 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11337 /* indicate possible fields in caps */
11339 data = (guint8 *) field->data + 8;
11341 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11342 (gint) * data, NULL);
11344 /* add codec_data if provided */
11349 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11350 data = prefix->data;
11351 len = QT_UINT32 (data);
11354 buf = gst_buffer_new_and_alloc (len);
11355 gst_buffer_fill (buf, 0, data + 8, len);
11356 gst_caps_set_simple (entry->caps,
11357 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11358 gst_buffer_unref (buf);
11367 GstBuffer *seqh = NULL;
11368 const guint8 *gamma_data = NULL;
11369 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11371 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11374 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11375 QT_FP32 (gamma_data), NULL);
11378 /* sorry for the bad name, but we don't know what this is, other
11379 * than its own fourcc */
11380 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11382 gst_buffer_unref (seqh);
11385 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11386 buf = gst_buffer_new_and_alloc (len);
11387 gst_buffer_fill (buf, 0, stsd_data, len);
11388 gst_caps_set_simple (entry->caps,
11389 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11390 gst_buffer_unref (buf);
11395 /* https://developer.apple.com/standards/qtff-2001.pdf,
11396 * page 92, "Video Sample Description", under table 3.1 */
11399 const gint compressor_offset =
11400 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11401 const gint min_size = compressor_offset + 32 + 2 + 2;
11404 guint16 color_table_id = 0;
11407 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11409 /* recover information on interlaced/progressive */
11410 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11414 len = QT_UINT32 (jpeg->data);
11415 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11417 if (len >= min_size) {
11418 gst_byte_reader_init (&br, jpeg->data, len);
11420 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11421 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11422 if (color_table_id != 0) {
11423 /* the spec says there can be concatenated chunks in the data, and we want
11424 * to find one called field. Walk through them. */
11425 gint offset = min_size;
11426 while (offset + 8 < len) {
11427 guint32 size = 0, tag;
11428 ok = gst_byte_reader_get_uint32_le (&br, &size);
11429 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11430 if (!ok || size < 8) {
11431 GST_WARNING_OBJECT (qtdemux,
11432 "Failed to walk optional chunk list");
11435 GST_DEBUG_OBJECT (qtdemux,
11436 "Found optional %4.4s chunk, size %u",
11437 (const char *) &tag, size);
11438 if (tag == FOURCC_fiel) {
11439 guint8 n_fields = 0, ordering = 0;
11440 gst_byte_reader_get_uint8 (&br, &n_fields);
11441 gst_byte_reader_get_uint8 (&br, &ordering);
11442 if (n_fields == 1 || n_fields == 2) {
11443 GST_DEBUG_OBJECT (qtdemux,
11444 "Found fiel tag with %u fields, ordering %u",
11445 n_fields, ordering);
11447 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11448 "interlace-mode", G_TYPE_STRING, "interleaved",
11451 GST_WARNING_OBJECT (qtdemux,
11452 "Found fiel tag with invalid fields (%u)", n_fields);
11458 GST_DEBUG_OBJECT (qtdemux,
11459 "Color table ID is 0, not trying to get interlacedness");
11462 GST_WARNING_OBJECT (qtdemux,
11463 "Length of jpeg chunk is too small, not trying to get interlacedness");
11471 gst_caps_set_simple (entry->caps,
11472 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11478 GNode *xith, *xdxt;
11480 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11481 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11485 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11489 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11490 /* collect the headers and store them in a stream list so that we can
11491 * send them out first */
11492 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11502 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11503 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11506 ovc1_data = ovc1->data;
11507 ovc1_len = QT_UINT32 (ovc1_data);
11508 if (ovc1_len <= 198) {
11509 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11512 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11513 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11514 gst_caps_set_simple (entry->caps,
11515 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11516 gst_buffer_unref (buf);
11521 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11522 const guint8 *vc1_data = stsd_entry_data + 0x56;
11528 if (QT_UINT32 (vc1_data) <= len)
11529 size = QT_UINT32 (vc1_data) - 8;
11534 /* No real data, so break out */
11537 switch (QT_FOURCC (vc1_data + 0x4)) {
11538 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11542 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11543 buf = gst_buffer_new_and_alloc (size);
11544 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11545 gst_caps_set_simple (entry->caps,
11546 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11547 gst_buffer_unref (buf);
11554 vc1_data += size + 8;
11560 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11561 const guint8 *av1_data = stsd_entry_data + 0x56;
11564 while (len >= 0x8) {
11567 if (QT_UINT32 (av1_data) <= len)
11568 size = QT_UINT32 (av1_data) - 0x8;
11573 /* No real data, so break out */
11576 switch (QT_FOURCC (av1_data + 0x4)) {
11579 /* parse, if found */
11581 guint8 pres_delay_field;
11583 GST_DEBUG_OBJECT (qtdemux,
11584 "found av1C codec_data in stsd of size %d", size);
11586 /* not enough data, just ignore and hope for the best */
11591 * 4 bytes: atom length
11596 * 1 bits: initial_presentation_delay_present
11597 * 4 bits: initial_presentation_delay (if present else reserved
11601 if (av1_data[9] != 0) {
11602 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
11606 /* We skip initial_presentation_delay* for now */
11607 pres_delay_field = *(av1_data + 12);
11608 if (pres_delay_field & (1 << 5)) {
11609 gst_caps_set_simple (entry->caps,
11610 "presentation-delay", G_TYPE_INT,
11611 (gint) (pres_delay_field & 0x0F) + 1, NULL);
11614 buf = gst_buffer_new_and_alloc (size - 5);
11615 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
11616 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
11617 gst_caps_set_simple (entry->caps,
11618 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11619 gst_buffer_unref (buf);
11628 av1_data += size + 8;
11638 GST_INFO_OBJECT (qtdemux,
11639 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11640 GST_FOURCC_ARGS (fourcc), entry->caps);
11642 } else if (stream->subtype == FOURCC_soun) {
11644 int version, samplesize;
11645 guint16 compression_id;
11646 gboolean amrwb = FALSE;
11649 /* sample description entry (16) + sound sample description v0 (20) */
11653 version = QT_UINT32 (stsd_entry_data + offset);
11654 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11655 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11656 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11657 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11659 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11660 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11661 QT_UINT32 (stsd_entry_data + offset + 4));
11662 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11663 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11664 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11665 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11666 QT_UINT16 (stsd_entry_data + offset + 14));
11667 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11669 if (compression_id == 0xfffe)
11670 entry->sampled = TRUE;
11672 /* first assume uncompressed audio */
11673 entry->bytes_per_sample = samplesize / 8;
11674 entry->samples_per_frame = entry->n_channels;
11675 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11676 entry->samples_per_packet = entry->samples_per_frame;
11677 entry->bytes_per_packet = entry->bytes_per_sample;
11681 /* Yes, these have to be hard-coded */
11684 entry->samples_per_packet = 6;
11685 entry->bytes_per_packet = 1;
11686 entry->bytes_per_frame = 1 * entry->n_channels;
11687 entry->bytes_per_sample = 1;
11688 entry->samples_per_frame = 6 * entry->n_channels;
11693 entry->samples_per_packet = 3;
11694 entry->bytes_per_packet = 1;
11695 entry->bytes_per_frame = 1 * entry->n_channels;
11696 entry->bytes_per_sample = 1;
11697 entry->samples_per_frame = 3 * entry->n_channels;
11702 entry->samples_per_packet = 64;
11703 entry->bytes_per_packet = 34;
11704 entry->bytes_per_frame = 34 * entry->n_channels;
11705 entry->bytes_per_sample = 2;
11706 entry->samples_per_frame = 64 * entry->n_channels;
11712 entry->samples_per_packet = 1;
11713 entry->bytes_per_packet = 1;
11714 entry->bytes_per_frame = 1 * entry->n_channels;
11715 entry->bytes_per_sample = 1;
11716 entry->samples_per_frame = 1 * entry->n_channels;
11721 entry->samples_per_packet = 160;
11722 entry->bytes_per_packet = 33;
11723 entry->bytes_per_frame = 33 * entry->n_channels;
11724 entry->bytes_per_sample = 2;
11725 entry->samples_per_frame = 160 * entry->n_channels;
11732 if (version == 0x00010000) {
11733 /* sample description entry (16) + sound sample description v1 (20+16) */
11745 /* only parse extra decoding config for non-pcm audio */
11746 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11747 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11748 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11749 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11751 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11752 entry->samples_per_packet);
11753 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11754 entry->bytes_per_packet);
11755 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11756 entry->bytes_per_frame);
11757 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11758 entry->bytes_per_sample);
11760 if (!entry->sampled && entry->bytes_per_packet) {
11761 entry->samples_per_frame = (entry->bytes_per_frame /
11762 entry->bytes_per_packet) * entry->samples_per_packet;
11763 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11764 entry->samples_per_frame);
11769 } else if (version == 0x00020000) {
11776 /* sample description entry (16) + sound sample description v2 (56) */
11780 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
11781 entry->rate = qtfp.fp;
11782 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11784 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11785 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11786 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11787 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11788 QT_UINT32 (stsd_entry_data + offset + 20));
11789 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11790 QT_UINT32 (stsd_entry_data + offset + 24));
11791 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11792 QT_UINT32 (stsd_entry_data + offset + 28));
11793 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11794 QT_UINT32 (stsd_entry_data + offset + 32));
11795 } else if (version != 0x00000) {
11796 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11801 gst_caps_unref (entry->caps);
11803 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11804 stsd_entry_data + 32, len - 16, &codec);
11812 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
11814 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
11816 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
11818 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11821 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11822 gst_caps_set_simple (entry->caps,
11823 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
11830 const guint8 *owma_data;
11831 const gchar *codec_name = NULL;
11835 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11836 /* FIXME this should also be gst_riff_strf_auds,
11837 * but the latter one is actually missing bits-per-sample :( */
11842 gint32 nSamplesPerSec;
11843 gint32 nAvgBytesPerSec;
11844 gint16 nBlockAlign;
11845 gint16 wBitsPerSample;
11848 WAVEFORMATEX *wfex;
11850 GST_DEBUG_OBJECT (qtdemux, "parse owma");
11851 owma_data = stsd_entry_data;
11852 owma_len = QT_UINT32 (owma_data);
11853 if (owma_len <= 54) {
11854 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
11857 wfex = (WAVEFORMATEX *) (owma_data + 36);
11858 buf = gst_buffer_new_and_alloc (owma_len - 54);
11859 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
11860 if (wfex->wFormatTag == 0x0161) {
11861 codec_name = "Windows Media Audio";
11863 } else if (wfex->wFormatTag == 0x0162) {
11864 codec_name = "Windows Media Audio 9 Pro";
11866 } else if (wfex->wFormatTag == 0x0163) {
11867 codec_name = "Windows Media Audio 9 Lossless";
11868 /* is that correct? gstffmpegcodecmap.c is missing it, but
11869 * fluendo codec seems to support it */
11873 gst_caps_set_simple (entry->caps,
11874 "codec_data", GST_TYPE_BUFFER, buf,
11875 "wmaversion", G_TYPE_INT, version,
11876 "block_align", G_TYPE_INT,
11877 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
11878 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
11879 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
11880 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
11881 gst_buffer_unref (buf);
11885 codec = g_strdup (codec_name);
11891 gint len = QT_UINT32 (stsd_entry_data) - offset;
11892 const guint8 *wfex_data = stsd_entry_data + offset;
11893 const gchar *codec_name = NULL;
11895 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11896 /* FIXME this should also be gst_riff_strf_auds,
11897 * but the latter one is actually missing bits-per-sample :( */
11902 gint32 nSamplesPerSec;
11903 gint32 nAvgBytesPerSec;
11904 gint16 nBlockAlign;
11905 gint16 wBitsPerSample;
11910 /* FIXME: unify with similar wavformatex parsing code above */
11911 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
11917 if (QT_UINT32 (wfex_data) <= len)
11918 size = QT_UINT32 (wfex_data) - 8;
11923 /* No real data, so break out */
11926 switch (QT_FOURCC (wfex_data + 4)) {
11927 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
11929 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
11934 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
11935 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
11936 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
11937 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
11938 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
11939 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
11940 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
11942 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
11943 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
11944 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
11945 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
11946 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
11947 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
11949 if (wfex.wFormatTag == 0x0161) {
11950 codec_name = "Windows Media Audio";
11952 } else if (wfex.wFormatTag == 0x0162) {
11953 codec_name = "Windows Media Audio 9 Pro";
11955 } else if (wfex.wFormatTag == 0x0163) {
11956 codec_name = "Windows Media Audio 9 Lossless";
11957 /* is that correct? gstffmpegcodecmap.c is missing it, but
11958 * fluendo codec seems to support it */
11962 gst_caps_set_simple (entry->caps,
11963 "wmaversion", G_TYPE_INT, version,
11964 "block_align", G_TYPE_INT, wfex.nBlockAlign,
11965 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
11966 "width", G_TYPE_INT, wfex.wBitsPerSample,
11967 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
11969 if (size > wfex.cbSize) {
11972 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
11973 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
11974 size - wfex.cbSize);
11975 gst_caps_set_simple (entry->caps,
11976 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11977 gst_buffer_unref (buf);
11979 GST_WARNING_OBJECT (qtdemux, "no codec data");
11984 codec = g_strdup (codec_name);
11992 wfex_data += size + 8;
11998 const guint8 *opus_data;
11999 guint8 *channel_mapping = NULL;
12002 guint8 channel_mapping_family;
12003 guint8 stream_count;
12004 guint8 coupled_count;
12007 opus_data = stsd_entry_data;
12009 channels = GST_READ_UINT8 (opus_data + 45);
12010 rate = GST_READ_UINT32_LE (opus_data + 48);
12011 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12012 stream_count = GST_READ_UINT8 (opus_data + 55);
12013 coupled_count = GST_READ_UINT8 (opus_data + 56);
12015 if (channels > 0) {
12016 channel_mapping = g_malloc (channels * sizeof (guint8));
12017 for (i = 0; i < channels; i++)
12018 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12021 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12022 channel_mapping_family, stream_count, coupled_count,
12034 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12035 GST_TAG_AUDIO_CODEC, codec, NULL);
12039 /* some bitrate info may have ended up in caps */
12040 s = gst_caps_get_structure (entry->caps, 0);
12041 gst_structure_get_int (s, "bitrate", &bitrate);
12043 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12044 GST_TAG_BITRATE, bitrate, NULL);
12047 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12048 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12049 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12051 else if (!stream->protected)
12058 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12060 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12062 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12066 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12067 16 bits is a byte-swapped wave-style codec identifier,
12068 and we can find a WAVE header internally to a 'wave' atom here.
12069 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12070 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12073 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12074 if (len < offset + 20) {
12075 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12077 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12078 const guint8 *data = stsd_entry_data + offset + 16;
12080 GNode *waveheadernode;
12082 wavenode = g_node_new ((guint8 *) data);
12083 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12084 const guint8 *waveheader;
12087 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12088 if (waveheadernode) {
12089 waveheader = (const guint8 *) waveheadernode->data;
12090 headerlen = QT_UINT32 (waveheader);
12092 if (headerlen > 8) {
12093 gst_riff_strf_auds *header = NULL;
12094 GstBuffer *headerbuf;
12100 headerbuf = gst_buffer_new_and_alloc (headerlen);
12101 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12103 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12104 headerbuf, &header, &extra)) {
12105 gst_caps_unref (entry->caps);
12106 /* FIXME: Need to do something with the channel reorder map */
12108 gst_riff_create_audio_caps (header->format, NULL, header,
12109 extra, NULL, NULL, NULL);
12112 gst_buffer_unref (extra);
12117 GST_DEBUG ("Didn't find waveheadernode for this codec");
12119 g_node_destroy (wavenode);
12122 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12123 stream->stream_tags);
12127 /* FIXME: what is in the chunk? */
12130 gint len = QT_UINT32 (stsd_data);
12132 /* seems to be always = 116 = 0x74 */
12138 gint len = QT_UINT32 (stsd_entry_data);
12141 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12143 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12144 gst_caps_set_simple (entry->caps,
12145 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12146 gst_buffer_unref (buf);
12148 gst_caps_set_simple (entry->caps,
12149 "samplesize", G_TYPE_INT, samplesize, NULL);
12154 GNode *alac, *wave = NULL;
12156 /* apparently, m4a has this atom appended directly in the stsd entry,
12157 * while mov has it in a wave atom */
12158 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12160 /* alac now refers to stsd entry atom */
12161 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12163 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12165 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12168 const guint8 *alac_data = alac->data;
12169 gint len = QT_UINT32 (alac->data);
12173 GST_DEBUG_OBJECT (qtdemux,
12174 "discarding alac atom with unexpected len %d", len);
12176 /* codec-data contains alac atom size and prefix,
12177 * ffmpeg likes it that way, not quite gst-ish though ...*/
12178 buf = gst_buffer_new_and_alloc (len);
12179 gst_buffer_fill (buf, 0, alac->data, len);
12180 gst_caps_set_simple (entry->caps,
12181 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12182 gst_buffer_unref (buf);
12184 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12185 entry->n_channels = QT_UINT8 (alac_data + 21);
12186 entry->rate = QT_UINT32 (alac_data + 32);
12189 gst_caps_set_simple (entry->caps,
12190 "samplesize", G_TYPE_INT, samplesize, NULL);
12195 /* The codingname of the sample entry is 'fLaC' */
12196 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12199 /* The 'dfLa' box is added to the sample entry to convey
12200 initializing information for the decoder. */
12201 const GNode *dfla =
12202 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12205 const guint32 len = QT_UINT32 (dfla->data);
12207 /* Must contain at least dfLa box header (12),
12208 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12210 GST_DEBUG_OBJECT (qtdemux,
12211 "discarding dfla atom with unexpected len %d", len);
12213 /* skip dfLa header to get the METADATA_BLOCKs */
12214 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12215 const guint32 metadata_blocks_len = len - 12;
12217 gchar *stream_marker = g_strdup ("fLaC");
12218 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12219 strlen (stream_marker));
12222 guint32 remainder = 0;
12223 guint32 block_size = 0;
12224 gboolean is_last = FALSE;
12226 GValue array = G_VALUE_INIT;
12227 GValue value = G_VALUE_INIT;
12229 g_value_init (&array, GST_TYPE_ARRAY);
12230 g_value_init (&value, GST_TYPE_BUFFER);
12232 gst_value_set_buffer (&value, block);
12233 gst_value_array_append_value (&array, &value);
12234 g_value_reset (&value);
12236 gst_buffer_unref (block);
12238 /* check there's at least one METADATA_BLOCK_HEADER's worth
12239 * of data, and we haven't already finished parsing */
12240 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12241 remainder = metadata_blocks_len - index;
12243 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12245 (metadata_blocks[index + 1] << 16) +
12246 (metadata_blocks[index + 2] << 8) +
12247 metadata_blocks[index + 3];
12249 /* be careful not to read off end of box */
12250 if (block_size > remainder) {
12254 is_last = metadata_blocks[index] >> 7;
12256 block = gst_buffer_new_and_alloc (block_size);
12258 gst_buffer_fill (block, 0, &metadata_blocks[index],
12261 gst_value_set_buffer (&value, block);
12262 gst_value_array_append_value (&array, &value);
12263 g_value_reset (&value);
12265 gst_buffer_unref (block);
12267 index += block_size;
12270 /* only append the metadata if we successfully read all of it */
12272 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12273 (stream)->caps, 0), "streamheader", &array);
12275 GST_WARNING_OBJECT (qtdemux,
12276 "discarding all METADATA_BLOCKs due to invalid "
12277 "block_size %d at idx %d, rem %d", block_size, index,
12281 g_value_unset (&value);
12282 g_value_unset (&array);
12284 /* The sample rate obtained from the stsd may not be accurate
12285 * since it cannot represent rates greater than 65535Hz, so
12286 * override that value with the sample rate from the
12287 * METADATA_BLOCK_STREAMINFO block */
12288 CUR_STREAM (stream)->rate =
12289 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12300 gint len = QT_UINT32 (stsd_entry_data);
12303 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12306 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12308 /* If we have enough data, let's try to get the 'damr' atom. See
12309 * the 3GPP container spec (26.244) for more details. */
12310 if ((len - 0x34) > 8 &&
12311 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12312 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12313 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12316 gst_caps_set_simple (entry->caps,
12317 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12318 gst_buffer_unref (buf);
12324 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12325 gint len = QT_UINT32 (stsd_entry_data);
12328 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12330 if (sound_version == 1) {
12331 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12332 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12333 guint8 codec_data[2];
12335 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12337 gint sample_rate_index =
12338 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12340 /* build AAC codec data */
12341 codec_data[0] = profile << 3;
12342 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12343 codec_data[1] = (sample_rate_index & 0x01) << 7;
12344 codec_data[1] |= (channels & 0xF) << 3;
12346 buf = gst_buffer_new_and_alloc (2);
12347 gst_buffer_fill (buf, 0, codec_data, 2);
12348 gst_caps_set_simple (entry->caps,
12349 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12350 gst_buffer_unref (buf);
12356 /* Fully handled elsewhere */
12359 GST_INFO_OBJECT (qtdemux,
12360 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12364 GST_INFO_OBJECT (qtdemux,
12365 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12366 GST_FOURCC_ARGS (fourcc), entry->caps);
12368 } else if (stream->subtype == FOURCC_strm) {
12369 if (fourcc == FOURCC_rtsp) {
12370 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12372 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12373 GST_FOURCC_ARGS (fourcc));
12374 goto unknown_stream;
12376 entry->sampled = TRUE;
12377 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12378 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12379 || stream->subtype == FOURCC_clcp) {
12381 entry->sampled = TRUE;
12382 entry->sparse = TRUE;
12385 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12388 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12389 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12394 /* hunt for sort-of codec data */
12398 GNode *mp4s = NULL;
12399 GNode *esds = NULL;
12401 /* look for palette in a stsd->mp4s->esds sub-atom */
12402 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12404 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12405 if (esds == NULL) {
12407 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12411 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12412 stream->stream_tags);
12416 GST_INFO_OBJECT (qtdemux,
12417 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12420 GST_INFO_OBJECT (qtdemux,
12421 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12422 GST_FOURCC_ARGS (fourcc), entry->caps);
12424 /* everything in 1 sample */
12425 entry->sampled = TRUE;
12428 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12431 if (entry->caps == NULL)
12432 goto unknown_stream;
12435 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12436 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12442 /* promote to sampled format */
12443 if (entry->fourcc == FOURCC_samr) {
12444 /* force mono 8000 Hz for AMR */
12445 entry->sampled = TRUE;
12446 entry->n_channels = 1;
12447 entry->rate = 8000;
12448 } else if (entry->fourcc == FOURCC_sawb) {
12449 /* force mono 16000 Hz for AMR-WB */
12450 entry->sampled = TRUE;
12451 entry->n_channels = 1;
12452 entry->rate = 16000;
12453 } else if (entry->fourcc == FOURCC_mp4a) {
12454 entry->sampled = TRUE;
12458 stsd_entry_data += len;
12459 remaining_stsd_len -= len;
12463 /* collect sample information */
12464 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12465 goto samples_failed;
12467 if (qtdemux->fragmented) {
12470 /* need all moov samples as basis; probably not many if any at all */
12471 /* prevent moof parsing taking of at this time */
12472 offset = qtdemux->moof_offset;
12473 qtdemux->moof_offset = 0;
12474 if (stream->n_samples &&
12475 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12476 qtdemux->moof_offset = offset;
12477 goto samples_failed;
12479 qtdemux->moof_offset = 0;
12480 /* movie duration more reliable in this case (e.g. mehd) */
12481 if (qtdemux->segment.duration &&
12482 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12484 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12487 /* configure segments */
12488 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12489 goto segments_failed;
12491 /* add some language tag, if useful */
12492 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12493 strcmp (stream->lang_id, "und")) {
12494 const gchar *lang_code;
12496 /* convert ISO 639-2 code to ISO 639-1 */
12497 lang_code = gst_tag_get_language_code (stream->lang_id);
12498 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12499 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12502 /* Check for UDTA tags */
12503 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12504 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12507 /* Insert and sort new stream in track-id order.
12508 * This will help in comparing old/new streams during stream update check */
12509 g_ptr_array_add (qtdemux->active_streams, stream);
12510 g_ptr_array_sort (qtdemux->active_streams,
12511 (GCompareFunc) qtdemux_track_id_compare_func);
12512 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
12513 QTDEMUX_N_STREAMS (qtdemux));
12520 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12521 (_("This file is corrupt and cannot be played.")), (NULL));
12523 gst_qtdemux_stream_unref (stream);
12528 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12529 gst_qtdemux_stream_unref (stream);
12535 /* we posted an error already */
12536 /* free stbl sub-atoms */
12537 gst_qtdemux_stbl_free (stream);
12538 gst_qtdemux_stream_unref (stream);
12543 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12549 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12550 GST_FOURCC_ARGS (stream->subtype));
12551 gst_qtdemux_stream_unref (stream);
12556 /* If we can estimate the overall bitrate, and don't have information about the
12557 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12558 * the overall bitrate minus the sum of the bitrates of all other streams. This
12559 * should be useful for the common case where we have one audio and one video
12560 * stream and can estimate the bitrate of one, but not the other. */
12562 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12564 QtDemuxStream *stream = NULL;
12565 gint64 size, sys_bitrate, sum_bitrate = 0;
12566 GstClockTime duration;
12570 if (qtdemux->fragmented)
12573 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12575 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12577 GST_DEBUG_OBJECT (qtdemux,
12578 "Size in bytes of the stream not known - bailing");
12582 /* Subtract the header size */
12583 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12584 size, qtdemux->header_size);
12586 if (size < qtdemux->header_size)
12589 size = size - qtdemux->header_size;
12591 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12592 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12596 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12597 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
12598 switch (str->subtype) {
12601 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12602 CUR_STREAM (str)->caps);
12603 /* retrieve bitrate, prefer avg then max */
12605 if (str->stream_tags) {
12606 if (gst_tag_list_get_uint (str->stream_tags,
12607 GST_TAG_MAXIMUM_BITRATE, &bitrate))
12608 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12609 if (gst_tag_list_get_uint (str->stream_tags,
12610 GST_TAG_NOMINAL_BITRATE, &bitrate))
12611 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12612 if (gst_tag_list_get_uint (str->stream_tags,
12613 GST_TAG_BITRATE, &bitrate))
12614 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12617 sum_bitrate += bitrate;
12620 GST_DEBUG_OBJECT (qtdemux,
12621 ">1 stream with unknown bitrate - bailing");
12628 /* For other subtypes, we assume no significant impact on bitrate */
12634 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12638 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12640 if (sys_bitrate < sum_bitrate) {
12641 /* This can happen, since sum_bitrate might be derived from maximum
12642 * bitrates and not average bitrates */
12643 GST_DEBUG_OBJECT (qtdemux,
12644 "System bitrate less than sum bitrate - bailing");
12648 bitrate = sys_bitrate - sum_bitrate;
12649 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12650 ", Stream bitrate = %u", sys_bitrate, bitrate);
12652 if (!stream->stream_tags)
12653 stream->stream_tags = gst_tag_list_new_empty ();
12655 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12657 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12658 GST_TAG_BITRATE, bitrate, NULL);
12661 static GstFlowReturn
12662 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12664 GstFlowReturn ret = GST_FLOW_OK;
12667 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12669 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12670 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12671 guint32 sample_num = 0;
12673 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12674 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12676 if (qtdemux->fragmented) {
12677 /* need all moov samples first */
12678 GST_OBJECT_LOCK (qtdemux);
12679 while (stream->n_samples == 0)
12680 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12682 GST_OBJECT_UNLOCK (qtdemux);
12684 /* discard any stray moof */
12685 qtdemux->moof_offset = 0;
12688 /* prepare braking */
12689 if (ret != GST_FLOW_ERROR)
12692 /* in pull mode, we should have parsed some sample info by now;
12693 * and quite some code will not handle no samples.
12694 * in push mode, we'll just have to deal with it */
12695 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12696 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12697 g_ptr_array_remove_index (qtdemux->active_streams, i);
12700 } else if (stream->track_id == qtdemux->chapters_track_id &&
12701 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
12702 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12703 so that it doesn't look like a subtitle track */
12704 g_ptr_array_remove_index (qtdemux->active_streams, i);
12709 /* parse the initial sample for use in setting the frame rate cap */
12710 while (sample_num == 0 && sample_num < stream->n_samples) {
12711 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12721 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
12723 return g_strcmp0 (stream->stream_id, stream_id) == 0;
12727 qtdemux_is_streams_update (GstQTDemux * qtdemux)
12731 /* Different length, updated */
12732 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
12735 /* streams in list are sorted in track-id order */
12736 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12737 /* Different stream-id, updated */
12738 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
12739 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
12747 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
12748 QtDemuxStream * oldstream, QtDemuxStream * newstream)
12750 /* Connect old stream's srcpad to new stream */
12751 newstream->pad = oldstream->pad;
12752 oldstream->pad = NULL;
12754 /* unset new_stream to prevent stream-start event */
12755 newstream->new_stream = FALSE;
12757 return gst_qtdemux_configure_stream (qtdemux, newstream);
12760 /* g_ptr_array_find_with_equal_func is available since 2.54,
12761 * replacement until we can depend unconditionally on the real one in GLib */
12762 #if !GLIB_CHECK_VERSION(2,54,0)
12763 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
12765 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
12766 gconstpointer needle, GEqualFunc equal_func, guint * index_)
12770 g_return_val_if_fail (haystack != NULL, FALSE);
12772 if (equal_func == NULL)
12773 equal_func = g_direct_equal;
12775 for (i = 0; i < haystack->len; i++) {
12776 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
12777 if (index_ != NULL)
12788 qtdemux_update_streams (GstQTDemux * qtdemux)
12791 g_assert (qtdemux->streams_aware);
12793 /* At below, figure out which stream in active_streams has identical stream-id
12794 * with that of in old_streams. If there is matching stream-id,
12795 * corresponding newstream will not be exposed again,
12796 * but demux will reuse srcpad of matched old stream
12798 * active_streams : newly created streams from the latest moov
12799 * old_streams : existing streams (belong to previous moov)
12802 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12803 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12804 QtDemuxStream *oldstream = NULL;
12807 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12808 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12810 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
12811 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
12812 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
12814 /* null pad stream cannot be reused */
12815 if (oldstream->pad == NULL)
12820 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
12822 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
12825 /* we don't need to preserve order of old streams */
12826 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
12830 /* now we have all info and can expose */
12831 list = stream->stream_tags;
12832 stream->stream_tags = NULL;
12833 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12841 /* Must be called with expose lock */
12842 static GstFlowReturn
12843 qtdemux_expose_streams (GstQTDemux * qtdemux)
12847 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
12849 if (!qtdemux_is_streams_update (qtdemux)) {
12850 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
12851 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12852 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12853 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
12854 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
12855 return GST_FLOW_ERROR;
12858 g_ptr_array_set_size (qtdemux->old_streams, 0);
12859 qtdemux->need_segment = TRUE;
12861 return GST_FLOW_OK;
12864 if (qtdemux->streams_aware) {
12865 if (!qtdemux_update_streams (qtdemux))
12866 return GST_FLOW_ERROR;
12868 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12869 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12872 /* now we have all info and can expose */
12873 list = stream->stream_tags;
12874 stream->stream_tags = NULL;
12875 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12876 return GST_FLOW_ERROR;
12881 gst_qtdemux_guess_bitrate (qtdemux);
12883 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
12885 /* If we have still old_streams, it's no more used stream */
12886 for (i = 0; i < qtdemux->old_streams->len; i++) {
12887 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
12892 event = gst_event_new_eos ();
12893 if (qtdemux->segment_seqnum)
12894 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
12896 gst_pad_push_event (stream->pad, event);
12900 g_ptr_array_set_size (qtdemux->old_streams, 0);
12902 /* check if we should post a redirect in case there is a single trak
12903 * and it is a redirecting trak */
12904 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
12905 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
12908 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
12909 "an external content");
12910 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
12911 gst_structure_new ("redirect",
12912 "new-location", G_TYPE_STRING,
12913 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
12914 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
12915 qtdemux->posted_redirect = TRUE;
12918 g_ptr_array_foreach (qtdemux->active_streams,
12919 (GFunc) qtdemux_do_allocation, qtdemux);
12921 qtdemux->need_segment = TRUE;
12923 qtdemux->exposed = TRUE;
12924 return GST_FLOW_OK;
12927 /* check if major or compatible brand is 3GP */
12928 static inline gboolean
12929 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
12932 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12934 } else if (qtdemux->comp_brands != NULL) {
12938 gboolean res = FALSE;
12940 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
12943 while (size >= 4) {
12944 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12949 gst_buffer_unmap (qtdemux->comp_brands, &map);
12956 /* check if tag is a spec'ed 3GP tag keyword storing a string */
12957 static inline gboolean
12958 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
12960 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
12961 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
12962 || fourcc == FOURCC_albm;
12966 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
12967 const char *tag, const char *dummy, GNode * node)
12969 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12973 gdouble longitude, latitude, altitude;
12976 len = QT_UINT32 (node->data);
12983 /* TODO: language code skipped */
12985 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
12988 /* do not alarm in trivial case, but bail out otherwise */
12989 if (*(data + offset) != 0) {
12990 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
12994 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12995 GST_TAG_GEO_LOCATION_NAME, name, NULL);
12996 offset += strlen (name);
13000 if (len < offset + 2 + 4 + 4 + 4)
13003 /* +1 +1 = skip null-terminator and location role byte */
13005 /* table in spec says unsigned, semantics say negative has meaning ... */
13006 longitude = QT_SFP32 (data + offset);
13009 latitude = QT_SFP32 (data + offset);
13012 altitude = QT_SFP32 (data + offset);
13014 /* one invalid means all are invalid */
13015 if (longitude >= -180.0 && longitude <= 180.0 &&
13016 latitude >= -90.0 && latitude <= 90.0) {
13017 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13018 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
13019 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
13020 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
13023 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
13030 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
13037 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
13038 const char *tag, const char *dummy, GNode * node)
13044 len = QT_UINT32 (node->data);
13048 y = QT_UINT16 ((guint8 *) node->data + 12);
13050 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
13053 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
13055 date = g_date_new_dmy (1, 1, y);
13056 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13057 g_date_free (date);
13061 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
13062 const char *tag, const char *dummy, GNode * node)
13065 char *tag_str = NULL;
13070 len = QT_UINT32 (node->data);
13075 entity = (guint8 *) node->data + offset;
13076 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
13077 GST_DEBUG_OBJECT (qtdemux,
13078 "classification info: %c%c%c%c invalid classification entity",
13079 entity[0], entity[1], entity[2], entity[3]);
13084 table = QT_UINT16 ((guint8 *) node->data + offset);
13086 /* Language code skipped */
13090 /* Tag format: "XXXX://Y[YYYY]/classification info string"
13091 * XXXX: classification entity, fixed length 4 chars.
13092 * Y[YYYY]: classification table, max 5 chars.
13094 tag_str = g_strdup_printf ("----://%u/%s",
13095 table, (char *) node->data + offset);
13097 /* memcpy To be sure we're preserving byte order */
13098 memcpy (tag_str, entity, 4);
13099 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
13101 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
13110 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
13116 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
13117 const char *tag, const char *dummy, GNode * node)
13119 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13125 gboolean ret = TRUE;
13126 const gchar *charset = NULL;
13128 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13130 len = QT_UINT32 (data->data);
13131 type = QT_UINT32 ((guint8 *) data->data + 8);
13132 if (type == 0x00000001 && len > 16) {
13133 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
13136 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13137 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13140 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13144 len = QT_UINT32 (node->data);
13145 type = QT_UINT32 ((guint8 *) node->data + 4);
13146 if ((type >> 24) == 0xa9 && len > 8 + 4) {
13150 /* Type starts with the (C) symbol, so the next data is a list
13151 * of (string size(16), language code(16), string) */
13153 str_len = QT_UINT16 ((guint8 *) node->data + 8);
13154 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
13156 /* the string + fourcc + size + 2 16bit fields,
13157 * means that there are more tags in this atom */
13158 if (len > str_len + 8 + 4) {
13159 /* TODO how to represent the same tag in different languages? */
13160 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
13161 "text alternatives, reading only first one");
13165 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
13166 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
13168 if (lang_code < 0x800) { /* MAC encoded string */
13171 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
13172 QT_FOURCC ((guint8 *) node->data + 4))) {
13173 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
13175 /* we go for 3GP style encoding if major brands claims so,
13176 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
13177 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13178 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
13179 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
13181 /* 16-bit Language code is ignored here as well */
13182 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
13189 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
13190 ret = FALSE; /* may have to fallback */
13193 GError *err = NULL;
13195 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
13196 charset, NULL, NULL, &err);
13198 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
13199 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
13201 g_error_free (err);
13204 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13205 len - offset, env_vars);
13208 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13209 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13213 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13220 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
13221 const char *tag, const char *dummy, GNode * node)
13223 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
13227 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
13228 const char *tag, const char *dummy, GNode * node)
13230 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13232 char *s, *t, *k = NULL;
13237 /* first try normal string tag if major brand not 3GP */
13238 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
13239 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
13240 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
13241 * let's try it 3gpp way after minor safety check */
13243 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
13249 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
13253 len = QT_UINT32 (data);
13257 count = QT_UINT8 (data + 14);
13259 for (; count; count--) {
13262 if (offset + 1 > len)
13264 slen = QT_UINT8 (data + offset);
13266 if (offset + slen > len)
13268 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13271 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
13273 t = g_strjoin (",", k, s, NULL);
13281 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
13288 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
13289 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
13298 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
13304 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
13305 const char *tag1, const char *tag2, GNode * node)
13312 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13314 len = QT_UINT32 (data->data);
13315 type = QT_UINT32 ((guint8 *) data->data + 8);
13316 if (type == 0x00000000 && len >= 22) {
13317 n1 = QT_UINT16 ((guint8 *) data->data + 18);
13318 n2 = QT_UINT16 ((guint8 *) data->data + 20);
13320 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
13321 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
13324 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
13325 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
13332 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
13333 const char *tag1, const char *dummy, GNode * node)
13340 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13342 len = QT_UINT32 (data->data);
13343 type = QT_UINT32 ((guint8 *) data->data + 8);
13344 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
13345 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13346 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
13347 n1 = QT_UINT16 ((guint8 *) data->data + 16);
13349 /* do not add bpm=0 */
13350 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
13351 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
13359 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
13360 const char *tag1, const char *dummy, GNode * node)
13367 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13369 len = QT_UINT32 (data->data);
13370 type = QT_UINT32 ((guint8 *) data->data + 8);
13371 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
13372 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13373 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
13374 num = QT_UINT32 ((guint8 *) data->data + 16);
13376 /* do not add num=0 */
13377 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
13378 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
13385 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
13386 const char *tag1, const char *dummy, GNode * node)
13393 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13395 len = QT_UINT32 (data->data);
13396 type = QT_UINT32 ((guint8 *) data->data + 8);
13397 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
13398 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
13399 GstTagImageType image_type;
13401 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
13402 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
13404 image_type = GST_TAG_IMAGE_TYPE_NONE;
13407 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
13408 len - 16, image_type))) {
13409 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
13410 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
13411 gst_sample_unref (sample);
13418 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
13419 const char *tag, const char *dummy, GNode * node)
13422 GstDateTime *datetime = NULL;
13427 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13429 len = QT_UINT32 (data->data);
13430 type = QT_UINT32 ((guint8 *) data->data + 8);
13431 if (type == 0x00000001 && len > 16) {
13432 guint y, m = 1, d = 1;
13435 s = g_strndup ((char *) data->data + 16, len - 16);
13436 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
13437 datetime = gst_date_time_new_from_iso8601_string (s);
13438 if (datetime != NULL) {
13439 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
13441 gst_date_time_unref (datetime);
13444 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
13445 if (ret >= 1 && y > 1500 && y < 3000) {
13448 date = g_date_new_dmy (d, m, y);
13449 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13450 g_date_free (date);
13452 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
13460 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
13461 const char *tag, const char *dummy, GNode * node)
13465 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13467 /* re-route to normal string tag if major brand says so
13468 * or no data atom and compatible brand suggests so */
13469 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13470 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
13471 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
13476 guint len, type, n;
13478 len = QT_UINT32 (data->data);
13479 type = QT_UINT32 ((guint8 *) data->data + 8);
13480 if (type == 0x00000000 && len >= 18) {
13481 n = QT_UINT16 ((guint8 *) data->data + 16);
13483 const gchar *genre;
13485 genre = gst_tag_id3_genre_get (n - 1);
13486 if (genre != NULL) {
13487 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
13488 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
13496 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
13497 const gchar * tag, guint8 * data, guint32 datasize)
13502 /* make a copy to have \0 at the end */
13503 datacopy = g_strndup ((gchar *) data, datasize);
13505 /* convert the str to double */
13506 if (sscanf (datacopy, "%lf", &value) == 1) {
13507 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
13508 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
13510 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
13518 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
13519 const char *tag, const char *tag_bis, GNode * node)
13528 const gchar *meanstr;
13529 const gchar *namestr;
13531 /* checking the whole ---- atom size for consistency */
13532 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
13533 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
13537 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
13539 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
13543 meansize = QT_UINT32 (mean->data);
13544 if (meansize <= 12) {
13545 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
13548 meanstr = ((gchar *) mean->data) + 12;
13551 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
13553 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
13557 namesize = QT_UINT32 (name->data);
13558 if (namesize <= 12) {
13559 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
13562 namestr = ((gchar *) name->data) + 12;
13570 * uint24 - data type
13574 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13576 GST_WARNING_OBJECT (demux, "No data atom in this tag");
13579 datasize = QT_UINT32 (data->data);
13580 if (datasize <= 16) {
13581 GST_WARNING_OBJECT (demux, "Data atom too small");
13584 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
13586 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
13587 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
13588 static const struct
13590 const gchar name[28];
13591 const gchar tag[28];
13594 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
13595 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
13596 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
13597 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
13598 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
13599 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
13600 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
13601 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
13605 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
13606 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
13607 switch (gst_tag_get_type (tags[i].tag)) {
13608 case G_TYPE_DOUBLE:
13609 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
13610 ((guint8 *) data->data) + 16, datasize - 16);
13612 case G_TYPE_STRING:
13613 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
13622 if (i == G_N_ELEMENTS (tags))
13632 #ifndef GST_DISABLE_GST_DEBUG
13634 gchar *namestr_dbg;
13635 gchar *meanstr_dbg;
13637 meanstr_dbg = g_strndup (meanstr, meansize);
13638 namestr_dbg = g_strndup (namestr, namesize);
13640 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
13641 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
13643 g_free (namestr_dbg);
13644 g_free (meanstr_dbg);
13651 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
13652 const char *tag_bis, GNode * node)
13657 GstTagList *id32_taglist = NULL;
13659 GST_LOG_OBJECT (demux, "parsing ID32");
13662 len = GST_READ_UINT32_BE (data);
13664 /* need at least full box and language tag */
13668 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
13669 gst_buffer_fill (buf, 0, data + 14, len - 14);
13671 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
13672 if (id32_taglist) {
13673 GST_LOG_OBJECT (demux, "parsing ok");
13674 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
13675 gst_tag_list_unref (id32_taglist);
13677 GST_LOG_OBJECT (demux, "parsing failed");
13680 gst_buffer_unref (buf);
13683 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
13684 const char *tag, const char *tag_bis, GNode * node);
13687 FOURCC_pcst -> if media is a podcast -> bool
13688 FOURCC_cpil -> if media is part of a compilation -> bool
13689 FOURCC_pgap -> if media is part of a gapless context -> bool
13690 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
13693 static const struct
13696 const gchar *gst_tag;
13697 const gchar *gst_tag_bis;
13698 const GstQTDemuxAddTagFunc func;
13701 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13702 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13703 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
13704 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13705 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13706 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
13707 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13708 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13709 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13710 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13711 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13712 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13713 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13714 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13715 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13716 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13717 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
13718 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
13719 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
13720 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13721 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13722 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
13723 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13724 qtdemux_tag_add_num}, {
13725 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13726 qtdemux_tag_add_num}, {
13727 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
13728 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
13729 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
13730 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
13731 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
13732 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
13733 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13734 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13735 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
13736 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
13737 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
13738 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13739 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13740 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
13741 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
13742 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13743 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
13744 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
13745 qtdemux_tag_add_classification}, {
13746 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
13747 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
13748 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
13750 /* This is a special case, some tags are stored in this
13751 * 'reverse dns naming', according to:
13752 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
13755 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
13756 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
13757 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
13760 struct _GstQtDemuxTagList
13763 GstTagList *taglist;
13765 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
13768 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
13774 const gchar *style;
13779 GstQTDemux *demux = qtdemuxtaglist->demux;
13780 GstTagList *taglist = qtdemuxtaglist->taglist;
13783 len = QT_UINT32 (data);
13784 buf = gst_buffer_new_and_alloc (len);
13785 gst_buffer_fill (buf, 0, data, len);
13787 /* heuristic to determine style of tag */
13788 if (QT_FOURCC (data + 4) == FOURCC_____ ||
13789 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
13791 else if (demux->major_brand == FOURCC_qt__)
13792 style = "quicktime";
13793 /* fall back to assuming iso/3gp tag style */
13797 /* santize the name for the caps. */
13798 for (i = 0; i < 4; i++) {
13799 guint8 d = data[4 + i];
13800 if (g_ascii_isalnum (d))
13801 ndata[i] = g_ascii_tolower (d);
13806 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
13807 ndata[0], ndata[1], ndata[2], ndata[3]);
13808 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
13810 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
13811 sample = gst_sample_new (buf, NULL, NULL, s);
13812 gst_buffer_unref (buf);
13813 g_free (media_type);
13815 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
13818 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
13819 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
13821 gst_sample_unref (sample);
13825 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
13832 GstQtDemuxTagList demuxtaglist;
13834 demuxtaglist.demux = qtdemux;
13835 demuxtaglist.taglist = taglist;
13837 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
13838 if (meta != NULL) {
13839 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
13840 if (ilst == NULL) {
13841 GST_LOG_OBJECT (qtdemux, "no ilst");
13846 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
13850 while (i < G_N_ELEMENTS (add_funcs)) {
13851 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
13855 len = QT_UINT32 (node->data);
13857 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
13858 GST_FOURCC_ARGS (add_funcs[i].fourcc));
13860 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
13861 add_funcs[i].gst_tag_bis, node);
13863 g_node_destroy (node);
13869 /* parsed nodes have been removed, pass along remainder as blob */
13870 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
13871 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
13873 /* parse up XMP_ node if existing */
13874 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
13875 if (xmp_ != NULL) {
13877 GstTagList *xmptaglist;
13879 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
13880 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
13881 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
13882 gst_buffer_unref (buf);
13884 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
13886 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
13892 GstStructure *structure; /* helper for sort function */
13894 guint min_req_bitrate;
13895 guint min_req_qt_version;
13899 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13901 GstQtReference *ref_a = (GstQtReference *) a;
13902 GstQtReference *ref_b = (GstQtReference *) b;
13904 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13905 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13907 /* known bitrates go before unknown; higher bitrates go first */
13908 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13911 /* sort the redirects and post a message for the application.
13914 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13916 GstQtReference *best;
13919 GValue list_val = { 0, };
13922 g_assert (references != NULL);
13924 references = g_list_sort (references, qtdemux_redirects_sort_func);
13926 best = (GstQtReference *) references->data;
13928 g_value_init (&list_val, GST_TYPE_LIST);
13930 for (l = references; l != NULL; l = l->next) {
13931 GstQtReference *ref = (GstQtReference *) l->data;
13932 GValue struct_val = { 0, };
13934 ref->structure = gst_structure_new ("redirect",
13935 "new-location", G_TYPE_STRING, ref->location, NULL);
13937 if (ref->min_req_bitrate > 0) {
13938 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13939 ref->min_req_bitrate, NULL);
13942 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13943 g_value_set_boxed (&struct_val, ref->structure);
13944 gst_value_list_append_value (&list_val, &struct_val);
13945 g_value_unset (&struct_val);
13946 /* don't free anything here yet, since we need best->structure below */
13949 g_assert (best != NULL);
13950 s = gst_structure_copy (best->structure);
13952 if (g_list_length (references) > 1) {
13953 gst_structure_set_value (s, "locations", &list_val);
13956 g_value_unset (&list_val);
13958 for (l = references; l != NULL; l = l->next) {
13959 GstQtReference *ref = (GstQtReference *) l->data;
13961 gst_structure_free (ref->structure);
13962 g_free (ref->location);
13965 g_list_free (references);
13967 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13968 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13969 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13970 qtdemux->posted_redirect = TRUE;
13973 /* look for redirect nodes, collect all redirect information and
13977 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13979 GNode *rmra, *rmda, *rdrf;
13981 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13983 GList *redirects = NULL;
13985 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13987 GstQtReference ref = { NULL, NULL, 0, 0 };
13988 GNode *rmdr, *rmvc;
13990 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13991 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13992 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13993 ref.min_req_bitrate);
13996 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13997 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13998 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14000 #ifndef GST_DISABLE_GST_DEBUG
14001 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14003 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14005 GST_LOG_OBJECT (qtdemux,
14006 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14007 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14008 bitmask, check_type);
14009 if (package == FOURCC_qtim && check_type == 0) {
14010 ref.min_req_qt_version = version;
14014 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14020 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14021 if (ref_len > 20) {
14022 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14023 ref_data = (guint8 *) rdrf->data + 20;
14024 if (ref_type == FOURCC_alis) {
14025 guint record_len, record_version, fn_len;
14027 if (ref_len > 70) {
14028 /* MacOSX alias record, google for alias-layout.txt */
14029 record_len = QT_UINT16 (ref_data + 4);
14030 record_version = QT_UINT16 (ref_data + 4 + 2);
14031 fn_len = QT_UINT8 (ref_data + 50);
14032 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14033 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14036 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14039 } else if (ref_type == FOURCC_url_) {
14040 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14042 GST_DEBUG_OBJECT (qtdemux,
14043 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14044 GST_FOURCC_ARGS (ref_type));
14046 if (ref.location != NULL) {
14047 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14049 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
14051 GST_WARNING_OBJECT (qtdemux,
14052 "Failed to extract redirect location from rdrf atom");
14055 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14059 /* look for others */
14060 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14063 if (redirects != NULL) {
14064 qtdemux_process_redirects (qtdemux, redirects);
14070 static GstTagList *
14071 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14075 if (tags == NULL) {
14076 tags = gst_tag_list_new_empty ();
14077 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14080 if (qtdemux->major_brand == FOURCC_mjp2)
14081 fmt = "Motion JPEG 2000";
14082 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14084 else if (qtdemux->major_brand == FOURCC_qt__)
14086 else if (qtdemux->fragmented)
14089 fmt = "ISO MP4/M4A";
14091 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14092 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14094 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14100 /* we have read the complete moov node now.
14101 * This function parses all of the relevant info, creates the traks and
14102 * prepares all data structures for playback
14105 qtdemux_parse_tree (GstQTDemux * qtdemux)
14112 guint64 creation_time;
14113 GstDateTime *datetime = NULL;
14116 /* make sure we have a usable taglist */
14117 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14119 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14120 if (mvhd == NULL) {
14121 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14122 return qtdemux_parse_redirects (qtdemux);
14125 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14126 if (version == 1) {
14127 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14128 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14129 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14130 } else if (version == 0) {
14131 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14132 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14133 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14135 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14139 /* Moving qt creation time (secs since 1904) to unix time */
14140 if (creation_time != 0) {
14141 /* Try to use epoch first as it should be faster and more commonly found */
14142 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14145 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14146 /* some data cleansing sanity */
14147 g_get_current_time (&now);
14148 if (now.tv_sec + 24 * 3600 < creation_time) {
14149 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14151 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14154 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14155 GDateTime *dt, *dt_local;
14157 dt = g_date_time_add_seconds (base_dt, creation_time);
14158 dt_local = g_date_time_to_local (dt);
14159 datetime = gst_date_time_new_from_g_date_time (dt_local);
14161 g_date_time_unref (base_dt);
14162 g_date_time_unref (dt);
14166 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14167 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14169 gst_date_time_unref (datetime);
14172 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14173 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14175 /* check for fragmented file and get some (default) data */
14176 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14179 GstByteReader mehd_data;
14181 /* let track parsing or anyone know weird stuff might happen ... */
14182 qtdemux->fragmented = TRUE;
14184 /* compensate for total duration */
14185 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14187 qtdemux_parse_mehd (qtdemux, &mehd_data);
14190 /* Update the movie segment duration, unless it was directly given to us
14191 * by upstream. Otherwise let it as is, as we don't want to mangle the
14192 * duration provided by upstream that may come e.g. from a MPD file. */
14193 if (!qtdemux->upstream_format_is_time) {
14194 GstClockTime duration;
14195 /* set duration in the segment info */
14196 gst_qtdemux_get_duration (qtdemux, &duration);
14197 qtdemux->segment.duration = duration;
14198 /* also do not exceed duration; stop is set that way post seek anyway,
14199 * and segment activation falls back to duration,
14200 * whereas loop only checks stop, so let's align this here as well */
14201 qtdemux->segment.stop = duration;
14204 /* parse all traks */
14205 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14207 qtdemux_parse_trak (qtdemux, trak);
14208 /* iterate all siblings */
14209 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14212 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14215 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14217 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14219 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14222 /* maybe also some tags in meta box */
14223 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14225 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14226 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14228 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14231 /* parse any protection system info */
14232 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14234 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14235 qtdemux_parse_pssh (qtdemux, pssh);
14236 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14239 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14244 /* taken from ffmpeg */
14246 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14258 len = (len << 7) | (c & 0x7f);
14267 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14268 gsize codec_data_size)
14270 GList *list = NULL;
14271 guint8 *p = codec_data;
14272 gint i, offset, num_packets;
14273 guint *length, last;
14275 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14277 if (codec_data == NULL || codec_data_size == 0)
14280 /* start of the stream and vorbis audio or theora video, need to
14281 * send the codec_priv data as first three packets */
14282 num_packets = p[0] + 1;
14283 GST_DEBUG_OBJECT (qtdemux,
14284 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14285 (guint) num_packets, codec_data_size);
14287 /* Let's put some limits, Don't think there even is a xiph codec
14288 * with more than 3-4 headers */
14289 if (G_UNLIKELY (num_packets > 16)) {
14290 GST_WARNING_OBJECT (qtdemux,
14291 "Unlikely number of xiph headers, most likely not valid");
14295 length = g_alloca (num_packets * sizeof (guint));
14299 /* first packets, read length values */
14300 for (i = 0; i < num_packets - 1; i++) {
14302 while (offset < codec_data_size) {
14303 length[i] += p[offset];
14304 if (p[offset++] != 0xff)
14309 if (offset + last > codec_data_size)
14312 /* last packet is the remaining size */
14313 length[i] = codec_data_size - offset - last;
14315 for (i = 0; i < num_packets; i++) {
14318 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14320 if (offset + length[i] > codec_data_size)
14323 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
14324 list = g_list_append (list, hdr);
14326 offset += length[i];
14335 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14341 /* this can change the codec originally present in @list */
14343 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14344 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14346 int len = QT_UINT32 (esds->data);
14347 guint8 *ptr = esds->data;
14348 guint8 *end = ptr + len;
14350 guint8 *data_ptr = NULL;
14352 guint8 object_type_id = 0;
14353 guint8 stream_type = 0;
14354 const char *codec_name = NULL;
14355 GstCaps *caps = NULL;
14357 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14359 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14361 while (ptr + 1 < end) {
14362 tag = QT_UINT8 (ptr);
14363 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14365 len = read_descr_size (ptr, end, &ptr);
14366 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14368 /* Check the stated amount of data is available for reading */
14369 if (len < 0 || ptr + len > end)
14373 case ES_DESCRIPTOR_TAG:
14374 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14375 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14378 case DECODER_CONFIG_DESC_TAG:{
14379 guint max_bitrate, avg_bitrate;
14381 object_type_id = QT_UINT8 (ptr);
14382 stream_type = QT_UINT8 (ptr + 1) >> 2;
14383 max_bitrate = QT_UINT32 (ptr + 5);
14384 avg_bitrate = QT_UINT32 (ptr + 9);
14385 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14386 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14387 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14388 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14389 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14390 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14391 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14392 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14394 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14395 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14396 avg_bitrate, NULL);
14401 case DECODER_SPECIFIC_INFO_TAG:
14402 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14403 if (object_type_id == 0xe0 && len == 0x40) {
14409 GST_DEBUG_OBJECT (qtdemux,
14410 "Have VOBSUB palette. Creating palette event");
14411 /* move to decConfigDescr data and read palette */
14413 for (i = 0; i < 16; i++) {
14414 clut[i] = QT_UINT32 (data);
14418 s = gst_structure_new ("application/x-gst-dvd", "event",
14419 G_TYPE_STRING, "dvd-spu-clut-change",
14420 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14421 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14422 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14423 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14424 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14425 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14426 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14427 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14430 /* store event and trigger custom processing */
14431 stream->pending_event =
14432 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14434 /* Generic codec_data handler puts it on the caps */
14441 case SL_CONFIG_DESC_TAG:
14442 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
14446 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14448 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14454 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14455 * in use, and should also be used to override some other parameters for some
14457 switch (object_type_id) {
14458 case 0x20: /* MPEG-4 */
14459 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14460 * profile_and_level_indication */
14461 if (data_ptr != NULL && data_len >= 5 &&
14462 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14463 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14464 data_ptr + 4, data_len - 4);
14466 break; /* Nothing special needed here */
14467 case 0x21: /* H.264 */
14468 codec_name = "H.264 / AVC";
14469 caps = gst_caps_new_simple ("video/x-h264",
14470 "stream-format", G_TYPE_STRING, "avc",
14471 "alignment", G_TYPE_STRING, "au", NULL);
14473 case 0x40: /* AAC (any) */
14474 case 0x66: /* AAC Main */
14475 case 0x67: /* AAC LC */
14476 case 0x68: /* AAC SSR */
14477 /* Override channels and rate based on the codec_data, as it's often
14479 /* Only do so for basic setup without HE-AAC extension */
14480 if (data_ptr && data_len == 2) {
14481 guint channels, rate;
14483 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14485 entry->n_channels = channels;
14487 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
14489 entry->rate = rate;
14492 /* Set level and profile if possible */
14493 if (data_ptr != NULL && data_len >= 2) {
14494 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
14495 data_ptr, data_len);
14497 const gchar *profile_str = NULL;
14500 guint8 *codec_data;
14501 gint rate_idx, profile;
14503 /* No codec_data, let's invent something.
14504 * FIXME: This is wrong for SBR! */
14506 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
14508 buffer = gst_buffer_new_and_alloc (2);
14509 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
14510 codec_data = map.data;
14513 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
14516 switch (object_type_id) {
14518 profile_str = "main";
14522 profile_str = "lc";
14526 profile_str = "ssr";
14534 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14536 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14538 gst_buffer_unmap (buffer, &map);
14539 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14540 GST_TYPE_BUFFER, buffer, NULL);
14541 gst_buffer_unref (buffer);
14544 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14545 G_TYPE_STRING, profile_str, NULL);
14549 case 0x60: /* MPEG-2, various profiles */
14555 codec_name = "MPEG-2 video";
14556 caps = gst_caps_new_simple ("video/mpeg",
14557 "mpegversion", G_TYPE_INT, 2,
14558 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14560 case 0x69: /* MPEG-2 BC audio */
14561 case 0x6B: /* MPEG-1 audio */
14562 caps = gst_caps_new_simple ("audio/mpeg",
14563 "mpegversion", G_TYPE_INT, 1, NULL);
14564 codec_name = "MPEG-1 audio";
14566 case 0x6A: /* MPEG-1 */
14567 codec_name = "MPEG-1 video";
14568 caps = gst_caps_new_simple ("video/mpeg",
14569 "mpegversion", G_TYPE_INT, 1,
14570 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14572 case 0x6C: /* MJPEG */
14574 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14576 codec_name = "Motion-JPEG";
14578 case 0x6D: /* PNG */
14580 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14582 codec_name = "PNG still images";
14584 case 0x6E: /* JPEG2000 */
14585 codec_name = "JPEG-2000";
14586 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14588 case 0xA4: /* Dirac */
14589 codec_name = "Dirac";
14590 caps = gst_caps_new_empty_simple ("video/x-dirac");
14592 case 0xA5: /* AC3 */
14593 codec_name = "AC-3 audio";
14594 caps = gst_caps_new_simple ("audio/x-ac3",
14595 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14597 case 0xA9: /* AC3 */
14598 codec_name = "DTS audio";
14599 caps = gst_caps_new_simple ("audio/x-dts",
14600 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14603 if (stream_type == 0x05 && data_ptr) {
14605 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
14608 GValue arr_val = G_VALUE_INIT;
14609 GValue buf_val = G_VALUE_INIT;
14612 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
14613 codec_name = "Vorbis";
14614 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
14615 g_value_init (&arr_val, GST_TYPE_ARRAY);
14616 g_value_init (&buf_val, GST_TYPE_BUFFER);
14617 for (tmp = headers; tmp; tmp = tmp->next) {
14618 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
14619 gst_value_array_append_value (&arr_val, &buf_val);
14621 s = gst_caps_get_structure (caps, 0);
14622 gst_structure_take_value (s, "streamheader", &arr_val);
14623 g_value_unset (&buf_val);
14624 g_list_free (headers);
14631 case 0xE1: /* QCELP */
14632 /* QCELP, the codec_data is a riff tag (little endian) with
14633 * 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). */
14634 caps = gst_caps_new_empty_simple ("audio/qcelp");
14635 codec_name = "QCELP";
14641 /* If we have a replacement caps, then change our caps for this stream */
14643 gst_caps_unref (entry->caps);
14644 entry->caps = caps;
14647 if (codec_name && list)
14648 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14649 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14651 /* Add the codec_data attribute to caps, if we have it */
14655 buffer = gst_buffer_new_and_alloc (data_len);
14656 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14658 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14659 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14661 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14663 gst_buffer_unref (buffer);
14668 static inline GstCaps *
14669 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14673 char *s, fourstr[5];
14675 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14676 for (i = 0; i < 4; i++) {
14677 if (!g_ascii_isalnum (fourstr[i]))
14680 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14681 caps = gst_caps_new_empty_simple (s);
14686 #define _codec(name) \
14688 if (codec_name) { \
14689 *codec_name = g_strdup (name); \
14694 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14695 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14696 const guint8 * stsd_entry_data, gchar ** codec_name)
14698 GstCaps *caps = NULL;
14699 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14703 _codec ("PNG still images");
14704 caps = gst_caps_new_empty_simple ("image/png");
14707 _codec ("JPEG still images");
14709 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14712 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14713 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14714 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14715 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14716 _codec ("Motion-JPEG");
14718 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14721 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14722 _codec ("Motion-JPEG format B");
14723 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14726 _codec ("JPEG-2000");
14727 /* override to what it should be according to spec, avoid palette_data */
14728 entry->bits_per_sample = 24;
14729 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14732 _codec ("Sorensen video v.3");
14733 caps = gst_caps_new_simple ("video/x-svq",
14734 "svqversion", G_TYPE_INT, 3, NULL);
14736 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14737 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14738 _codec ("Sorensen video v.1");
14739 caps = gst_caps_new_simple ("video/x-svq",
14740 "svqversion", G_TYPE_INT, 1, NULL);
14742 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14743 caps = gst_caps_new_empty_simple ("video/x-raw");
14744 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14745 _codec ("Windows Raw RGB");
14746 stream->alignment = 32;
14752 bps = QT_UINT16 (stsd_entry_data + 82);
14755 format = GST_VIDEO_FORMAT_RGB15;
14758 format = GST_VIDEO_FORMAT_RGB16;
14761 format = GST_VIDEO_FORMAT_RGB;
14764 format = GST_VIDEO_FORMAT_ARGB;
14772 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14773 format = GST_VIDEO_FORMAT_I420;
14775 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14776 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14777 format = GST_VIDEO_FORMAT_I420;
14780 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14781 format = GST_VIDEO_FORMAT_UYVY;
14783 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14784 format = GST_VIDEO_FORMAT_v308;
14786 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14787 format = GST_VIDEO_FORMAT_v216;
14790 format = GST_VIDEO_FORMAT_v210;
14792 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14793 format = GST_VIDEO_FORMAT_r210;
14795 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14796 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14797 format = GST_VIDEO_FORMAT_v410;
14800 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14801 * but different order than AYUV
14802 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14803 format = GST_VIDEO_FORMAT_v408;
14806 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14807 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14808 _codec ("MPEG-1 video");
14809 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14810 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14812 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14813 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14814 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14815 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14816 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14817 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14818 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14819 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14820 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14821 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14822 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14823 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14824 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14825 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14826 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14827 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14828 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14829 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14830 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14831 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14832 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14833 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14834 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14835 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14836 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14837 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14838 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14839 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14840 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14841 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14842 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14843 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14844 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14845 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14846 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14847 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14848 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14849 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14850 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14851 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14852 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14853 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14854 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14855 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14856 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14857 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14858 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14859 _codec ("MPEG-2 video");
14860 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14861 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14863 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14864 _codec ("GIF still images");
14865 caps = gst_caps_new_empty_simple ("image/gif");
14868 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14870 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14872 /* ffmpeg uses the height/width props, don't know why */
14873 caps = gst_caps_new_simple ("video/x-h263",
14874 "variant", G_TYPE_STRING, "itu", NULL);
14878 _codec ("MPEG-4 video");
14879 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14880 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14882 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14883 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14884 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14885 caps = gst_caps_new_simple ("video/x-msmpeg",
14886 "msmpegversion", G_TYPE_INT, 43, NULL);
14888 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14890 caps = gst_caps_new_simple ("video/x-divx",
14891 "divxversion", G_TYPE_INT, 3, NULL);
14893 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14894 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14896 caps = gst_caps_new_simple ("video/x-divx",
14897 "divxversion", G_TYPE_INT, 4, NULL);
14899 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14901 caps = gst_caps_new_simple ("video/x-divx",
14902 "divxversion", G_TYPE_INT, 5, NULL);
14905 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14907 caps = gst_caps_new_simple ("video/x-ffv",
14908 "ffvversion", G_TYPE_INT, 1, NULL);
14911 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14912 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14917 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14918 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14919 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14923 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14924 _codec ("Cinepak");
14925 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14927 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14928 _codec ("Apple QuickDraw");
14929 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14931 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14932 _codec ("Apple video");
14933 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14937 _codec ("H.264 / AVC");
14938 caps = gst_caps_new_simple ("video/x-h264",
14939 "stream-format", G_TYPE_STRING, "avc",
14940 "alignment", G_TYPE_STRING, "au", NULL);
14943 _codec ("H.264 / AVC");
14944 caps = gst_caps_new_simple ("video/x-h264",
14945 "stream-format", G_TYPE_STRING, "avc3",
14946 "alignment", G_TYPE_STRING, "au", NULL);
14950 _codec ("H.265 / HEVC");
14951 caps = gst_caps_new_simple ("video/x-h265",
14952 "stream-format", G_TYPE_STRING, "hvc1",
14953 "alignment", G_TYPE_STRING, "au", NULL);
14956 _codec ("H.265 / HEVC");
14957 caps = gst_caps_new_simple ("video/x-h265",
14958 "stream-format", G_TYPE_STRING, "hev1",
14959 "alignment", G_TYPE_STRING, "au", NULL);
14962 _codec ("Run-length encoding");
14963 caps = gst_caps_new_simple ("video/x-rle",
14964 "layout", G_TYPE_STRING, "quicktime", NULL);
14967 _codec ("Run-length encoding");
14968 caps = gst_caps_new_simple ("video/x-rle",
14969 "layout", G_TYPE_STRING, "microsoft", NULL);
14971 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14972 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14973 _codec ("Indeo Video 3");
14974 caps = gst_caps_new_simple ("video/x-indeo",
14975 "indeoversion", G_TYPE_INT, 3, NULL);
14977 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14978 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14979 _codec ("Intel Video 4");
14980 caps = gst_caps_new_simple ("video/x-indeo",
14981 "indeoversion", G_TYPE_INT, 4, NULL);
14985 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14986 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14987 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14988 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14989 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14990 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14991 _codec ("DV Video");
14992 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14993 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14995 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14996 case FOURCC_dv5p: /* DVCPRO50 PAL */
14997 _codec ("DVCPro50 Video");
14998 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14999 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15001 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15002 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15003 _codec ("DVCProHD Video");
15004 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15005 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15007 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15008 _codec ("Apple Graphics (SMC)");
15009 caps = gst_caps_new_empty_simple ("video/x-smc");
15011 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15013 caps = gst_caps_new_empty_simple ("video/x-vp3");
15015 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15016 _codec ("VP6 Flash");
15017 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15021 caps = gst_caps_new_empty_simple ("video/x-theora");
15022 /* theora uses one byte of padding in the data stream because it does not
15023 * allow 0 sized packets while theora does */
15024 entry->padding = 1;
15028 caps = gst_caps_new_empty_simple ("video/x-dirac");
15030 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15031 _codec ("TIFF still images");
15032 caps = gst_caps_new_empty_simple ("image/tiff");
15034 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15035 _codec ("Apple Intermediate Codec");
15036 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15038 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15039 _codec ("AVID DNxHD");
15040 caps = gst_caps_from_string ("video/x-dnxhd");
15044 _codec ("On2 VP8");
15045 caps = gst_caps_from_string ("video/x-vp8");
15048 _codec ("Google VP9");
15049 caps = gst_caps_from_string ("video/x-vp9");
15052 _codec ("Apple ProRes LT");
15054 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15058 _codec ("Apple ProRes HQ");
15060 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15064 _codec ("Apple ProRes");
15066 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15070 _codec ("Apple ProRes Proxy");
15072 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15076 _codec ("Apple ProRes 4444");
15078 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15082 _codec ("Apple ProRes 4444 XQ");
15084 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15088 _codec ("GoPro CineForm");
15089 caps = gst_caps_from_string ("video/x-cineform");
15094 caps = gst_caps_new_simple ("video/x-wmv",
15095 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15099 caps = gst_caps_new_empty_simple ("video/x-av1");
15101 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15104 caps = _get_unknown_codec_name ("video", fourcc);
15109 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15112 gst_video_info_init (&info);
15113 gst_video_info_set_format (&info, format, entry->width, entry->height);
15115 caps = gst_video_info_to_caps (&info);
15116 *codec_name = gst_pb_utils_get_codec_description (caps);
15118 /* enable clipping for raw video streams */
15119 stream->need_clip = TRUE;
15120 stream->alignment = 32;
15127 round_up_pow2 (guint n)
15139 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15140 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15141 int len, gchar ** codec_name)
15144 const GstStructure *s;
15147 GstAudioFormat format = 0;
15150 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15152 depth = entry->bytes_per_packet * 8;
15155 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15157 /* 8-bit audio is unsigned */
15159 format = GST_AUDIO_FORMAT_U8;
15160 /* otherwise it's signed and big-endian just like 'twos' */
15162 endian = G_BIG_ENDIAN;
15169 endian = G_LITTLE_ENDIAN;
15172 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15174 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15178 caps = gst_caps_new_simple ("audio/x-raw",
15179 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15180 "layout", G_TYPE_STRING, "interleaved", NULL);
15181 stream->alignment = GST_ROUND_UP_8 (depth);
15182 stream->alignment = round_up_pow2 (stream->alignment);
15185 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
15186 _codec ("Raw 64-bit floating-point audio");
15187 caps = gst_caps_new_simple ("audio/x-raw",
15188 "format", G_TYPE_STRING, "F64BE",
15189 "layout", G_TYPE_STRING, "interleaved", NULL);
15190 stream->alignment = 8;
15192 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
15193 _codec ("Raw 32-bit floating-point audio");
15194 caps = gst_caps_new_simple ("audio/x-raw",
15195 "format", G_TYPE_STRING, "F32BE",
15196 "layout", G_TYPE_STRING, "interleaved", NULL);
15197 stream->alignment = 4;
15200 _codec ("Raw 24-bit PCM audio");
15201 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15203 caps = gst_caps_new_simple ("audio/x-raw",
15204 "format", G_TYPE_STRING, "S24BE",
15205 "layout", G_TYPE_STRING, "interleaved", NULL);
15206 stream->alignment = 4;
15208 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
15209 _codec ("Raw 32-bit PCM audio");
15210 caps = gst_caps_new_simple ("audio/x-raw",
15211 "format", G_TYPE_STRING, "S32BE",
15212 "layout", G_TYPE_STRING, "interleaved", NULL);
15213 stream->alignment = 4;
15215 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
15216 _codec ("Raw 16-bit PCM audio");
15217 caps = gst_caps_new_simple ("audio/x-raw",
15218 "format", G_TYPE_STRING, "S16LE",
15219 "layout", G_TYPE_STRING, "interleaved", NULL);
15220 stream->alignment = 2;
15223 _codec ("Mu-law audio");
15224 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15227 _codec ("A-law audio");
15228 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15232 _codec ("Microsoft ADPCM");
15233 /* Microsoft ADPCM-ACM code 2 */
15234 caps = gst_caps_new_simple ("audio/x-adpcm",
15235 "layout", G_TYPE_STRING, "microsoft", NULL);
15239 _codec ("DVI/IMA ADPCM");
15240 caps = gst_caps_new_simple ("audio/x-adpcm",
15241 "layout", G_TYPE_STRING, "dvi", NULL);
15245 _codec ("DVI/Intel IMA ADPCM");
15246 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15247 caps = gst_caps_new_simple ("audio/x-adpcm",
15248 "layout", G_TYPE_STRING, "quicktime", NULL);
15252 /* MPEG layer 3, CBR only (pre QT4.1) */
15254 _codec ("MPEG-1 layer 3");
15255 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15256 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15257 "mpegversion", G_TYPE_INT, 1, NULL);
15259 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15260 _codec ("MPEG-1 layer 2");
15262 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15263 "mpegversion", G_TYPE_INT, 1, NULL);
15266 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15267 _codec ("EAC-3 audio");
15268 caps = gst_caps_new_simple ("audio/x-eac3",
15269 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15270 entry->sampled = TRUE;
15272 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15274 _codec ("AC-3 audio");
15275 caps = gst_caps_new_simple ("audio/x-ac3",
15276 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15277 entry->sampled = TRUE;
15279 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15280 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15281 _codec ("DTS audio");
15282 caps = gst_caps_new_simple ("audio/x-dts",
15283 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15284 entry->sampled = TRUE;
15286 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15287 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15288 _codec ("DTS-HD audio");
15289 caps = gst_caps_new_simple ("audio/x-dts",
15290 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15291 entry->sampled = TRUE;
15295 caps = gst_caps_new_simple ("audio/x-mace",
15296 "maceversion", G_TYPE_INT, 3, NULL);
15300 caps = gst_caps_new_simple ("audio/x-mace",
15301 "maceversion", G_TYPE_INT, 6, NULL);
15303 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15305 caps = gst_caps_new_empty_simple ("application/ogg");
15307 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15308 _codec ("DV audio");
15309 caps = gst_caps_new_empty_simple ("audio/x-dv");
15312 _codec ("MPEG-4 AAC audio");
15313 caps = gst_caps_new_simple ("audio/mpeg",
15314 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15315 "stream-format", G_TYPE_STRING, "raw", NULL);
15317 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15318 _codec ("QDesign Music");
15319 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15322 _codec ("QDesign Music v.2");
15323 /* FIXME: QDesign music version 2 (no constant) */
15324 if (FALSE && data) {
15325 caps = gst_caps_new_simple ("audio/x-qdm2",
15326 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15327 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15328 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15330 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15334 _codec ("GSM audio");
15335 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15338 _codec ("AMR audio");
15339 caps = gst_caps_new_empty_simple ("audio/AMR");
15342 _codec ("AMR-WB audio");
15343 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15346 _codec ("Quicktime IMA ADPCM");
15347 caps = gst_caps_new_simple ("audio/x-adpcm",
15348 "layout", G_TYPE_STRING, "quicktime", NULL);
15351 _codec ("Apple lossless audio");
15352 caps = gst_caps_new_empty_simple ("audio/x-alac");
15355 _codec ("Free Lossless Audio Codec");
15356 caps = gst_caps_new_simple ("audio/x-flac",
15357 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15359 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15360 _codec ("QualComm PureVoice");
15361 caps = gst_caps_from_string ("audio/qcelp");
15366 caps = gst_caps_new_empty_simple ("audio/x-wma");
15370 caps = gst_caps_new_empty_simple ("audio/x-opus");
15377 GstAudioFormat format;
15380 FLAG_IS_FLOAT = 0x1,
15381 FLAG_IS_BIG_ENDIAN = 0x2,
15382 FLAG_IS_SIGNED = 0x4,
15383 FLAG_IS_PACKED = 0x8,
15384 FLAG_IS_ALIGNED_HIGH = 0x10,
15385 FLAG_IS_NON_INTERLEAVED = 0x20
15387 _codec ("Raw LPCM audio");
15389 if (data && len >= 36) {
15390 depth = QT_UINT32 (data + 24);
15391 flags = QT_UINT32 (data + 28);
15392 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15394 if ((flags & FLAG_IS_FLOAT) == 0) {
15399 if ((flags & FLAG_IS_ALIGNED_HIGH))
15402 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15403 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15404 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15405 caps = gst_caps_new_simple ("audio/x-raw",
15406 "format", G_TYPE_STRING,
15408 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15409 "UNKNOWN", "layout", G_TYPE_STRING,
15410 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15411 "interleaved", NULL);
15412 stream->alignment = GST_ROUND_UP_8 (depth);
15413 stream->alignment = round_up_pow2 (stream->alignment);
15418 if (flags & FLAG_IS_BIG_ENDIAN)
15419 format = GST_AUDIO_FORMAT_F64BE;
15421 format = GST_AUDIO_FORMAT_F64LE;
15423 if (flags & FLAG_IS_BIG_ENDIAN)
15424 format = GST_AUDIO_FORMAT_F32BE;
15426 format = GST_AUDIO_FORMAT_F32LE;
15428 caps = gst_caps_new_simple ("audio/x-raw",
15429 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15430 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15431 "non-interleaved" : "interleaved", NULL);
15432 stream->alignment = width / 8;
15436 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15440 caps = _get_unknown_codec_name ("audio", fourcc);
15446 GstCaps *templ_caps =
15447 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
15448 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
15449 gst_caps_unref (caps);
15450 gst_caps_unref (templ_caps);
15451 caps = intersection;
15454 /* enable clipping for raw audio streams */
15455 s = gst_caps_get_structure (caps, 0);
15456 name = gst_structure_get_name (s);
15457 if (g_str_has_prefix (name, "audio/x-raw")) {
15458 stream->need_clip = TRUE;
15459 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
15460 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
15466 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15467 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15468 const guint8 * stsd_entry_data, gchar ** codec_name)
15472 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15476 _codec ("DVD subtitle");
15477 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
15478 stream->need_process = TRUE;
15481 _codec ("Quicktime timed text");
15484 _codec ("3GPP timed text");
15486 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
15488 /* actual text piece needs to be extracted */
15489 stream->need_process = TRUE;
15492 _codec ("XML subtitles");
15493 caps = gst_caps_new_empty_simple ("application/ttml+xml");
15496 _codec ("CEA 608 Closed Caption");
15498 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
15499 G_TYPE_STRING, "s334-1a", NULL);
15500 stream->need_process = TRUE;
15501 stream->need_split = TRUE;
15504 _codec ("CEA 708 Closed Caption");
15506 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
15507 G_TYPE_STRING, "cdp", NULL);
15508 stream->need_process = TRUE;
15513 caps = _get_unknown_codec_name ("text", fourcc);
15521 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15522 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15523 const guint8 * stsd_entry_data, gchar ** codec_name)
15529 _codec ("MPEG 1 video");
15530 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15531 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15541 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
15542 const gchar * system_id)
15546 if (!qtdemux->protection_system_ids)
15547 qtdemux->protection_system_ids =
15548 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
15549 /* Check whether we already have an entry for this system ID. */
15550 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
15551 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
15552 if (g_ascii_strcasecmp (system_id, id) == 0) {
15556 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
15557 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,