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_remove_range (qtdemux->active_streams,
2178 0, qtdemux->active_streams->len);
2179 g_ptr_array_remove_range (qtdemux->old_streams,
2180 0, qtdemux->old_streams->len);
2181 qtdemux->n_video_streams = 0;
2182 qtdemux->n_audio_streams = 0;
2183 qtdemux->n_sub_streams = 0;
2184 qtdemux->exposed = FALSE;
2185 qtdemux->fragmented = FALSE;
2186 qtdemux->mss_mode = FALSE;
2187 gst_caps_replace (&qtdemux->media_caps, NULL);
2188 qtdemux->timescale = 0;
2189 qtdemux->got_moov = FALSE;
2190 qtdemux->cenc_aux_info_offset = 0;
2191 qtdemux->cenc_aux_info_sizes = NULL;
2192 qtdemux->cenc_aux_sample_count = 0;
2193 if (qtdemux->protection_system_ids) {
2194 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2195 qtdemux->protection_system_ids = NULL;
2197 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2198 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2199 GST_BIN_FLAG_STREAMS_AWARE);
2201 if (qtdemux->preferred_protection_system_id) {
2202 g_free (qtdemux->preferred_protection_system_id);
2203 qtdemux->preferred_protection_system_id = NULL;
2205 } else if (qtdemux->mss_mode) {
2206 gst_flow_combiner_reset (qtdemux->flowcombiner);
2207 g_ptr_array_foreach (qtdemux->active_streams,
2208 (GFunc) gst_qtdemux_stream_clear, NULL);
2210 gst_flow_combiner_reset (qtdemux->flowcombiner);
2211 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2212 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2213 stream->sent_eos = FALSE;
2214 stream->time_position = 0;
2215 stream->accumulated_base = 0;
2221 /* Maps the @segment to the qt edts internal segments and pushes
2222 * the correspnding segment event.
2224 * If it ends up being at a empty segment, a gap will be pushed and the next
2225 * edts segment will be activated in sequence.
2227 * To be used in push-mode only */
2229 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2233 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2234 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2236 stream->time_position = segment->start;
2238 /* in push mode we should be guaranteed that we will have empty segments
2239 * at the beginning and then one segment after, other scenarios are not
2240 * supported and are discarded when parsing the edts */
2241 for (i = 0; i < stream->n_segments; i++) {
2242 if (stream->segments[i].stop_time > segment->start) {
2243 /* push the empty segment and move to the next one */
2244 gst_qtdemux_activate_segment (qtdemux, stream, i,
2245 stream->time_position);
2246 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2247 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2248 stream->time_position);
2250 /* accumulate previous segments */
2251 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2252 stream->accumulated_base +=
2253 (stream->segment.stop -
2254 stream->segment.start) / ABS (stream->segment.rate);
2258 g_assert (i == stream->n_segments - 1);
2265 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2276 for (i = 0; i < len; i++) {
2277 QtDemuxStream *stream = g_ptr_array_index (src, i);
2279 #ifndef GST_DISABLE_GST_DEBUG
2280 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2281 stream, GST_STR_NULL (stream->stream_id), dest);
2283 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2286 g_ptr_array_remove_range (src, 0, len);
2290 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2293 GstQTDemux *demux = GST_QTDEMUX (parent);
2294 gboolean res = TRUE;
2296 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2298 switch (GST_EVENT_TYPE (event)) {
2299 case GST_EVENT_SEGMENT:
2302 QtDemuxStream *stream;
2306 /* some debug output */
2307 gst_event_copy_segment (event, &segment);
2308 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2311 if (segment.format == GST_FORMAT_TIME) {
2312 demux->upstream_format_is_time = TRUE;
2313 demux->segment_seqnum = gst_event_get_seqnum (event);
2315 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2316 "not in time format");
2318 /* chain will send initial newsegment after pads have been added */
2319 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2320 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2325 /* check if this matches a time seek we received previously
2326 * FIXME for backwards compatibility reasons we use the
2327 * seek_offset here to compare. In the future we might want to
2328 * change this to use the seqnum as it uniquely should identify
2329 * the segment that corresponds to the seek. */
2330 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2331 ", received segment offset %" G_GINT64_FORMAT,
2332 demux->seek_offset, segment.start);
2333 if (segment.format == GST_FORMAT_BYTES
2334 && demux->seek_offset == segment.start) {
2335 GST_OBJECT_LOCK (demux);
2336 offset = segment.start;
2338 segment.format = GST_FORMAT_TIME;
2339 segment.start = demux->push_seek_start;
2340 segment.stop = demux->push_seek_stop;
2341 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2342 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2343 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2344 GST_OBJECT_UNLOCK (demux);
2347 /* we only expect a BYTE segment, e.g. following a seek */
2348 if (segment.format == GST_FORMAT_BYTES) {
2349 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2350 offset = segment.start;
2352 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2353 NULL, (gint64 *) & segment.start);
2354 if ((gint64) segment.start < 0)
2357 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2358 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2359 NULL, (gint64 *) & segment.stop);
2360 /* keyframe seeking should already arrange for start >= stop,
2361 * but make sure in other rare cases */
2362 segment.stop = MAX (segment.stop, segment.start);
2364 } else if (segment.format == GST_FORMAT_TIME) {
2365 /* push all data on the adapter before starting this
2367 gst_qtdemux_process_adapter (demux, TRUE);
2369 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2373 /* We shouldn't modify upstream driven TIME FORMAT segment */
2374 if (!demux->upstream_format_is_time) {
2375 /* accept upstream's notion of segment and distribute along */
2376 segment.format = GST_FORMAT_TIME;
2377 segment.position = segment.time = segment.start;
2378 segment.duration = demux->segment.duration;
2379 segment.base = gst_segment_to_running_time (&demux->segment,
2380 GST_FORMAT_TIME, demux->segment.position);
2383 gst_segment_copy_into (&segment, &demux->segment);
2384 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2386 /* map segment to internal qt segments and push on each stream */
2387 if (QTDEMUX_N_STREAMS (demux)) {
2388 demux->need_segment = TRUE;
2389 gst_qtdemux_check_send_pending_segment (demux);
2392 /* clear leftover in current segment, if any */
2393 gst_adapter_clear (demux->adapter);
2395 /* set up streaming thread */
2396 demux->offset = offset;
2397 if (demux->upstream_format_is_time) {
2398 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2399 "set values to restart reading from a new atom");
2400 demux->neededbytes = 16;
2403 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2406 demux->todrop = stream->samples[idx].offset - offset;
2407 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2409 /* set up for EOS */
2410 demux->neededbytes = -1;
2415 gst_event_unref (event);
2419 case GST_EVENT_FLUSH_START:
2421 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2422 gst_event_unref (event);
2425 QTDEMUX_EXPOSE_LOCK (demux);
2426 res = gst_pad_event_default (demux->sinkpad, parent, event);
2427 QTDEMUX_EXPOSE_UNLOCK (demux);
2430 case GST_EVENT_FLUSH_STOP:
2434 dur = demux->segment.duration;
2435 gst_qtdemux_reset (demux, FALSE);
2436 demux->segment.duration = dur;
2438 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2439 gst_event_unref (event);
2445 /* If we are in push mode, and get an EOS before we've seen any streams,
2446 * then error out - we have nowhere to send the EOS */
2447 if (!demux->pullbased) {
2449 gboolean has_valid_stream = FALSE;
2450 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2451 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2452 has_valid_stream = TRUE;
2456 if (!has_valid_stream)
2457 gst_qtdemux_post_no_playable_stream_error (demux);
2459 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2460 (guint) gst_adapter_available (demux->adapter));
2461 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2467 case GST_EVENT_CAPS:{
2468 GstCaps *caps = NULL;
2470 gst_event_parse_caps (event, &caps);
2471 gst_qtdemux_setcaps (demux, caps);
2473 gst_event_unref (event);
2476 case GST_EVENT_PROTECTION:
2478 const gchar *system_id = NULL;
2480 gst_event_parse_protection (event, &system_id, NULL, NULL);
2481 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2483 gst_qtdemux_append_protection_system_id (demux, system_id);
2484 /* save the event for later, for source pads that have not been created */
2485 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2486 /* send it to all pads that already exist */
2487 gst_qtdemux_push_event (demux, event);
2491 case GST_EVENT_STREAM_START:
2494 gst_event_unref (event);
2496 /* Drain all the buffers */
2497 gst_qtdemux_process_adapter (demux, TRUE);
2498 gst_qtdemux_reset (demux, FALSE);
2499 /* We expect new moov box after new stream-start event */
2500 if (demux->exposed) {
2501 gst_qtdemux_stream_concat (demux,
2502 demux->old_streams, demux->active_streams);
2511 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2518 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2521 GstQTDemux *demux = GST_QTDEMUX (parent);
2522 gboolean res = FALSE;
2524 switch (GST_QUERY_TYPE (query)) {
2525 case GST_QUERY_BITRATE:
2527 GstClockTime duration;
2529 /* populate demux->upstream_size if not done yet */
2530 gst_qtdemux_check_seekability (demux);
2532 if (demux->upstream_size != -1
2533 && gst_qtdemux_get_duration (demux, &duration)) {
2535 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2538 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2539 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2540 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2542 /* TODO: better results based on ranges/index tables */
2543 gst_query_set_bitrate (query, bitrate);
2549 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2559 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2561 GstQTDemux *demux = GST_QTDEMUX (element);
2563 GST_OBJECT_LOCK (demux);
2564 if (demux->element_index)
2565 gst_object_unref (demux->element_index);
2567 demux->element_index = gst_object_ref (index);
2569 demux->element_index = NULL;
2571 GST_OBJECT_UNLOCK (demux);
2572 /* object lock might be taken again */
2574 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2575 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2576 demux->element_index, demux->index_id);
2580 gst_qtdemux_get_index (GstElement * element)
2582 GstIndex *result = NULL;
2583 GstQTDemux *demux = GST_QTDEMUX (element);
2585 GST_OBJECT_LOCK (demux);
2586 if (demux->element_index)
2587 result = gst_object_ref (demux->element_index);
2588 GST_OBJECT_UNLOCK (demux);
2590 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2597 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2599 g_free ((gpointer) stream->stco.data);
2600 stream->stco.data = NULL;
2601 g_free ((gpointer) stream->stsz.data);
2602 stream->stsz.data = NULL;
2603 g_free ((gpointer) stream->stsc.data);
2604 stream->stsc.data = NULL;
2605 g_free ((gpointer) stream->stts.data);
2606 stream->stts.data = NULL;
2607 g_free ((gpointer) stream->stss.data);
2608 stream->stss.data = NULL;
2609 g_free ((gpointer) stream->stps.data);
2610 stream->stps.data = NULL;
2611 g_free ((gpointer) stream->ctts.data);
2612 stream->ctts.data = NULL;
2616 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2618 g_free (stream->segments);
2619 stream->segments = NULL;
2620 stream->segment_index = -1;
2621 stream->accumulated_base = 0;
2625 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2627 g_free (stream->samples);
2628 stream->samples = NULL;
2629 gst_qtdemux_stbl_free (stream);
2632 g_free (stream->ra_entries);
2633 stream->ra_entries = NULL;
2634 stream->n_ra_entries = 0;
2636 stream->sample_index = -1;
2637 stream->stbl_index = -1;
2638 stream->n_samples = 0;
2639 stream->time_position = 0;
2641 stream->n_samples_moof = 0;
2642 stream->duration_moof = 0;
2643 stream->duration_last_moof = 0;
2647 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2650 if (stream->allocator)
2651 gst_object_unref (stream->allocator);
2652 while (stream->buffers) {
2653 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2654 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2656 for (i = 0; i < stream->stsd_entries_length; i++) {
2657 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2658 if (entry->rgb8_palette) {
2659 gst_memory_unref (entry->rgb8_palette);
2660 entry->rgb8_palette = NULL;
2662 entry->sparse = FALSE;
2665 if (stream->stream_tags)
2666 gst_tag_list_unref (stream->stream_tags);
2668 stream->stream_tags = gst_tag_list_new_empty ();
2669 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2670 g_free (stream->redirect_uri);
2671 stream->redirect_uri = NULL;
2672 stream->sent_eos = FALSE;
2673 stream->protected = FALSE;
2674 if (stream->protection_scheme_info) {
2675 if (stream->protection_scheme_type == FOURCC_cenc) {
2676 QtDemuxCencSampleSetInfo *info =
2677 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2678 if (info->default_properties)
2679 gst_structure_free (info->default_properties);
2680 if (info->crypto_info)
2681 g_ptr_array_free (info->crypto_info, TRUE);
2683 g_free (stream->protection_scheme_info);
2684 stream->protection_scheme_info = NULL;
2686 stream->protection_scheme_type = 0;
2687 stream->protection_scheme_version = 0;
2688 g_queue_foreach (&stream->protection_scheme_event_queue,
2689 (GFunc) gst_event_unref, NULL);
2690 g_queue_clear (&stream->protection_scheme_event_queue);
2691 gst_qtdemux_stream_flush_segments_data (stream);
2692 gst_qtdemux_stream_flush_samples_data (stream);
2696 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2699 gst_qtdemux_stream_clear (stream);
2700 for (i = 0; i < stream->stsd_entries_length; i++) {
2701 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2703 gst_caps_unref (entry->caps);
2707 g_free (stream->stsd_entries);
2708 stream->stsd_entries = NULL;
2709 stream->stsd_entries_length = 0;
2712 static QtDemuxStream *
2713 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2715 g_atomic_int_add (&stream->ref_count, 1);
2721 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2723 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2724 gst_qtdemux_stream_reset (stream);
2725 gst_tag_list_unref (stream->stream_tags);
2727 GstQTDemux *demux = stream->demux;
2728 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2729 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2731 g_free (stream->stream_id);
2736 static GstStateChangeReturn
2737 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2739 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2740 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2742 switch (transition) {
2743 case GST_STATE_CHANGE_READY_TO_PAUSED:
2744 gst_qtdemux_reset (qtdemux, TRUE);
2750 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2752 switch (transition) {
2753 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2754 gst_qtdemux_reset (qtdemux, TRUE);
2765 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2767 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2769 g_return_if_fail (GST_IS_CONTEXT (context));
2771 if (gst_context_has_context_type (context,
2772 "drm-preferred-decryption-system-id")) {
2773 const GstStructure *s;
2775 s = gst_context_get_structure (context);
2776 g_free (qtdemux->preferred_protection_system_id);
2777 qtdemux->preferred_protection_system_id =
2778 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2779 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2780 qtdemux->preferred_protection_system_id);
2783 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2787 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2789 /* counts as header data */
2790 qtdemux->header_size += length;
2792 /* only consider at least a sufficiently complete ftyp atom */
2796 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2797 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2798 GST_FOURCC_ARGS (qtdemux->major_brand));
2799 if (qtdemux->comp_brands)
2800 gst_buffer_unref (qtdemux->comp_brands);
2801 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2802 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2807 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2808 GstTagList * xmptaglist)
2810 /* Strip out bogus fields */
2812 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2813 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2814 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2816 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2819 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2821 /* prioritize native tags using _KEEP mode */
2822 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2823 gst_tag_list_unref (xmptaglist);
2828 qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
2829 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
2832 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2833 gst_buffer_fill (kid_buf, 0, kid, 16);
2834 if (info->default_properties)
2835 gst_structure_free (info->default_properties);
2836 info->default_properties =
2837 gst_structure_new ("application/x-cenc",
2838 "iv_size", G_TYPE_UINT, iv_size,
2839 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2840 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2841 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
2842 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
2843 gst_buffer_unref (kid_buf);
2847 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
2848 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
2850 guint32 algorithm_id = 0;
2852 gboolean is_encrypted = TRUE;
2855 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2856 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2861 if (algorithm_id == 0) {
2862 is_encrypted = FALSE;
2863 } else if (algorithm_id == 1) {
2864 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2865 } else if (algorithm_id == 2) {
2866 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2869 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2872 if (!gst_byte_reader_get_data (br, 16, &kid))
2875 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
2876 is_encrypted, iv_size, kid);
2877 gst_structure_set (info->default_properties, "piff_algorithm_id",
2878 G_TYPE_UINT, algorithm_id, NULL);
2884 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
2892 QtDemuxStream *stream;
2893 GstStructure *structure;
2894 QtDemuxCencSampleSetInfo *ss_info = NULL;
2895 const gchar *system_id;
2896 gboolean uses_sub_sample_encryption = FALSE;
2897 guint32 sample_count;
2899 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
2902 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
2904 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
2905 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2906 GST_WARNING_OBJECT (qtdemux,
2907 "Attempting PIFF box parsing on an unencrypted stream.");
2911 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2912 G_TYPE_STRING, &system_id, NULL);
2913 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
2915 stream->protected = TRUE;
2916 stream->protection_scheme_type = FOURCC_cenc;
2918 if (!stream->protection_scheme_info)
2919 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
2921 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2922 if (!ss_info->default_properties) {
2923 ss_info->default_properties =
2924 gst_structure_new ("application/x-cenc",
2925 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2930 if (ss_info->crypto_info) {
2931 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2932 g_ptr_array_free (ss_info->crypto_info, TRUE);
2933 ss_info->crypto_info = NULL;
2937 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2939 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2940 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2944 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2945 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2949 if ((flags & 0x000001)) {
2950 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
2953 } else if ((flags & 0x000002)) {
2954 uses_sub_sample_encryption = TRUE;
2957 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2959 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2963 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2964 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2968 ss_info->crypto_info =
2969 g_ptr_array_new_full (sample_count,
2970 (GDestroyNotify) qtdemux_gst_structure_free);
2972 for (i = 0; i < sample_count; ++i) {
2973 GstStructure *properties;
2977 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
2978 if (properties == NULL) {
2979 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2980 qtdemux->cenc_aux_sample_count = i;
2984 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2985 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2986 gst_structure_free (properties);
2987 qtdemux->cenc_aux_sample_count = i;
2990 buf = gst_buffer_new_wrapped (data, iv_size);
2991 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2992 gst_buffer_unref (buf);
2994 if (uses_sub_sample_encryption) {
2995 guint16 n_subsamples;
2996 const GValue *kid_buf_value;
2998 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2999 || n_subsamples == 0) {
3000 GST_ERROR_OBJECT (qtdemux,
3001 "failed to get subsample count for sample %u", i);
3002 gst_structure_free (properties);
3003 qtdemux->cenc_aux_sample_count = i;
3006 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3007 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3008 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3010 gst_structure_free (properties);
3011 qtdemux->cenc_aux_sample_count = i;
3014 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3017 gst_structure_get_value (ss_info->default_properties, "kid");
3019 gst_structure_set (properties,
3020 "subsample_count", G_TYPE_UINT, n_subsamples,
3021 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3022 gst_structure_set_value (properties, "kid", kid_buf_value);
3023 gst_buffer_unref (buf);
3025 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3028 g_ptr_array_add (ss_info->crypto_info, properties);
3031 qtdemux->cenc_aux_sample_count = sample_count;
3035 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3037 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3038 0x97, 0xA9, 0x42, 0xE8,
3039 0x9C, 0x71, 0x99, 0x94,
3040 0x91, 0xE3, 0xAF, 0xAC
3042 static const guint8 playready_uuid[] = {
3043 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3044 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3047 static const guint8 piff_sample_encryption_uuid[] = {
3048 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3049 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3054 /* counts as header data */
3055 qtdemux->header_size += length;
3057 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3059 if (length <= offset + 16) {
3060 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3064 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3066 GstTagList *taglist;
3068 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3069 length - offset - 16, NULL);
3070 taglist = gst_tag_list_from_xmp_buffer (buf);
3071 gst_buffer_unref (buf);
3073 /* make sure we have a usable taglist */
3074 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3076 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3078 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3080 const gunichar2 *s_utf16;
3083 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3084 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3085 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3086 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3090 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3091 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3093 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3094 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3096 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3097 GST_READ_UINT32_LE (buffer + offset),
3098 GST_READ_UINT32_LE (buffer + offset + 4),
3099 GST_READ_UINT32_LE (buffer + offset + 8),
3100 GST_READ_UINT32_LE (buffer + offset + 12));
3105 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3107 GstSidxParser sidx_parser;
3108 GstIsoffParserResult res;
3111 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3114 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3116 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3117 if (res == GST_ISOFF_QT_PARSER_DONE) {
3118 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3120 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3123 /* caller verifies at least 8 bytes in buf */
3125 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3126 guint64 * plength, guint32 * pfourcc)
3131 length = QT_UINT32 (data);
3132 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3133 fourcc = QT_FOURCC (data + 4);
3134 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3137 length = G_MAXUINT64;
3138 } else if (length == 1 && size >= 16) {
3139 /* this means we have an extended size, which is the 64 bit value of
3140 * the next 8 bytes */
3141 length = QT_UINT64 (data + 8);
3142 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3152 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3154 guint32 version = 0;
3155 GstClockTime duration = 0;
3157 if (!gst_byte_reader_get_uint32_be (br, &version))
3162 if (!gst_byte_reader_get_uint64_be (br, &duration))
3167 if (!gst_byte_reader_get_uint32_be (br, &dur))
3172 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3173 qtdemux->duration = duration;
3179 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3185 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3186 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3188 if (!stream->parsed_trex && qtdemux->moov_node) {
3190 GstByteReader trex_data;
3192 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3194 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3197 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3199 /* skip version/flags */
3200 if (!gst_byte_reader_skip (&trex_data, 4))
3202 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3204 if (id != stream->track_id)
3206 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3208 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3210 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3212 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3215 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3216 "duration %d, size %d, flags 0x%x", stream->track_id,
3219 stream->parsed_trex = TRUE;
3220 stream->def_sample_description_index = sdi;
3221 stream->def_sample_duration = dur;
3222 stream->def_sample_size = size;
3223 stream->def_sample_flags = flags;
3226 /* iterate all siblings */
3227 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3233 *ds_duration = stream->def_sample_duration;
3234 *ds_size = stream->def_sample_size;
3235 *ds_flags = stream->def_sample_flags;
3237 /* even then, above values are better than random ... */
3238 if (G_UNLIKELY (!stream->parsed_trex)) {
3239 GST_WARNING_OBJECT (qtdemux,
3240 "failed to find fragment defaults for stream %d", stream->track_id);
3247 /* This method should be called whenever a more accurate duration might
3248 * have been found. It will update all relevant variables if/where needed
3251 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3255 GstClockTime prevdur;
3257 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3259 if (movdur > qtdemux->duration) {
3260 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3261 GST_DEBUG_OBJECT (qtdemux,
3262 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3263 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3264 qtdemux->duration = movdur;
3265 GST_DEBUG_OBJECT (qtdemux,
3266 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3267 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3268 GST_TIME_ARGS (qtdemux->segment.stop));
3269 if (qtdemux->segment.duration == prevdur) {
3270 /* If the current segment has duration/stop identical to previous duration
3271 * update them also (because they were set at that point in time with
3272 * the wrong duration */
3273 /* We convert the value *from* the timescale version to avoid rounding errors */
3274 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3275 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3276 qtdemux->segment.duration = fixeddur;
3277 qtdemux->segment.stop = fixeddur;
3281 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3282 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3284 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3285 if (movdur > stream->duration) {
3286 GST_DEBUG_OBJECT (qtdemux,
3287 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3288 GST_TIME_ARGS (duration));
3289 stream->duration = movdur;
3290 /* internal duration tracking state has been updated above, so */
3291 /* preserve an open-ended dummy segment rather than repeatedly updating
3292 * it and spamming downstream accordingly with segment events */
3293 if (stream->dummy_segment &&
3294 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3295 /* Update all dummy values to new duration */
3296 stream->segments[0].stop_time = duration;
3297 stream->segments[0].duration = duration;
3298 stream->segments[0].media_stop = duration;
3300 /* let downstream know we possibly have a new stop time */
3301 if (stream->segment_index != -1) {
3304 if (qtdemux->segment.rate >= 0) {
3305 pos = stream->segment.start;
3307 pos = stream->segment.stop;
3310 gst_qtdemux_stream_update_segment (qtdemux, stream,
3311 stream->segment_index, pos, NULL, NULL);
3319 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3320 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3321 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3322 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3325 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3327 gint32 data_offset = 0;
3328 guint32 flags = 0, first_flags = 0, samples_count = 0;
3331 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3332 QtDemuxSample *sample;
3333 gboolean ismv = FALSE;
3334 gint64 initial_offset;
3336 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3337 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3338 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3339 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3341 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3342 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3346 /* presence of stss or not can't really tell us much,
3347 * and flags and so on tend to be marginally reliable in these files */
3348 if (stream->subtype == FOURCC_soun) {
3349 GST_DEBUG_OBJECT (qtdemux,
3350 "sound track in fragmented file; marking all keyframes");
3351 stream->all_keyframe = TRUE;
3354 if (!gst_byte_reader_skip (trun, 1) ||
3355 !gst_byte_reader_get_uint24_be (trun, &flags))
3358 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3361 if (flags & TR_DATA_OFFSET) {
3362 /* note this is really signed */
3363 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3365 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3366 /* default base offset = first byte of moof */
3367 if (*base_offset == -1) {
3368 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3369 *base_offset = moof_offset;
3371 *running_offset = *base_offset + data_offset;
3373 /* if no offset at all, that would mean data starts at moof start,
3374 * which is a bit wrong and is ismv crappy way, so compensate
3375 * assuming data is in mdat following moof */
3376 if (*base_offset == -1) {
3377 *base_offset = moof_offset + moof_length + 8;
3378 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3381 if (*running_offset == -1)
3382 *running_offset = *base_offset;
3385 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3387 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3388 data_offset, flags, samples_count);
3390 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3391 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3392 GST_DEBUG_OBJECT (qtdemux,
3393 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3394 flags ^= TR_FIRST_SAMPLE_FLAGS;
3396 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3398 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3402 /* FIXME ? spec says other bits should also be checked to determine
3403 * entry size (and prefix size for that matter) */
3405 dur_offset = size_offset = 0;
3406 if (flags & TR_SAMPLE_DURATION) {
3407 GST_LOG_OBJECT (qtdemux, "entry duration present");
3408 dur_offset = entry_size;
3411 if (flags & TR_SAMPLE_SIZE) {
3412 GST_LOG_OBJECT (qtdemux, "entry size present");
3413 size_offset = entry_size;
3416 if (flags & TR_SAMPLE_FLAGS) {
3417 GST_LOG_OBJECT (qtdemux, "entry flags present");
3418 flags_offset = entry_size;
3421 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3422 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3423 ct_offset = entry_size;
3427 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3429 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3431 if (stream->n_samples + samples_count >=
3432 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3435 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3436 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3437 (stream->n_samples + samples_count) *
3438 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3440 /* create a new array of samples if it's the first sample parsed */
3441 if (stream->n_samples == 0) {
3442 g_assert (stream->samples == NULL);
3443 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3444 /* or try to reallocate it with space enough to insert the new samples */
3446 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3447 stream->n_samples + samples_count);
3448 if (stream->samples == NULL)
3451 if (qtdemux->fragment_start != -1) {
3452 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3453 qtdemux->fragment_start = -1;
3455 if (stream->n_samples == 0) {
3456 if (decode_ts > 0) {
3457 timestamp = decode_ts;
3458 } else if (stream->pending_seek != NULL) {
3459 /* if we don't have a timestamp from a tfdt box, we'll use the one
3460 * from the mfra seek table */
3461 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3462 GST_TIME_ARGS (stream->pending_seek->ts));
3464 /* FIXME: this is not fully correct, the timestamp refers to the random
3465 * access sample refered to in the tfra entry, which may not necessarily
3466 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3467 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3472 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3473 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3474 GST_TIME_ARGS (gst_ts));
3476 /* subsequent fragments extend stream */
3478 stream->samples[stream->n_samples - 1].timestamp +
3479 stream->samples[stream->n_samples - 1].duration;
3481 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3482 * difference (1 sec.) between decode_ts and timestamp, prefer the
3484 if (has_tfdt && !qtdemux->upstream_format_is_time
3485 && ABSDIFF (decode_ts, timestamp) >
3486 MAX (stream->duration_last_moof / 2,
3487 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3488 GST_INFO_OBJECT (qtdemux,
3489 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3490 ") are significantly different (more than %" GST_TIME_FORMAT
3491 "), using decode_ts",
3492 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3493 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3494 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3495 MAX (stream->duration_last_moof / 2,
3496 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3497 timestamp = decode_ts;
3500 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3501 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3502 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3506 initial_offset = *running_offset;
3508 sample = stream->samples + stream->n_samples;
3509 for (i = 0; i < samples_count; i++) {
3510 guint32 dur, size, sflags, ct;
3512 /* first read sample data */
3513 if (flags & TR_SAMPLE_DURATION) {
3514 dur = QT_UINT32 (data + dur_offset);
3516 dur = d_sample_duration;
3518 if (flags & TR_SAMPLE_SIZE) {
3519 size = QT_UINT32 (data + size_offset);
3521 size = d_sample_size;
3523 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3525 sflags = first_flags;
3527 sflags = d_sample_flags;
3529 } else if (flags & TR_SAMPLE_FLAGS) {
3530 sflags = QT_UINT32 (data + flags_offset);
3532 sflags = d_sample_flags;
3534 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3535 ct = QT_UINT32 (data + ct_offset);
3541 /* fill the sample information */
3542 sample->offset = *running_offset;
3543 sample->pts_offset = ct;
3544 sample->size = size;
3545 sample->timestamp = timestamp;
3546 sample->duration = dur;
3547 /* sample-is-difference-sample */
3548 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3549 * now idea how it relates to bitfield other than massive LE/BE confusion */
3550 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3551 *running_offset += size;
3553 stream->duration_moof += dur;
3557 /* Update total duration if needed */
3558 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3560 /* Pre-emptively figure out size of mdat based on trun information.
3561 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3562 * size, else we will still be able to use this when dealing with gap'ed
3564 qtdemux->mdatleft = *running_offset - initial_offset;
3565 qtdemux->mdatoffset = initial_offset;
3566 qtdemux->mdatsize = qtdemux->mdatleft;
3568 stream->n_samples += samples_count;
3569 stream->n_samples_moof += samples_count;
3571 if (stream->pending_seek != NULL)
3572 stream->pending_seek = NULL;
3578 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3583 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3589 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3590 "be larger than %uMB (broken file?)", stream->n_samples,
3591 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3596 /* find stream with @id */
3597 static inline QtDemuxStream *
3598 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3600 QtDemuxStream *stream;
3604 if (G_UNLIKELY (!id)) {
3605 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3609 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3610 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3611 if (stream->track_id == id)
3614 if (qtdemux->mss_mode) {
3615 /* mss should have only 1 stream anyway */
3616 return QTDEMUX_NTH_STREAM (qtdemux, 0);
3623 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3624 guint32 * fragment_number)
3626 if (!gst_byte_reader_skip (mfhd, 4))
3628 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3633 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3639 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3640 QtDemuxStream ** stream, guint32 * default_sample_duration,
3641 guint32 * default_sample_size, guint32 * default_sample_flags,
3642 gint64 * base_offset)
3645 guint32 track_id = 0;
3647 if (!gst_byte_reader_skip (tfhd, 1) ||
3648 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3651 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3654 *stream = qtdemux_find_stream (qtdemux, track_id);
3655 if (G_UNLIKELY (!*stream))
3656 goto unknown_stream;
3658 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3659 *base_offset = qtdemux->moof_offset;
3661 if (flags & TF_BASE_DATA_OFFSET)
3662 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3665 /* obtain stream defaults */
3666 qtdemux_parse_trex (qtdemux, *stream,
3667 default_sample_duration, default_sample_size, default_sample_flags);
3669 (*stream)->stsd_sample_description_id =
3670 (*stream)->def_sample_description_index - 1;
3672 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
3673 guint32 sample_description_index;
3674 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3676 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3679 if (qtdemux->mss_mode) {
3680 /* mss has no stsd entry */
3681 (*stream)->stsd_sample_description_id = 0;
3684 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3685 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3688 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3689 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3692 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3693 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3700 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3705 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
3711 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3712 guint64 * decode_time)
3714 guint32 version = 0;
3716 if (!gst_byte_reader_get_uint32_be (br, &version))
3721 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3724 guint32 dec_time = 0;
3725 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3727 *decode_time = dec_time;
3730 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3737 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3742 /* Returns a pointer to a GstStructure containing the properties of
3743 * the stream sample identified by @sample_index. The caller must unref
3744 * the returned object after use. Returns NULL if unsuccessful. */
3745 static GstStructure *
3746 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3747 QtDemuxStream * stream, guint sample_index)
3749 QtDemuxCencSampleSetInfo *info = NULL;
3751 g_return_val_if_fail (stream != NULL, NULL);
3752 g_return_val_if_fail (stream->protected, NULL);
3753 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3755 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3757 /* Currently, cenc properties for groups of samples are not supported, so
3758 * simply return a copy of the default sample properties */
3759 return gst_structure_copy (info->default_properties);
3762 /* Parses the sizes of sample auxiliary information contained within a stream,
3763 * as given in a saiz box. Returns array of sample_count guint8 size values,
3764 * or NULL on failure */
3766 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3767 GstByteReader * br, guint32 * sample_count)
3771 guint8 default_info_size;
3773 g_return_val_if_fail (qtdemux != NULL, NULL);
3774 g_return_val_if_fail (stream != NULL, NULL);
3775 g_return_val_if_fail (br != NULL, NULL);
3776 g_return_val_if_fail (sample_count != NULL, NULL);
3778 if (!gst_byte_reader_get_uint32_be (br, &flags))
3782 /* aux_info_type and aux_info_type_parameter are ignored */
3783 if (!gst_byte_reader_skip (br, 8))
3787 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3789 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3791 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3793 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3796 if (default_info_size == 0) {
3797 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3801 info_sizes = g_new (guint8, *sample_count);
3802 memset (info_sizes, default_info_size, *sample_count);
3808 /* Parses the offset of sample auxiliary information contained within a stream,
3809 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3811 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3812 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3817 guint32 aux_info_type = 0;
3818 guint32 aux_info_type_parameter = 0;
3819 guint32 entry_count;
3822 const guint8 *aux_info_type_data = NULL;
3824 g_return_val_if_fail (qtdemux != NULL, FALSE);
3825 g_return_val_if_fail (stream != NULL, FALSE);
3826 g_return_val_if_fail (br != NULL, FALSE);
3827 g_return_val_if_fail (offset != NULL, FALSE);
3829 if (!gst_byte_reader_get_uint8 (br, &version))
3832 if (!gst_byte_reader_get_uint24_be (br, &flags))
3837 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3839 aux_info_type = QT_FOURCC (aux_info_type_data);
3841 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3843 } else if (stream->protected) {
3844 aux_info_type = stream->protection_scheme_type;
3846 aux_info_type = CUR_STREAM (stream)->fourcc;
3850 *info_type = aux_info_type;
3851 if (info_type_parameter)
3852 *info_type_parameter = aux_info_type_parameter;
3854 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3855 "aux_info_type_parameter: %#06x",
3856 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3858 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3861 if (entry_count != 1) {
3862 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3867 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3869 *offset = (guint64) off_32;
3871 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3876 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3881 qtdemux_gst_structure_free (GstStructure * gststructure)
3884 gst_structure_free (gststructure);
3888 /* Parses auxiliary information relating to samples protected using Common
3889 * Encryption (cenc); the format of this information is defined in
3890 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
3892 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3893 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3895 QtDemuxCencSampleSetInfo *ss_info = NULL;
3898 GPtrArray *old_crypto_info = NULL;
3899 guint old_entries = 0;
3901 g_return_val_if_fail (qtdemux != NULL, FALSE);
3902 g_return_val_if_fail (stream != NULL, FALSE);
3903 g_return_val_if_fail (br != NULL, FALSE);
3904 g_return_val_if_fail (stream->protected, FALSE);
3905 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3907 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3909 if (ss_info->crypto_info) {
3910 old_crypto_info = ss_info->crypto_info;
3911 /* Count number of non-null entries remaining at the tail end */
3912 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3913 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3919 ss_info->crypto_info =
3920 g_ptr_array_new_full (sample_count + old_entries,
3921 (GDestroyNotify) qtdemux_gst_structure_free);
3923 /* We preserve old entries because we parse the next moof in advance
3924 * of consuming all samples from the previous moof, and otherwise
3925 * we'd discard the corresponding crypto info for the samples
3926 * from the previous fragment. */
3928 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3930 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3931 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3933 g_ptr_array_index (old_crypto_info, i) = NULL;
3937 if (old_crypto_info) {
3938 /* Everything now belongs to the new array */
3939 g_ptr_array_free (old_crypto_info, TRUE);
3942 for (i = 0; i < sample_count; ++i) {
3943 GstStructure *properties;
3944 guint16 n_subsamples = 0;
3949 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3950 if (properties == NULL) {
3951 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3954 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3955 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3956 gst_structure_free (properties);
3959 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
3960 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3961 gst_structure_free (properties);
3964 buf = gst_buffer_new_wrapped (data, iv_size);
3965 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3966 gst_buffer_unref (buf);
3967 size = info_sizes[i];
3968 if (size > iv_size) {
3969 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3970 || !(n_subsamples > 0)) {
3971 gst_structure_free (properties);
3972 GST_ERROR_OBJECT (qtdemux,
3973 "failed to get subsample count for sample %u", i);
3976 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3977 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3978 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3980 gst_structure_free (properties);
3983 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3985 gst_structure_free (properties);
3988 gst_structure_set (properties,
3989 "subsample_count", G_TYPE_UINT, n_subsamples,
3990 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3991 gst_buffer_unref (buf);
3993 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3995 g_ptr_array_add (ss_info->crypto_info, properties);
4000 /* Converts a UUID in raw byte form to a string representation, as defined in
4001 * RFC 4122. The caller takes ownership of the returned string and is
4002 * responsible for freeing it after use. */
4004 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4006 const guint8 *uuid = (const guint8 *) uuid_bytes;
4008 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4009 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4010 uuid[0], uuid[1], uuid[2], uuid[3],
4011 uuid[4], uuid[5], uuid[6], uuid[7],
4012 uuid[8], uuid[9], uuid[10], uuid[11],
4013 uuid[12], uuid[13], uuid[14], uuid[15]);
4016 /* Parses a Protection System Specific Header box (pssh), as defined in the
4017 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4018 * information needed by a specific content protection system in order to
4019 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4022 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4024 gchar *sysid_string;
4025 guint32 pssh_size = QT_UINT32 (node->data);
4026 GstBuffer *pssh = NULL;
4027 GstEvent *event = NULL;
4028 guint32 parent_box_type;
4031 if (G_UNLIKELY (pssh_size < 32U)) {
4032 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4037 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4039 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4041 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4042 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4043 gst_buffer_get_size (pssh));
4045 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4047 /* Push an event containing the pssh box onto the queues of all streams. */
4048 event = gst_event_new_protection (sysid_string, pssh,
4049 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4050 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4051 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4052 GST_TRACE_OBJECT (qtdemux,
4053 "adding protection event for stream %s and system %s",
4054 stream->stream_id, sysid_string);
4055 g_queue_push_tail (&stream->protection_scheme_event_queue,
4056 gst_event_ref (event));
4058 g_free (sysid_string);
4059 gst_event_unref (event);
4060 gst_buffer_unref (pssh);
4065 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4066 guint64 moof_offset, QtDemuxStream * stream)
4068 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4070 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4071 GNode *saiz_node, *saio_node, *pssh_node;
4072 GstByteReader saiz_data, saio_data;
4073 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4074 gint64 base_offset, running_offset;
4076 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4078 /* NOTE @stream ignored */
4080 moof_node = g_node_new ((guint8 *) buffer);
4081 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4082 qtdemux_node_dump (qtdemux, moof_node);
4084 /* Get fragment number from mfhd and check it's valid */
4086 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4087 if (mfhd_node == NULL)
4089 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4091 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4093 /* unknown base_offset to start with */
4094 base_offset = running_offset = -1;
4095 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4097 guint64 decode_time = 0;
4099 /* Fragment Header node */
4101 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4105 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4106 &ds_size, &ds_flags, &base_offset))
4109 /* The following code assumes at most a single set of sample auxiliary
4110 * data in the fragment (consisting of a saiz box and a corresponding saio
4111 * box); in theory, however, there could be multiple sets of sample
4112 * auxiliary data in a fragment. */
4114 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4117 guint32 info_type = 0;
4119 guint32 info_type_parameter = 0;
4121 g_free (qtdemux->cenc_aux_info_sizes);
4123 qtdemux->cenc_aux_info_sizes =
4124 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4125 &qtdemux->cenc_aux_sample_count);
4126 if (qtdemux->cenc_aux_info_sizes == NULL) {
4127 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4131 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4134 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4135 g_free (qtdemux->cenc_aux_info_sizes);
4136 qtdemux->cenc_aux_info_sizes = NULL;
4140 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4141 &info_type, &info_type_parameter, &offset))) {
4142 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4143 g_free (qtdemux->cenc_aux_info_sizes);
4144 qtdemux->cenc_aux_info_sizes = NULL;
4147 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4148 offset += (guint64) (base_offset - qtdemux->moof_offset);
4149 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4151 if (offset > length) {
4152 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4153 qtdemux->cenc_aux_info_offset = offset;
4155 gst_byte_reader_init (&br, buffer + offset, length - offset);
4156 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4157 qtdemux->cenc_aux_info_sizes,
4158 qtdemux->cenc_aux_sample_count)) {
4159 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4160 g_free (qtdemux->cenc_aux_info_sizes);
4161 qtdemux->cenc_aux_info_sizes = NULL;
4169 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4172 /* We'll use decode_time to interpolate timestamps
4173 * in case the input timestamps are missing */
4174 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4176 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4177 " (%" GST_TIME_FORMAT ")", decode_time,
4178 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4179 decode_time) : GST_CLOCK_TIME_NONE));
4181 /* Discard the fragment buffer timestamp info to avoid using it.
4182 * Rely on tfdt instead as it is more accurate than the timestamp
4183 * that is fetched from a manifest/playlist and is usually
4185 qtdemux->fragment_start = -1;
4188 if (G_UNLIKELY (!stream)) {
4189 /* we lost track of offset, we'll need to regain it,
4190 * but can delay complaining until later or avoid doing so altogether */
4194 if (G_UNLIKELY (base_offset < -1))
4197 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4199 if (!qtdemux->pullbased) {
4200 /* Sample tables can grow enough to be problematic if the system memory
4201 * is very low (e.g. embedded devices) and the videos very long
4202 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4203 * Fortunately, we can easily discard them for each new fragment when
4204 * we know qtdemux will not receive seeks outside of the current fragment.
4205 * adaptivedemux honors this assumption.
4206 * This optimization is also useful for applications that use qtdemux as
4207 * a push-based simple demuxer, like Media Source Extensions. */
4208 gst_qtdemux_stream_flush_samples_data (stream);
4211 /* initialise moof sample data */
4212 stream->n_samples_moof = 0;
4213 stream->duration_last_moof = stream->duration_moof;
4214 stream->duration_moof = 0;
4216 /* Track Run node */
4218 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4221 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4222 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4223 &running_offset, decode_time, (tfdt_node != NULL));
4224 /* iterate all siblings */
4225 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4229 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4231 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4232 guint32 box_length = QT_UINT32 (uuid_buffer);
4234 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4237 /* if no new base_offset provided for next traf,
4238 * base is end of current traf */
4239 base_offset = running_offset;
4240 running_offset = -1;
4242 if (stream->n_samples_moof && stream->duration_moof)
4243 stream->new_caps = TRUE;
4246 /* iterate all siblings */
4247 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4250 /* parse any protection system info */
4251 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4253 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4254 qtdemux_parse_pssh (qtdemux, pssh_node);
4255 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4258 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4259 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4261 /* Unless the user has explictly requested another seek, perform an
4262 * internal seek to the time specified in the tfdt.
4264 * This way if the user opens a file where the first tfdt is 1 hour
4265 * into the presentation, they will not have to wait 1 hour for run
4266 * time to catch up and actual playback to start. */
4269 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4270 "performing an internal seek to %" GST_TIME_FORMAT,
4271 GST_TIME_ARGS (min_dts));
4273 qtdemux->segment.start = min_dts;
4274 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4276 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4277 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4278 stream->time_position = min_dts;
4281 /* Before this code was run a segment was already sent when the moov was
4282 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4283 * be emitted after a moov, and we can emit a second segment anyway for
4284 * special cases like this. */
4285 qtdemux->need_segment = TRUE;
4288 qtdemux->first_moof_already_parsed = TRUE;
4290 g_node_destroy (moof_node);
4295 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4300 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4305 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4310 g_node_destroy (moof_node);
4311 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4312 (_("This file is corrupt and cannot be played.")), (NULL));
4318 /* might be used if some day we actually use mfra & co
4319 * for random access to fragments,
4320 * but that will require quite some modifications and much less relying
4321 * on a sample array */
4325 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4327 QtDemuxStream *stream;
4328 guint32 ver_flags, track_id, len, num_entries, i;
4329 guint value_size, traf_size, trun_size, sample_size;
4330 guint64 time = 0, moof_offset = 0;
4332 GstBuffer *buf = NULL;
4337 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4339 if (!gst_byte_reader_skip (&tfra, 8))
4342 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4345 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4346 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4347 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4350 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4352 stream = qtdemux_find_stream (qtdemux, track_id);
4354 goto unknown_trackid;
4356 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4357 sample_size = (len & 3) + 1;
4358 trun_size = ((len & 12) >> 2) + 1;
4359 traf_size = ((len & 48) >> 4) + 1;
4361 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4362 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4364 if (num_entries == 0)
4367 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4368 value_size + value_size + traf_size + trun_size + sample_size))
4371 g_free (stream->ra_entries);
4372 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4373 stream->n_ra_entries = num_entries;
4375 for (i = 0; i < num_entries; i++) {
4376 qt_atom_parser_get_offset (&tfra, value_size, &time);
4377 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4378 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4379 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4380 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4382 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4384 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4385 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4387 stream->ra_entries[i].ts = time;
4388 stream->ra_entries[i].moof_offset = moof_offset;
4390 /* don't want to go through the entire file and read all moofs at startup */
4392 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4393 if (ret != GST_FLOW_OK)
4395 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4396 moof_offset, stream);
4397 gst_buffer_unref (buf);
4401 check_update_duration (qtdemux, time);
4408 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4413 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4418 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4424 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4426 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4427 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4428 GstBuffer *mfro = NULL, *mfra = NULL;
4430 gboolean ret = FALSE;
4431 GNode *mfra_node, *tfra_node;
4432 guint64 mfra_offset = 0;
4433 guint32 fourcc, mfra_size;
4436 /* query upstream size in bytes */
4437 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4438 goto size_query_failed;
4440 /* mfro box should be at the very end of the file */
4441 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4442 if (flow != GST_FLOW_OK)
4445 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4447 fourcc = QT_FOURCC (mfro_map.data + 4);
4448 if (fourcc != FOURCC_mfro)
4451 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4452 if (mfro_map.size < 16)
4453 goto invalid_mfro_size;
4455 mfra_size = QT_UINT32 (mfro_map.data + 12);
4456 if (mfra_size >= len)
4457 goto invalid_mfra_size;
4459 mfra_offset = len - mfra_size;
4461 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4462 mfra_offset, mfra_size);
4464 /* now get and parse mfra box */
4465 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4466 if (flow != GST_FLOW_OK)
4469 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4471 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4472 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4474 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4477 qtdemux_parse_tfra (qtdemux, tfra_node);
4478 /* iterate all siblings */
4479 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4481 g_node_destroy (mfra_node);
4483 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4489 if (mfro_map.memory != NULL)
4490 gst_buffer_unmap (mfro, &mfro_map);
4491 gst_buffer_unref (mfro);
4494 if (mfra_map.memory != NULL)
4495 gst_buffer_unmap (mfra, &mfra_map);
4496 gst_buffer_unref (mfra);
4503 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4508 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4513 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4518 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4524 add_offset (guint64 offset, guint64 advance)
4526 /* Avoid 64-bit overflow by clamping */
4527 if (offset > G_MAXUINT64 - advance)
4529 return offset + advance;
4532 static GstFlowReturn
4533 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4537 GstBuffer *buf = NULL;
4538 GstFlowReturn ret = GST_FLOW_OK;
4539 guint64 cur_offset = qtdemux->offset;
4542 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4543 if (G_UNLIKELY (ret != GST_FLOW_OK))
4545 gst_buffer_map (buf, &map, GST_MAP_READ);
4546 if (G_LIKELY (map.size >= 8))
4547 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4548 gst_buffer_unmap (buf, &map);
4549 gst_buffer_unref (buf);
4551 /* maybe we already got most we needed, so only consider this eof */
4552 if (G_UNLIKELY (length == 0)) {
4553 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4554 (_("Invalid atom size.")),
4555 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4556 GST_FOURCC_ARGS (fourcc)));
4563 /* record for later parsing when needed */
4564 if (!qtdemux->moof_offset) {
4565 qtdemux->moof_offset = qtdemux->offset;
4567 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4570 qtdemux->offset += length; /* skip moof and keep going */
4572 if (qtdemux->got_moov) {
4573 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4585 GST_LOG_OBJECT (qtdemux,
4586 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4587 GST_FOURCC_ARGS (fourcc), cur_offset);
4588 qtdemux->offset = add_offset (qtdemux->offset, length);
4593 GstBuffer *moov = NULL;
4595 if (qtdemux->got_moov) {
4596 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4597 qtdemux->offset = add_offset (qtdemux->offset, length);
4601 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4602 if (ret != GST_FLOW_OK)
4604 gst_buffer_map (moov, &map, GST_MAP_READ);
4606 if (length != map.size) {
4607 /* Some files have a 'moov' atom at the end of the file which contains
4608 * a terminal 'free' atom where the body of the atom is missing.
4609 * Check for, and permit, this special case.
4611 if (map.size >= 8) {
4612 guint8 *final_data = map.data + (map.size - 8);
4613 guint32 final_length = QT_UINT32 (final_data);
4614 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4616 if (final_fourcc == FOURCC_free
4617 && map.size + final_length - 8 == length) {
4618 /* Ok, we've found that special case. Allocate a new buffer with
4619 * that free atom actually present. */
4620 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4621 gst_buffer_fill (newmoov, 0, map.data, map.size);
4622 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4623 gst_buffer_unmap (moov, &map);
4624 gst_buffer_unref (moov);
4626 gst_buffer_map (moov, &map, GST_MAP_READ);
4631 if (length != map.size) {
4632 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4633 (_("This file is incomplete and cannot be played.")),
4634 ("We got less than expected (received %" G_GSIZE_FORMAT
4635 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4636 (guint) length, cur_offset));
4637 gst_buffer_unmap (moov, &map);
4638 gst_buffer_unref (moov);
4639 ret = GST_FLOW_ERROR;
4642 qtdemux->offset += length;
4644 qtdemux_parse_moov (qtdemux, map.data, length);
4645 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4647 qtdemux_parse_tree (qtdemux);
4648 if (qtdemux->moov_node_compressed) {
4649 g_node_destroy (qtdemux->moov_node_compressed);
4650 g_free (qtdemux->moov_node->data);
4652 qtdemux->moov_node_compressed = NULL;
4653 g_node_destroy (qtdemux->moov_node);
4654 qtdemux->moov_node = NULL;
4655 gst_buffer_unmap (moov, &map);
4656 gst_buffer_unref (moov);
4657 qtdemux->got_moov = TRUE;
4663 GstBuffer *ftyp = NULL;
4665 /* extract major brand; might come in handy for ISO vs QT issues */
4666 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4667 if (ret != GST_FLOW_OK)
4669 qtdemux->offset += length;
4670 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4671 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4672 gst_buffer_unmap (ftyp, &map);
4673 gst_buffer_unref (ftyp);
4678 GstBuffer *uuid = NULL;
4680 /* uuid are extension atoms */
4681 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4682 if (ret != GST_FLOW_OK)
4684 qtdemux->offset += length;
4685 gst_buffer_map (uuid, &map, GST_MAP_READ);
4686 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4687 gst_buffer_unmap (uuid, &map);
4688 gst_buffer_unref (uuid);
4693 GstBuffer *sidx = NULL;
4694 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4695 if (ret != GST_FLOW_OK)
4697 qtdemux->offset += length;
4698 gst_buffer_map (sidx, &map, GST_MAP_READ);
4699 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4700 gst_buffer_unmap (sidx, &map);
4701 gst_buffer_unref (sidx);
4706 GstBuffer *unknown = NULL;
4708 GST_LOG_OBJECT (qtdemux,
4709 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4710 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4712 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4713 if (ret != GST_FLOW_OK)
4715 gst_buffer_map (unknown, &map, GST_MAP_READ);
4716 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4717 gst_buffer_unmap (unknown, &map);
4718 gst_buffer_unref (unknown);
4719 qtdemux->offset += length;
4725 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4726 /* digested all data, show what we have */
4727 qtdemux_prepare_streams (qtdemux);
4728 QTDEMUX_EXPOSE_LOCK (qtdemux);
4729 ret = qtdemux_expose_streams (qtdemux);
4730 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
4732 qtdemux->state = QTDEMUX_STATE_MOVIE;
4733 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4740 /* Seeks to the previous keyframe of the indexed stream and
4741 * aligns other streams with respect to the keyframe timestamp
4742 * of indexed stream. Only called in case of Reverse Playback
4744 static GstFlowReturn
4745 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4747 guint32 seg_idx = 0, k_index = 0;
4748 guint32 ref_seg_idx, ref_k_index;
4749 GstClockTime k_pos = 0, last_stop = 0;
4750 QtDemuxSegment *seg = NULL;
4751 QtDemuxStream *ref_str = NULL;
4752 guint64 seg_media_start_mov; /* segment media start time in mov format */
4756 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4757 * and finally align all the other streams on that timestamp with their
4758 * respective keyframes */
4759 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4760 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4762 /* No candidate yet, take the first stream */
4768 /* So that stream has a segment, we prefer video streams */
4769 if (str->subtype == FOURCC_vide) {
4775 if (G_UNLIKELY (!ref_str)) {
4776 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4780 if (G_UNLIKELY (!ref_str->from_sample)) {
4781 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4785 /* So that stream has been playing from from_sample to to_sample. We will
4786 * get the timestamp of the previous sample and search for a keyframe before
4787 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4788 if (ref_str->subtype == FOURCC_vide) {
4789 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4790 ref_str->from_sample - 1, FALSE);
4792 if (ref_str->from_sample >= 10)
4793 k_index = ref_str->from_sample - 10;
4799 ref_str->samples[k_index].timestamp +
4800 ref_str->samples[k_index].pts_offset;
4802 /* get current segment for that stream */
4803 seg = &ref_str->segments[ref_str->segment_index];
4804 /* Use segment start in original timescale for comparisons */
4805 seg_media_start_mov = seg->trak_media_start;
4807 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4808 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4809 k_index, target_ts, seg_media_start_mov,
4810 GST_TIME_ARGS (seg->media_start));
4812 /* Crawl back through segments to find the one containing this I frame */
4813 while (target_ts < seg_media_start_mov) {
4814 GST_DEBUG_OBJECT (qtdemux,
4815 "keyframe position (sample %u) is out of segment %u " " target %"
4816 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4817 ref_str->segment_index, target_ts, seg_media_start_mov);
4819 if (G_UNLIKELY (!ref_str->segment_index)) {
4820 /* Reached first segment, let's consider it's EOS */
4823 ref_str->segment_index--;
4824 seg = &ref_str->segments[ref_str->segment_index];
4825 /* Use segment start in original timescale for comparisons */
4826 seg_media_start_mov = seg->trak_media_start;
4828 /* Calculate time position of the keyframe and where we should stop */
4830 QTSTREAMTIME_TO_GSTTIME (ref_str,
4831 target_ts - seg->trak_media_start) + seg->time;
4833 QTSTREAMTIME_TO_GSTTIME (ref_str,
4834 ref_str->samples[ref_str->from_sample].timestamp -
4835 seg->trak_media_start) + seg->time;
4837 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4838 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4839 k_index, GST_TIME_ARGS (k_pos));
4841 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4842 qtdemux->segment.position = last_stop;
4843 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4844 GST_TIME_ARGS (last_stop));
4846 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4847 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4851 ref_seg_idx = ref_str->segment_index;
4852 ref_k_index = k_index;
4854 /* Align them all on this */
4855 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4857 GstClockTime seg_time = 0;
4858 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4860 /* aligning reference stream again might lead to backing up to yet another
4861 * keyframe (due to timestamp rounding issues),
4862 * potentially putting more load on downstream; so let's try to avoid */
4863 if (str == ref_str) {
4864 seg_idx = ref_seg_idx;
4865 seg = &str->segments[seg_idx];
4866 k_index = ref_k_index;
4867 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
4868 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
4870 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4871 GST_DEBUG_OBJECT (qtdemux,
4872 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
4873 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
4875 /* get segment and time in the segment */
4876 seg = &str->segments[seg_idx];
4877 seg_time = k_pos - seg->time;
4879 /* get the media time in the segment.
4880 * No adjustment for empty "filler" segments */
4881 if (seg->media_start != GST_CLOCK_TIME_NONE)
4882 seg_time += seg->media_start;
4884 /* get the index of the sample with media time */
4885 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4886 GST_DEBUG_OBJECT (qtdemux,
4887 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
4888 GST_TIME_ARGS (seg_time), index);
4890 /* find previous keyframe */
4891 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
4894 /* Remember until where we want to go */
4895 str->to_sample = str->from_sample - 1;
4896 /* Define our time position */
4898 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4899 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4900 if (seg->media_start != GST_CLOCK_TIME_NONE)
4901 str->time_position -= seg->media_start;
4903 /* Now seek back in time */
4904 gst_qtdemux_move_stream (qtdemux, str, k_index);
4905 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
4906 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
4907 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4913 return GST_FLOW_EOS;
4917 * Gets the current qt segment start, stop and position for the
4918 * given time offset. This is used in update_segment()
4921 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
4922 QtDemuxStream * stream, GstClockTime offset,
4923 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
4925 GstClockTime seg_time;
4926 GstClockTime start, stop, time;
4927 QtDemuxSegment *segment;
4929 segment = &stream->segments[stream->segment_index];
4931 /* get time in this segment */
4932 seg_time = (offset - segment->time) * segment->rate;
4934 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4935 GST_TIME_ARGS (seg_time));
4937 if (G_UNLIKELY (seg_time > segment->duration)) {
4938 GST_LOG_OBJECT (stream->pad,
4939 "seg_time > segment->duration %" GST_TIME_FORMAT,
4940 GST_TIME_ARGS (segment->duration));
4941 seg_time = segment->duration;
4944 /* qtdemux->segment.stop is in outside-time-realm, whereas
4945 * segment->media_stop is in track-time-realm.
4947 * In order to compare the two, we need to bring segment.stop
4948 * into the track-time-realm
4950 * FIXME - does this comment still hold? Don't see any conversion here */
4952 stop = qtdemux->segment.stop;
4953 if (stop == GST_CLOCK_TIME_NONE)
4954 stop = qtdemux->segment.duration;
4955 if (stop == GST_CLOCK_TIME_NONE)
4956 stop = segment->media_stop;
4959 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4961 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4962 start = segment->time + seg_time;
4964 stop = start - seg_time + segment->duration;
4965 } else if (qtdemux->segment.rate >= 0) {
4966 start = MIN (segment->media_start + seg_time, stop);
4969 if (segment->media_start >= qtdemux->segment.start) {
4970 time = segment->time;
4972 time = segment->time + (qtdemux->segment.start - segment->media_start);
4975 start = MAX (segment->media_start, qtdemux->segment.start);
4976 stop = MIN (segment->media_start + seg_time, stop);
4985 * Updates the qt segment used for the stream and pushes a new segment event
4986 * downstream on this stream's pad.
4989 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4990 gint seg_idx, GstClockTime offset, GstClockTime * _start,
4991 GstClockTime * _stop)
4993 QtDemuxSegment *segment;
4994 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
4998 /* update the current segment */
4999 stream->segment_index = seg_idx;
5001 /* get the segment */
5002 segment = &stream->segments[seg_idx];
5004 if (G_UNLIKELY (offset < segment->time)) {
5005 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5006 GST_TIME_ARGS (segment->time));
5010 /* segment lies beyond total indicated duration */
5011 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5012 segment->time > qtdemux->segment.duration)) {
5013 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5014 " < segment->time %" GST_TIME_FORMAT,
5015 GST_TIME_ARGS (qtdemux->segment.duration),
5016 GST_TIME_ARGS (segment->time));
5020 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5021 &start, &stop, &time);
5023 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5024 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5025 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5027 /* combine global rate with that of the segment */
5028 rate = segment->rate * qtdemux->segment.rate;
5030 /* Copy flags from main segment */
5031 stream->segment.flags = qtdemux->segment.flags;
5033 /* update the segment values used for clipping */
5034 stream->segment.offset = qtdemux->segment.offset;
5035 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5036 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5037 stream->segment.rate = rate;
5038 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5039 stream->cslg_shift);
5040 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5041 stream->cslg_shift);
5042 stream->segment.time = time;
5043 stream->segment.position = stream->segment.start;
5045 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5048 /* now prepare and send the segment */
5050 event = gst_event_new_segment (&stream->segment);
5051 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5052 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5054 gst_pad_push_event (stream->pad, event);
5055 /* assume we can send more data now */
5056 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5057 /* clear to send tags on this pad now */
5058 gst_qtdemux_push_tags (qtdemux, stream);
5069 /* activate the given segment number @seg_idx of @stream at time @offset.
5070 * @offset is an absolute global position over all the segments.
5072 * This will push out a NEWSEGMENT event with the right values and
5073 * position the stream index to the first decodable sample before
5077 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5078 guint32 seg_idx, GstClockTime offset)
5080 QtDemuxSegment *segment;
5081 guint32 index, kf_index;
5082 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5084 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5085 seg_idx, GST_TIME_ARGS (offset));
5087 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5091 segment = &stream->segments[stream->segment_index];
5093 /* in the fragmented case, we pick a fragment that starts before our
5094 * desired position and rely on downstream to wait for a keyframe
5095 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5096 * tfra entries tells us which trun/sample the key unit is in, but we don't
5097 * make use of this additional information at the moment) */
5098 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5099 stream->to_sample = G_MAXUINT32;
5102 /* well, it will be taken care of below */
5103 qtdemux->fragmented_seek_pending = FALSE;
5104 /* FIXME ideally the do_fragmented_seek can be done right here,
5105 * rather than at loop level
5106 * (which might even allow handling edit lists in a fragmented file) */
5109 /* We don't need to look for a sample in push-based */
5110 if (!qtdemux->pullbased)
5113 /* and move to the keyframe before the indicated media time of the
5115 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5116 if (qtdemux->segment.rate >= 0) {
5117 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5118 stream->to_sample = G_MAXUINT32;
5119 GST_DEBUG_OBJECT (stream->pad,
5120 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5121 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5122 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5124 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5125 stream->to_sample = index;
5126 GST_DEBUG_OBJECT (stream->pad,
5127 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5128 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5129 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5132 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5133 "this is an empty segment");
5137 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5138 * encountered an error and printed a message so we return appropriately */
5142 /* we're at the right spot */
5143 if (index == stream->sample_index) {
5144 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5148 /* find keyframe of the target index */
5149 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5152 /* indent does stupid stuff with stream->samples[].timestamp */
5154 /* if we move forwards, we don't have to go back to the previous
5155 * keyframe since we already sent that. We can also just jump to
5156 * the keyframe right before the target index if there is one. */
5157 if (index > stream->sample_index) {
5158 /* moving forwards check if we move past a keyframe */
5159 if (kf_index > stream->sample_index) {
5160 GST_DEBUG_OBJECT (stream->pad,
5161 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5162 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5163 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5164 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5166 GST_DEBUG_OBJECT (stream->pad,
5167 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5168 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5169 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5172 GST_DEBUG_OBJECT (stream->pad,
5173 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5174 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5175 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5176 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5184 /* prepare to get the current sample of @stream, getting essential values.
5186 * This function will also prepare and send the segment when needed.
5188 * Return FALSE if the stream is EOS.
5193 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5194 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5195 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5196 gboolean * keyframe)
5198 QtDemuxSample *sample;
5199 GstClockTime time_position;
5202 g_return_val_if_fail (stream != NULL, FALSE);
5204 time_position = stream->time_position;
5205 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5208 seg_idx = stream->segment_index;
5209 if (G_UNLIKELY (seg_idx == -1)) {
5210 /* find segment corresponding to time_position if we are looking
5212 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5215 /* different segment, activate it, sample_index will be set. */
5216 if (G_UNLIKELY (stream->segment_index != seg_idx))
5217 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5219 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5220 segments[stream->segment_index]))) {
5221 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5223 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5224 " prepare empty sample");
5227 *pts = *dts = time_position;
5228 *duration = seg->duration - (time_position - seg->time);
5235 if (stream->sample_index == -1)
5236 stream->sample_index = 0;
5238 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5239 stream->sample_index, stream->n_samples);
5241 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5242 if (!qtdemux->fragmented)
5245 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5249 GST_OBJECT_LOCK (qtdemux);
5250 flow = qtdemux_add_fragmented_samples (qtdemux);
5251 GST_OBJECT_UNLOCK (qtdemux);
5253 if (flow != GST_FLOW_OK)
5256 while (stream->sample_index >= stream->n_samples);
5259 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5260 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5261 stream->sample_index);
5265 /* now get the info for the sample we're at */
5266 sample = &stream->samples[stream->sample_index];
5268 *dts = QTSAMPLE_DTS (stream, sample);
5269 *pts = QTSAMPLE_PTS (stream, sample);
5270 *offset = sample->offset;
5271 *size = sample->size;
5272 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5273 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5280 stream->time_position = GST_CLOCK_TIME_NONE;
5285 /* move to the next sample in @stream.
5287 * Moves to the next segment when needed.
5290 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5292 QtDemuxSample *sample;
5293 QtDemuxSegment *segment;
5295 /* get current segment */
5296 segment = &stream->segments[stream->segment_index];
5298 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5299 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5303 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5304 /* Mark the stream as EOS */
5305 GST_DEBUG_OBJECT (qtdemux,
5306 "reached max allowed sample %u, mark EOS", stream->to_sample);
5307 stream->time_position = GST_CLOCK_TIME_NONE;
5311 /* move to next sample */
5312 stream->sample_index++;
5313 stream->offset_in_sample = 0;
5315 /* reached the last sample, we need the next segment */
5316 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5319 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5320 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5321 stream->sample_index);
5325 /* get next sample */
5326 sample = &stream->samples[stream->sample_index];
5328 /* see if we are past the segment */
5329 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5332 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5333 /* inside the segment, update time_position, looks very familiar to
5334 * GStreamer segments, doesn't it? */
5335 stream->time_position =
5336 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5338 /* not yet in segment, time does not yet increment. This means
5339 * that we are still prerolling keyframes to the decoder so it can
5340 * decode the first sample of the segment. */
5341 stream->time_position = segment->time;
5345 /* move to the next segment */
5348 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5350 if (stream->segment_index == stream->n_segments - 1) {
5351 /* are we at the end of the last segment, we're EOS */
5352 stream->time_position = GST_CLOCK_TIME_NONE;
5354 /* else we're only at the end of the current segment */
5355 stream->time_position = segment->stop_time;
5357 /* make sure we select a new segment */
5359 /* accumulate previous segments */
5360 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5361 stream->accumulated_base +=
5362 (stream->segment.stop -
5363 stream->segment.start) / ABS (stream->segment.rate);
5365 stream->segment_index = -1;
5370 gst_qtdemux_sync_streams (GstQTDemux * demux)
5374 if (QTDEMUX_N_STREAMS (demux) <= 1)
5377 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5378 QtDemuxStream *stream;
5379 GstClockTime end_time;
5381 stream = QTDEMUX_NTH_STREAM (demux, i);
5386 /* TODO advance time on subtitle streams here, if any some day */
5388 /* some clips/trailers may have unbalanced streams at the end,
5389 * so send EOS on shorter stream to prevent stalling others */
5391 /* do not mess with EOS if SEGMENT seeking */
5392 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5395 if (demux->pullbased) {
5396 /* loop mode is sample time based */
5397 if (!STREAM_IS_EOS (stream))
5400 /* push mode is byte position based */
5401 if (stream->n_samples &&
5402 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5406 if (stream->sent_eos)
5409 /* only act if some gap */
5410 end_time = stream->segments[stream->n_segments - 1].stop_time;
5411 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5412 ", stream end: %" GST_TIME_FORMAT,
5413 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5414 if (GST_CLOCK_TIME_IS_VALID (end_time)
5415 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5418 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5419 GST_PAD_NAME (stream->pad));
5420 stream->sent_eos = TRUE;
5421 event = gst_event_new_eos ();
5422 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5423 gst_event_set_seqnum (event, demux->segment_seqnum);
5424 gst_pad_push_event (stream->pad, event);
5429 /* EOS and NOT_LINKED need to be combined. This means that we return:
5431 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5432 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5434 static GstFlowReturn
5435 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5438 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5441 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5444 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5446 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5450 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5451 * completely clipped
5453 * Should be used only with raw buffers */
5455 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5458 guint64 start, stop, cstart, cstop, diff;
5459 GstClockTime pts, duration;
5461 gint num_rate, denom_rate;
5466 osize = size = gst_buffer_get_size (buf);
5469 /* depending on the type, setup the clip parameters */
5470 if (stream->subtype == FOURCC_soun) {
5471 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5472 num_rate = GST_SECOND;
5473 denom_rate = (gint) CUR_STREAM (stream)->rate;
5475 } else if (stream->subtype == FOURCC_vide) {
5477 num_rate = CUR_STREAM (stream)->fps_n;
5478 denom_rate = CUR_STREAM (stream)->fps_d;
5483 if (frame_size <= 0)
5484 goto bad_frame_size;
5486 /* we can only clip if we have a valid pts */
5487 pts = GST_BUFFER_PTS (buf);
5488 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5491 duration = GST_BUFFER_DURATION (buf);
5493 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5495 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5499 stop = start + duration;
5501 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5502 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5505 /* see if some clipping happened */
5506 diff = cstart - start;
5512 /* bring clipped time to samples and to bytes */
5513 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5516 GST_DEBUG_OBJECT (qtdemux,
5517 "clipping start to %" GST_TIME_FORMAT " %"
5518 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5524 diff = stop - cstop;
5529 /* bring clipped time to samples and then to bytes */
5530 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5532 GST_DEBUG_OBJECT (qtdemux,
5533 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5534 " bytes", GST_TIME_ARGS (cstop), diff);
5539 if (offset != 0 || size != osize)
5540 gst_buffer_resize (buf, offset, size);
5542 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5543 GST_BUFFER_PTS (buf) = pts;
5544 GST_BUFFER_DURATION (buf) = duration;
5548 /* dropped buffer */
5551 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5556 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5561 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5566 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5567 gst_buffer_unref (buf);
5573 gst_qtdemux_align_buffer (GstQTDemux * demux,
5574 GstBuffer * buffer, gsize alignment)
5578 gst_buffer_map (buffer, &map, GST_MAP_READ);
5580 if (map.size < sizeof (guintptr)) {
5581 gst_buffer_unmap (buffer, &map);
5585 if (((guintptr) map.data) & (alignment - 1)) {
5586 GstBuffer *new_buffer;
5587 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5589 new_buffer = gst_buffer_new_allocate (NULL,
5590 gst_buffer_get_size (buffer), ¶ms);
5592 /* Copy data "by hand", so ensure alignment is kept: */
5593 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5595 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5596 GST_DEBUG_OBJECT (demux,
5597 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5600 gst_buffer_unmap (buffer, &map);
5601 gst_buffer_unref (buffer);
5606 gst_buffer_unmap (buffer, &map);
5611 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
5617 /* We are converting from pairs to triplets */
5618 *res = ccpair_size / 2 * 3;
5619 storage = g_malloc (*res);
5620 for (i = 0; i * 2 < ccpair_size; i += 1) {
5621 /* FIXME: Use line offset 0 as we simply can't know here */
5623 storage[i * 3] = 0x80 | 0x00;
5625 storage[i * 3] = 0x00 | 0x00;
5626 storage[i * 3 + 1] = ccpair[i * 2];
5627 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5634 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
5638 guint32 atom_length, fourcc;
5639 QtDemuxStreamStsdEntry *stsd_entry;
5641 GST_MEMDUMP ("caption atom", data, size);
5643 /* There might be multiple atoms */
5648 atom_length = QT_UINT32 (data);
5649 fourcc = QT_FOURCC (data + 4);
5650 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5653 GST_DEBUG_OBJECT (stream->pad, "here");
5655 /* Check if we have somethig compatible */
5656 stsd_entry = CUR_STREAM (stream);
5657 switch (stsd_entry->fourcc) {
5659 guint8 *cdat = NULL, *cdt2 = NULL;
5660 gsize cdat_size = 0, cdt2_size = 0;
5661 /* Should be cdat or cdt2 */
5662 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
5663 GST_WARNING_OBJECT (stream->pad,
5664 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5665 GST_FOURCC_ARGS (fourcc));
5669 /* Convert to S334-1 Annex A byte triplet */
5670 if (fourcc == FOURCC_cdat)
5671 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
5673 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
5674 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5677 /* Check for another atom ? */
5678 if (size > atom_length + 8) {
5679 guint32 new_atom_length = QT_UINT32 (data + atom_length);
5680 if (size >= atom_length + new_atom_length) {
5681 fourcc = QT_FOURCC (data + atom_length + 4);
5682 if (fourcc == FOURCC_cdat) {
5685 convert_to_s334_1a (data + atom_length + 8,
5686 new_atom_length - 8, 1, &cdat_size);
5688 GST_WARNING_OBJECT (stream->pad,
5689 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5693 convert_to_s334_1a (data + atom_length + 8,
5694 new_atom_length - 8, 2, &cdt2_size);
5696 GST_WARNING_OBJECT (stream->pad,
5697 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5702 *cclen = cdat_size + cdt2_size;
5703 res = g_malloc (*cclen);
5705 memcpy (res, cdat, cdat_size);
5707 memcpy (res + cdat_size, cdt2, cdt2_size);
5713 if (fourcc != FOURCC_ccdp) {
5714 GST_WARNING_OBJECT (stream->pad,
5715 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5716 GST_FOURCC_ARGS (fourcc));
5719 *cclen = atom_length - 8;
5720 res = g_memdup (data + 8, *cclen);
5723 /* Keep this here in case other closed caption formats are added */
5724 g_assert_not_reached ();
5728 GST_MEMDUMP ("Output", res, *cclen);
5733 GST_WARNING ("[cdat] atom is too small or invalid");
5737 /* the input buffer metadata must be writable,
5738 * but time/duration etc not yet set and need not be preserved */
5740 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5747 /* not many cases for now */
5748 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
5749 /* send a one time dvd clut event */
5750 if (stream->pending_event && stream->pad)
5751 gst_pad_push_event (stream->pad, stream->pending_event);
5752 stream->pending_event = NULL;
5755 if (G_UNLIKELY (stream->subtype != FOURCC_text
5756 && stream->subtype != FOURCC_sbtl &&
5757 stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
5761 gst_buffer_map (buf, &map, GST_MAP_READ);
5763 /* empty buffer is sent to terminate previous subtitle */
5764 if (map.size <= 2) {
5765 gst_buffer_unmap (buf, &map);
5766 gst_buffer_unref (buf);
5769 if (stream->subtype == FOURCC_subp) {
5770 /* That's all the processing needed for subpictures */
5771 gst_buffer_unmap (buf, &map);
5775 if (stream->subtype == FOURCC_clcp) {
5778 /* For closed caption, we need to extract the information from the
5779 * [cdat],[cdt2] or [ccdp] atom */
5780 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
5781 gst_buffer_unmap (buf, &map);
5782 gst_buffer_unref (buf);
5784 buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
5786 /* Conversion failed or there's nothing */
5792 nsize = GST_READ_UINT16_BE (map.data);
5793 nsize = MIN (nsize, map.size - 2);
5795 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5798 /* takes care of UTF-8 validation or UTF-16 recognition,
5799 * no other encoding expected */
5800 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5801 gst_buffer_unmap (buf, &map);
5803 gst_buffer_unref (buf);
5804 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5806 /* this should not really happen unless the subtitle is corrupted */
5807 gst_buffer_unref (buf);
5811 /* FIXME ? convert optional subsequent style info to markup */
5816 static GstFlowReturn
5817 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5820 GstFlowReturn ret = GST_FLOW_OK;
5821 GstClockTime pts, duration;
5823 if (stream->need_clip)
5824 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5826 if (G_UNLIKELY (buf == NULL))
5829 if (G_UNLIKELY (stream->discont)) {
5830 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5831 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5832 stream->discont = FALSE;
5834 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5837 GST_LOG_OBJECT (qtdemux,
5838 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
5839 ", duration %" GST_TIME_FORMAT " on pad %s",
5840 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
5841 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
5842 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
5844 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
5845 GstStructure *crypto_info;
5846 QtDemuxCencSampleSetInfo *info =
5847 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5851 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5852 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
5853 GST_PTR_FORMAT, event);
5854 gst_pad_push_event (stream->pad, event);
5857 if (info->crypto_info == NULL) {
5858 GST_DEBUG_OBJECT (qtdemux,
5859 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
5861 /* The end of the crypto_info array matches our n_samples position,
5862 * so count backward from there */
5863 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
5864 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5865 /* steal structure from array */
5866 crypto_info = g_ptr_array_index (info->crypto_info, index);
5867 g_ptr_array_index (info->crypto_info, index) = NULL;
5868 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
5869 info->crypto_info->len);
5870 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5871 GST_ERROR_OBJECT (qtdemux,
5872 "failed to attach cenc metadata to buffer");
5874 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
5875 index, stream->sample_index);
5880 if (stream->alignment > 1)
5881 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
5883 pts = GST_BUFFER_PTS (buf);
5884 duration = GST_BUFFER_DURATION (buf);
5886 ret = gst_pad_push (stream->pad, buf);
5888 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
5889 /* mark position in stream, we'll need this to know when to send GAP event */
5890 stream->segment.position = pts + duration;
5898 static GstFlowReturn
5899 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5902 GstFlowReturn ret = GST_FLOW_OK;
5904 if (stream->subtype == FOURCC_clcp
5905 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
5907 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
5908 guint n_triplets, i;
5909 guint field1_off = 0, field2_off = 0;
5911 /* We have to split CEA608 buffers so that each outgoing buffer contains
5912 * one byte pair per field according to the framerate of the video track.
5914 * If there is only a single byte pair per field we don't have to do
5918 gst_buffer_map (buf, &map, GST_MAP_READ);
5920 n_triplets = map.size / 3;
5921 for (i = 0; i < n_triplets; i++) {
5922 if (map.data[3 * i] & 0x80)
5928 g_assert (n_field1 || n_field2);
5930 /* If there's more than 1 frame we have to split, otherwise we can just
5932 if (n_field1 > 1 || n_field2 > 1) {
5934 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
5935 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
5937 for (i = 0; i < n_output_buffers; i++) {
5939 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
5943 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
5944 outptr = outmap.data;
5947 gboolean found = FALSE;
5949 while (map.data + field1_off < map.data + map.size) {
5950 if (map.data[field1_off] & 0x80) {
5951 memcpy (outptr, &map.data[field1_off], 3);
5960 const guint8 empty[] = { 0x80, 0x80, 0x80 };
5962 memcpy (outptr, empty, 3);
5969 gboolean found = FALSE;
5971 while (map.data + field2_off < map.data + map.size) {
5972 if ((map.data[field2_off] & 0x80) == 0) {
5973 memcpy (outptr, &map.data[field2_off], 3);
5982 const guint8 empty[] = { 0x00, 0x80, 0x80 };
5984 memcpy (outptr, empty, 3);
5990 gst_buffer_unmap (outbuf, &outmap);
5992 GST_BUFFER_PTS (outbuf) =
5993 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
5994 GST_SECOND * CUR_STREAM (stream)->fps_d,
5995 CUR_STREAM (stream)->fps_n);
5996 GST_BUFFER_DURATION (outbuf) =
5997 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
5998 CUR_STREAM (stream)->fps_n);
5999 GST_BUFFER_OFFSET (outbuf) = -1;
6000 GST_BUFFER_OFFSET_END (outbuf) = -1;
6002 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6004 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6007 gst_buffer_unmap (buf, &map);
6008 gst_buffer_unref (buf);
6010 gst_buffer_unmap (buf, &map);
6011 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6014 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6020 /* Sets a buffer's attributes properly and pushes it downstream.
6021 * Also checks for additional actions and custom processing that may
6022 * need to be done first.
6024 static GstFlowReturn
6025 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6026 QtDemuxStream * stream, GstBuffer * buf,
6027 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6028 gboolean keyframe, GstClockTime position, guint64 byte_position)
6030 GstFlowReturn ret = GST_FLOW_OK;
6032 /* offset the timestamps according to the edit list */
6034 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6038 gst_buffer_map (buf, &map, GST_MAP_READ);
6039 url = g_strndup ((gchar *) map.data, map.size);
6040 gst_buffer_unmap (buf, &map);
6041 if (url != NULL && strlen (url) != 0) {
6042 /* we have RTSP redirect now */
6043 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6044 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6045 gst_structure_new ("redirect",
6046 "new-location", G_TYPE_STRING, url, NULL)));
6047 qtdemux->posted_redirect = TRUE;
6049 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6055 /* position reporting */
6056 if (qtdemux->segment.rate >= 0) {
6057 qtdemux->segment.position = position;
6058 gst_qtdemux_sync_streams (qtdemux);
6061 if (G_UNLIKELY (!stream->pad)) {
6062 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6063 gst_buffer_unref (buf);
6067 /* send out pending buffers */
6068 while (stream->buffers) {
6069 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6071 if (G_UNLIKELY (stream->discont)) {
6072 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6073 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6074 stream->discont = FALSE;
6076 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6079 if (stream->alignment > 1)
6080 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6081 gst_pad_push (stream->pad, buffer);
6083 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6086 /* we're going to modify the metadata */
6087 buf = gst_buffer_make_writable (buf);
6089 if (G_UNLIKELY (stream->need_process))
6090 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6096 GST_BUFFER_DTS (buf) = dts;
6097 GST_BUFFER_PTS (buf) = pts;
6098 GST_BUFFER_DURATION (buf) = duration;
6099 GST_BUFFER_OFFSET (buf) = -1;
6100 GST_BUFFER_OFFSET_END (buf) = -1;
6103 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6104 stream->on_keyframe = FALSE;
6106 stream->on_keyframe = TRUE;
6109 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6110 gst_buffer_append_memory (buf,
6111 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6113 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6114 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6117 if (G_UNLIKELY (qtdemux->element_index)) {
6118 GstClockTime stream_time;
6121 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6123 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6124 GST_LOG_OBJECT (qtdemux,
6125 "adding association %" GST_TIME_FORMAT "-> %"
6126 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6127 gst_index_add_association (qtdemux->element_index,
6129 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6130 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6131 GST_FORMAT_BYTES, byte_position, NULL);
6136 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6142 static const QtDemuxRandomAccessEntry *
6143 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6144 GstClockTime pos, gboolean after)
6146 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6147 guint n_entries = stream->n_ra_entries;
6150 /* we assume the table is sorted */
6151 for (i = 0; i < n_entries; ++i) {
6152 if (entries[i].ts > pos)
6156 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6157 * probably okay to assume that the index lists the very first fragment */
6164 return &entries[i - 1];
6168 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6170 const QtDemuxRandomAccessEntry *best_entry = NULL;
6173 GST_OBJECT_LOCK (qtdemux);
6175 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6177 /* first see if we can determine where to go to using mfra,
6178 * before we start clearing things */
6179 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6180 const QtDemuxRandomAccessEntry *entry;
6181 QtDemuxStream *stream;
6182 gboolean is_audio_or_video;
6184 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6186 if (stream->ra_entries == NULL)
6189 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6190 is_audio_or_video = TRUE;
6192 is_audio_or_video = FALSE;
6195 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6196 stream->time_position, !is_audio_or_video);
6198 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6199 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6201 stream->pending_seek = entry;
6203 /* decide position to jump to just based on audio/video tracks, not subs */
6204 if (!is_audio_or_video)
6207 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6211 /* no luck, will handle seek otherwise */
6212 if (best_entry == NULL) {
6213 GST_OBJECT_UNLOCK (qtdemux);
6217 /* ok, now we can prepare for processing as of located moof */
6218 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6219 QtDemuxStream *stream;
6221 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6223 g_free (stream->samples);
6224 stream->samples = NULL;
6225 stream->n_samples = 0;
6226 stream->stbl_index = -1; /* no samples have yet been parsed */
6227 stream->sample_index = -1;
6229 if (stream->protection_scheme_info) {
6230 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6231 if (stream->protection_scheme_type == FOURCC_cenc) {
6232 QtDemuxCencSampleSetInfo *info =
6233 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6234 if (info->crypto_info) {
6235 g_ptr_array_free (info->crypto_info, TRUE);
6236 info->crypto_info = NULL;
6242 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6243 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6244 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6245 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6247 qtdemux->moof_offset = best_entry->moof_offset;
6249 qtdemux_add_fragmented_samples (qtdemux);
6251 GST_OBJECT_UNLOCK (qtdemux);
6255 static GstFlowReturn
6256 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6258 GstFlowReturn ret = GST_FLOW_OK;
6259 GstBuffer *buf = NULL;
6260 QtDemuxStream *stream, *target_stream = NULL;
6261 GstClockTime min_time;
6263 GstClockTime dts = GST_CLOCK_TIME_NONE;
6264 GstClockTime pts = GST_CLOCK_TIME_NONE;
6265 GstClockTime duration = 0;
6266 gboolean keyframe = FALSE;
6267 guint sample_size = 0;
6272 if (qtdemux->fragmented_seek_pending) {
6273 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6274 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6275 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6276 qtdemux->fragmented_seek_pending = FALSE;
6278 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6282 /* Figure out the next stream sample to output, min_time is expressed in
6283 * global time and runs over the edit list segments. */
6284 min_time = G_MAXUINT64;
6285 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6286 GstClockTime position;
6288 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6289 position = stream->time_position;
6291 /* position of -1 is EOS */
6292 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6293 min_time = position;
6294 target_stream = stream;
6298 if (G_UNLIKELY (target_stream == NULL)) {
6299 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6303 /* check for segment end */
6304 if (G_UNLIKELY (qtdemux->segment.stop != -1
6305 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6306 || (qtdemux->segment.rate < 0
6307 && qtdemux->segment.start > min_time))
6308 && target_stream->on_keyframe)) {
6309 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6310 target_stream->time_position = GST_CLOCK_TIME_NONE;
6314 /* gap events for subtitle streams */
6315 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6316 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6317 if (stream->pad && (stream->subtype == FOURCC_subp
6318 || stream->subtype == FOURCC_text
6319 || stream->subtype == FOURCC_sbtl)) {
6320 /* send one second gap events until the stream catches up */
6321 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6322 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6323 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6324 stream->segment.position + GST_SECOND < min_time) {
6326 gst_event_new_gap (stream->segment.position, GST_SECOND);
6327 gst_pad_push_event (stream->pad, gap);
6328 stream->segment.position += GST_SECOND;
6333 stream = target_stream;
6334 /* fetch info for the current sample of this stream */
6335 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6336 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6339 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6340 if (stream->new_caps) {
6341 gst_qtdemux_configure_stream (qtdemux, stream);
6342 qtdemux_do_allocation (stream, qtdemux);
6345 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6346 if (G_UNLIKELY (qtdemux->segment.
6347 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6348 if (stream->subtype == FOURCC_vide && !keyframe) {
6349 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6355 GST_DEBUG_OBJECT (qtdemux,
6356 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6357 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6358 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6359 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6360 GST_TIME_ARGS (duration));
6362 if (G_UNLIKELY (empty)) {
6363 /* empty segment, push a gap if there's a second or more
6364 * difference and move to the next one */
6365 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6366 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6367 stream->segment.position = pts + duration;
6371 /* hmm, empty sample, skip and move to next sample */
6372 if (G_UNLIKELY (sample_size <= 0))
6375 /* last pushed sample was out of boundary, goto next sample */
6376 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6379 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6382 GST_DEBUG_OBJECT (qtdemux,
6383 "size %d larger than stream max_buffer_size %d, trimming",
6384 sample_size, stream->max_buffer_size);
6386 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6389 if (qtdemux->cenc_aux_info_offset > 0) {
6392 GstBuffer *aux_info = NULL;
6394 /* pull the data stored before the sample */
6396 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6397 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6398 if (G_UNLIKELY (ret != GST_FLOW_OK))
6400 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6401 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6402 gst_byte_reader_init (&br, map.data + 8, map.size);
6403 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6404 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6405 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6406 gst_buffer_unmap (aux_info, &map);
6407 gst_buffer_unref (aux_info);
6408 ret = GST_FLOW_ERROR;
6411 gst_buffer_unmap (aux_info, &map);
6412 gst_buffer_unref (aux_info);
6415 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6418 if (stream->use_allocator) {
6419 /* if we have a per-stream allocator, use it */
6420 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6423 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6425 if (G_UNLIKELY (ret != GST_FLOW_OK))
6428 if (size != sample_size) {
6429 pts += gst_util_uint64_scale_int (GST_SECOND,
6430 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6433 gst_util_uint64_scale_int (GST_SECOND,
6434 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6437 gst_util_uint64_scale_int (GST_SECOND,
6438 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6441 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6442 dts, pts, duration, keyframe, min_time, offset);
6444 if (size != sample_size) {
6445 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6446 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6448 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6450 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6451 if (time_position >= segment->media_start) {
6452 /* inside the segment, update time_position, looks very familiar to
6453 * GStreamer segments, doesn't it? */
6454 stream->time_position = (time_position - segment->media_start) +
6457 /* not yet in segment, time does not yet increment. This means
6458 * that we are still prerolling keyframes to the decoder so it can
6459 * decode the first sample of the segment. */
6460 stream->time_position = segment->time;
6465 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6466 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6467 * we have no more data for the pad to push */
6468 if (ret == GST_FLOW_EOS)
6471 stream->offset_in_sample += size;
6472 if (stream->offset_in_sample >= sample_size) {
6473 gst_qtdemux_advance_sample (qtdemux, stream);
6478 gst_qtdemux_advance_sample (qtdemux, stream);
6486 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6492 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6493 /* EOS will be raised if all are EOS */
6500 gst_qtdemux_loop (GstPad * pad)
6502 GstQTDemux *qtdemux;
6506 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6508 cur_offset = qtdemux->offset;
6509 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6510 cur_offset, qt_demux_state_string (qtdemux->state));
6512 switch (qtdemux->state) {
6513 case QTDEMUX_STATE_INITIAL:
6514 case QTDEMUX_STATE_HEADER:
6515 ret = gst_qtdemux_loop_state_header (qtdemux);
6517 case QTDEMUX_STATE_MOVIE:
6518 ret = gst_qtdemux_loop_state_movie (qtdemux);
6519 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6520 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6528 /* if something went wrong, pause */
6529 if (ret != GST_FLOW_OK)
6533 gst_object_unref (qtdemux);
6539 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6540 (NULL), ("streaming stopped, invalid state"));
6541 gst_pad_pause_task (pad);
6542 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6547 const gchar *reason = gst_flow_get_name (ret);
6549 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6551 gst_pad_pause_task (pad);
6553 /* fatal errors need special actions */
6555 if (ret == GST_FLOW_EOS) {
6556 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
6557 /* we have no streams, post an error */
6558 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6560 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6563 if ((stop = qtdemux->segment.stop) == -1)
6564 stop = qtdemux->segment.duration;
6566 if (qtdemux->segment.rate >= 0) {
6567 GstMessage *message;
6570 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6571 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6572 GST_FORMAT_TIME, stop);
6573 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6574 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6575 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6576 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6578 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6579 gst_qtdemux_push_event (qtdemux, event);
6581 GstMessage *message;
6584 /* For Reverse Playback */
6585 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6586 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6587 GST_FORMAT_TIME, qtdemux->segment.start);
6588 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6589 qtdemux->segment.start);
6590 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6591 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6592 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6594 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6595 gst_qtdemux_push_event (qtdemux, event);
6600 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6601 event = gst_event_new_eos ();
6602 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
6603 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6604 gst_qtdemux_push_event (qtdemux, event);
6606 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6607 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6608 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6617 * Returns if there are samples to be played.
6620 has_next_entry (GstQTDemux * demux)
6622 QtDemuxStream *stream;
6625 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6627 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6628 stream = QTDEMUX_NTH_STREAM (demux, i);
6630 if (stream->sample_index == -1) {
6631 stream->sample_index = 0;
6632 stream->offset_in_sample = 0;
6635 if (stream->sample_index >= stream->n_samples) {
6636 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6639 GST_DEBUG_OBJECT (demux, "Found a sample");
6643 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6650 * Returns the size of the first entry at the current offset.
6651 * If -1, there are none (which means EOS or empty file).
6654 next_entry_size (GstQTDemux * demux)
6656 QtDemuxStream *stream, *target_stream = NULL;
6657 guint64 smalloffs = (guint64) - 1;
6658 QtDemuxSample *sample;
6661 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6664 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6665 stream = QTDEMUX_NTH_STREAM (demux, i);
6667 if (stream->sample_index == -1) {
6668 stream->sample_index = 0;
6669 stream->offset_in_sample = 0;
6672 if (stream->sample_index >= stream->n_samples) {
6673 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6677 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6678 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6679 stream->sample_index);
6683 sample = &stream->samples[stream->sample_index];
6685 GST_LOG_OBJECT (demux,
6686 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
6687 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
6688 stream->sample_index, sample->offset, sample->size);
6690 if (((smalloffs == -1)
6691 || (sample->offset < smalloffs)) && (sample->size)) {
6692 smalloffs = sample->offset;
6693 target_stream = stream;
6700 GST_LOG_OBJECT (demux,
6701 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
6702 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
6704 stream = target_stream;
6705 sample = &stream->samples[stream->sample_index];
6707 if (sample->offset >= demux->offset) {
6708 demux->todrop = sample->offset - demux->offset;
6709 return sample->size + demux->todrop;
6712 GST_DEBUG_OBJECT (demux,
6713 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6718 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6720 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6722 gst_element_post_message (GST_ELEMENT_CAST (demux),
6723 gst_message_new_element (GST_OBJECT_CAST (demux),
6724 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6728 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6733 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6736 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6737 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6738 GST_SEEK_TYPE_NONE, -1);
6740 /* store seqnum to drop flush events, they don't need to reach downstream */
6741 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6742 res = gst_pad_push_event (demux->sinkpad, event);
6743 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
6748 /* check for seekable upstream, above and beyond a mere query */
6750 gst_qtdemux_check_seekability (GstQTDemux * demux)
6753 gboolean seekable = FALSE;
6754 gint64 start = -1, stop = -1;
6756 if (demux->upstream_size)
6759 if (demux->upstream_format_is_time)
6762 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6763 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6764 GST_DEBUG_OBJECT (demux, "seeking query failed");
6768 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6770 /* try harder to query upstream size if we didn't get it the first time */
6771 if (seekable && stop == -1) {
6772 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6773 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6776 /* if upstream doesn't know the size, it's likely that it's not seekable in
6777 * practice even if it technically may be seekable */
6778 if (seekable && (start != 0 || stop <= start)) {
6779 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6784 gst_query_unref (query);
6786 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6787 G_GUINT64_FORMAT ")", seekable, start, stop);
6788 demux->upstream_seekable = seekable;
6789 demux->upstream_size = seekable ? stop : -1;
6793 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6795 g_return_if_fail (bytes <= demux->todrop);
6797 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6798 gst_adapter_flush (demux->adapter, bytes);
6799 demux->neededbytes -= bytes;
6800 demux->offset += bytes;
6801 demux->todrop -= bytes;
6804 /* PUSH-MODE only: Send a segment, if not done already. */
6806 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
6808 if (G_UNLIKELY (demux->need_segment)) {
6811 if (!demux->upstream_format_is_time) {
6812 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
6814 GstEvent *segment_event;
6815 segment_event = gst_event_new_segment (&demux->segment);
6816 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6817 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
6818 gst_qtdemux_push_event (demux, segment_event);
6821 demux->need_segment = FALSE;
6823 /* clear to send tags on all streams */
6824 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6825 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6826 gst_qtdemux_push_tags (demux, stream);
6827 if (CUR_STREAM (stream)->sparse) {
6828 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6829 gst_pad_push_event (stream->pad,
6830 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
6836 /* Used for push mode only. */
6838 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
6839 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
6841 GstClockTime ts, dur;
6845 stream->segments[segment_index].duration - (pos -
6846 stream->segments[segment_index].time);
6847 stream->time_position += dur;
6849 /* Only gaps with a duration of at least one second are propagated.
6850 * Same workaround as in pull mode.
6851 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
6852 if (dur >= GST_SECOND) {
6854 gap = gst_event_new_gap (ts, dur);
6856 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
6857 "segment: %" GST_PTR_FORMAT, gap);
6858 gst_pad_push_event (stream->pad, gap);
6862 static GstFlowReturn
6863 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6867 demux = GST_QTDEMUX (parent);
6869 GST_DEBUG_OBJECT (demux,
6870 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
6871 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
6872 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
6873 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
6874 gst_buffer_get_size (inbuf), demux->offset);
6876 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6877 gboolean is_gap_input = FALSE;
6880 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6882 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6883 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
6886 /* Check if we can land back on our feet in the case where upstream is
6887 * handling the seeking/pushing of samples with gaps in between (like
6888 * in the case of trick-mode DASH for example) */
6889 if (demux->upstream_format_is_time
6890 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
6891 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6893 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6894 GST_LOG_OBJECT (demux,
6895 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
6896 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
6898 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
6899 stream, GST_BUFFER_OFFSET (inbuf));
6901 QtDemuxSample *sample = &stream->samples[res];
6902 GST_LOG_OBJECT (demux,
6903 "Checking if sample %d from track-id %u is valid (offset:%"
6904 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
6905 stream->track_id, sample->offset, sample->size);
6906 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
6907 GST_LOG_OBJECT (demux,
6908 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
6910 is_gap_input = TRUE;
6911 /* We can go back to standard playback mode */
6912 demux->state = QTDEMUX_STATE_MOVIE;
6913 /* Remember which sample this stream is at */
6914 stream->sample_index = res;
6915 /* Finally update all push-based values to the expected values */
6916 demux->neededbytes = stream->samples[res].size;
6917 demux->offset = GST_BUFFER_OFFSET (inbuf);
6919 demux->mdatsize - demux->offset + demux->mdatoffset;
6924 if (!is_gap_input) {
6925 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
6926 /* Reset state if it's a real discont */
6927 demux->neededbytes = 16;
6928 demux->state = QTDEMUX_STATE_INITIAL;
6929 demux->offset = GST_BUFFER_OFFSET (inbuf);
6930 gst_adapter_clear (demux->adapter);
6933 /* Reverse fragmented playback, need to flush all we have before
6934 * consuming a new fragment.
6935 * The samples array have the timestamps calculated by accumulating the
6936 * durations but this won't work for reverse playback of fragments as
6937 * the timestamps of a subsequent fragment should be smaller than the
6938 * previously received one. */
6939 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
6940 gst_qtdemux_process_adapter (demux, TRUE);
6941 g_ptr_array_foreach (demux->active_streams,
6942 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
6946 gst_adapter_push (demux->adapter, inbuf);
6948 GST_DEBUG_OBJECT (demux,
6949 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6950 demux->neededbytes, gst_adapter_available (demux->adapter));
6952 return gst_qtdemux_process_adapter (demux, FALSE);
6955 static GstFlowReturn
6956 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6958 GstFlowReturn ret = GST_FLOW_OK;
6960 /* we never really mean to buffer that much */
6961 if (demux->neededbytes == -1) {
6965 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6966 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6968 #ifndef GST_DISABLE_GST_DEBUG
6970 guint64 discont_offset, distance_from_discont;
6972 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
6973 distance_from_discont =
6974 gst_adapter_distance_from_discont (demux->adapter);
6976 GST_DEBUG_OBJECT (demux,
6977 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
6978 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
6979 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
6980 demux->offset, discont_offset, distance_from_discont);
6984 switch (demux->state) {
6985 case QTDEMUX_STATE_INITIAL:{
6990 gst_qtdemux_check_seekability (demux);
6992 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6994 /* get fourcc/length, set neededbytes */
6995 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6997 gst_adapter_unmap (demux->adapter);
6999 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7000 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7002 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7003 (_("This file is invalid and cannot be played.")),
7004 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7005 GST_FOURCC_ARGS (fourcc)));
7006 ret = GST_FLOW_ERROR;
7009 if (fourcc == FOURCC_mdat) {
7010 gint next_entry = next_entry_size (demux);
7011 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7012 || !demux->fragmented)) {
7013 /* we have the headers, start playback */
7014 demux->state = QTDEMUX_STATE_MOVIE;
7015 demux->neededbytes = next_entry;
7016 demux->mdatleft = size;
7017 demux->mdatsize = demux->mdatleft;
7019 /* no headers yet, try to get them */
7022 guint64 old, target;
7025 old = demux->offset;
7026 target = old + size;
7028 /* try to jump over the atom with a seek */
7029 /* only bother if it seems worth doing so,
7030 * and avoids possible upstream/server problems */
7031 if (demux->upstream_seekable &&
7032 demux->upstream_size > 4 * (1 << 20)) {
7033 res = qtdemux_seek_offset (demux, target);
7035 GST_DEBUG_OBJECT (demux, "skipping seek");
7040 GST_DEBUG_OBJECT (demux, "seek success");
7041 /* remember the offset fo the first mdat so we can seek back to it
7042 * after we have the headers */
7043 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7044 demux->first_mdat = old;
7045 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7048 /* seek worked, continue reading */
7049 demux->offset = target;
7050 demux->neededbytes = 16;
7051 demux->state = QTDEMUX_STATE_INITIAL;
7053 /* seek failed, need to buffer */
7054 demux->offset = old;
7055 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7056 /* there may be multiple mdat (or alike) buffers */
7058 if (demux->mdatbuffer)
7059 bs = gst_buffer_get_size (demux->mdatbuffer);
7062 if (size + bs > 10 * (1 << 20))
7064 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7065 demux->neededbytes = size;
7066 if (!demux->mdatbuffer)
7067 demux->mdatoffset = demux->offset;
7070 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7071 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7072 (_("This file is invalid and cannot be played.")),
7073 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7074 GST_FOURCC_ARGS (fourcc), size));
7075 ret = GST_FLOW_ERROR;
7078 /* this means we already started buffering and still no moov header,
7079 * let's continue buffering everything till we get moov */
7080 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7081 || fourcc == FOURCC_moof))
7083 demux->neededbytes = size;
7084 demux->state = QTDEMUX_STATE_HEADER;
7088 case QTDEMUX_STATE_HEADER:{
7092 GST_DEBUG_OBJECT (demux, "In header");
7094 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7096 /* parse the header */
7097 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7099 if (fourcc == FOURCC_moov) {
7100 /* in usual fragmented setup we could try to scan for more
7101 * and end up at the the moov (after mdat) again */
7102 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7104 || demux->last_moov_offset == demux->offset)) {
7105 GST_DEBUG_OBJECT (demux,
7106 "Skipping moov atom as we have (this) one already");
7108 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7110 if (demux->got_moov && demux->fragmented) {
7111 GST_DEBUG_OBJECT (demux,
7112 "Got a second moov, clean up data from old one");
7113 if (demux->moov_node_compressed) {
7114 g_node_destroy (demux->moov_node_compressed);
7115 if (demux->moov_node)
7116 g_free (demux->moov_node->data);
7118 demux->moov_node_compressed = NULL;
7119 if (demux->moov_node)
7120 g_node_destroy (demux->moov_node);
7121 demux->moov_node = NULL;
7124 demux->last_moov_offset = demux->offset;
7126 /* Update streams with new moov */
7127 gst_qtdemux_stream_concat (demux,
7128 demux->old_streams, demux->active_streams);
7130 qtdemux_parse_moov (demux, data, demux->neededbytes);
7131 qtdemux_node_dump (demux, demux->moov_node);
7132 qtdemux_parse_tree (demux);
7133 qtdemux_prepare_streams (demux);
7134 QTDEMUX_EXPOSE_LOCK (demux);
7135 qtdemux_expose_streams (demux);
7136 QTDEMUX_EXPOSE_UNLOCK (demux);
7138 demux->got_moov = TRUE;
7140 gst_qtdemux_check_send_pending_segment (demux);
7142 if (demux->moov_node_compressed) {
7143 g_node_destroy (demux->moov_node_compressed);
7144 g_free (demux->moov_node->data);
7146 demux->moov_node_compressed = NULL;
7147 g_node_destroy (demux->moov_node);
7148 demux->moov_node = NULL;
7149 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7151 } else if (fourcc == FOURCC_moof) {
7152 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7154 GstClockTime prev_pts;
7155 guint64 prev_offset;
7156 guint64 adapter_discont_offset, adapter_discont_dist;
7158 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7161 * The timestamp of the moof buffer is relevant as some scenarios
7162 * won't have the initial timestamp in the atoms. Whenever a new
7163 * buffer has started, we get that buffer's PTS and use it as a base
7164 * timestamp for the trun entries.
7166 * To keep track of the current buffer timestamp and starting point
7167 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7168 * from the beggining of the buffer, with the distance and demux->offset
7169 * we know if it is still the same buffer or not.
7171 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7172 prev_offset = demux->offset - dist;
7173 if (demux->fragment_start_offset == -1
7174 || prev_offset > demux->fragment_start_offset) {
7175 demux->fragment_start_offset = prev_offset;
7176 demux->fragment_start = prev_pts;
7177 GST_DEBUG_OBJECT (demux,
7178 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7179 GST_TIME_FORMAT, demux->fragment_start_offset,
7180 GST_TIME_ARGS (demux->fragment_start));
7183 /* We can't use prev_offset() here because this would require
7184 * upstream to set consistent and correct offsets on all buffers
7185 * since the discont. Nothing ever did that in the past and we
7186 * would break backwards compatibility here then.
7187 * Instead take the offset we had at the last discont and count
7188 * the bytes from there. This works with old code as there would
7189 * be no discont between moov and moof, and also works with
7190 * adaptivedemux which correctly sets offset and will set the
7191 * DISCONT flag accordingly when needed.
7193 * We also only do this for upstream TIME segments as otherwise
7194 * there are potential backwards compatibility problems with
7195 * seeking in PUSH mode and upstream providing inconsistent
7197 adapter_discont_offset =
7198 gst_adapter_offset_at_discont (demux->adapter);
7199 adapter_discont_dist =
7200 gst_adapter_distance_from_discont (demux->adapter);
7202 GST_DEBUG_OBJECT (demux,
7203 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7204 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7205 demux->offset, adapter_discont_offset, adapter_discont_dist);
7207 if (demux->upstream_format_is_time) {
7208 demux->moof_offset = adapter_discont_offset;
7209 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7210 demux->moof_offset += adapter_discont_dist;
7211 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7212 demux->moof_offset = demux->offset;
7214 demux->moof_offset = demux->offset;
7217 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7218 demux->moof_offset, NULL)) {
7219 gst_adapter_unmap (demux->adapter);
7220 ret = GST_FLOW_ERROR;
7224 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7225 if (demux->mss_mode && !demux->exposed) {
7226 QTDEMUX_EXPOSE_LOCK (demux);
7227 qtdemux_expose_streams (demux);
7228 QTDEMUX_EXPOSE_UNLOCK (demux);
7231 gst_qtdemux_check_send_pending_segment (demux);
7233 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7235 } else if (fourcc == FOURCC_ftyp) {
7236 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7237 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7238 } else if (fourcc == FOURCC_uuid) {
7239 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7240 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7241 } else if (fourcc == FOURCC_sidx) {
7242 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7243 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7247 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7251 /* [free] and [skip] are padding atoms */
7252 GST_DEBUG_OBJECT (demux,
7253 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7254 GST_FOURCC_ARGS (fourcc));
7257 GST_WARNING_OBJECT (demux,
7258 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7259 GST_FOURCC_ARGS (fourcc));
7260 /* Let's jump that one and go back to initial state */
7264 gst_adapter_unmap (demux->adapter);
7267 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7268 gsize remaining_data_size = 0;
7270 /* the mdat was before the header */
7271 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7272 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7273 /* restore our adapter/offset view of things with upstream;
7274 * put preceding buffered data ahead of current moov data.
7275 * This should also handle evil mdat, moov, mdat cases and alike */
7276 gst_adapter_flush (demux->adapter, demux->neededbytes);
7278 /* Store any remaining data after the mdat for later usage */
7279 remaining_data_size = gst_adapter_available (demux->adapter);
7280 if (remaining_data_size > 0) {
7281 g_assert (demux->restoredata_buffer == NULL);
7282 demux->restoredata_buffer =
7283 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7284 demux->restoredata_offset = demux->offset + demux->neededbytes;
7285 GST_DEBUG_OBJECT (demux,
7286 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7287 G_GUINT64_FORMAT, remaining_data_size,
7288 demux->restoredata_offset);
7291 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7292 demux->mdatbuffer = NULL;
7293 demux->offset = demux->mdatoffset;
7294 demux->neededbytes = next_entry_size (demux);
7295 demux->state = QTDEMUX_STATE_MOVIE;
7296 demux->mdatleft = gst_adapter_available (demux->adapter);
7297 demux->mdatsize = demux->mdatleft;
7299 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7300 gst_adapter_flush (demux->adapter, demux->neededbytes);
7302 /* only go back to the mdat if there are samples to play */
7303 if (demux->got_moov && demux->first_mdat != -1
7304 && has_next_entry (demux)) {
7307 /* we need to seek back */
7308 res = qtdemux_seek_offset (demux, demux->first_mdat);
7310 demux->offset = demux->first_mdat;
7312 GST_DEBUG_OBJECT (demux, "Seek back failed");
7315 demux->offset += demux->neededbytes;
7317 demux->neededbytes = 16;
7318 demux->state = QTDEMUX_STATE_INITIAL;
7323 case QTDEMUX_STATE_BUFFER_MDAT:{
7327 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7329 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7330 gst_buffer_extract (buf, 0, fourcc, 4);
7331 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7332 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7333 if (demux->mdatbuffer)
7334 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7336 demux->mdatbuffer = buf;
7337 demux->offset += demux->neededbytes;
7338 demux->neededbytes = 16;
7339 demux->state = QTDEMUX_STATE_INITIAL;
7340 gst_qtdemux_post_progress (demux, 1, 1);
7344 case QTDEMUX_STATE_MOVIE:{
7345 QtDemuxStream *stream = NULL;
7346 QtDemuxSample *sample;
7347 GstClockTime dts, pts, duration;
7351 GST_DEBUG_OBJECT (demux,
7352 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7354 if (demux->fragmented) {
7355 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7357 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7358 /* if needed data starts within this atom,
7359 * then it should not exceed this atom */
7360 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7361 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7362 (_("This file is invalid and cannot be played.")),
7363 ("sample data crosses atom boundary"));
7364 ret = GST_FLOW_ERROR;
7367 demux->mdatleft -= demux->neededbytes;
7369 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7370 /* so we are dropping more than left in this atom */
7371 gst_qtdemux_drop_data (demux, demux->mdatleft);
7372 demux->mdatleft = 0;
7374 /* need to resume atom parsing so we do not miss any other pieces */
7375 demux->state = QTDEMUX_STATE_INITIAL;
7376 demux->neededbytes = 16;
7378 /* check if there was any stored post mdat data from previous buffers */
7379 if (demux->restoredata_buffer) {
7380 g_assert (gst_adapter_available (demux->adapter) == 0);
7382 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7383 demux->restoredata_buffer = NULL;
7384 demux->offset = demux->restoredata_offset;
7391 if (demux->todrop) {
7392 if (demux->cenc_aux_info_offset > 0) {
7396 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7397 data = gst_adapter_map (demux->adapter, demux->todrop);
7398 gst_byte_reader_init (&br, data + 8, demux->todrop);
7399 if (!qtdemux_parse_cenc_aux_info (demux,
7400 QTDEMUX_NTH_STREAM (demux, 0), &br,
7401 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7402 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7403 ret = GST_FLOW_ERROR;
7404 gst_adapter_unmap (demux->adapter);
7405 g_free (demux->cenc_aux_info_sizes);
7406 demux->cenc_aux_info_sizes = NULL;
7409 demux->cenc_aux_info_offset = 0;
7410 g_free (demux->cenc_aux_info_sizes);
7411 demux->cenc_aux_info_sizes = NULL;
7412 gst_adapter_unmap (demux->adapter);
7414 gst_qtdemux_drop_data (demux, demux->todrop);
7418 /* initial newsegment sent here after having added pads,
7419 * possible others in sink_event */
7420 gst_qtdemux_check_send_pending_segment (demux);
7422 /* Figure out which stream this packet belongs to */
7423 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7424 stream = QTDEMUX_NTH_STREAM (demux, i);
7425 if (stream->sample_index >= stream->n_samples) {
7426 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7430 GST_LOG_OBJECT (demux,
7431 "Checking track-id %u (sample_index:%d / offset:%"
7432 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7433 stream->sample_index,
7434 stream->samples[stream->sample_index].offset,
7435 stream->samples[stream->sample_index].size);
7437 if (stream->samples[stream->sample_index].offset == demux->offset)
7441 if (G_UNLIKELY (stream == NULL))
7442 goto unknown_stream;
7444 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7446 if (stream->new_caps) {
7447 gst_qtdemux_configure_stream (demux, stream);
7450 /* Put data in a buffer, set timestamps, caps, ... */
7451 sample = &stream->samples[stream->sample_index];
7453 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7454 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7455 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7457 dts = QTSAMPLE_DTS (stream, sample);
7458 pts = QTSAMPLE_PTS (stream, sample);
7459 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7460 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7462 /* check for segment end */
7463 if (G_UNLIKELY (demux->segment.stop != -1
7464 && demux->segment.stop <= pts && stream->on_keyframe)
7465 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7466 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7467 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7469 /* skip this data, stream is EOS */
7470 gst_adapter_flush (demux->adapter, demux->neededbytes);
7471 demux->offset += demux->neededbytes;
7473 /* check if all streams are eos */
7475 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7476 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
7485 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7487 /* FIXME: should either be an assert or a plain check */
7488 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7490 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7491 dts, pts, duration, keyframe, dts, demux->offset);
7495 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7497 /* skip this data, stream is EOS */
7498 gst_adapter_flush (demux->adapter, demux->neededbytes);
7501 stream->sample_index++;
7502 stream->offset_in_sample = 0;
7504 /* update current offset and figure out size of next buffer */
7505 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7506 demux->offset, demux->neededbytes);
7507 demux->offset += demux->neededbytes;
7508 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7512 if (ret == GST_FLOW_EOS) {
7513 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7514 demux->neededbytes = -1;
7518 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7519 if (demux->fragmented) {
7520 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7521 /* there may be more to follow, only finish this atom */
7522 demux->todrop = demux->mdatleft;
7523 demux->neededbytes = demux->todrop;
7528 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7529 goto non_ok_unlinked_flow;
7538 /* when buffering movie data, at least show user something is happening */
7539 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7540 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7541 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7542 demux->neededbytes);
7549 non_ok_unlinked_flow:
7551 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7552 gst_flow_get_name (ret));
7557 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7558 ret = GST_FLOW_ERROR;
7563 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7569 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7570 (NULL), ("qtdemuxer invalid state %d", demux->state));
7571 ret = GST_FLOW_ERROR;
7576 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7577 (NULL), ("no 'moov' atom within the first 10 MB"));
7578 ret = GST_FLOW_ERROR;
7584 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7589 query = gst_query_new_scheduling ();
7591 if (!gst_pad_peer_query (sinkpad, query)) {
7592 gst_query_unref (query);
7596 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7597 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7598 gst_query_unref (query);
7603 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7604 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7608 GST_DEBUG_OBJECT (sinkpad, "activating push");
7609 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7614 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7615 GstPadMode mode, gboolean active)
7618 GstQTDemux *demux = GST_QTDEMUX (parent);
7621 case GST_PAD_MODE_PUSH:
7622 demux->pullbased = FALSE;
7625 case GST_PAD_MODE_PULL:
7627 demux->pullbased = TRUE;
7628 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7631 res = gst_pad_stop_task (sinkpad);
7643 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7649 memset (&z, 0, sizeof (z));
7654 if ((ret = inflateInit (&z)) != Z_OK) {
7655 GST_ERROR ("inflateInit() returned %d", ret);
7659 z.next_in = z_buffer;
7660 z.avail_in = z_length;
7662 buffer = (guint8 *) g_malloc (*length);
7663 z.avail_out = *length;
7664 z.next_out = (Bytef *) buffer;
7666 ret = inflate (&z, Z_NO_FLUSH);
7667 if (ret == Z_STREAM_END) {
7669 } else if (ret != Z_OK) {
7670 GST_WARNING ("inflate() returned %d", ret);
7675 buffer = (guint8 *) g_realloc (buffer, *length);
7676 z.next_out = (Bytef *) (buffer + z.total_out);
7677 z.avail_out += 4096;
7678 } while (z.avail_in > 0);
7680 if (ret != Z_STREAM_END) {
7685 *length = z.total_out;
7692 #endif /* HAVE_ZLIB */
7695 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7699 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7701 /* counts as header data */
7702 qtdemux->header_size += length;
7704 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7705 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7707 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7714 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7715 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7716 if (dcom == NULL || cmvd == NULL)
7717 goto invalid_compression;
7719 dcom_len = QT_UINT32 (dcom->data);
7721 goto invalid_compression;
7723 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7727 guint uncompressed_length;
7728 guint compressed_length;
7732 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7734 goto invalid_compression;
7736 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7737 compressed_length = cmvd_len - 12;
7738 GST_LOG ("length = %u", uncompressed_length);
7741 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7742 compressed_length, &uncompressed_length);
7745 qtdemux->moov_node_compressed = qtdemux->moov_node;
7746 qtdemux->moov_node = g_node_new (buf);
7748 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7749 uncompressed_length);
7753 #endif /* HAVE_ZLIB */
7755 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7756 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7763 invalid_compression:
7765 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
7771 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
7774 while (G_UNLIKELY (buf < end)) {
7778 if (G_UNLIKELY (buf + 4 > end)) {
7779 GST_LOG_OBJECT (qtdemux, "buffer overrun");
7782 len = QT_UINT32 (buf);
7783 if (G_UNLIKELY (len == 0)) {
7784 GST_LOG_OBJECT (qtdemux, "empty container");
7787 if (G_UNLIKELY (len < 8)) {
7788 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
7791 if (G_UNLIKELY (len > (end - buf))) {
7792 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
7793 (gint) (end - buf));
7797 child = g_node_new ((guint8 *) buf);
7798 g_node_append (node, child);
7799 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
7800 qtdemux_parse_node (qtdemux, child, buf, len);
7808 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
7811 int len = QT_UINT32 (xdxt->data);
7812 guint8 *buf = xdxt->data;
7813 guint8 *end = buf + len;
7816 /* skip size and type */
7824 size = QT_UINT32 (buf);
7825 type = QT_FOURCC (buf + 4);
7827 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
7829 if (buf + size > end || size <= 0)
7835 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
7836 GST_FOURCC_ARGS (type));
7840 buffer = gst_buffer_new_and_alloc (size);
7841 gst_buffer_fill (buffer, 0, buf, size);
7842 stream->buffers = g_slist_append (stream->buffers, buffer);
7843 GST_LOG_OBJECT (qtdemux, "parsing theora header");
7846 buffer = gst_buffer_new_and_alloc (size);
7847 gst_buffer_fill (buffer, 0, buf, size);
7848 stream->buffers = g_slist_append (stream->buffers, buffer);
7849 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
7852 buffer = gst_buffer_new_and_alloc (size);
7853 gst_buffer_fill (buffer, 0, buf, size);
7854 stream->buffers = g_slist_append (stream->buffers, buffer);
7855 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
7858 GST_WARNING_OBJECT (qtdemux,
7859 "unknown theora cookie %" GST_FOURCC_FORMAT,
7860 GST_FOURCC_ARGS (type));
7869 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
7873 guint32 node_length = 0;
7874 const QtNodeType *type;
7877 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
7879 if (G_UNLIKELY (length < 8))
7880 goto not_enough_data;
7882 node_length = QT_UINT32 (buffer);
7883 fourcc = QT_FOURCC (buffer + 4);
7885 /* ignore empty nodes */
7886 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
7889 type = qtdemux_type_get (fourcc);
7891 end = buffer + length;
7893 GST_LOG_OBJECT (qtdemux,
7894 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
7895 GST_FOURCC_ARGS (fourcc), node_length, type->name);
7897 if (node_length > length)
7898 goto broken_atom_size;
7900 if (type->flags & QT_FLAG_CONTAINER) {
7901 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7906 if (node_length < 20) {
7907 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
7910 GST_DEBUG_OBJECT (qtdemux,
7911 "parsing stsd (sample table, sample description) atom");
7912 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
7913 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7924 /* also read alac (or whatever) in stead of mp4a in the following,
7925 * since a similar layout is used in other cases as well */
7926 if (fourcc == FOURCC_mp4a)
7928 else if (fourcc == FOURCC_fLaC)
7933 /* There are two things we might encounter here: a true mp4a atom, and
7934 an mp4a entry in an stsd atom. The latter is what we're interested
7935 in, and it looks like an atom, but isn't really one. The true mp4a
7936 atom is short, so we detect it based on length here. */
7937 if (length < min_size) {
7938 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7939 GST_FOURCC_ARGS (fourcc));
7943 /* 'version' here is the sound sample description version. Types 0 and
7944 1 are documented in the QTFF reference, but type 2 is not: it's
7945 described in Apple header files instead (struct SoundDescriptionV2
7947 version = QT_UINT16 (buffer + 16);
7949 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7950 GST_FOURCC_ARGS (fourcc), version);
7952 /* parse any esds descriptors */
7964 GST_WARNING_OBJECT (qtdemux,
7965 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7966 GST_FOURCC_ARGS (fourcc), version);
7971 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7997 /* codec_data is contained inside these atoms, which all have
7998 * the same format. */
7999 /* video sample description size is 86 bytes without extension.
8000 * node_length have to be bigger than 86 bytes because video sample
8001 * description can include extenstions such as esds, fiel, glbl, etc. */
8002 if (node_length < 86) {
8003 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8004 " sample description length too short (%u < 86)",
8005 GST_FOURCC_ARGS (fourcc), node_length);
8009 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8010 GST_FOURCC_ARGS (fourcc));
8012 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8014 * revision level (2 bytes) : must be set to 0. */
8015 version = QT_UINT32 (buffer + 16);
8016 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8018 /* compressor name : PASCAL string and informative purposes
8019 * first byte : the number of bytes to be displayed.
8020 * it has to be less than 32 because it is reserved
8021 * space of 32 bytes total including itself. */
8022 str_len = QT_UINT8 (buffer + 50);
8024 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8025 (char *) buffer + 51);
8027 GST_WARNING_OBJECT (qtdemux,
8028 "compressorname length too big (%u > 31)", str_len);
8030 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8032 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8037 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8039 /* You are reading this correctly. QTFF specifies that the
8040 * metadata atom is a short atom, whereas ISO BMFF specifies
8041 * it's a full atom. But since so many people are doing things
8042 * differently, we actually peek into the atom to see which
8045 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8046 GST_FOURCC_ARGS (fourcc));
8049 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8050 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8051 * starts with a 'hdlr' atom */
8052 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8053 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8054 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8055 * with version/flags both set to zero */
8056 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8058 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8063 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8064 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8065 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8074 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8075 GST_FOURCC_ARGS (fourcc));
8079 version = QT_UINT32 (buffer + 12);
8080 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8087 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8092 if (length < offset) {
8093 GST_WARNING_OBJECT (qtdemux,
8094 "skipping too small %" GST_FOURCC_FORMAT " box",
8095 GST_FOURCC_ARGS (fourcc));
8098 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8104 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8109 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8114 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8118 if (!strcmp (type->name, "unknown"))
8119 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8123 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8124 GST_FOURCC_ARGS (fourcc));
8130 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8131 (_("This file is corrupt and cannot be played.")),
8132 ("Not enough data for an atom header, got only %u bytes", length));
8137 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8138 (_("This file is corrupt and cannot be played.")),
8139 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8140 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8147 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8151 guint32 child_fourcc;
8153 for (child = g_node_first_child (node); child;
8154 child = g_node_next_sibling (child)) {
8155 buffer = (guint8 *) child->data;
8157 child_fourcc = QT_FOURCC (buffer + 4);
8159 if (G_UNLIKELY (child_fourcc == fourcc)) {
8167 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8168 GstByteReader * parser)
8172 guint32 child_fourcc, child_len;
8174 for (child = g_node_first_child (node); child;
8175 child = g_node_next_sibling (child)) {
8176 buffer = (guint8 *) child->data;
8178 child_len = QT_UINT32 (buffer);
8179 child_fourcc = QT_FOURCC (buffer + 4);
8181 if (G_UNLIKELY (child_fourcc == fourcc)) {
8182 if (G_UNLIKELY (child_len < (4 + 4)))
8184 /* FIXME: must verify if atom length < parent atom length */
8185 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8193 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8195 return g_node_nth_child (node, index);
8199 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8200 GstByteReader * parser)
8204 guint32 child_fourcc, child_len;
8206 for (child = g_node_next_sibling (node); child;
8207 child = g_node_next_sibling (child)) {
8208 buffer = (guint8 *) child->data;
8210 child_fourcc = QT_FOURCC (buffer + 4);
8212 if (child_fourcc == fourcc) {
8214 child_len = QT_UINT32 (buffer);
8215 if (G_UNLIKELY (child_len < (4 + 4)))
8217 /* FIXME: must verify if atom length < parent atom length */
8218 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8227 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8229 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8233 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8235 /* FIXME: This can only reliably work if demuxers have a
8236 * separate streaming thread per srcpad. This should be
8237 * done in a demuxer base class, which integrates parts
8240 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8245 query = gst_query_new_allocation (stream->caps, FALSE);
8247 if (!gst_pad_peer_query (stream->pad, query)) {
8248 /* not a problem, just debug a little */
8249 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8252 if (stream->allocator)
8253 gst_object_unref (stream->allocator);
8255 if (gst_query_get_n_allocation_params (query) > 0) {
8256 /* try the allocator */
8257 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8259 stream->use_allocator = TRUE;
8261 stream->allocator = NULL;
8262 gst_allocation_params_init (&stream->params);
8263 stream->use_allocator = FALSE;
8265 gst_query_unref (query);
8270 pad_query (const GValue * item, GValue * value, gpointer user_data)
8272 GstPad *pad = g_value_get_object (item);
8273 GstQuery *query = user_data;
8276 res = gst_pad_peer_query (pad, query);
8279 g_value_set_boolean (value, TRUE);
8283 GST_INFO_OBJECT (pad, "pad peer query failed");
8288 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8289 GstPadDirection direction)
8292 GstIteratorFoldFunction func = pad_query;
8293 GValue res = { 0, };
8295 g_value_init (&res, G_TYPE_BOOLEAN);
8296 g_value_set_boolean (&res, FALSE);
8299 if (direction == GST_PAD_SRC)
8300 it = gst_element_iterate_src_pads (element);
8302 it = gst_element_iterate_sink_pads (element);
8304 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8305 gst_iterator_resync (it);
8307 gst_iterator_free (it);
8309 return g_value_get_boolean (&res);
8313 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8314 QtDemuxStream * stream)
8318 GstElement *element = GST_ELEMENT (qtdemux);
8320 gchar **filtered_sys_ids;
8321 GValue event_list = G_VALUE_INIT;
8324 /* 1. Check if we already have the context. */
8325 if (qtdemux->preferred_protection_system_id != NULL) {
8326 GST_LOG_OBJECT (element,
8327 "already have the protection context, no need to request it again");
8331 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8332 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8333 (const gchar **) qtdemux->protection_system_ids->pdata);
8335 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8336 qtdemux->protection_system_ids->len - 1);
8337 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8338 "decryptors for %u of them, running context request",
8339 qtdemux->protection_system_ids->len,
8340 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8343 if (stream->protection_scheme_event_queue.length) {
8344 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8345 stream->protection_scheme_event_queue.length);
8346 walk = stream->protection_scheme_event_queue.tail;
8348 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8349 qtdemux->protection_event_queue.length);
8350 walk = qtdemux->protection_event_queue.tail;
8353 g_value_init (&event_list, GST_TYPE_LIST);
8354 for (; walk; walk = g_list_previous (walk)) {
8355 GValue *event_value = g_new0 (GValue, 1);
8356 g_value_init (event_value, GST_TYPE_EVENT);
8357 g_value_set_boxed (event_value, walk->data);
8358 gst_value_list_append_and_take_value (&event_list, event_value);
8361 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8362 * check if downstream already has a context of the specific type
8363 * 2b) Query upstream as above.
8365 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8366 st = gst_query_writable_structure (query);
8367 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8368 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8370 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8371 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8372 gst_query_parse_context (query, &ctxt);
8373 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8374 gst_element_set_context (element, ctxt);
8375 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8376 gst_query_parse_context (query, &ctxt);
8377 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8378 gst_element_set_context (element, ctxt);
8380 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8381 * the required context type and afterwards check if a
8382 * usable context was set now as in 1). The message could
8383 * be handled by the parent bins of the element and the
8388 GST_INFO_OBJECT (element, "posting need context message");
8389 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8390 "drm-preferred-decryption-system-id");
8391 st = (GstStructure *) gst_message_get_structure (msg);
8392 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8393 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8396 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8397 gst_element_post_message (element, msg);
8400 g_strfreev (filtered_sys_ids);
8401 g_value_unset (&event_list);
8402 gst_query_unref (query);
8406 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8407 QtDemuxStream * stream)
8410 const gchar *selected_system = NULL;
8412 g_return_val_if_fail (qtdemux != NULL, FALSE);
8413 g_return_val_if_fail (stream != NULL, FALSE);
8414 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8417 if (stream->protection_scheme_type != FOURCC_cenc) {
8418 GST_ERROR_OBJECT (qtdemux,
8419 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8420 GST_FOURCC_ARGS (stream->protection_scheme_type));
8423 if (qtdemux->protection_system_ids == NULL) {
8424 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
8425 "cenc protection system information has been found");
8429 gst_qtdemux_request_protection_context (qtdemux, stream);
8430 if (qtdemux->preferred_protection_system_id != NULL) {
8431 const gchar *preferred_system_array[] =
8432 { qtdemux->preferred_protection_system_id, NULL };
8434 selected_system = gst_protection_select_system (preferred_system_array);
8436 if (selected_system) {
8437 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8438 qtdemux->preferred_protection_system_id);
8440 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8441 "because there is no available decryptor",
8442 qtdemux->preferred_protection_system_id);
8446 if (!selected_system) {
8447 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8448 selected_system = gst_protection_select_system ((const gchar **)
8449 qtdemux->protection_system_ids->pdata);
8450 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8451 qtdemux->protection_system_ids->len - 1);
8454 if (!selected_system) {
8455 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8456 "suitable decryptor element has been found");
8460 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8463 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8464 if (!gst_structure_has_name (s, "application/x-cenc")) {
8465 gst_structure_set (s,
8466 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8467 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8469 gst_structure_set_name (s, "application/x-cenc");
8475 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
8477 /* fps is calculated base on the duration of the average framerate since
8478 * qt does not have a fixed framerate. */
8479 gboolean fps_available = TRUE;
8480 guint32 first_duration = 0;
8482 if (stream->n_samples > 0)
8483 first_duration = stream->samples[0].duration;
8485 if ((stream->n_samples == 1 && first_duration == 0)
8486 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8488 CUR_STREAM (stream)->fps_n = 0;
8489 CUR_STREAM (stream)->fps_d = 1;
8491 if (stream->duration == 0 || stream->n_samples < 2) {
8492 CUR_STREAM (stream)->fps_n = stream->timescale;
8493 CUR_STREAM (stream)->fps_d = 1;
8494 fps_available = FALSE;
8496 GstClockTime avg_duration;
8500 /* duration and n_samples can be updated for fragmented format
8501 * so, framerate of fragmented format is calculated using data in a moof */
8502 if (qtdemux->fragmented && stream->n_samples_moof > 0
8503 && stream->duration_moof > 0) {
8504 n_samples = stream->n_samples_moof;
8505 duration = stream->duration_moof;
8507 n_samples = stream->n_samples;
8508 duration = stream->duration;
8511 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8512 /* stream->duration is guint64, timescale, n_samples are guint32 */
8514 gst_util_uint64_scale_round (duration -
8515 first_duration, GST_SECOND,
8516 (guint64) (stream->timescale) * (n_samples - 1));
8518 GST_LOG_OBJECT (qtdemux,
8519 "Calculating avg sample duration based on stream (or moof) duration %"
8521 " minus first sample %u, leaving %d samples gives %"
8522 GST_TIME_FORMAT, duration, first_duration,
8523 n_samples - 1, GST_TIME_ARGS (avg_duration));
8526 gst_video_guess_framerate (avg_duration,
8527 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
8529 GST_DEBUG_OBJECT (qtdemux,
8530 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8531 stream->timescale, CUR_STREAM (stream)->fps_n,
8532 CUR_STREAM (stream)->fps_d);
8536 return fps_available;
8540 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8542 if (stream->subtype == FOURCC_vide) {
8543 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8545 if (CUR_STREAM (stream)->caps) {
8546 CUR_STREAM (stream)->caps =
8547 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8549 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
8550 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8551 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8552 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8554 /* set framerate if calculated framerate is reliable */
8555 if (fps_available) {
8556 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8557 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8558 CUR_STREAM (stream)->fps_d, NULL);
8561 /* calculate pixel-aspect-ratio using display width and height */
8562 GST_DEBUG_OBJECT (qtdemux,
8563 "video size %dx%d, target display size %dx%d",
8564 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8565 stream->display_width, stream->display_height);
8566 /* qt file might have pasp atom */
8567 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8568 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8569 CUR_STREAM (stream)->par_h);
8570 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8571 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8572 CUR_STREAM (stream)->par_h, NULL);
8573 } else if (stream->display_width > 0 && stream->display_height > 0
8574 && CUR_STREAM (stream)->width > 0
8575 && CUR_STREAM (stream)->height > 0) {
8578 /* calculate the pixel aspect ratio using the display and pixel w/h */
8579 n = stream->display_width * CUR_STREAM (stream)->height;
8580 d = stream->display_height * CUR_STREAM (stream)->width;
8583 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8584 CUR_STREAM (stream)->par_w = n;
8585 CUR_STREAM (stream)->par_h = d;
8586 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8587 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8588 CUR_STREAM (stream)->par_h, NULL);
8591 if (CUR_STREAM (stream)->interlace_mode > 0) {
8592 if (CUR_STREAM (stream)->interlace_mode == 1) {
8593 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8594 G_TYPE_STRING, "progressive", NULL);
8595 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8596 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8597 G_TYPE_STRING, "interleaved", NULL);
8598 if (CUR_STREAM (stream)->field_order == 9) {
8599 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8600 G_TYPE_STRING, "top-field-first", NULL);
8601 } else if (CUR_STREAM (stream)->field_order == 14) {
8602 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8603 G_TYPE_STRING, "bottom-field-first", NULL);
8608 /* Create incomplete colorimetry here if needed */
8609 if (CUR_STREAM (stream)->colorimetry.range ||
8610 CUR_STREAM (stream)->colorimetry.matrix ||
8611 CUR_STREAM (stream)->colorimetry.transfer
8612 || CUR_STREAM (stream)->colorimetry.primaries) {
8613 gchar *colorimetry =
8614 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8615 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8616 G_TYPE_STRING, colorimetry, NULL);
8617 g_free (colorimetry);
8620 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8621 guint par_w = 1, par_h = 1;
8623 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8624 par_w = CUR_STREAM (stream)->par_w;
8625 par_h = CUR_STREAM (stream)->par_h;
8628 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8629 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8631 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8634 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8635 "multiview-mode", G_TYPE_STRING,
8636 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8637 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8638 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8643 else if (stream->subtype == FOURCC_soun) {
8644 if (CUR_STREAM (stream)->caps) {
8645 CUR_STREAM (stream)->caps =
8646 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8647 if (CUR_STREAM (stream)->rate > 0)
8648 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8649 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8650 if (CUR_STREAM (stream)->n_channels > 0)
8651 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8652 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8653 if (CUR_STREAM (stream)->n_channels > 2) {
8654 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8655 * correctly; this is just the minimum we can do - assume
8656 * we don't actually have any channel positions. */
8657 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8658 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8663 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
8664 const GstStructure *s;
8665 QtDemuxStream *fps_stream = NULL;
8666 gboolean fps_available = FALSE;
8668 /* CEA608 closed caption tracks are a bit special in that each sample
8669 * can contain CCs for multiple frames, and CCs can be omitted and have to
8670 * be inferred from the duration of the sample then.
8672 * As such we take the framerate from the (first) video track here for
8673 * CEA608 as there must be one CC byte pair for every video frame
8674 * according to the spec.
8676 * For CEA708 all is fine and there is one sample per frame.
8679 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8680 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
8683 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
8684 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
8686 if (tmp->subtype == FOURCC_vide) {
8693 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
8694 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
8695 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
8698 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8699 fps_stream = stream;
8702 CUR_STREAM (stream)->caps =
8703 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8705 /* set framerate if calculated framerate is reliable */
8706 if (fps_available) {
8707 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8708 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8709 CUR_STREAM (stream)->fps_d, NULL);
8714 GstCaps *prev_caps = NULL;
8716 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8717 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8718 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8719 gst_pad_set_active (stream->pad, TRUE);
8721 gst_pad_use_fixed_caps (stream->pad);
8723 if (stream->protected) {
8724 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8725 GST_ERROR_OBJECT (qtdemux,
8726 "Failed to configure protected stream caps.");
8731 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8732 CUR_STREAM (stream)->caps);
8733 if (stream->new_stream) {
8735 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8738 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8741 gst_event_parse_stream_flags (event, &stream_flags);
8742 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8743 qtdemux->have_group_id = TRUE;
8745 qtdemux->have_group_id = FALSE;
8746 gst_event_unref (event);
8747 } else if (!qtdemux->have_group_id) {
8748 qtdemux->have_group_id = TRUE;
8749 qtdemux->group_id = gst_util_group_id_next ();
8752 stream->new_stream = FALSE;
8753 event = gst_event_new_stream_start (stream->stream_id);
8754 if (qtdemux->have_group_id)
8755 gst_event_set_group_id (event, qtdemux->group_id);
8756 if (stream->disabled)
8757 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8758 if (CUR_STREAM (stream)->sparse) {
8759 stream_flags |= GST_STREAM_FLAG_SPARSE;
8761 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8763 gst_event_set_stream_flags (event, stream_flags);
8764 gst_pad_push_event (stream->pad, event);
8767 prev_caps = gst_pad_get_current_caps (stream->pad);
8769 if (CUR_STREAM (stream)->caps) {
8771 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8772 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8773 CUR_STREAM (stream)->caps);
8774 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8776 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8779 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8783 gst_caps_unref (prev_caps);
8784 stream->new_caps = FALSE;
8790 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8791 QtDemuxStream * stream)
8793 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8796 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8797 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8798 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8799 stream->stsd_entries_length)) {
8800 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8801 (_("This file is invalid and cannot be played.")),
8802 ("New sample description id is out of bounds (%d >= %d)",
8803 stream->stsd_sample_description_id, stream->stsd_entries_length));
8805 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8806 stream->new_caps = TRUE;
8811 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8812 QtDemuxStream * stream, GstTagList * list)
8814 gboolean ret = TRUE;
8816 if (stream->subtype == FOURCC_vide) {
8817 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8820 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8823 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8824 gst_object_unref (stream->pad);
8830 qtdemux->n_video_streams++;
8831 } else if (stream->subtype == FOURCC_soun) {
8832 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
8835 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
8837 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8838 gst_object_unref (stream->pad);
8843 qtdemux->n_audio_streams++;
8844 } else if (stream->subtype == FOURCC_strm) {
8845 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
8846 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
8847 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
8848 || stream->subtype == FOURCC_clcp) {
8849 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
8852 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
8854 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8855 gst_object_unref (stream->pad);
8860 qtdemux->n_sub_streams++;
8861 } else if (CUR_STREAM (stream)->caps) {
8862 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8865 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8867 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8868 gst_object_unref (stream->pad);
8873 qtdemux->n_video_streams++;
8875 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
8882 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
8883 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
8884 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
8885 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
8887 if (stream->stream_tags)
8888 gst_tag_list_unref (stream->stream_tags);
8889 stream->stream_tags = list;
8891 /* global tags go on each pad anyway */
8892 stream->send_global_tags = TRUE;
8893 /* send upstream GST_EVENT_PROTECTION events that were received before
8894 this source pad was created */
8895 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
8896 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
8900 gst_tag_list_unref (list);
8904 /* find next atom with @fourcc starting at @offset */
8905 static GstFlowReturn
8906 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
8907 guint64 * length, guint32 fourcc)
8913 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
8914 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
8920 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
8921 if (G_UNLIKELY (ret != GST_FLOW_OK))
8923 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
8926 gst_buffer_unref (buf);
8929 gst_buffer_map (buf, &map, GST_MAP_READ);
8930 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
8931 gst_buffer_unmap (buf, &map);
8932 gst_buffer_unref (buf);
8934 if (G_UNLIKELY (*length == 0)) {
8935 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
8936 ret = GST_FLOW_ERROR;
8940 if (lfourcc == fourcc) {
8941 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
8945 GST_LOG_OBJECT (qtdemux,
8946 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
8947 GST_FOURCC_ARGS (fourcc), *offset);
8956 /* might simply have had last one */
8957 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
8962 /* should only do something in pull mode */
8963 /* call with OBJECT lock */
8964 static GstFlowReturn
8965 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
8967 guint64 length, offset;
8968 GstBuffer *buf = NULL;
8969 GstFlowReturn ret = GST_FLOW_OK;
8970 GstFlowReturn res = GST_FLOW_OK;
8973 offset = qtdemux->moof_offset;
8974 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
8977 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8978 return GST_FLOW_EOS;
8981 /* best not do pull etc with lock held */
8982 GST_OBJECT_UNLOCK (qtdemux);
8984 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8985 if (ret != GST_FLOW_OK)
8988 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
8989 if (G_UNLIKELY (ret != GST_FLOW_OK))
8991 gst_buffer_map (buf, &map, GST_MAP_READ);
8992 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
8993 gst_buffer_unmap (buf, &map);
8994 gst_buffer_unref (buf);
8999 gst_buffer_unmap (buf, &map);
9000 gst_buffer_unref (buf);
9004 /* look for next moof */
9005 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9006 if (G_UNLIKELY (ret != GST_FLOW_OK))
9010 GST_OBJECT_LOCK (qtdemux);
9012 qtdemux->moof_offset = offset;
9018 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9020 res = GST_FLOW_ERROR;
9025 /* maybe upstream temporarily flushing */
9026 if (ret != GST_FLOW_FLUSHING) {
9027 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9030 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9031 /* resume at current position next time */
9038 /* initialise bytereaders for stbl sub-atoms */
9040 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9042 stream->stbl_index = -1; /* no samples have yet been parsed */
9043 stream->sample_index = -1;
9045 /* time-to-sample atom */
9046 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9049 /* copy atom data into a new buffer for later use */
9050 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
9052 /* skip version + flags */
9053 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9054 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9056 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9058 /* make sure there's enough data */
9059 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9060 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9061 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9062 stream->n_sample_times);
9063 if (!stream->n_sample_times)
9067 /* sync sample atom */
9068 stream->stps_present = FALSE;
9069 if ((stream->stss_present =
9070 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9071 &stream->stss) ? TRUE : FALSE) == TRUE) {
9072 /* copy atom data into a new buffer for later use */
9073 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
9075 /* skip version + flags */
9076 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9077 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9080 if (stream->n_sample_syncs) {
9081 /* make sure there's enough data */
9082 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9086 /* partial sync sample atom */
9087 if ((stream->stps_present =
9088 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9089 &stream->stps) ? TRUE : FALSE) == TRUE) {
9090 /* copy atom data into a new buffer for later use */
9091 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
9093 /* skip version + flags */
9094 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9095 !gst_byte_reader_get_uint32_be (&stream->stps,
9096 &stream->n_sample_partial_syncs))
9099 /* if there are no entries, the stss table contains the real
9101 if (stream->n_sample_partial_syncs) {
9102 /* make sure there's enough data */
9103 if (!qt_atom_parser_has_chunks (&stream->stps,
9104 stream->n_sample_partial_syncs, 4))
9111 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9114 /* copy atom data into a new buffer for later use */
9115 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
9117 /* skip version + flags */
9118 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9119 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9122 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9125 if (!stream->n_samples)
9128 /* sample-to-chunk atom */
9129 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9132 /* copy atom data into a new buffer for later use */
9133 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
9135 /* skip version + flags */
9136 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9137 !gst_byte_reader_get_uint32_be (&stream->stsc,
9138 &stream->n_samples_per_chunk))
9141 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9142 stream->n_samples_per_chunk);
9144 /* make sure there's enough data */
9145 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9151 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9152 stream->co_size = sizeof (guint32);
9153 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9155 stream->co_size = sizeof (guint64);
9159 /* copy atom data into a new buffer for later use */
9160 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
9162 /* skip version + flags */
9163 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9166 /* chunks_are_samples == TRUE means treat chunks as samples */
9167 stream->chunks_are_samples = stream->sample_size
9168 && !CUR_STREAM (stream)->sampled;
9169 if (stream->chunks_are_samples) {
9170 /* treat chunks as samples */
9171 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9174 /* skip number of entries */
9175 if (!gst_byte_reader_skip (&stream->stco, 4))
9178 /* make sure there are enough data in the stsz atom */
9179 if (!stream->sample_size) {
9180 /* different sizes for each sample */
9181 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9186 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9187 stream->n_samples, (guint) sizeof (QtDemuxSample),
9188 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9190 if (stream->n_samples >=
9191 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9192 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9193 "be larger than %uMB (broken file?)", stream->n_samples,
9194 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9198 g_assert (stream->samples == NULL);
9199 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9200 if (!stream->samples) {
9201 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9206 /* composition time-to-sample */
9207 if ((stream->ctts_present =
9208 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9209 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9210 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9212 /* copy atom data into a new buffer for later use */
9213 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
9215 /* skip version + flags */
9216 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9217 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9218 &stream->n_composition_times))
9221 /* make sure there's enough data */
9222 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9226 /* This is optional, if missing we iterate the ctts */
9227 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9228 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9229 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9230 g_free ((gpointer) cslg.data);
9234 gint32 cslg_least = 0;
9235 guint num_entries, pos;
9238 pos = gst_byte_reader_get_pos (&stream->ctts);
9239 num_entries = stream->n_composition_times;
9241 stream->cslg_shift = 0;
9243 for (i = 0; i < num_entries; i++) {
9246 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9247 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9248 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9249 * slightly inaccurate PTS could be more usable than corrupted one */
9250 if (G_UNLIKELY ((ABS (offset) / 2) > stream->duration)) {
9251 GST_WARNING_OBJECT (qtdemux,
9252 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9253 " larger than duration %" G_GUINT64_FORMAT,
9254 offset, stream->duration);
9256 stream->cslg_shift = 0;
9257 stream->ctts_present = FALSE;
9261 if (offset < cslg_least)
9262 cslg_least = offset;
9266 stream->cslg_shift = ABS (cslg_least);
9268 stream->cslg_shift = 0;
9270 /* reset the reader so we can generate sample table */
9271 gst_byte_reader_set_pos (&stream->ctts, pos);
9274 /* Ensure the cslg_shift value is consistent so we can use it
9275 * unconditionnally to produce TS and Segment */
9276 stream->cslg_shift = 0;
9283 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9284 (_("This file is corrupt and cannot be played.")), (NULL));
9289 gst_qtdemux_stbl_free (stream);
9290 if (!qtdemux->fragmented) {
9291 /* not quite good */
9292 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9295 /* may pick up samples elsewhere */
9301 /* collect samples from the next sample to be parsed up to sample @n for @stream
9302 * by reading the info from @stbl
9304 * This code can be executed from both the streaming thread and the seeking
9305 * thread so it takes the object lock to protect itself
9308 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9311 QtDemuxSample *samples, *first, *cur, *last;
9312 guint32 n_samples_per_chunk;
9315 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9316 GST_FOURCC_FORMAT ", pad %s",
9317 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9318 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9320 n_samples = stream->n_samples;
9323 goto out_of_samples;
9325 GST_OBJECT_LOCK (qtdemux);
9326 if (n <= stream->stbl_index)
9327 goto already_parsed;
9329 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9331 if (!stream->stsz.data) {
9332 /* so we already parsed and passed all the moov samples;
9333 * onto fragmented ones */
9334 g_assert (qtdemux->fragmented);
9338 /* pointer to the sample table */
9339 samples = stream->samples;
9341 /* starts from -1, moves to the next sample index to parse */
9342 stream->stbl_index++;
9344 /* keep track of the first and last sample to fill */
9345 first = &samples[stream->stbl_index];
9348 if (!stream->chunks_are_samples) {
9349 /* set the sample sizes */
9350 if (stream->sample_size == 0) {
9351 /* different sizes for each sample */
9352 for (cur = first; cur <= last; cur++) {
9353 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9354 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9355 (guint) (cur - samples), cur->size);
9358 /* samples have the same size */
9359 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9360 for (cur = first; cur <= last; cur++)
9361 cur->size = stream->sample_size;
9365 n_samples_per_chunk = stream->n_samples_per_chunk;
9368 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9371 if (stream->stsc_chunk_index >= stream->last_chunk
9372 || stream->stsc_chunk_index < stream->first_chunk) {
9373 stream->first_chunk =
9374 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9375 stream->samples_per_chunk =
9376 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9378 stream->stsd_sample_description_id =
9379 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9381 /* chunk numbers are counted from 1 it seems */
9382 if (G_UNLIKELY (stream->first_chunk == 0))
9385 --stream->first_chunk;
9387 /* the last chunk of each entry is calculated by taking the first chunk
9388 * of the next entry; except if there is no next, where we fake it with
9390 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9391 stream->last_chunk = G_MAXUINT32;
9393 stream->last_chunk =
9394 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9395 if (G_UNLIKELY (stream->last_chunk == 0))
9398 --stream->last_chunk;
9401 GST_LOG_OBJECT (qtdemux,
9402 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9403 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9404 stream->samples_per_chunk, stream->stsd_sample_description_id);
9406 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9409 if (stream->last_chunk != G_MAXUINT32) {
9410 if (!qt_atom_parser_peek_sub (&stream->stco,
9411 stream->first_chunk * stream->co_size,
9412 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9417 stream->co_chunk = stream->stco;
9418 if (!gst_byte_reader_skip (&stream->co_chunk,
9419 stream->first_chunk * stream->co_size))
9423 stream->stsc_chunk_index = stream->first_chunk;
9426 last_chunk = stream->last_chunk;
9428 if (stream->chunks_are_samples) {
9429 cur = &samples[stream->stsc_chunk_index];
9431 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9434 stream->stsc_chunk_index = j;
9439 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9442 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9443 "%" G_GUINT64_FORMAT, j, cur->offset);
9445 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9446 CUR_STREAM (stream)->bytes_per_frame > 0) {
9448 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9449 CUR_STREAM (stream)->samples_per_frame *
9450 CUR_STREAM (stream)->bytes_per_frame;
9452 cur->size = stream->samples_per_chunk;
9455 GST_DEBUG_OBJECT (qtdemux,
9456 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9457 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9458 stream->stco_sample_index)), cur->size);
9460 cur->timestamp = stream->stco_sample_index;
9461 cur->duration = stream->samples_per_chunk;
9462 cur->keyframe = TRUE;
9465 stream->stco_sample_index += stream->samples_per_chunk;
9467 stream->stsc_chunk_index = j;
9469 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9470 guint32 samples_per_chunk;
9471 guint64 chunk_offset;
9473 if (!stream->stsc_sample_index
9474 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9475 &stream->chunk_offset))
9478 samples_per_chunk = stream->samples_per_chunk;
9479 chunk_offset = stream->chunk_offset;
9481 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9482 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9483 G_GUINT64_FORMAT " and size %d",
9484 (guint) (cur - samples), chunk_offset, cur->size);
9486 cur->offset = chunk_offset;
9487 chunk_offset += cur->size;
9490 if (G_UNLIKELY (cur > last)) {
9492 stream->stsc_sample_index = k + 1;
9493 stream->chunk_offset = chunk_offset;
9494 stream->stsc_chunk_index = j;
9498 stream->stsc_sample_index = 0;
9500 stream->stsc_chunk_index = j;
9502 stream->stsc_index++;
9505 if (stream->chunks_are_samples)
9509 guint32 n_sample_times;
9511 n_sample_times = stream->n_sample_times;
9514 for (i = stream->stts_index; i < n_sample_times; i++) {
9515 guint32 stts_samples;
9516 gint32 stts_duration;
9519 if (stream->stts_sample_index >= stream->stts_samples
9520 || !stream->stts_sample_index) {
9522 stream->stts_samples =
9523 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9524 stream->stts_duration =
9525 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9527 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9528 i, stream->stts_samples, stream->stts_duration);
9530 stream->stts_sample_index = 0;
9533 stts_samples = stream->stts_samples;
9534 stts_duration = stream->stts_duration;
9535 stts_time = stream->stts_time;
9537 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9538 GST_DEBUG_OBJECT (qtdemux,
9539 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9540 (guint) (cur - samples), j,
9541 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9543 cur->timestamp = stts_time;
9544 cur->duration = stts_duration;
9546 /* avoid 32-bit wrap-around,
9547 * but still mind possible 'negative' duration */
9548 stts_time += (gint64) stts_duration;
9551 if (G_UNLIKELY (cur > last)) {
9553 stream->stts_time = stts_time;
9554 stream->stts_sample_index = j + 1;
9555 if (stream->stts_sample_index >= stream->stts_samples)
9556 stream->stts_index++;
9560 stream->stts_sample_index = 0;
9561 stream->stts_time = stts_time;
9562 stream->stts_index++;
9564 /* fill up empty timestamps with the last timestamp, this can happen when
9565 * the last samples do not decode and so we don't have timestamps for them.
9566 * We however look at the last timestamp to estimate the track length so we
9567 * need something in here. */
9568 for (; cur < last; cur++) {
9569 GST_DEBUG_OBJECT (qtdemux,
9570 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9571 (guint) (cur - samples),
9572 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9573 cur->timestamp = stream->stts_time;
9579 /* sample sync, can be NULL */
9580 if (stream->stss_present == TRUE) {
9581 guint32 n_sample_syncs;
9583 n_sample_syncs = stream->n_sample_syncs;
9585 if (!n_sample_syncs) {
9586 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9587 stream->all_keyframe = TRUE;
9589 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9590 /* note that the first sample is index 1, not 0 */
9593 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9595 if (G_LIKELY (index > 0 && index <= n_samples)) {
9597 samples[index].keyframe = TRUE;
9598 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9599 /* and exit if we have enough samples */
9600 if (G_UNLIKELY (index >= n)) {
9607 stream->stss_index = i;
9610 /* stps marks partial sync frames like open GOP I-Frames */
9611 if (stream->stps_present == TRUE) {
9612 guint32 n_sample_partial_syncs;
9614 n_sample_partial_syncs = stream->n_sample_partial_syncs;
9616 /* if there are no entries, the stss table contains the real
9618 if (n_sample_partial_syncs) {
9619 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
9620 /* note that the first sample is index 1, not 0 */
9623 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
9625 if (G_LIKELY (index > 0 && index <= n_samples)) {
9627 samples[index].keyframe = TRUE;
9628 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9629 /* and exit if we have enough samples */
9630 if (G_UNLIKELY (index >= n)) {
9637 stream->stps_index = i;
9641 /* no stss, all samples are keyframes */
9642 stream->all_keyframe = TRUE;
9643 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
9648 /* composition time to sample */
9649 if (stream->ctts_present == TRUE) {
9650 guint32 n_composition_times;
9652 gint32 ctts_soffset;
9654 /* Fill in the pts_offsets */
9656 n_composition_times = stream->n_composition_times;
9658 for (i = stream->ctts_index; i < n_composition_times; i++) {
9659 if (stream->ctts_sample_index >= stream->ctts_count
9660 || !stream->ctts_sample_index) {
9661 stream->ctts_count =
9662 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
9663 stream->ctts_soffset =
9664 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9665 stream->ctts_sample_index = 0;
9668 ctts_count = stream->ctts_count;
9669 ctts_soffset = stream->ctts_soffset;
9671 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
9672 cur->pts_offset = ctts_soffset;
9675 if (G_UNLIKELY (cur > last)) {
9677 stream->ctts_sample_index = j + 1;
9681 stream->ctts_sample_index = 0;
9682 stream->ctts_index++;
9686 stream->stbl_index = n;
9687 /* if index has been completely parsed, free data that is no-longer needed */
9688 if (n + 1 == stream->n_samples) {
9689 gst_qtdemux_stbl_free (stream);
9690 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
9691 if (qtdemux->pullbased) {
9692 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
9693 while (n + 1 == stream->n_samples)
9694 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
9698 GST_OBJECT_UNLOCK (qtdemux);
9705 GST_LOG_OBJECT (qtdemux,
9706 "Tried to parse up to sample %u but this sample has already been parsed",
9708 /* if fragmented, there may be more */
9709 if (qtdemux->fragmented && n == stream->stbl_index)
9711 GST_OBJECT_UNLOCK (qtdemux);
9717 GST_LOG_OBJECT (qtdemux,
9718 "Tried to parse up to sample %u but there are only %u samples", n + 1,
9720 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9721 (_("This file is corrupt and cannot be played.")), (NULL));
9726 GST_OBJECT_UNLOCK (qtdemux);
9727 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9728 (_("This file is corrupt and cannot be played.")), (NULL));
9733 /* collect all segment info for @stream.
9736 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
9740 /* accept edts if they contain gaps at start and there is only
9741 * one media segment */
9742 gboolean allow_pushbased_edts = TRUE;
9743 gint media_segments_count = 0;
9745 /* parse and prepare segment info from the edit list */
9746 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
9747 stream->n_segments = 0;
9748 stream->segments = NULL;
9749 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
9752 gint segment_number, entry_size;
9755 const guint8 *buffer;
9759 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
9760 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
9763 buffer = elst->data;
9765 size = QT_UINT32 (buffer);
9766 /* version, flags, n_segments */
9768 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9771 version = QT_UINT8 (buffer + 8);
9772 entry_size = (version == 1) ? 20 : 12;
9774 n_segments = QT_UINT32 (buffer + 12);
9776 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
9777 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9781 /* we might allocate a bit too much, at least allocate 1 segment */
9782 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
9784 /* segments always start from 0 */
9788 for (segment_number = 0; segment_number < n_segments; segment_number++) {
9791 gboolean empty_edit = FALSE;
9792 QtDemuxSegment *segment;
9794 GstClockTime media_start = GST_CLOCK_TIME_NONE;
9797 media_time = QT_UINT64 (buffer + 8);
9798 duration = QT_UINT64 (buffer);
9799 if (media_time == G_MAXUINT64)
9802 media_time = QT_UINT32 (buffer + 4);
9803 duration = QT_UINT32 (buffer);
9804 if (media_time == G_MAXUINT32)
9809 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
9811 segment = &stream->segments[segment_number];
9813 /* time and duration expressed in global timescale */
9814 segment->time = stime;
9815 if (duration != 0 || empty_edit) {
9816 /* edge case: empty edits with duration=zero are treated here.
9817 * (files should not have these anyway). */
9819 /* add non scaled values so we don't cause roundoff errors */
9821 stime = QTTIME_TO_GSTTIME (qtdemux, time);
9822 segment->duration = stime - segment->time;
9824 /* zero duration does not imply media_start == media_stop
9825 * but, only specify media_start. The edit ends with the track. */
9826 stime = segment->duration = GST_CLOCK_TIME_NONE;
9827 /* Don't allow more edits after this one. */
9828 n_segments = segment_number + 1;
9830 segment->stop_time = stime;
9832 segment->trak_media_start = media_time;
9833 /* media_time expressed in stream timescale */
9835 segment->media_start = media_start;
9836 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
9837 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
9838 media_segments_count++;
9840 segment->media_start = GST_CLOCK_TIME_NONE;
9841 segment->media_stop = GST_CLOCK_TIME_NONE;
9843 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
9845 if (rate_int <= 1) {
9846 /* 0 is not allowed, some programs write 1 instead of the floating point
9848 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
9852 segment->rate = rate_int / 65536.0;
9855 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
9856 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
9857 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
9858 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
9859 segment_number, GST_TIME_ARGS (segment->time),
9860 GST_TIME_ARGS (segment->duration),
9861 GST_TIME_ARGS (segment->media_start), media_time,
9862 GST_TIME_ARGS (segment->media_stop),
9863 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
9865 if (segment->stop_time > qtdemux->segment.stop &&
9866 !qtdemux->upstream_format_is_time) {
9867 GST_WARNING_OBJECT (qtdemux, "Segment %d "
9868 " extends to %" GST_TIME_FORMAT
9869 " past the end of the declared movie duration %" GST_TIME_FORMAT
9870 " movie segment will be extended", segment_number,
9871 GST_TIME_ARGS (segment->stop_time),
9872 GST_TIME_ARGS (qtdemux->segment.stop));
9873 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
9876 buffer += entry_size;
9878 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
9879 stream->n_segments = n_segments;
9880 if (media_segments_count != 1)
9881 allow_pushbased_edts = FALSE;
9885 /* push based does not handle segments, so act accordingly here,
9886 * and warn if applicable */
9887 if (!qtdemux->pullbased && !allow_pushbased_edts) {
9888 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
9889 /* remove and use default one below, we stream like it anyway */
9890 g_free (stream->segments);
9891 stream->segments = NULL;
9892 stream->n_segments = 0;
9895 /* no segments, create one to play the complete trak */
9896 if (stream->n_segments == 0) {
9897 GstClockTime stream_duration =
9898 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
9900 if (stream->segments == NULL)
9901 stream->segments = g_new (QtDemuxSegment, 1);
9903 /* represent unknown our way */
9904 if (stream_duration == 0)
9905 stream_duration = GST_CLOCK_TIME_NONE;
9907 stream->segments[0].time = 0;
9908 stream->segments[0].stop_time = stream_duration;
9909 stream->segments[0].duration = stream_duration;
9910 stream->segments[0].media_start = 0;
9911 stream->segments[0].media_stop = stream_duration;
9912 stream->segments[0].rate = 1.0;
9913 stream->segments[0].trak_media_start = 0;
9915 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
9916 GST_TIME_ARGS (stream_duration));
9917 stream->n_segments = 1;
9918 stream->dummy_segment = TRUE;
9920 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
9926 * Parses the stsd atom of a svq3 trak looking for
9927 * the SMI and gama atoms.
9930 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
9931 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
9933 const guint8 *_gamma = NULL;
9934 GstBuffer *_seqh = NULL;
9935 const guint8 *stsd_data = stsd_entry_data;
9936 guint32 length = QT_UINT32 (stsd_data);
9940 GST_WARNING_OBJECT (qtdemux, "stsd too short");
9946 version = QT_UINT16 (stsd_data);
9951 while (length > 8) {
9952 guint32 fourcc, size;
9954 size = QT_UINT32 (stsd_data);
9955 fourcc = QT_FOURCC (stsd_data + 4);
9956 data = stsd_data + 8;
9959 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
9960 "svq3 atom parsing");
9969 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
9970 " for gama atom, expected 12", size);
9975 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
9977 if (_seqh != NULL) {
9978 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
9979 " found, ignoring");
9981 seqh_size = QT_UINT32 (data + 4);
9982 if (seqh_size > 0) {
9983 _seqh = gst_buffer_new_and_alloc (seqh_size);
9984 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
9991 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
9992 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
9996 if (size <= length) {
10002 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10005 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10006 G_GUINT16_FORMAT, version);
10016 } else if (_seqh) {
10017 gst_buffer_unref (_seqh);
10022 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10025 GstByteReader dref;
10029 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10030 * atom that might contain a 'data' atom with the rtsp uri.
10031 * This case was reported in bug #597497, some info about
10032 * the hndl atom can be found in TN1195
10034 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10035 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10038 guint32 dref_num_entries = 0;
10039 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10040 gst_byte_reader_skip (&dref, 4) &&
10041 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10044 /* search dref entries for hndl atom */
10045 for (i = 0; i < dref_num_entries; i++) {
10046 guint32 size = 0, type;
10047 guint8 string_len = 0;
10048 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10049 qt_atom_parser_get_fourcc (&dref, &type)) {
10050 if (type == FOURCC_hndl) {
10051 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10053 /* skip data reference handle bytes and the
10054 * following pascal string and some extra 4
10055 * bytes I have no idea what are */
10056 if (!gst_byte_reader_skip (&dref, 4) ||
10057 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10058 !gst_byte_reader_skip (&dref, string_len + 4)) {
10059 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10063 /* iterate over the atoms to find the data atom */
10064 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10068 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10069 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10070 if (atom_type == FOURCC_data) {
10071 const guint8 *uri_aux = NULL;
10073 /* found the data atom that might contain the rtsp uri */
10074 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10075 "hndl atom, interpreting it as an URI");
10076 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10078 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10079 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10081 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10082 "didn't contain a rtsp address");
10084 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10089 /* skipping to the next entry */
10090 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10093 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10100 /* skip to the next entry */
10101 if (!gst_byte_reader_skip (&dref, size - 8))
10104 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10107 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10113 #define AMR_NB_ALL_MODES 0x81ff
10114 #define AMR_WB_ALL_MODES 0x83ff
10116 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10118 /* The 'damr' atom is of the form:
10120 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10121 * 32 b 8 b 16 b 8 b 8 b
10123 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10124 * represents the highest mode used in the stream (and thus the maximum
10125 * bitrate), with a couple of special cases as seen below.
10128 /* Map of frame type ID -> bitrate */
10129 static const guint nb_bitrates[] = {
10130 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10132 static const guint wb_bitrates[] = {
10133 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10139 gst_buffer_map (buf, &map, GST_MAP_READ);
10141 if (map.size != 0x11) {
10142 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10146 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10147 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10148 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10152 mode_set = QT_UINT16 (map.data + 13);
10154 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10155 max_mode = 7 + (wb ? 1 : 0);
10157 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10158 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10160 if (max_mode == -1) {
10161 GST_DEBUG ("No mode indication was found (mode set) = %x",
10166 gst_buffer_unmap (buf, &map);
10167 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10170 gst_buffer_unmap (buf, &map);
10175 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10176 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10179 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10185 if (gst_byte_reader_get_remaining (reader) < 36)
10188 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10189 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10190 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10191 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10192 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10193 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10194 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10195 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10196 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10198 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10199 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10200 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10202 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10203 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10205 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10206 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10213 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10214 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10221 * This macro will only compare value abdegh, it expects cfi to have already
10224 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10225 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10227 /* only handle the cases where the last column has standard values */
10228 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10229 const gchar *rotation_tag = NULL;
10231 /* no rotation needed */
10232 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10234 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10235 rotation_tag = "rotate-90";
10236 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10237 rotation_tag = "rotate-180";
10238 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10239 rotation_tag = "rotate-270";
10241 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10244 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10246 if (rotation_tag != NULL) {
10247 if (*taglist == NULL)
10248 *taglist = gst_tag_list_new_empty ();
10249 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10250 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10253 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10257 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10258 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10259 * Common Encryption (cenc), the function will also parse the tenc box (defined
10260 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10261 * (typically an enc[v|a|t|s] sample entry); the function will set
10262 * @original_fmt to the fourcc of the original unencrypted stream format.
10263 * Returns TRUE if successful; FALSE otherwise. */
10265 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10266 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10272 QtDemuxCencSampleSetInfo *info;
10274 const guint8 *tenc_data;
10276 g_return_val_if_fail (qtdemux != NULL, FALSE);
10277 g_return_val_if_fail (stream != NULL, FALSE);
10278 g_return_val_if_fail (container != NULL, FALSE);
10279 g_return_val_if_fail (original_fmt != NULL, FALSE);
10281 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10282 if (G_UNLIKELY (!sinf)) {
10283 if (stream->protection_scheme_type == FOURCC_cenc) {
10284 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10285 "mandatory for Common Encryption");
10291 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10292 if (G_UNLIKELY (!frma)) {
10293 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10297 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10298 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10299 GST_FOURCC_ARGS (*original_fmt));
10301 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10303 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10306 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10307 stream->protection_scheme_version =
10308 QT_UINT32 ((const guint8 *) schm->data + 16);
10310 GST_DEBUG_OBJECT (qtdemux,
10311 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10312 "protection_scheme_version: %#010x",
10313 GST_FOURCC_ARGS (stream->protection_scheme_type),
10314 stream->protection_scheme_version);
10316 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10318 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10321 if (stream->protection_scheme_type != FOURCC_cenc &&
10322 stream->protection_scheme_type != FOURCC_piff) {
10323 GST_ERROR_OBJECT (qtdemux,
10324 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10325 GST_FOURCC_ARGS (stream->protection_scheme_type));
10329 if (G_UNLIKELY (!stream->protection_scheme_info))
10330 stream->protection_scheme_info =
10331 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10333 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10335 if (stream->protection_scheme_type == FOURCC_cenc) {
10336 guint32 is_encrypted;
10338 const guint8 *default_kid;
10340 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10342 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10343 "which is mandatory for Common Encryption");
10346 tenc_data = (const guint8 *) tenc->data + 12;
10347 is_encrypted = QT_UINT24 (tenc_data);
10348 iv_size = QT_UINT8 (tenc_data + 3);
10349 default_kid = (tenc_data + 4);
10350 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
10351 is_encrypted, iv_size, default_kid);
10352 } else if (stream->protection_scheme_type == FOURCC_piff) {
10354 static const guint8 piff_track_encryption_uuid[] = {
10355 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10356 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10359 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10361 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10362 "which is mandatory for Common Encryption");
10366 tenc_data = (const guint8 *) tenc->data + 8;
10367 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10368 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10369 GST_ERROR_OBJECT (qtdemux,
10370 "Unsupported track encryption box with uuid: %s", box_uuid);
10374 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10375 gst_byte_reader_init (&br, tenc_data, 20);
10376 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10377 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10380 stream->protection_scheme_type = FOURCC_cenc;
10387 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10388 QtDemuxStream ** stream2)
10390 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10393 /* parse the traks.
10394 * With each track we associate a new QtDemuxStream that contains all the info
10396 * traks that do not decode to something (like strm traks) will not have a pad.
10399 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10401 GstByteReader tkhd;
10416 QtDemuxStream *stream = NULL;
10417 const guint8 *stsd_data;
10418 const guint8 *stsd_entry_data;
10419 guint remaining_stsd_len;
10420 guint stsd_entry_count;
10422 guint16 lang_code; /* quicktime lang code or packed iso code */
10424 guint32 tkhd_flags = 0;
10425 guint8 tkhd_version = 0;
10426 guint32 w = 0, h = 0;
10427 guint value_size, stsd_len, len;
10431 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10433 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10434 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10435 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10438 /* pick between 64 or 32 bits */
10439 value_size = tkhd_version == 1 ? 8 : 4;
10440 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10441 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10444 /* Check if current moov has duplicated track_id */
10445 if (qtdemux_find_stream (qtdemux, track_id))
10446 goto existing_stream;
10448 stream = _create_stream (qtdemux, track_id);
10449 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10451 /* need defaults for fragments */
10452 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10454 if ((tkhd_flags & 1) == 0)
10455 stream->disabled = TRUE;
10457 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10458 tkhd_version, tkhd_flags, stream->track_id);
10460 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10463 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10464 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10465 if (qtdemux->major_brand != FOURCC_mjp2 ||
10466 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
10470 len = QT_UINT32 ((guint8 *) mdhd->data);
10471 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
10472 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
10473 if (version == 0x01000000) {
10476 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
10477 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
10478 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
10482 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
10483 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
10484 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
10487 if (lang_code < 0x400) {
10488 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
10489 } else if (lang_code == 0x7fff) {
10490 stream->lang_id[0] = 0; /* unspecified */
10492 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
10493 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
10494 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
10495 stream->lang_id[3] = 0;
10498 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
10499 stream->timescale);
10500 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
10502 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
10503 lang_code, stream->lang_id);
10505 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
10508 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
10509 /* chapters track reference */
10510 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
10512 gsize length = GST_READ_UINT32_BE (chap->data);
10513 if (qtdemux->chapters_track_id)
10514 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
10516 if (length >= 12) {
10517 qtdemux->chapters_track_id =
10518 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
10523 /* fragmented files may have bogus duration in moov */
10524 if (!qtdemux->fragmented &&
10525 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
10526 guint64 tdur1, tdur2;
10528 /* don't overflow */
10529 tdur1 = stream->timescale * (guint64) qtdemux->duration;
10530 tdur2 = qtdemux->timescale * (guint64) stream->duration;
10533 * some of those trailers, nowadays, have prologue images that are
10534 * themselves video tracks as well. I haven't really found a way to
10535 * identify those yet, except for just looking at their duration. */
10536 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
10537 GST_WARNING_OBJECT (qtdemux,
10538 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
10539 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
10540 "found, assuming preview image or something; skipping track",
10541 stream->duration, stream->timescale, qtdemux->duration,
10542 qtdemux->timescale);
10543 gst_qtdemux_stream_unref (stream);
10548 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
10551 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
10552 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
10554 len = QT_UINT32 ((guint8 *) hdlr->data);
10556 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
10557 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
10558 GST_FOURCC_ARGS (stream->subtype));
10560 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
10563 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
10566 /*parse svmi header if existing */
10567 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10569 len = QT_UINT32 ((guint8 *) svmi->data);
10570 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10572 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10573 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10574 guint8 frame_type, frame_layout;
10576 /* MPEG-A stereo video */
10577 if (qtdemux->major_brand == FOURCC_ss02)
10578 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10580 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10581 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10582 switch (frame_type) {
10584 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10587 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10590 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10593 /* mode 3 is primary/secondary view sequence, ie
10594 * left/right views in separate tracks. See section 7.2
10595 * of ISO/IEC 23000-11:2009 */
10596 GST_FIXME_OBJECT (qtdemux,
10597 "Implement stereo video in separate streams");
10600 if ((frame_layout & 0x1) == 0)
10601 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10603 GST_LOG_OBJECT (qtdemux,
10604 "StereoVideo: composition type: %u, is_left_first: %u",
10605 frame_type, frame_layout);
10606 stream->multiview_mode = mode;
10607 stream->multiview_flags = flags;
10611 /* parse rest of tkhd */
10612 if (stream->subtype == FOURCC_vide) {
10615 /* version 1 uses some 64-bit ints */
10616 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
10619 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
10622 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
10623 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
10626 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
10627 &stream->stream_tags);
10631 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
10633 stsd_data = (const guint8 *) stsd->data;
10635 /* stsd should at least have one entry */
10636 stsd_len = QT_UINT32 (stsd_data);
10637 if (stsd_len < 24) {
10638 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
10639 if (stream->subtype == FOURCC_vivo) {
10640 gst_qtdemux_stream_unref (stream);
10647 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
10648 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10649 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10650 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10652 stsd_entry_data = stsd_data + 16;
10653 remaining_stsd_len = stsd_len - 16;
10654 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10656 gchar *codec = NULL;
10657 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10659 /* and that entry should fit within stsd */
10660 len = QT_UINT32 (stsd_entry_data);
10661 if (len > remaining_stsd_len)
10664 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10665 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10666 GST_FOURCC_ARGS (entry->fourcc));
10667 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10669 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10670 goto error_encrypted;
10672 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10673 /* FIXME this looks wrong, there might be multiple children
10674 * with the same type */
10675 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10676 stream->protected = TRUE;
10677 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10678 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10681 if (stream->subtype == FOURCC_vide) {
10686 gint depth, palette_size, palette_count;
10687 guint32 *palette_data = NULL;
10689 entry->sampled = TRUE;
10691 stream->display_width = w >> 16;
10692 stream->display_height = h >> 16;
10695 if (len < 86) /* TODO verify */
10698 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10699 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10700 entry->fps_n = 0; /* this is filled in later */
10701 entry->fps_d = 0; /* this is filled in later */
10702 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10703 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10705 /* if color_table_id is 0, ctab atom must follow; however some files
10706 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10707 * if color table is not present we'll correct the value */
10708 if (entry->color_table_id == 0 &&
10710 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10711 entry->color_table_id = -1;
10714 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10715 entry->width, entry->height, entry->bits_per_sample,
10716 entry->color_table_id);
10718 depth = entry->bits_per_sample;
10720 /* more than 32 bits means grayscale */
10721 gray = (depth > 32);
10722 /* low 32 bits specify the depth */
10725 /* different number of palette entries is determined by depth. */
10727 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10728 palette_count = (1 << depth);
10729 palette_size = palette_count * 4;
10731 if (entry->color_table_id) {
10732 switch (palette_count) {
10736 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10739 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10744 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10746 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10751 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10753 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10756 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10757 (_("The video in this file might not play correctly.")),
10758 ("unsupported palette depth %d", depth));
10762 gint i, j, start, end;
10768 start = QT_UINT32 (stsd_entry_data + offset + 70);
10769 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10770 end = QT_UINT16 (stsd_entry_data + offset + 76);
10772 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10773 start, end, palette_count);
10780 if (len < 94 + (end - start) * 8)
10783 /* palette is always the same size */
10784 palette_data = g_malloc0 (256 * 4);
10785 palette_size = 256 * 4;
10787 for (j = 0, i = start; i <= end; j++, i++) {
10788 guint32 a, r, g, b;
10790 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10791 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10792 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10793 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10795 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10796 (g & 0xff00) | (b >> 8);
10801 gst_caps_unref (entry->caps);
10804 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10806 if (G_UNLIKELY (!entry->caps)) {
10807 g_free (palette_data);
10808 goto unknown_stream;
10812 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10813 GST_TAG_VIDEO_CODEC, codec, NULL);
10818 if (palette_data) {
10821 if (entry->rgb8_palette)
10822 gst_memory_unref (entry->rgb8_palette);
10823 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10824 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10826 s = gst_caps_get_structure (entry->caps, 0);
10828 /* non-raw video has a palette_data property. raw video has the palette as
10829 * an extra plane that we append to the output buffers before we push
10831 if (!gst_structure_has_name (s, "video/x-raw")) {
10832 GstBuffer *palette;
10834 palette = gst_buffer_new ();
10835 gst_buffer_append_memory (palette, entry->rgb8_palette);
10836 entry->rgb8_palette = NULL;
10838 gst_caps_set_simple (entry->caps, "palette_data",
10839 GST_TYPE_BUFFER, palette, NULL);
10840 gst_buffer_unref (palette);
10842 } else if (palette_count != 0) {
10843 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10844 (NULL), ("Unsupported palette depth %d", depth));
10847 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10848 QT_UINT16 (stsd_entry_data + offset + 32));
10854 /* pick 'the' stsd child */
10855 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10856 // We should skip parsing the stsd for non-protected streams if
10857 // the entry doesn't match the fourcc, since they don't change
10858 // format. However, for protected streams we can have partial
10859 // encryption, where parts of the stream are encrypted and parts
10860 // not. For both parts of such streams, we should ensure the
10861 // esds overrides are parsed for both from the stsd.
10862 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10863 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
10865 else if (!stream->protected)
10870 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10871 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10872 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10873 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10877 const guint8 *pasp_data = (const guint8 *) pasp->data;
10878 gint len = QT_UINT32 (pasp_data);
10881 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10882 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10884 CUR_STREAM (stream)->par_w = 0;
10885 CUR_STREAM (stream)->par_h = 0;
10888 CUR_STREAM (stream)->par_w = 0;
10889 CUR_STREAM (stream)->par_h = 0;
10893 const guint8 *fiel_data = (const guint8 *) fiel->data;
10894 gint len = QT_UINT32 (fiel_data);
10897 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10898 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10903 const guint8 *colr_data = (const guint8 *) colr->data;
10904 gint len = QT_UINT32 (colr_data);
10906 if (len == 19 || len == 18) {
10907 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10909 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10910 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10911 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10912 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10913 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10915 switch (primaries) {
10917 CUR_STREAM (stream)->colorimetry.primaries =
10918 GST_VIDEO_COLOR_PRIMARIES_BT709;
10921 CUR_STREAM (stream)->colorimetry.primaries =
10922 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
10925 CUR_STREAM (stream)->colorimetry.primaries =
10926 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
10929 CUR_STREAM (stream)->colorimetry.primaries =
10930 GST_VIDEO_COLOR_PRIMARIES_BT2020;
10936 switch (transfer_function) {
10938 CUR_STREAM (stream)->colorimetry.transfer =
10939 GST_VIDEO_TRANSFER_BT709;
10942 CUR_STREAM (stream)->colorimetry.transfer =
10943 GST_VIDEO_TRANSFER_SMPTE240M;
10951 CUR_STREAM (stream)->colorimetry.matrix =
10952 GST_VIDEO_COLOR_MATRIX_BT709;
10955 CUR_STREAM (stream)->colorimetry.matrix =
10956 GST_VIDEO_COLOR_MATRIX_BT601;
10959 CUR_STREAM (stream)->colorimetry.matrix =
10960 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
10963 CUR_STREAM (stream)->colorimetry.matrix =
10964 GST_VIDEO_COLOR_MATRIX_BT2020;
10970 CUR_STREAM (stream)->colorimetry.range =
10971 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10972 GST_VIDEO_COLOR_RANGE_16_235;
10974 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10977 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10982 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10983 stream->stream_tags);
10990 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10991 const guint8 *avc_data = stsd_entry_data + 0x56;
10994 while (len >= 0x8) {
10997 if (QT_UINT32 (avc_data) <= len)
10998 size = QT_UINT32 (avc_data) - 0x8;
11003 /* No real data, so break out */
11006 switch (QT_FOURCC (avc_data + 0x4)) {
11009 /* parse, if found */
11012 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11014 /* First 4 bytes are the length of the atom, the next 4 bytes
11015 * are the fourcc, the next 1 byte is the version, and the
11016 * subsequent bytes are profile_tier_level structure like data. */
11017 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11018 avc_data + 8 + 1, size - 1);
11019 buf = gst_buffer_new_and_alloc (size);
11020 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11021 gst_caps_set_simple (entry->caps,
11022 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11023 gst_buffer_unref (buf);
11031 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11033 /* First 4 bytes are the length of the atom, the next 4 bytes
11034 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11035 * next 1 byte is the version, and the
11036 * subsequent bytes are sequence parameter set like data. */
11038 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11040 gst_codec_utils_h264_caps_set_level_and_profile
11041 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11043 buf = gst_buffer_new_and_alloc (size);
11044 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11045 gst_caps_set_simple (entry->caps,
11046 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11047 gst_buffer_unref (buf);
11053 guint avg_bitrate, max_bitrate;
11055 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11059 max_bitrate = QT_UINT32 (avc_data + 0xc);
11060 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11062 if (!max_bitrate && !avg_bitrate)
11065 /* Some muxers seem to swap the average and maximum bitrates
11066 * (I'm looking at you, YouTube), so we swap for sanity. */
11067 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11068 guint temp = avg_bitrate;
11070 avg_bitrate = max_bitrate;
11071 max_bitrate = temp;
11074 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11075 gst_tag_list_add (stream->stream_tags,
11076 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11077 max_bitrate, NULL);
11079 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11080 gst_tag_list_add (stream->stream_tags,
11081 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11093 avc_data += size + 8;
11102 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11103 const guint8 *hevc_data = stsd_entry_data + 0x56;
11106 while (len >= 0x8) {
11109 if (QT_UINT32 (hevc_data) <= len)
11110 size = QT_UINT32 (hevc_data) - 0x8;
11115 /* No real data, so break out */
11118 switch (QT_FOURCC (hevc_data + 0x4)) {
11121 /* parse, if found */
11124 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11126 /* First 4 bytes are the length of the atom, the next 4 bytes
11127 * are the fourcc, the next 1 byte is the version, and the
11128 * subsequent bytes are sequence parameter set like data. */
11129 gst_codec_utils_h265_caps_set_level_tier_and_profile
11130 (entry->caps, hevc_data + 8 + 1, size - 1);
11132 buf = gst_buffer_new_and_alloc (size);
11133 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11134 gst_caps_set_simple (entry->caps,
11135 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11136 gst_buffer_unref (buf);
11143 hevc_data += size + 8;
11156 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11157 GST_FOURCC_ARGS (fourcc));
11159 /* codec data might be in glbl extension atom */
11161 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11167 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11169 len = QT_UINT32 (data);
11172 buf = gst_buffer_new_and_alloc (len);
11173 gst_buffer_fill (buf, 0, data + 8, len);
11174 gst_caps_set_simple (entry->caps,
11175 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11176 gst_buffer_unref (buf);
11183 /* see annex I of the jpeg2000 spec */
11184 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11185 const guint8 *data;
11186 const gchar *colorspace = NULL;
11188 guint32 ncomp_map = 0;
11189 gint32 *comp_map = NULL;
11190 guint32 nchan_def = 0;
11191 gint32 *chan_def = NULL;
11193 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11194 /* some required atoms */
11195 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11198 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11202 /* number of components; redundant with info in codestream, but useful
11204 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11205 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11207 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11209 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11212 GST_DEBUG_OBJECT (qtdemux, "found colr");
11213 /* extract colour space info */
11214 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11215 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11217 colorspace = "sRGB";
11220 colorspace = "GRAY";
11223 colorspace = "sYUV";
11231 /* colr is required, and only values 16, 17, and 18 are specified,
11232 so error if we have no colorspace */
11235 /* extract component mapping */
11236 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11238 guint32 cmap_len = 0;
11240 cmap_len = QT_UINT32 (cmap->data);
11241 if (cmap_len >= 8) {
11242 /* normal box, subtract off header */
11244 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11245 if (cmap_len % 4 == 0) {
11246 ncomp_map = (cmap_len / 4);
11247 comp_map = g_new0 (gint32, ncomp_map);
11248 for (i = 0; i < ncomp_map; i++) {
11251 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11252 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11253 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11254 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11259 /* extract channel definitions */
11260 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11262 guint32 cdef_len = 0;
11264 cdef_len = QT_UINT32 (cdef->data);
11265 if (cdef_len >= 10) {
11266 /* normal box, subtract off header and len */
11268 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11269 if (cdef_len % 6 == 0) {
11270 nchan_def = (cdef_len / 6);
11271 chan_def = g_new0 (gint32, nchan_def);
11272 for (i = 0; i < nchan_def; i++)
11274 for (i = 0; i < nchan_def; i++) {
11275 guint16 cn, typ, asoc;
11276 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11277 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11278 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11279 if (cn < nchan_def) {
11282 chan_def[cn] = asoc;
11285 chan_def[cn] = 0; /* alpha */
11288 chan_def[cn] = -typ;
11296 gst_caps_set_simple (entry->caps,
11297 "num-components", G_TYPE_INT, ncomp, NULL);
11298 gst_caps_set_simple (entry->caps,
11299 "colorspace", G_TYPE_STRING, colorspace, NULL);
11302 GValue arr = { 0, };
11303 GValue elt = { 0, };
11305 g_value_init (&arr, GST_TYPE_ARRAY);
11306 g_value_init (&elt, G_TYPE_INT);
11307 for (i = 0; i < ncomp_map; i++) {
11308 g_value_set_int (&elt, comp_map[i]);
11309 gst_value_array_append_value (&arr, &elt);
11311 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11312 "component-map", &arr);
11313 g_value_unset (&elt);
11314 g_value_unset (&arr);
11319 GValue arr = { 0, };
11320 GValue elt = { 0, };
11322 g_value_init (&arr, GST_TYPE_ARRAY);
11323 g_value_init (&elt, G_TYPE_INT);
11324 for (i = 0; i < nchan_def; i++) {
11325 g_value_set_int (&elt, chan_def[i]);
11326 gst_value_array_append_value (&arr, &elt);
11328 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11329 "channel-definitions", &arr);
11330 g_value_unset (&elt);
11331 g_value_unset (&arr);
11335 /* some optional atoms */
11336 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11337 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11339 /* indicate possible fields in caps */
11341 data = (guint8 *) field->data + 8;
11343 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11344 (gint) * data, NULL);
11346 /* add codec_data if provided */
11351 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11352 data = prefix->data;
11353 len = QT_UINT32 (data);
11356 buf = gst_buffer_new_and_alloc (len);
11357 gst_buffer_fill (buf, 0, data + 8, len);
11358 gst_caps_set_simple (entry->caps,
11359 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11360 gst_buffer_unref (buf);
11369 GstBuffer *seqh = NULL;
11370 const guint8 *gamma_data = NULL;
11371 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11373 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11376 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11377 QT_FP32 (gamma_data), NULL);
11380 /* sorry for the bad name, but we don't know what this is, other
11381 * than its own fourcc */
11382 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11384 gst_buffer_unref (seqh);
11387 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11388 buf = gst_buffer_new_and_alloc (len);
11389 gst_buffer_fill (buf, 0, stsd_data, len);
11390 gst_caps_set_simple (entry->caps,
11391 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11392 gst_buffer_unref (buf);
11397 /* https://developer.apple.com/standards/qtff-2001.pdf,
11398 * page 92, "Video Sample Description", under table 3.1 */
11401 const gint compressor_offset =
11402 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11403 const gint min_size = compressor_offset + 32 + 2 + 2;
11406 guint16 color_table_id = 0;
11409 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11411 /* recover information on interlaced/progressive */
11412 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11416 len = QT_UINT32 (jpeg->data);
11417 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11419 if (len >= min_size) {
11420 gst_byte_reader_init (&br, jpeg->data, len);
11422 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11423 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11424 if (color_table_id != 0) {
11425 /* the spec says there can be concatenated chunks in the data, and we want
11426 * to find one called field. Walk through them. */
11427 gint offset = min_size;
11428 while (offset + 8 < len) {
11429 guint32 size = 0, tag;
11430 ok = gst_byte_reader_get_uint32_le (&br, &size);
11431 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11432 if (!ok || size < 8) {
11433 GST_WARNING_OBJECT (qtdemux,
11434 "Failed to walk optional chunk list");
11437 GST_DEBUG_OBJECT (qtdemux,
11438 "Found optional %4.4s chunk, size %u",
11439 (const char *) &tag, size);
11440 if (tag == FOURCC_fiel) {
11441 guint8 n_fields = 0, ordering = 0;
11442 gst_byte_reader_get_uint8 (&br, &n_fields);
11443 gst_byte_reader_get_uint8 (&br, &ordering);
11444 if (n_fields == 1 || n_fields == 2) {
11445 GST_DEBUG_OBJECT (qtdemux,
11446 "Found fiel tag with %u fields, ordering %u",
11447 n_fields, ordering);
11449 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11450 "interlace-mode", G_TYPE_STRING, "interleaved",
11453 GST_WARNING_OBJECT (qtdemux,
11454 "Found fiel tag with invalid fields (%u)", n_fields);
11460 GST_DEBUG_OBJECT (qtdemux,
11461 "Color table ID is 0, not trying to get interlacedness");
11464 GST_WARNING_OBJECT (qtdemux,
11465 "Length of jpeg chunk is too small, not trying to get interlacedness");
11473 gst_caps_set_simple (entry->caps,
11474 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11480 GNode *xith, *xdxt;
11482 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11483 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11487 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11491 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11492 /* collect the headers and store them in a stream list so that we can
11493 * send them out first */
11494 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11504 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11505 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11508 ovc1_data = ovc1->data;
11509 ovc1_len = QT_UINT32 (ovc1_data);
11510 if (ovc1_len <= 198) {
11511 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11514 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11515 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11516 gst_caps_set_simple (entry->caps,
11517 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11518 gst_buffer_unref (buf);
11523 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11524 const guint8 *vc1_data = stsd_entry_data + 0x56;
11530 if (QT_UINT32 (vc1_data) <= len)
11531 size = QT_UINT32 (vc1_data) - 8;
11536 /* No real data, so break out */
11539 switch (QT_FOURCC (vc1_data + 0x4)) {
11540 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11544 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11545 buf = gst_buffer_new_and_alloc (size);
11546 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11547 gst_caps_set_simple (entry->caps,
11548 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11549 gst_buffer_unref (buf);
11556 vc1_data += size + 8;
11562 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11563 const guint8 *av1_data = stsd_entry_data + 0x56;
11566 while (len >= 0x8) {
11569 if (QT_UINT32 (av1_data) <= len)
11570 size = QT_UINT32 (av1_data) - 0x8;
11575 /* No real data, so break out */
11578 switch (QT_FOURCC (av1_data + 0x4)) {
11581 /* parse, if found */
11583 guint8 pres_delay_field;
11585 GST_DEBUG_OBJECT (qtdemux,
11586 "found av1C codec_data in stsd of size %d", size);
11588 /* not enough data, just ignore and hope for the best */
11593 * 4 bytes: atom length
11598 * 1 bits: initial_presentation_delay_present
11599 * 4 bits: initial_presentation_delay (if present else reserved
11603 if (av1_data[9] != 0) {
11604 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
11608 /* We skip initial_presentation_delay* for now */
11609 pres_delay_field = *(av1_data + 12);
11610 if (pres_delay_field & (1 << 5)) {
11611 gst_caps_set_simple (entry->caps,
11612 "presentation-delay", G_TYPE_INT,
11613 (gint) (pres_delay_field & 0x0F) + 1, NULL);
11616 buf = gst_buffer_new_and_alloc (size - 5);
11617 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
11618 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
11619 gst_caps_set_simple (entry->caps,
11620 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11621 gst_buffer_unref (buf);
11630 av1_data += size + 8;
11640 GST_INFO_OBJECT (qtdemux,
11641 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11642 GST_FOURCC_ARGS (fourcc), entry->caps);
11644 } else if (stream->subtype == FOURCC_soun) {
11646 int version, samplesize;
11647 guint16 compression_id;
11648 gboolean amrwb = FALSE;
11651 /* sample description entry (16) + sound sample description v0 (20) */
11655 version = QT_UINT32 (stsd_entry_data + offset);
11656 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11657 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11658 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11659 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11661 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11662 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11663 QT_UINT32 (stsd_entry_data + offset + 4));
11664 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11665 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11666 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11667 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11668 QT_UINT16 (stsd_entry_data + offset + 14));
11669 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11671 if (compression_id == 0xfffe)
11672 entry->sampled = TRUE;
11674 /* first assume uncompressed audio */
11675 entry->bytes_per_sample = samplesize / 8;
11676 entry->samples_per_frame = entry->n_channels;
11677 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11678 entry->samples_per_packet = entry->samples_per_frame;
11679 entry->bytes_per_packet = entry->bytes_per_sample;
11683 /* Yes, these have to be hard-coded */
11686 entry->samples_per_packet = 6;
11687 entry->bytes_per_packet = 1;
11688 entry->bytes_per_frame = 1 * entry->n_channels;
11689 entry->bytes_per_sample = 1;
11690 entry->samples_per_frame = 6 * entry->n_channels;
11695 entry->samples_per_packet = 3;
11696 entry->bytes_per_packet = 1;
11697 entry->bytes_per_frame = 1 * entry->n_channels;
11698 entry->bytes_per_sample = 1;
11699 entry->samples_per_frame = 3 * entry->n_channels;
11704 entry->samples_per_packet = 64;
11705 entry->bytes_per_packet = 34;
11706 entry->bytes_per_frame = 34 * entry->n_channels;
11707 entry->bytes_per_sample = 2;
11708 entry->samples_per_frame = 64 * entry->n_channels;
11714 entry->samples_per_packet = 1;
11715 entry->bytes_per_packet = 1;
11716 entry->bytes_per_frame = 1 * entry->n_channels;
11717 entry->bytes_per_sample = 1;
11718 entry->samples_per_frame = 1 * entry->n_channels;
11723 entry->samples_per_packet = 160;
11724 entry->bytes_per_packet = 33;
11725 entry->bytes_per_frame = 33 * entry->n_channels;
11726 entry->bytes_per_sample = 2;
11727 entry->samples_per_frame = 160 * entry->n_channels;
11734 if (version == 0x00010000) {
11735 /* sample description entry (16) + sound sample description v1 (20+16) */
11747 /* only parse extra decoding config for non-pcm audio */
11748 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11749 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11750 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11751 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11753 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11754 entry->samples_per_packet);
11755 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11756 entry->bytes_per_packet);
11757 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11758 entry->bytes_per_frame);
11759 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11760 entry->bytes_per_sample);
11762 if (!entry->sampled && entry->bytes_per_packet) {
11763 entry->samples_per_frame = (entry->bytes_per_frame /
11764 entry->bytes_per_packet) * entry->samples_per_packet;
11765 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11766 entry->samples_per_frame);
11771 } else if (version == 0x00020000) {
11778 /* sample description entry (16) + sound sample description v2 (56) */
11782 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
11783 entry->rate = qtfp.fp;
11784 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11786 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11787 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11788 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11789 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11790 QT_UINT32 (stsd_entry_data + offset + 20));
11791 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11792 QT_UINT32 (stsd_entry_data + offset + 24));
11793 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11794 QT_UINT32 (stsd_entry_data + offset + 28));
11795 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11796 QT_UINT32 (stsd_entry_data + offset + 32));
11797 } else if (version != 0x00000) {
11798 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11803 gst_caps_unref (entry->caps);
11805 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11806 stsd_entry_data + 32, len - 16, &codec);
11814 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
11816 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
11818 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
11820 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11823 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11824 gst_caps_set_simple (entry->caps,
11825 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
11832 const guint8 *owma_data;
11833 const gchar *codec_name = NULL;
11837 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11838 /* FIXME this should also be gst_riff_strf_auds,
11839 * but the latter one is actually missing bits-per-sample :( */
11844 gint32 nSamplesPerSec;
11845 gint32 nAvgBytesPerSec;
11846 gint16 nBlockAlign;
11847 gint16 wBitsPerSample;
11850 WAVEFORMATEX *wfex;
11852 GST_DEBUG_OBJECT (qtdemux, "parse owma");
11853 owma_data = stsd_entry_data;
11854 owma_len = QT_UINT32 (owma_data);
11855 if (owma_len <= 54) {
11856 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
11859 wfex = (WAVEFORMATEX *) (owma_data + 36);
11860 buf = gst_buffer_new_and_alloc (owma_len - 54);
11861 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
11862 if (wfex->wFormatTag == 0x0161) {
11863 codec_name = "Windows Media Audio";
11865 } else if (wfex->wFormatTag == 0x0162) {
11866 codec_name = "Windows Media Audio 9 Pro";
11868 } else if (wfex->wFormatTag == 0x0163) {
11869 codec_name = "Windows Media Audio 9 Lossless";
11870 /* is that correct? gstffmpegcodecmap.c is missing it, but
11871 * fluendo codec seems to support it */
11875 gst_caps_set_simple (entry->caps,
11876 "codec_data", GST_TYPE_BUFFER, buf,
11877 "wmaversion", G_TYPE_INT, version,
11878 "block_align", G_TYPE_INT,
11879 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
11880 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
11881 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
11882 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
11883 gst_buffer_unref (buf);
11887 codec = g_strdup (codec_name);
11893 gint len = QT_UINT32 (stsd_entry_data) - offset;
11894 const guint8 *wfex_data = stsd_entry_data + offset;
11895 const gchar *codec_name = NULL;
11897 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11898 /* FIXME this should also be gst_riff_strf_auds,
11899 * but the latter one is actually missing bits-per-sample :( */
11904 gint32 nSamplesPerSec;
11905 gint32 nAvgBytesPerSec;
11906 gint16 nBlockAlign;
11907 gint16 wBitsPerSample;
11912 /* FIXME: unify with similar wavformatex parsing code above */
11913 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
11919 if (QT_UINT32 (wfex_data) <= len)
11920 size = QT_UINT32 (wfex_data) - 8;
11925 /* No real data, so break out */
11928 switch (QT_FOURCC (wfex_data + 4)) {
11929 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
11931 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
11936 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
11937 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
11938 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
11939 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
11940 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
11941 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
11942 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
11944 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
11945 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
11946 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
11947 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
11948 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
11949 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
11951 if (wfex.wFormatTag == 0x0161) {
11952 codec_name = "Windows Media Audio";
11954 } else if (wfex.wFormatTag == 0x0162) {
11955 codec_name = "Windows Media Audio 9 Pro";
11957 } else if (wfex.wFormatTag == 0x0163) {
11958 codec_name = "Windows Media Audio 9 Lossless";
11959 /* is that correct? gstffmpegcodecmap.c is missing it, but
11960 * fluendo codec seems to support it */
11964 gst_caps_set_simple (entry->caps,
11965 "wmaversion", G_TYPE_INT, version,
11966 "block_align", G_TYPE_INT, wfex.nBlockAlign,
11967 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
11968 "width", G_TYPE_INT, wfex.wBitsPerSample,
11969 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
11971 if (size > wfex.cbSize) {
11974 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
11975 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
11976 size - wfex.cbSize);
11977 gst_caps_set_simple (entry->caps,
11978 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11979 gst_buffer_unref (buf);
11981 GST_WARNING_OBJECT (qtdemux, "no codec data");
11986 codec = g_strdup (codec_name);
11994 wfex_data += size + 8;
12000 const guint8 *opus_data;
12001 guint8 *channel_mapping = NULL;
12004 guint8 channel_mapping_family;
12005 guint8 stream_count;
12006 guint8 coupled_count;
12009 opus_data = stsd_entry_data;
12011 channels = GST_READ_UINT8 (opus_data + 45);
12012 rate = GST_READ_UINT32_LE (opus_data + 48);
12013 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12014 stream_count = GST_READ_UINT8 (opus_data + 55);
12015 coupled_count = GST_READ_UINT8 (opus_data + 56);
12017 if (channels > 0) {
12018 channel_mapping = g_malloc (channels * sizeof (guint8));
12019 for (i = 0; i < channels; i++)
12020 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12023 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12024 channel_mapping_family, stream_count, coupled_count,
12036 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12037 GST_TAG_AUDIO_CODEC, codec, NULL);
12041 /* some bitrate info may have ended up in caps */
12042 s = gst_caps_get_structure (entry->caps, 0);
12043 gst_structure_get_int (s, "bitrate", &bitrate);
12045 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12046 GST_TAG_BITRATE, bitrate, NULL);
12049 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12050 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12051 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12053 else if (!stream->protected)
12060 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12062 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12064 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12068 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12069 16 bits is a byte-swapped wave-style codec identifier,
12070 and we can find a WAVE header internally to a 'wave' atom here.
12071 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12072 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12075 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12076 if (len < offset + 20) {
12077 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12079 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12080 const guint8 *data = stsd_entry_data + offset + 16;
12082 GNode *waveheadernode;
12084 wavenode = g_node_new ((guint8 *) data);
12085 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12086 const guint8 *waveheader;
12089 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12090 if (waveheadernode) {
12091 waveheader = (const guint8 *) waveheadernode->data;
12092 headerlen = QT_UINT32 (waveheader);
12094 if (headerlen > 8) {
12095 gst_riff_strf_auds *header = NULL;
12096 GstBuffer *headerbuf;
12102 headerbuf = gst_buffer_new_and_alloc (headerlen);
12103 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12105 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12106 headerbuf, &header, &extra)) {
12107 gst_caps_unref (entry->caps);
12108 /* FIXME: Need to do something with the channel reorder map */
12110 gst_riff_create_audio_caps (header->format, NULL, header,
12111 extra, NULL, NULL, NULL);
12114 gst_buffer_unref (extra);
12119 GST_DEBUG ("Didn't find waveheadernode for this codec");
12121 g_node_destroy (wavenode);
12124 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12125 stream->stream_tags);
12129 /* FIXME: what is in the chunk? */
12132 gint len = QT_UINT32 (stsd_data);
12134 /* seems to be always = 116 = 0x74 */
12140 gint len = QT_UINT32 (stsd_entry_data);
12143 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12145 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12146 gst_caps_set_simple (entry->caps,
12147 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12148 gst_buffer_unref (buf);
12150 gst_caps_set_simple (entry->caps,
12151 "samplesize", G_TYPE_INT, samplesize, NULL);
12156 GNode *alac, *wave = NULL;
12158 /* apparently, m4a has this atom appended directly in the stsd entry,
12159 * while mov has it in a wave atom */
12160 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12162 /* alac now refers to stsd entry atom */
12163 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12165 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12167 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12170 const guint8 *alac_data = alac->data;
12171 gint len = QT_UINT32 (alac->data);
12175 GST_DEBUG_OBJECT (qtdemux,
12176 "discarding alac atom with unexpected len %d", len);
12178 /* codec-data contains alac atom size and prefix,
12179 * ffmpeg likes it that way, not quite gst-ish though ...*/
12180 buf = gst_buffer_new_and_alloc (len);
12181 gst_buffer_fill (buf, 0, alac->data, len);
12182 gst_caps_set_simple (entry->caps,
12183 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12184 gst_buffer_unref (buf);
12186 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12187 entry->n_channels = QT_UINT8 (alac_data + 21);
12188 entry->rate = QT_UINT32 (alac_data + 32);
12191 gst_caps_set_simple (entry->caps,
12192 "samplesize", G_TYPE_INT, samplesize, NULL);
12197 /* The codingname of the sample entry is 'fLaC' */
12198 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12201 /* The 'dfLa' box is added to the sample entry to convey
12202 initializing information for the decoder. */
12203 const GNode *dfla =
12204 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12207 const guint32 len = QT_UINT32 (dfla->data);
12209 /* Must contain at least dfLa box header (12),
12210 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12212 GST_DEBUG_OBJECT (qtdemux,
12213 "discarding dfla atom with unexpected len %d", len);
12215 /* skip dfLa header to get the METADATA_BLOCKs */
12216 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12217 const guint32 metadata_blocks_len = len - 12;
12219 gchar *stream_marker = g_strdup ("fLaC");
12220 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12221 strlen (stream_marker));
12224 guint32 remainder = 0;
12225 guint32 block_size = 0;
12226 gboolean is_last = FALSE;
12228 GValue array = G_VALUE_INIT;
12229 GValue value = G_VALUE_INIT;
12231 g_value_init (&array, GST_TYPE_ARRAY);
12232 g_value_init (&value, GST_TYPE_BUFFER);
12234 gst_value_set_buffer (&value, block);
12235 gst_value_array_append_value (&array, &value);
12236 g_value_reset (&value);
12238 gst_buffer_unref (block);
12240 /* check there's at least one METADATA_BLOCK_HEADER's worth
12241 * of data, and we haven't already finished parsing */
12242 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12243 remainder = metadata_blocks_len - index;
12245 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12247 (metadata_blocks[index + 1] << 16) +
12248 (metadata_blocks[index + 2] << 8) +
12249 metadata_blocks[index + 3];
12251 /* be careful not to read off end of box */
12252 if (block_size > remainder) {
12256 is_last = metadata_blocks[index] >> 7;
12258 block = gst_buffer_new_and_alloc (block_size);
12260 gst_buffer_fill (block, 0, &metadata_blocks[index],
12263 gst_value_set_buffer (&value, block);
12264 gst_value_array_append_value (&array, &value);
12265 g_value_reset (&value);
12267 gst_buffer_unref (block);
12269 index += block_size;
12272 /* only append the metadata if we successfully read all of it */
12274 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12275 (stream)->caps, 0), "streamheader", &array);
12277 GST_WARNING_OBJECT (qtdemux,
12278 "discarding all METADATA_BLOCKs due to invalid "
12279 "block_size %d at idx %d, rem %d", block_size, index,
12283 g_value_unset (&value);
12284 g_value_unset (&array);
12286 /* The sample rate obtained from the stsd may not be accurate
12287 * since it cannot represent rates greater than 65535Hz, so
12288 * override that value with the sample rate from the
12289 * METADATA_BLOCK_STREAMINFO block */
12290 CUR_STREAM (stream)->rate =
12291 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12302 gint len = QT_UINT32 (stsd_entry_data);
12305 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12308 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12310 /* If we have enough data, let's try to get the 'damr' atom. See
12311 * the 3GPP container spec (26.244) for more details. */
12312 if ((len - 0x34) > 8 &&
12313 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12314 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12315 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12318 gst_caps_set_simple (entry->caps,
12319 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12320 gst_buffer_unref (buf);
12326 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12327 gint len = QT_UINT32 (stsd_entry_data);
12330 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12332 if (sound_version == 1) {
12333 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12334 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12335 guint8 codec_data[2];
12337 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12339 gint sample_rate_index =
12340 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12342 /* build AAC codec data */
12343 codec_data[0] = profile << 3;
12344 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12345 codec_data[1] = (sample_rate_index & 0x01) << 7;
12346 codec_data[1] |= (channels & 0xF) << 3;
12348 buf = gst_buffer_new_and_alloc (2);
12349 gst_buffer_fill (buf, 0, codec_data, 2);
12350 gst_caps_set_simple (entry->caps,
12351 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12352 gst_buffer_unref (buf);
12358 /* Fully handled elsewhere */
12361 GST_INFO_OBJECT (qtdemux,
12362 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12366 GST_INFO_OBJECT (qtdemux,
12367 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12368 GST_FOURCC_ARGS (fourcc), entry->caps);
12370 } else if (stream->subtype == FOURCC_strm) {
12371 if (fourcc == FOURCC_rtsp) {
12372 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12374 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12375 GST_FOURCC_ARGS (fourcc));
12376 goto unknown_stream;
12378 entry->sampled = TRUE;
12379 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12380 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12381 || stream->subtype == FOURCC_clcp) {
12383 entry->sampled = TRUE;
12384 entry->sparse = TRUE;
12387 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12390 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12391 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12396 /* hunt for sort-of codec data */
12400 GNode *mp4s = NULL;
12401 GNode *esds = NULL;
12403 /* look for palette in a stsd->mp4s->esds sub-atom */
12404 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12406 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12407 if (esds == NULL) {
12409 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12413 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12414 stream->stream_tags);
12418 GST_INFO_OBJECT (qtdemux,
12419 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12422 GST_INFO_OBJECT (qtdemux,
12423 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12424 GST_FOURCC_ARGS (fourcc), entry->caps);
12426 /* everything in 1 sample */
12427 entry->sampled = TRUE;
12430 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12433 if (entry->caps == NULL)
12434 goto unknown_stream;
12437 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12438 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12444 /* promote to sampled format */
12445 if (entry->fourcc == FOURCC_samr) {
12446 /* force mono 8000 Hz for AMR */
12447 entry->sampled = TRUE;
12448 entry->n_channels = 1;
12449 entry->rate = 8000;
12450 } else if (entry->fourcc == FOURCC_sawb) {
12451 /* force mono 16000 Hz for AMR-WB */
12452 entry->sampled = TRUE;
12453 entry->n_channels = 1;
12454 entry->rate = 16000;
12455 } else if (entry->fourcc == FOURCC_mp4a) {
12456 entry->sampled = TRUE;
12460 stsd_entry_data += len;
12461 remaining_stsd_len -= len;
12465 /* collect sample information */
12466 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12467 goto samples_failed;
12469 if (qtdemux->fragmented) {
12472 /* need all moov samples as basis; probably not many if any at all */
12473 /* prevent moof parsing taking of at this time */
12474 offset = qtdemux->moof_offset;
12475 qtdemux->moof_offset = 0;
12476 if (stream->n_samples &&
12477 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12478 qtdemux->moof_offset = offset;
12479 goto samples_failed;
12481 qtdemux->moof_offset = 0;
12482 /* movie duration more reliable in this case (e.g. mehd) */
12483 if (qtdemux->segment.duration &&
12484 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12486 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12489 /* configure segments */
12490 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12491 goto segments_failed;
12493 /* add some language tag, if useful */
12494 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12495 strcmp (stream->lang_id, "und")) {
12496 const gchar *lang_code;
12498 /* convert ISO 639-2 code to ISO 639-1 */
12499 lang_code = gst_tag_get_language_code (stream->lang_id);
12500 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12501 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12504 /* Check for UDTA tags */
12505 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12506 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12509 /* Insert and sort new stream in track-id order.
12510 * This will help in comparing old/new streams during stream update check */
12511 g_ptr_array_add (qtdemux->active_streams, stream);
12512 g_ptr_array_sort (qtdemux->active_streams,
12513 (GCompareFunc) qtdemux_track_id_compare_func);
12514 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
12515 QTDEMUX_N_STREAMS (qtdemux));
12522 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12523 (_("This file is corrupt and cannot be played.")), (NULL));
12525 gst_qtdemux_stream_unref (stream);
12530 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12531 gst_qtdemux_stream_unref (stream);
12537 /* we posted an error already */
12538 /* free stbl sub-atoms */
12539 gst_qtdemux_stbl_free (stream);
12540 gst_qtdemux_stream_unref (stream);
12545 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12551 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12552 GST_FOURCC_ARGS (stream->subtype));
12553 gst_qtdemux_stream_unref (stream);
12558 /* If we can estimate the overall bitrate, and don't have information about the
12559 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12560 * the overall bitrate minus the sum of the bitrates of all other streams. This
12561 * should be useful for the common case where we have one audio and one video
12562 * stream and can estimate the bitrate of one, but not the other. */
12564 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12566 QtDemuxStream *stream = NULL;
12567 gint64 size, sys_bitrate, sum_bitrate = 0;
12568 GstClockTime duration;
12572 if (qtdemux->fragmented)
12575 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12577 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12579 GST_DEBUG_OBJECT (qtdemux,
12580 "Size in bytes of the stream not known - bailing");
12584 /* Subtract the header size */
12585 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12586 size, qtdemux->header_size);
12588 if (size < qtdemux->header_size)
12591 size = size - qtdemux->header_size;
12593 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12594 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12598 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12599 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
12600 switch (str->subtype) {
12603 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12604 CUR_STREAM (str)->caps);
12605 /* retrieve bitrate, prefer avg then max */
12607 if (str->stream_tags) {
12608 if (gst_tag_list_get_uint (str->stream_tags,
12609 GST_TAG_MAXIMUM_BITRATE, &bitrate))
12610 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12611 if (gst_tag_list_get_uint (str->stream_tags,
12612 GST_TAG_NOMINAL_BITRATE, &bitrate))
12613 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12614 if (gst_tag_list_get_uint (str->stream_tags,
12615 GST_TAG_BITRATE, &bitrate))
12616 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12619 sum_bitrate += bitrate;
12622 GST_DEBUG_OBJECT (qtdemux,
12623 ">1 stream with unknown bitrate - bailing");
12630 /* For other subtypes, we assume no significant impact on bitrate */
12636 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12640 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12642 if (sys_bitrate < sum_bitrate) {
12643 /* This can happen, since sum_bitrate might be derived from maximum
12644 * bitrates and not average bitrates */
12645 GST_DEBUG_OBJECT (qtdemux,
12646 "System bitrate less than sum bitrate - bailing");
12650 bitrate = sys_bitrate - sum_bitrate;
12651 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12652 ", Stream bitrate = %u", sys_bitrate, bitrate);
12654 if (!stream->stream_tags)
12655 stream->stream_tags = gst_tag_list_new_empty ();
12657 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12659 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12660 GST_TAG_BITRATE, bitrate, NULL);
12663 static GstFlowReturn
12664 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12666 GstFlowReturn ret = GST_FLOW_OK;
12669 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12671 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12672 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12673 guint32 sample_num = 0;
12675 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12676 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12678 if (qtdemux->fragmented) {
12679 /* need all moov samples first */
12680 GST_OBJECT_LOCK (qtdemux);
12681 while (stream->n_samples == 0)
12682 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12684 GST_OBJECT_UNLOCK (qtdemux);
12686 /* discard any stray moof */
12687 qtdemux->moof_offset = 0;
12690 /* prepare braking */
12691 if (ret != GST_FLOW_ERROR)
12694 /* in pull mode, we should have parsed some sample info by now;
12695 * and quite some code will not handle no samples.
12696 * in push mode, we'll just have to deal with it */
12697 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12698 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12699 g_ptr_array_remove_index (qtdemux->active_streams, i);
12702 } else if (stream->track_id == qtdemux->chapters_track_id &&
12703 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
12704 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12705 so that it doesn't look like a subtitle track */
12706 g_ptr_array_remove_index (qtdemux->active_streams, i);
12711 /* parse the initial sample for use in setting the frame rate cap */
12712 while (sample_num == 0 && sample_num < stream->n_samples) {
12713 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12723 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
12725 return g_strcmp0 (stream->stream_id, stream_id) == 0;
12729 qtdemux_is_streams_update (GstQTDemux * qtdemux)
12733 /* Different length, updated */
12734 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
12737 /* streams in list are sorted in track-id order */
12738 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12739 /* Different stream-id, updated */
12740 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
12741 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
12749 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
12750 QtDemuxStream * oldstream, QtDemuxStream * newstream)
12752 /* Connect old stream's srcpad to new stream */
12753 newstream->pad = oldstream->pad;
12754 oldstream->pad = NULL;
12756 /* unset new_stream to prevent stream-start event */
12757 newstream->new_stream = FALSE;
12759 return gst_qtdemux_configure_stream (qtdemux, newstream);
12762 /* g_ptr_array_find_with_equal_func is available since 2.54,
12763 * replacement until we can depend unconditionally on the real one in GLib */
12764 #if !GLIB_CHECK_VERSION(2,54,0)
12765 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
12767 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
12768 gconstpointer needle, GEqualFunc equal_func, guint * index_)
12772 g_return_val_if_fail (haystack != NULL, FALSE);
12774 if (equal_func == NULL)
12775 equal_func = g_direct_equal;
12777 for (i = 0; i < haystack->len; i++) {
12778 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
12779 if (index_ != NULL)
12790 qtdemux_update_streams (GstQTDemux * qtdemux)
12793 g_assert (qtdemux->streams_aware);
12795 /* At below, figure out which stream in active_streams has identical stream-id
12796 * with that of in old_streams. If there is matching stream-id,
12797 * corresponding newstream will not be exposed again,
12798 * but demux will reuse srcpad of matched old stream
12800 * active_streams : newly created streams from the latest moov
12801 * old_streams : existing streams (belong to previous moov)
12804 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12805 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12806 QtDemuxStream *oldstream = NULL;
12809 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12810 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12812 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
12813 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
12814 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
12816 /* null pad stream cannot be reused */
12817 if (oldstream->pad == NULL)
12822 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
12824 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
12827 /* we don't need to preserve order of old streams */
12828 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
12832 /* now we have all info and can expose */
12833 list = stream->stream_tags;
12834 stream->stream_tags = NULL;
12835 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12843 /* Must be called with expose lock */
12844 static GstFlowReturn
12845 qtdemux_expose_streams (GstQTDemux * qtdemux)
12849 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
12851 if (!qtdemux_is_streams_update (qtdemux)) {
12852 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
12853 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12854 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12855 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
12856 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
12857 return GST_FLOW_ERROR;
12860 g_ptr_array_remove_range (qtdemux->old_streams,
12861 0, qtdemux->old_streams->len);
12863 qtdemux->need_segment = TRUE;
12865 return GST_FLOW_OK;
12868 if (qtdemux->streams_aware) {
12869 if (!qtdemux_update_streams (qtdemux))
12870 return GST_FLOW_ERROR;
12872 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12873 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12876 /* now we have all info and can expose */
12877 list = stream->stream_tags;
12878 stream->stream_tags = NULL;
12879 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12880 return GST_FLOW_ERROR;
12885 gst_qtdemux_guess_bitrate (qtdemux);
12887 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
12889 /* If we have still old_streams, it's no more used stream */
12890 for (i = 0; i < qtdemux->old_streams->len; i++) {
12891 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
12896 event = gst_event_new_eos ();
12897 if (qtdemux->segment_seqnum)
12898 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
12900 gst_pad_push_event (stream->pad, event);
12904 g_ptr_array_remove_range (qtdemux->old_streams, 0, qtdemux->old_streams->len);
12906 /* check if we should post a redirect in case there is a single trak
12907 * and it is a redirecting trak */
12908 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
12909 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
12912 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
12913 "an external content");
12914 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
12915 gst_structure_new ("redirect",
12916 "new-location", G_TYPE_STRING,
12917 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
12918 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
12919 qtdemux->posted_redirect = TRUE;
12922 g_ptr_array_foreach (qtdemux->active_streams,
12923 (GFunc) qtdemux_do_allocation, qtdemux);
12925 qtdemux->need_segment = TRUE;
12927 qtdemux->exposed = TRUE;
12928 return GST_FLOW_OK;
12931 /* check if major or compatible brand is 3GP */
12932 static inline gboolean
12933 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
12936 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12938 } else if (qtdemux->comp_brands != NULL) {
12942 gboolean res = FALSE;
12944 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
12947 while (size >= 4) {
12948 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12953 gst_buffer_unmap (qtdemux->comp_brands, &map);
12960 /* check if tag is a spec'ed 3GP tag keyword storing a string */
12961 static inline gboolean
12962 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
12964 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
12965 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
12966 || fourcc == FOURCC_albm;
12970 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
12971 const char *tag, const char *dummy, GNode * node)
12973 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12977 gdouble longitude, latitude, altitude;
12980 len = QT_UINT32 (node->data);
12987 /* TODO: language code skipped */
12989 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
12992 /* do not alarm in trivial case, but bail out otherwise */
12993 if (*(data + offset) != 0) {
12994 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
12998 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12999 GST_TAG_GEO_LOCATION_NAME, name, NULL);
13000 offset += strlen (name);
13004 if (len < offset + 2 + 4 + 4 + 4)
13007 /* +1 +1 = skip null-terminator and location role byte */
13009 /* table in spec says unsigned, semantics say negative has meaning ... */
13010 longitude = QT_SFP32 (data + offset);
13013 latitude = QT_SFP32 (data + offset);
13016 altitude = QT_SFP32 (data + offset);
13018 /* one invalid means all are invalid */
13019 if (longitude >= -180.0 && longitude <= 180.0 &&
13020 latitude >= -90.0 && latitude <= 90.0) {
13021 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
13022 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
13023 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
13024 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
13027 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
13034 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
13041 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
13042 const char *tag, const char *dummy, GNode * node)
13048 len = QT_UINT32 (node->data);
13052 y = QT_UINT16 ((guint8 *) node->data + 12);
13054 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
13057 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
13059 date = g_date_new_dmy (1, 1, y);
13060 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13061 g_date_free (date);
13065 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
13066 const char *tag, const char *dummy, GNode * node)
13069 char *tag_str = NULL;
13074 len = QT_UINT32 (node->data);
13079 entity = (guint8 *) node->data + offset;
13080 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
13081 GST_DEBUG_OBJECT (qtdemux,
13082 "classification info: %c%c%c%c invalid classification entity",
13083 entity[0], entity[1], entity[2], entity[3]);
13088 table = QT_UINT16 ((guint8 *) node->data + offset);
13090 /* Language code skipped */
13094 /* Tag format: "XXXX://Y[YYYY]/classification info string"
13095 * XXXX: classification entity, fixed length 4 chars.
13096 * Y[YYYY]: classification table, max 5 chars.
13098 tag_str = g_strdup_printf ("----://%u/%s",
13099 table, (char *) node->data + offset);
13101 /* memcpy To be sure we're preserving byte order */
13102 memcpy (tag_str, entity, 4);
13103 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
13105 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
13114 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
13120 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
13121 const char *tag, const char *dummy, GNode * node)
13123 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13129 gboolean ret = TRUE;
13130 const gchar *charset = NULL;
13132 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13134 len = QT_UINT32 (data->data);
13135 type = QT_UINT32 ((guint8 *) data->data + 8);
13136 if (type == 0x00000001 && len > 16) {
13137 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
13140 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13141 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13144 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13148 len = QT_UINT32 (node->data);
13149 type = QT_UINT32 ((guint8 *) node->data + 4);
13150 if ((type >> 24) == 0xa9 && len > 8 + 4) {
13154 /* Type starts with the (C) symbol, so the next data is a list
13155 * of (string size(16), language code(16), string) */
13157 str_len = QT_UINT16 ((guint8 *) node->data + 8);
13158 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
13160 /* the string + fourcc + size + 2 16bit fields,
13161 * means that there are more tags in this atom */
13162 if (len > str_len + 8 + 4) {
13163 /* TODO how to represent the same tag in different languages? */
13164 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
13165 "text alternatives, reading only first one");
13169 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
13170 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
13172 if (lang_code < 0x800) { /* MAC encoded string */
13175 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
13176 QT_FOURCC ((guint8 *) node->data + 4))) {
13177 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
13179 /* we go for 3GP style encoding if major brands claims so,
13180 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
13181 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13182 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
13183 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
13185 /* 16-bit Language code is ignored here as well */
13186 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
13193 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
13194 ret = FALSE; /* may have to fallback */
13197 GError *err = NULL;
13199 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
13200 charset, NULL, NULL, &err);
13202 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
13203 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
13205 g_error_free (err);
13208 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13209 len - offset, env_vars);
13212 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
13213 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
13217 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
13224 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
13225 const char *tag, const char *dummy, GNode * node)
13227 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
13231 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
13232 const char *tag, const char *dummy, GNode * node)
13234 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
13236 char *s, *t, *k = NULL;
13241 /* first try normal string tag if major brand not 3GP */
13242 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
13243 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
13244 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
13245 * let's try it 3gpp way after minor safety check */
13247 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
13253 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
13257 len = QT_UINT32 (data);
13261 count = QT_UINT8 (data + 14);
13263 for (; count; count--) {
13266 if (offset + 1 > len)
13268 slen = QT_UINT8 (data + offset);
13270 if (offset + slen > len)
13272 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
13275 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
13277 t = g_strjoin (",", k, s, NULL);
13285 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
13292 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
13293 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
13302 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
13308 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
13309 const char *tag1, const char *tag2, GNode * node)
13316 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13318 len = QT_UINT32 (data->data);
13319 type = QT_UINT32 ((guint8 *) data->data + 8);
13320 if (type == 0x00000000 && len >= 22) {
13321 n1 = QT_UINT16 ((guint8 *) data->data + 18);
13322 n2 = QT_UINT16 ((guint8 *) data->data + 20);
13324 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
13325 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
13328 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
13329 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
13336 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
13337 const char *tag1, const char *dummy, GNode * node)
13344 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13346 len = QT_UINT32 (data->data);
13347 type = QT_UINT32 ((guint8 *) data->data + 8);
13348 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
13349 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13350 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
13351 n1 = QT_UINT16 ((guint8 *) data->data + 16);
13353 /* do not add bpm=0 */
13354 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
13355 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
13363 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
13364 const char *tag1, const char *dummy, GNode * node)
13371 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13373 len = QT_UINT32 (data->data);
13374 type = QT_UINT32 ((guint8 *) data->data + 8);
13375 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
13376 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
13377 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
13378 num = QT_UINT32 ((guint8 *) data->data + 16);
13380 /* do not add num=0 */
13381 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
13382 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
13389 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
13390 const char *tag1, const char *dummy, GNode * node)
13397 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13399 len = QT_UINT32 (data->data);
13400 type = QT_UINT32 ((guint8 *) data->data + 8);
13401 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
13402 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
13403 GstTagImageType image_type;
13405 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
13406 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
13408 image_type = GST_TAG_IMAGE_TYPE_NONE;
13411 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
13412 len - 16, image_type))) {
13413 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
13414 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
13415 gst_sample_unref (sample);
13422 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
13423 const char *tag, const char *dummy, GNode * node)
13426 GstDateTime *datetime = NULL;
13431 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13433 len = QT_UINT32 (data->data);
13434 type = QT_UINT32 ((guint8 *) data->data + 8);
13435 if (type == 0x00000001 && len > 16) {
13436 guint y, m = 1, d = 1;
13439 s = g_strndup ((char *) data->data + 16, len - 16);
13440 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
13441 datetime = gst_date_time_new_from_iso8601_string (s);
13442 if (datetime != NULL) {
13443 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
13445 gst_date_time_unref (datetime);
13448 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
13449 if (ret >= 1 && y > 1500 && y < 3000) {
13452 date = g_date_new_dmy (d, m, y);
13453 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13454 g_date_free (date);
13456 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
13464 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
13465 const char *tag, const char *dummy, GNode * node)
13469 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13471 /* re-route to normal string tag if major brand says so
13472 * or no data atom and compatible brand suggests so */
13473 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13474 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
13475 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
13480 guint len, type, n;
13482 len = QT_UINT32 (data->data);
13483 type = QT_UINT32 ((guint8 *) data->data + 8);
13484 if (type == 0x00000000 && len >= 18) {
13485 n = QT_UINT16 ((guint8 *) data->data + 16);
13487 const gchar *genre;
13489 genre = gst_tag_id3_genre_get (n - 1);
13490 if (genre != NULL) {
13491 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
13492 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
13500 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
13501 const gchar * tag, guint8 * data, guint32 datasize)
13506 /* make a copy to have \0 at the end */
13507 datacopy = g_strndup ((gchar *) data, datasize);
13509 /* convert the str to double */
13510 if (sscanf (datacopy, "%lf", &value) == 1) {
13511 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
13512 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
13514 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
13522 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
13523 const char *tag, const char *tag_bis, GNode * node)
13532 const gchar *meanstr;
13533 const gchar *namestr;
13535 /* checking the whole ---- atom size for consistency */
13536 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
13537 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
13541 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
13543 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
13547 meansize = QT_UINT32 (mean->data);
13548 if (meansize <= 12) {
13549 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
13552 meanstr = ((gchar *) mean->data) + 12;
13555 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
13557 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
13561 namesize = QT_UINT32 (name->data);
13562 if (namesize <= 12) {
13563 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
13566 namestr = ((gchar *) name->data) + 12;
13574 * uint24 - data type
13578 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13580 GST_WARNING_OBJECT (demux, "No data atom in this tag");
13583 datasize = QT_UINT32 (data->data);
13584 if (datasize <= 16) {
13585 GST_WARNING_OBJECT (demux, "Data atom too small");
13588 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
13590 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
13591 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
13592 static const struct
13594 const gchar name[28];
13595 const gchar tag[28];
13598 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
13599 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
13600 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
13601 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
13602 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
13603 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
13604 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
13605 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
13609 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
13610 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
13611 switch (gst_tag_get_type (tags[i].tag)) {
13612 case G_TYPE_DOUBLE:
13613 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
13614 ((guint8 *) data->data) + 16, datasize - 16);
13616 case G_TYPE_STRING:
13617 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
13626 if (i == G_N_ELEMENTS (tags))
13636 #ifndef GST_DISABLE_GST_DEBUG
13638 gchar *namestr_dbg;
13639 gchar *meanstr_dbg;
13641 meanstr_dbg = g_strndup (meanstr, meansize);
13642 namestr_dbg = g_strndup (namestr, namesize);
13644 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
13645 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
13647 g_free (namestr_dbg);
13648 g_free (meanstr_dbg);
13655 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
13656 const char *tag_bis, GNode * node)
13661 GstTagList *id32_taglist = NULL;
13663 GST_LOG_OBJECT (demux, "parsing ID32");
13666 len = GST_READ_UINT32_BE (data);
13668 /* need at least full box and language tag */
13672 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
13673 gst_buffer_fill (buf, 0, data + 14, len - 14);
13675 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
13676 if (id32_taglist) {
13677 GST_LOG_OBJECT (demux, "parsing ok");
13678 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
13679 gst_tag_list_unref (id32_taglist);
13681 GST_LOG_OBJECT (demux, "parsing failed");
13684 gst_buffer_unref (buf);
13687 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
13688 const char *tag, const char *tag_bis, GNode * node);
13691 FOURCC_pcst -> if media is a podcast -> bool
13692 FOURCC_cpil -> if media is part of a compilation -> bool
13693 FOURCC_pgap -> if media is part of a gapless context -> bool
13694 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
13697 static const struct
13700 const gchar *gst_tag;
13701 const gchar *gst_tag_bis;
13702 const GstQTDemuxAddTagFunc func;
13705 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13706 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13707 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
13708 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13709 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13710 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
13711 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13712 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13713 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13714 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13715 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13716 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13717 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13718 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13719 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13720 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13721 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
13722 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
13723 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
13724 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13725 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13726 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
13727 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13728 qtdemux_tag_add_num}, {
13729 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13730 qtdemux_tag_add_num}, {
13731 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
13732 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
13733 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
13734 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
13735 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
13736 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
13737 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13738 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13739 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
13740 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
13741 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
13742 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13743 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13744 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
13745 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
13746 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13747 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
13748 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
13749 qtdemux_tag_add_classification}, {
13750 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
13751 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
13752 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
13754 /* This is a special case, some tags are stored in this
13755 * 'reverse dns naming', according to:
13756 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
13759 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
13760 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
13761 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
13764 struct _GstQtDemuxTagList
13767 GstTagList *taglist;
13769 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
13772 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
13778 const gchar *style;
13783 GstQTDemux *demux = qtdemuxtaglist->demux;
13784 GstTagList *taglist = qtdemuxtaglist->taglist;
13787 len = QT_UINT32 (data);
13788 buf = gst_buffer_new_and_alloc (len);
13789 gst_buffer_fill (buf, 0, data, len);
13791 /* heuristic to determine style of tag */
13792 if (QT_FOURCC (data + 4) == FOURCC_____ ||
13793 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
13795 else if (demux->major_brand == FOURCC_qt__)
13796 style = "quicktime";
13797 /* fall back to assuming iso/3gp tag style */
13801 /* santize the name for the caps. */
13802 for (i = 0; i < 4; i++) {
13803 guint8 d = data[4 + i];
13804 if (g_ascii_isalnum (d))
13805 ndata[i] = g_ascii_tolower (d);
13810 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
13811 ndata[0], ndata[1], ndata[2], ndata[3]);
13812 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
13814 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
13815 sample = gst_sample_new (buf, NULL, NULL, s);
13816 gst_buffer_unref (buf);
13817 g_free (media_type);
13819 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
13822 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
13823 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
13825 gst_sample_unref (sample);
13829 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
13836 GstQtDemuxTagList demuxtaglist;
13838 demuxtaglist.demux = qtdemux;
13839 demuxtaglist.taglist = taglist;
13841 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
13842 if (meta != NULL) {
13843 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
13844 if (ilst == NULL) {
13845 GST_LOG_OBJECT (qtdemux, "no ilst");
13850 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
13854 while (i < G_N_ELEMENTS (add_funcs)) {
13855 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
13859 len = QT_UINT32 (node->data);
13861 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
13862 GST_FOURCC_ARGS (add_funcs[i].fourcc));
13864 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
13865 add_funcs[i].gst_tag_bis, node);
13867 g_node_destroy (node);
13873 /* parsed nodes have been removed, pass along remainder as blob */
13874 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
13875 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
13877 /* parse up XMP_ node if existing */
13878 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
13879 if (xmp_ != NULL) {
13881 GstTagList *xmptaglist;
13883 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
13884 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
13885 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
13886 gst_buffer_unref (buf);
13888 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
13890 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
13896 GstStructure *structure; /* helper for sort function */
13898 guint min_req_bitrate;
13899 guint min_req_qt_version;
13903 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13905 GstQtReference *ref_a = (GstQtReference *) a;
13906 GstQtReference *ref_b = (GstQtReference *) b;
13908 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13909 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13911 /* known bitrates go before unknown; higher bitrates go first */
13912 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13915 /* sort the redirects and post a message for the application.
13918 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13920 GstQtReference *best;
13923 GValue list_val = { 0, };
13926 g_assert (references != NULL);
13928 references = g_list_sort (references, qtdemux_redirects_sort_func);
13930 best = (GstQtReference *) references->data;
13932 g_value_init (&list_val, GST_TYPE_LIST);
13934 for (l = references; l != NULL; l = l->next) {
13935 GstQtReference *ref = (GstQtReference *) l->data;
13936 GValue struct_val = { 0, };
13938 ref->structure = gst_structure_new ("redirect",
13939 "new-location", G_TYPE_STRING, ref->location, NULL);
13941 if (ref->min_req_bitrate > 0) {
13942 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13943 ref->min_req_bitrate, NULL);
13946 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13947 g_value_set_boxed (&struct_val, ref->structure);
13948 gst_value_list_append_value (&list_val, &struct_val);
13949 g_value_unset (&struct_val);
13950 /* don't free anything here yet, since we need best->structure below */
13953 g_assert (best != NULL);
13954 s = gst_structure_copy (best->structure);
13956 if (g_list_length (references) > 1) {
13957 gst_structure_set_value (s, "locations", &list_val);
13960 g_value_unset (&list_val);
13962 for (l = references; l != NULL; l = l->next) {
13963 GstQtReference *ref = (GstQtReference *) l->data;
13965 gst_structure_free (ref->structure);
13966 g_free (ref->location);
13969 g_list_free (references);
13971 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13972 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13973 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13974 qtdemux->posted_redirect = TRUE;
13977 /* look for redirect nodes, collect all redirect information and
13981 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13983 GNode *rmra, *rmda, *rdrf;
13985 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13987 GList *redirects = NULL;
13989 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13991 GstQtReference ref = { NULL, NULL, 0, 0 };
13992 GNode *rmdr, *rmvc;
13994 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13995 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13996 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13997 ref.min_req_bitrate);
14000 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14001 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14002 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14004 #ifndef GST_DISABLE_GST_DEBUG
14005 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14007 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14009 GST_LOG_OBJECT (qtdemux,
14010 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14011 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14012 bitmask, check_type);
14013 if (package == FOURCC_qtim && check_type == 0) {
14014 ref.min_req_qt_version = version;
14018 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14024 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14025 if (ref_len > 20) {
14026 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14027 ref_data = (guint8 *) rdrf->data + 20;
14028 if (ref_type == FOURCC_alis) {
14029 guint record_len, record_version, fn_len;
14031 if (ref_len > 70) {
14032 /* MacOSX alias record, google for alias-layout.txt */
14033 record_len = QT_UINT16 (ref_data + 4);
14034 record_version = QT_UINT16 (ref_data + 4 + 2);
14035 fn_len = QT_UINT8 (ref_data + 50);
14036 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14037 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14040 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14043 } else if (ref_type == FOURCC_url_) {
14044 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14046 GST_DEBUG_OBJECT (qtdemux,
14047 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14048 GST_FOURCC_ARGS (ref_type));
14050 if (ref.location != NULL) {
14051 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14053 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
14055 GST_WARNING_OBJECT (qtdemux,
14056 "Failed to extract redirect location from rdrf atom");
14059 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14063 /* look for others */
14064 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14067 if (redirects != NULL) {
14068 qtdemux_process_redirects (qtdemux, redirects);
14074 static GstTagList *
14075 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14079 if (tags == NULL) {
14080 tags = gst_tag_list_new_empty ();
14081 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14084 if (qtdemux->major_brand == FOURCC_mjp2)
14085 fmt = "Motion JPEG 2000";
14086 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14088 else if (qtdemux->major_brand == FOURCC_qt__)
14090 else if (qtdemux->fragmented)
14093 fmt = "ISO MP4/M4A";
14095 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14096 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14098 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14104 /* we have read the complete moov node now.
14105 * This function parses all of the relevant info, creates the traks and
14106 * prepares all data structures for playback
14109 qtdemux_parse_tree (GstQTDemux * qtdemux)
14116 guint64 creation_time;
14117 GstDateTime *datetime = NULL;
14120 /* make sure we have a usable taglist */
14121 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14123 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14124 if (mvhd == NULL) {
14125 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14126 return qtdemux_parse_redirects (qtdemux);
14129 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14130 if (version == 1) {
14131 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14132 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14133 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14134 } else if (version == 0) {
14135 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14136 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14137 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14139 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14143 /* Moving qt creation time (secs since 1904) to unix time */
14144 if (creation_time != 0) {
14145 /* Try to use epoch first as it should be faster and more commonly found */
14146 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14149 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14150 /* some data cleansing sanity */
14151 g_get_current_time (&now);
14152 if (now.tv_sec + 24 * 3600 < creation_time) {
14153 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14155 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14158 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14159 GDateTime *dt, *dt_local;
14161 dt = g_date_time_add_seconds (base_dt, creation_time);
14162 dt_local = g_date_time_to_local (dt);
14163 datetime = gst_date_time_new_from_g_date_time (dt_local);
14165 g_date_time_unref (base_dt);
14166 g_date_time_unref (dt);
14170 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14171 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14173 gst_date_time_unref (datetime);
14176 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14177 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14179 /* check for fragmented file and get some (default) data */
14180 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14183 GstByteReader mehd_data;
14185 /* let track parsing or anyone know weird stuff might happen ... */
14186 qtdemux->fragmented = TRUE;
14188 /* compensate for total duration */
14189 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14191 qtdemux_parse_mehd (qtdemux, &mehd_data);
14194 /* Update the movie segment duration, unless it was directly given to us
14195 * by upstream. Otherwise let it as is, as we don't want to mangle the
14196 * duration provided by upstream that may come e.g. from a MPD file. */
14197 if (!qtdemux->upstream_format_is_time) {
14198 GstClockTime duration;
14199 /* set duration in the segment info */
14200 gst_qtdemux_get_duration (qtdemux, &duration);
14201 qtdemux->segment.duration = duration;
14202 /* also do not exceed duration; stop is set that way post seek anyway,
14203 * and segment activation falls back to duration,
14204 * whereas loop only checks stop, so let's align this here as well */
14205 qtdemux->segment.stop = duration;
14208 /* parse all traks */
14209 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14211 qtdemux_parse_trak (qtdemux, trak);
14212 /* iterate all siblings */
14213 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14216 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14219 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14221 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14223 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14226 /* maybe also some tags in meta box */
14227 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14229 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14230 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14232 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14235 /* parse any protection system info */
14236 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14238 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14239 qtdemux_parse_pssh (qtdemux, pssh);
14240 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14243 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14248 /* taken from ffmpeg */
14250 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14262 len = (len << 7) | (c & 0x7f);
14271 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14272 gsize codec_data_size)
14274 GList *list = NULL;
14275 guint8 *p = codec_data;
14276 gint i, offset, num_packets;
14277 guint *length, last;
14279 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14281 if (codec_data == NULL || codec_data_size == 0)
14284 /* start of the stream and vorbis audio or theora video, need to
14285 * send the codec_priv data as first three packets */
14286 num_packets = p[0] + 1;
14287 GST_DEBUG_OBJECT (qtdemux,
14288 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14289 (guint) num_packets, codec_data_size);
14291 /* Let's put some limits, Don't think there even is a xiph codec
14292 * with more than 3-4 headers */
14293 if (G_UNLIKELY (num_packets > 16)) {
14294 GST_WARNING_OBJECT (qtdemux,
14295 "Unlikely number of xiph headers, most likely not valid");
14299 length = g_alloca (num_packets * sizeof (guint));
14303 /* first packets, read length values */
14304 for (i = 0; i < num_packets - 1; i++) {
14306 while (offset < codec_data_size) {
14307 length[i] += p[offset];
14308 if (p[offset++] != 0xff)
14313 if (offset + last > codec_data_size)
14316 /* last packet is the remaining size */
14317 length[i] = codec_data_size - offset - last;
14319 for (i = 0; i < num_packets; i++) {
14322 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14324 if (offset + length[i] > codec_data_size)
14327 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
14328 list = g_list_append (list, hdr);
14330 offset += length[i];
14339 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14345 /* this can change the codec originally present in @list */
14347 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14348 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14350 int len = QT_UINT32 (esds->data);
14351 guint8 *ptr = esds->data;
14352 guint8 *end = ptr + len;
14354 guint8 *data_ptr = NULL;
14356 guint8 object_type_id = 0;
14357 guint8 stream_type = 0;
14358 const char *codec_name = NULL;
14359 GstCaps *caps = NULL;
14361 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14363 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14365 while (ptr + 1 < end) {
14366 tag = QT_UINT8 (ptr);
14367 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14369 len = read_descr_size (ptr, end, &ptr);
14370 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14372 /* Check the stated amount of data is available for reading */
14373 if (len < 0 || ptr + len > end)
14377 case ES_DESCRIPTOR_TAG:
14378 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14379 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14382 case DECODER_CONFIG_DESC_TAG:{
14383 guint max_bitrate, avg_bitrate;
14385 object_type_id = QT_UINT8 (ptr);
14386 stream_type = QT_UINT8 (ptr + 1) >> 2;
14387 max_bitrate = QT_UINT32 (ptr + 5);
14388 avg_bitrate = QT_UINT32 (ptr + 9);
14389 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14390 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14391 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14392 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14393 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14394 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14395 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14396 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14398 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14399 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14400 avg_bitrate, NULL);
14405 case DECODER_SPECIFIC_INFO_TAG:
14406 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14407 if (object_type_id == 0xe0 && len == 0x40) {
14413 GST_DEBUG_OBJECT (qtdemux,
14414 "Have VOBSUB palette. Creating palette event");
14415 /* move to decConfigDescr data and read palette */
14417 for (i = 0; i < 16; i++) {
14418 clut[i] = QT_UINT32 (data);
14422 s = gst_structure_new ("application/x-gst-dvd", "event",
14423 G_TYPE_STRING, "dvd-spu-clut-change",
14424 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14425 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14426 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14427 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14428 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14429 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14430 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14431 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14434 /* store event and trigger custom processing */
14435 stream->pending_event =
14436 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14438 /* Generic codec_data handler puts it on the caps */
14445 case SL_CONFIG_DESC_TAG:
14446 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
14450 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14452 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14458 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14459 * in use, and should also be used to override some other parameters for some
14461 switch (object_type_id) {
14462 case 0x20: /* MPEG-4 */
14463 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14464 * profile_and_level_indication */
14465 if (data_ptr != NULL && data_len >= 5 &&
14466 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14467 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14468 data_ptr + 4, data_len - 4);
14470 break; /* Nothing special needed here */
14471 case 0x21: /* H.264 */
14472 codec_name = "H.264 / AVC";
14473 caps = gst_caps_new_simple ("video/x-h264",
14474 "stream-format", G_TYPE_STRING, "avc",
14475 "alignment", G_TYPE_STRING, "au", NULL);
14477 case 0x40: /* AAC (any) */
14478 case 0x66: /* AAC Main */
14479 case 0x67: /* AAC LC */
14480 case 0x68: /* AAC SSR */
14481 /* Override channels and rate based on the codec_data, as it's often
14483 /* Only do so for basic setup without HE-AAC extension */
14484 if (data_ptr && data_len == 2) {
14485 guint channels, rate;
14487 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14489 entry->n_channels = channels;
14491 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
14493 entry->rate = rate;
14496 /* Set level and profile if possible */
14497 if (data_ptr != NULL && data_len >= 2) {
14498 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
14499 data_ptr, data_len);
14501 const gchar *profile_str = NULL;
14504 guint8 *codec_data;
14505 gint rate_idx, profile;
14507 /* No codec_data, let's invent something.
14508 * FIXME: This is wrong for SBR! */
14510 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
14512 buffer = gst_buffer_new_and_alloc (2);
14513 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
14514 codec_data = map.data;
14517 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
14520 switch (object_type_id) {
14522 profile_str = "main";
14526 profile_str = "lc";
14530 profile_str = "ssr";
14538 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14540 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14542 gst_buffer_unmap (buffer, &map);
14543 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14544 GST_TYPE_BUFFER, buffer, NULL);
14545 gst_buffer_unref (buffer);
14548 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14549 G_TYPE_STRING, profile_str, NULL);
14553 case 0x60: /* MPEG-2, various profiles */
14559 codec_name = "MPEG-2 video";
14560 caps = gst_caps_new_simple ("video/mpeg",
14561 "mpegversion", G_TYPE_INT, 2,
14562 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14564 case 0x69: /* MPEG-2 BC audio */
14565 case 0x6B: /* MPEG-1 audio */
14566 caps = gst_caps_new_simple ("audio/mpeg",
14567 "mpegversion", G_TYPE_INT, 1, NULL);
14568 codec_name = "MPEG-1 audio";
14570 case 0x6A: /* MPEG-1 */
14571 codec_name = "MPEG-1 video";
14572 caps = gst_caps_new_simple ("video/mpeg",
14573 "mpegversion", G_TYPE_INT, 1,
14574 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14576 case 0x6C: /* MJPEG */
14578 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14580 codec_name = "Motion-JPEG";
14582 case 0x6D: /* PNG */
14584 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14586 codec_name = "PNG still images";
14588 case 0x6E: /* JPEG2000 */
14589 codec_name = "JPEG-2000";
14590 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14592 case 0xA4: /* Dirac */
14593 codec_name = "Dirac";
14594 caps = gst_caps_new_empty_simple ("video/x-dirac");
14596 case 0xA5: /* AC3 */
14597 codec_name = "AC-3 audio";
14598 caps = gst_caps_new_simple ("audio/x-ac3",
14599 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14601 case 0xA9: /* AC3 */
14602 codec_name = "DTS audio";
14603 caps = gst_caps_new_simple ("audio/x-dts",
14604 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14607 if (stream_type == 0x05 && data_ptr) {
14609 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
14612 GValue arr_val = G_VALUE_INIT;
14613 GValue buf_val = G_VALUE_INIT;
14616 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
14617 codec_name = "Vorbis";
14618 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
14619 g_value_init (&arr_val, GST_TYPE_ARRAY);
14620 g_value_init (&buf_val, GST_TYPE_BUFFER);
14621 for (tmp = headers; tmp; tmp = tmp->next) {
14622 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
14623 gst_value_array_append_value (&arr_val, &buf_val);
14625 s = gst_caps_get_structure (caps, 0);
14626 gst_structure_take_value (s, "streamheader", &arr_val);
14627 g_value_unset (&buf_val);
14628 g_list_free (headers);
14635 case 0xE1: /* QCELP */
14636 /* QCELP, the codec_data is a riff tag (little endian) with
14637 * 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). */
14638 caps = gst_caps_new_empty_simple ("audio/qcelp");
14639 codec_name = "QCELP";
14645 /* If we have a replacement caps, then change our caps for this stream */
14647 gst_caps_unref (entry->caps);
14648 entry->caps = caps;
14651 if (codec_name && list)
14652 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14653 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14655 /* Add the codec_data attribute to caps, if we have it */
14659 buffer = gst_buffer_new_and_alloc (data_len);
14660 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14662 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14663 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14665 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14667 gst_buffer_unref (buffer);
14672 static inline GstCaps *
14673 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14677 char *s, fourstr[5];
14679 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14680 for (i = 0; i < 4; i++) {
14681 if (!g_ascii_isalnum (fourstr[i]))
14684 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14685 caps = gst_caps_new_empty_simple (s);
14690 #define _codec(name) \
14692 if (codec_name) { \
14693 *codec_name = g_strdup (name); \
14698 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14699 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14700 const guint8 * stsd_entry_data, gchar ** codec_name)
14702 GstCaps *caps = NULL;
14703 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14707 _codec ("PNG still images");
14708 caps = gst_caps_new_empty_simple ("image/png");
14711 _codec ("JPEG still images");
14713 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14716 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14717 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14718 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14719 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14720 _codec ("Motion-JPEG");
14722 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14725 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14726 _codec ("Motion-JPEG format B");
14727 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14730 _codec ("JPEG-2000");
14731 /* override to what it should be according to spec, avoid palette_data */
14732 entry->bits_per_sample = 24;
14733 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14736 _codec ("Sorensen video v.3");
14737 caps = gst_caps_new_simple ("video/x-svq",
14738 "svqversion", G_TYPE_INT, 3, NULL);
14740 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14741 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14742 _codec ("Sorensen video v.1");
14743 caps = gst_caps_new_simple ("video/x-svq",
14744 "svqversion", G_TYPE_INT, 1, NULL);
14746 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14747 caps = gst_caps_new_empty_simple ("video/x-raw");
14748 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14749 _codec ("Windows Raw RGB");
14750 stream->alignment = 32;
14756 bps = QT_UINT16 (stsd_entry_data + 82);
14759 format = GST_VIDEO_FORMAT_RGB15;
14762 format = GST_VIDEO_FORMAT_RGB16;
14765 format = GST_VIDEO_FORMAT_RGB;
14768 format = GST_VIDEO_FORMAT_ARGB;
14776 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14777 format = GST_VIDEO_FORMAT_I420;
14779 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14780 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14781 format = GST_VIDEO_FORMAT_I420;
14784 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14785 format = GST_VIDEO_FORMAT_UYVY;
14787 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14788 format = GST_VIDEO_FORMAT_v308;
14790 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14791 format = GST_VIDEO_FORMAT_v216;
14794 format = GST_VIDEO_FORMAT_v210;
14796 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14797 format = GST_VIDEO_FORMAT_r210;
14799 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14800 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14801 format = GST_VIDEO_FORMAT_v410;
14804 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14805 * but different order than AYUV
14806 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14807 format = GST_VIDEO_FORMAT_v408;
14810 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14811 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14812 _codec ("MPEG-1 video");
14813 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14814 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14816 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14817 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14818 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14819 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14820 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14821 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14822 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14823 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14824 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14825 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14826 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14827 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14828 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14829 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14830 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14831 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14832 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14833 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14834 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14835 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14836 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14837 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14838 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14839 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14840 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14841 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14842 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14843 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14844 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14845 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14846 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14847 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14848 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14849 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14850 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14851 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14852 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14853 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14854 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14855 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14856 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14857 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14858 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14859 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14860 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14861 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14862 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14863 _codec ("MPEG-2 video");
14864 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14865 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14867 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14868 _codec ("GIF still images");
14869 caps = gst_caps_new_empty_simple ("image/gif");
14872 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14874 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14876 /* ffmpeg uses the height/width props, don't know why */
14877 caps = gst_caps_new_simple ("video/x-h263",
14878 "variant", G_TYPE_STRING, "itu", NULL);
14882 _codec ("MPEG-4 video");
14883 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14884 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14886 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14887 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14888 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14889 caps = gst_caps_new_simple ("video/x-msmpeg",
14890 "msmpegversion", G_TYPE_INT, 43, NULL);
14892 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14894 caps = gst_caps_new_simple ("video/x-divx",
14895 "divxversion", G_TYPE_INT, 3, NULL);
14897 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14898 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14900 caps = gst_caps_new_simple ("video/x-divx",
14901 "divxversion", G_TYPE_INT, 4, NULL);
14903 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14905 caps = gst_caps_new_simple ("video/x-divx",
14906 "divxversion", G_TYPE_INT, 5, NULL);
14909 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14911 caps = gst_caps_new_simple ("video/x-ffv",
14912 "ffvversion", G_TYPE_INT, 1, NULL);
14915 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14916 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14921 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14922 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14923 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14927 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14928 _codec ("Cinepak");
14929 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14931 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14932 _codec ("Apple QuickDraw");
14933 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14935 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14936 _codec ("Apple video");
14937 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14941 _codec ("H.264 / AVC");
14942 caps = gst_caps_new_simple ("video/x-h264",
14943 "stream-format", G_TYPE_STRING, "avc",
14944 "alignment", G_TYPE_STRING, "au", NULL);
14947 _codec ("H.264 / AVC");
14948 caps = gst_caps_new_simple ("video/x-h264",
14949 "stream-format", G_TYPE_STRING, "avc3",
14950 "alignment", G_TYPE_STRING, "au", NULL);
14954 _codec ("H.265 / HEVC");
14955 caps = gst_caps_new_simple ("video/x-h265",
14956 "stream-format", G_TYPE_STRING, "hvc1",
14957 "alignment", G_TYPE_STRING, "au", NULL);
14960 _codec ("H.265 / HEVC");
14961 caps = gst_caps_new_simple ("video/x-h265",
14962 "stream-format", G_TYPE_STRING, "hev1",
14963 "alignment", G_TYPE_STRING, "au", NULL);
14966 _codec ("Run-length encoding");
14967 caps = gst_caps_new_simple ("video/x-rle",
14968 "layout", G_TYPE_STRING, "quicktime", NULL);
14971 _codec ("Run-length encoding");
14972 caps = gst_caps_new_simple ("video/x-rle",
14973 "layout", G_TYPE_STRING, "microsoft", NULL);
14975 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14976 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14977 _codec ("Indeo Video 3");
14978 caps = gst_caps_new_simple ("video/x-indeo",
14979 "indeoversion", G_TYPE_INT, 3, NULL);
14981 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14982 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14983 _codec ("Intel Video 4");
14984 caps = gst_caps_new_simple ("video/x-indeo",
14985 "indeoversion", G_TYPE_INT, 4, NULL);
14989 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14990 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14991 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14992 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14993 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14994 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14995 _codec ("DV Video");
14996 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14997 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14999 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15000 case FOURCC_dv5p: /* DVCPRO50 PAL */
15001 _codec ("DVCPro50 Video");
15002 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15003 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15005 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15006 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15007 _codec ("DVCProHD Video");
15008 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15009 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15011 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15012 _codec ("Apple Graphics (SMC)");
15013 caps = gst_caps_new_empty_simple ("video/x-smc");
15015 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15017 caps = gst_caps_new_empty_simple ("video/x-vp3");
15019 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15020 _codec ("VP6 Flash");
15021 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15025 caps = gst_caps_new_empty_simple ("video/x-theora");
15026 /* theora uses one byte of padding in the data stream because it does not
15027 * allow 0 sized packets while theora does */
15028 entry->padding = 1;
15032 caps = gst_caps_new_empty_simple ("video/x-dirac");
15034 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15035 _codec ("TIFF still images");
15036 caps = gst_caps_new_empty_simple ("image/tiff");
15038 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15039 _codec ("Apple Intermediate Codec");
15040 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15042 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15043 _codec ("AVID DNxHD");
15044 caps = gst_caps_from_string ("video/x-dnxhd");
15048 _codec ("On2 VP8");
15049 caps = gst_caps_from_string ("video/x-vp8");
15052 _codec ("Google VP9");
15053 caps = gst_caps_from_string ("video/x-vp9");
15056 _codec ("Apple ProRes LT");
15058 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15062 _codec ("Apple ProRes HQ");
15064 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15068 _codec ("Apple ProRes");
15070 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15074 _codec ("Apple ProRes Proxy");
15076 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15080 _codec ("Apple ProRes 4444");
15082 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15086 _codec ("Apple ProRes 4444 XQ");
15088 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15092 _codec ("GoPro CineForm");
15093 caps = gst_caps_from_string ("video/x-cineform");
15098 caps = gst_caps_new_simple ("video/x-wmv",
15099 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15103 caps = gst_caps_new_empty_simple ("video/x-av1");
15105 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15108 caps = _get_unknown_codec_name ("video", fourcc);
15113 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15116 gst_video_info_init (&info);
15117 gst_video_info_set_format (&info, format, entry->width, entry->height);
15119 caps = gst_video_info_to_caps (&info);
15120 *codec_name = gst_pb_utils_get_codec_description (caps);
15122 /* enable clipping for raw video streams */
15123 stream->need_clip = TRUE;
15124 stream->alignment = 32;
15131 round_up_pow2 (guint n)
15143 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15144 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15145 int len, gchar ** codec_name)
15148 const GstStructure *s;
15151 GstAudioFormat format = 0;
15154 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15156 depth = entry->bytes_per_packet * 8;
15159 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15161 /* 8-bit audio is unsigned */
15163 format = GST_AUDIO_FORMAT_U8;
15164 /* otherwise it's signed and big-endian just like 'twos' */
15166 endian = G_BIG_ENDIAN;
15173 endian = G_LITTLE_ENDIAN;
15176 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15178 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15182 caps = gst_caps_new_simple ("audio/x-raw",
15183 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15184 "layout", G_TYPE_STRING, "interleaved", NULL);
15185 stream->alignment = GST_ROUND_UP_8 (depth);
15186 stream->alignment = round_up_pow2 (stream->alignment);
15189 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
15190 _codec ("Raw 64-bit floating-point audio");
15191 caps = gst_caps_new_simple ("audio/x-raw",
15192 "format", G_TYPE_STRING, "F64BE",
15193 "layout", G_TYPE_STRING, "interleaved", NULL);
15194 stream->alignment = 8;
15196 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
15197 _codec ("Raw 32-bit floating-point audio");
15198 caps = gst_caps_new_simple ("audio/x-raw",
15199 "format", G_TYPE_STRING, "F32BE",
15200 "layout", G_TYPE_STRING, "interleaved", NULL);
15201 stream->alignment = 4;
15204 _codec ("Raw 24-bit PCM audio");
15205 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15207 caps = gst_caps_new_simple ("audio/x-raw",
15208 "format", G_TYPE_STRING, "S24BE",
15209 "layout", G_TYPE_STRING, "interleaved", NULL);
15210 stream->alignment = 4;
15212 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
15213 _codec ("Raw 32-bit PCM audio");
15214 caps = gst_caps_new_simple ("audio/x-raw",
15215 "format", G_TYPE_STRING, "S32BE",
15216 "layout", G_TYPE_STRING, "interleaved", NULL);
15217 stream->alignment = 4;
15219 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
15220 _codec ("Raw 16-bit PCM audio");
15221 caps = gst_caps_new_simple ("audio/x-raw",
15222 "format", G_TYPE_STRING, "S16LE",
15223 "layout", G_TYPE_STRING, "interleaved", NULL);
15224 stream->alignment = 2;
15227 _codec ("Mu-law audio");
15228 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15231 _codec ("A-law audio");
15232 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15236 _codec ("Microsoft ADPCM");
15237 /* Microsoft ADPCM-ACM code 2 */
15238 caps = gst_caps_new_simple ("audio/x-adpcm",
15239 "layout", G_TYPE_STRING, "microsoft", NULL);
15243 _codec ("DVI/IMA ADPCM");
15244 caps = gst_caps_new_simple ("audio/x-adpcm",
15245 "layout", G_TYPE_STRING, "dvi", NULL);
15249 _codec ("DVI/Intel IMA ADPCM");
15250 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15251 caps = gst_caps_new_simple ("audio/x-adpcm",
15252 "layout", G_TYPE_STRING, "quicktime", NULL);
15256 /* MPEG layer 3, CBR only (pre QT4.1) */
15258 _codec ("MPEG-1 layer 3");
15259 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15260 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15261 "mpegversion", G_TYPE_INT, 1, NULL);
15263 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15264 _codec ("MPEG-1 layer 2");
15266 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15267 "mpegversion", G_TYPE_INT, 1, NULL);
15270 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15271 _codec ("EAC-3 audio");
15272 caps = gst_caps_new_simple ("audio/x-eac3",
15273 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15274 entry->sampled = TRUE;
15276 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15278 _codec ("AC-3 audio");
15279 caps = gst_caps_new_simple ("audio/x-ac3",
15280 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15281 entry->sampled = TRUE;
15283 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15284 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15285 _codec ("DTS audio");
15286 caps = gst_caps_new_simple ("audio/x-dts",
15287 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15288 entry->sampled = TRUE;
15290 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15291 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15292 _codec ("DTS-HD audio");
15293 caps = gst_caps_new_simple ("audio/x-dts",
15294 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15295 entry->sampled = TRUE;
15299 caps = gst_caps_new_simple ("audio/x-mace",
15300 "maceversion", G_TYPE_INT, 3, NULL);
15304 caps = gst_caps_new_simple ("audio/x-mace",
15305 "maceversion", G_TYPE_INT, 6, NULL);
15307 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15309 caps = gst_caps_new_empty_simple ("application/ogg");
15311 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15312 _codec ("DV audio");
15313 caps = gst_caps_new_empty_simple ("audio/x-dv");
15316 _codec ("MPEG-4 AAC audio");
15317 caps = gst_caps_new_simple ("audio/mpeg",
15318 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15319 "stream-format", G_TYPE_STRING, "raw", NULL);
15321 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15322 _codec ("QDesign Music");
15323 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15326 _codec ("QDesign Music v.2");
15327 /* FIXME: QDesign music version 2 (no constant) */
15328 if (FALSE && data) {
15329 caps = gst_caps_new_simple ("audio/x-qdm2",
15330 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15331 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15332 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15334 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15338 _codec ("GSM audio");
15339 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15342 _codec ("AMR audio");
15343 caps = gst_caps_new_empty_simple ("audio/AMR");
15346 _codec ("AMR-WB audio");
15347 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15350 _codec ("Quicktime IMA ADPCM");
15351 caps = gst_caps_new_simple ("audio/x-adpcm",
15352 "layout", G_TYPE_STRING, "quicktime", NULL);
15355 _codec ("Apple lossless audio");
15356 caps = gst_caps_new_empty_simple ("audio/x-alac");
15359 _codec ("Free Lossless Audio Codec");
15360 caps = gst_caps_new_simple ("audio/x-flac",
15361 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15363 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15364 _codec ("QualComm PureVoice");
15365 caps = gst_caps_from_string ("audio/qcelp");
15370 caps = gst_caps_new_empty_simple ("audio/x-wma");
15374 caps = gst_caps_new_empty_simple ("audio/x-opus");
15381 GstAudioFormat format;
15384 FLAG_IS_FLOAT = 0x1,
15385 FLAG_IS_BIG_ENDIAN = 0x2,
15386 FLAG_IS_SIGNED = 0x4,
15387 FLAG_IS_PACKED = 0x8,
15388 FLAG_IS_ALIGNED_HIGH = 0x10,
15389 FLAG_IS_NON_INTERLEAVED = 0x20
15391 _codec ("Raw LPCM audio");
15393 if (data && len >= 36) {
15394 depth = QT_UINT32 (data + 24);
15395 flags = QT_UINT32 (data + 28);
15396 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15398 if ((flags & FLAG_IS_FLOAT) == 0) {
15403 if ((flags & FLAG_IS_ALIGNED_HIGH))
15406 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15407 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15408 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15409 caps = gst_caps_new_simple ("audio/x-raw",
15410 "format", G_TYPE_STRING,
15412 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15413 "UNKNOWN", "layout", G_TYPE_STRING,
15414 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15415 "interleaved", NULL);
15416 stream->alignment = GST_ROUND_UP_8 (depth);
15417 stream->alignment = round_up_pow2 (stream->alignment);
15422 if (flags & FLAG_IS_BIG_ENDIAN)
15423 format = GST_AUDIO_FORMAT_F64BE;
15425 format = GST_AUDIO_FORMAT_F64LE;
15427 if (flags & FLAG_IS_BIG_ENDIAN)
15428 format = GST_AUDIO_FORMAT_F32BE;
15430 format = GST_AUDIO_FORMAT_F32LE;
15432 caps = gst_caps_new_simple ("audio/x-raw",
15433 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15434 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15435 "non-interleaved" : "interleaved", NULL);
15436 stream->alignment = width / 8;
15440 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15444 caps = _get_unknown_codec_name ("audio", fourcc);
15450 GstCaps *templ_caps =
15451 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
15452 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
15453 gst_caps_unref (caps);
15454 gst_caps_unref (templ_caps);
15455 caps = intersection;
15458 /* enable clipping for raw audio streams */
15459 s = gst_caps_get_structure (caps, 0);
15460 name = gst_structure_get_name (s);
15461 if (g_str_has_prefix (name, "audio/x-raw")) {
15462 stream->need_clip = TRUE;
15463 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
15464 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
15470 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15471 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15472 const guint8 * stsd_entry_data, gchar ** codec_name)
15476 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15480 _codec ("DVD subtitle");
15481 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
15482 stream->need_process = TRUE;
15485 _codec ("Quicktime timed text");
15488 _codec ("3GPP timed text");
15490 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
15492 /* actual text piece needs to be extracted */
15493 stream->need_process = TRUE;
15496 _codec ("XML subtitles");
15497 caps = gst_caps_new_empty_simple ("application/ttml+xml");
15500 _codec ("CEA 608 Closed Caption");
15502 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
15503 G_TYPE_STRING, "s334-1a", NULL);
15504 stream->need_process = TRUE;
15505 stream->need_split = TRUE;
15508 _codec ("CEA 708 Closed Caption");
15510 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
15511 G_TYPE_STRING, "cdp", NULL);
15512 stream->need_process = TRUE;
15517 caps = _get_unknown_codec_name ("text", fourcc);
15525 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15526 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15527 const guint8 * stsd_entry_data, gchar ** codec_name)
15533 _codec ("MPEG 1 video");
15534 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15535 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15545 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
15546 const gchar * system_id)
15550 if (!qtdemux->protection_system_ids)
15551 qtdemux->protection_system_ids =
15552 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
15553 /* Check whether we already have an entry for this system ID. */
15554 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
15555 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
15556 if (g_ascii_strcasecmp (system_id, id) == 0) {
15560 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
15561 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,