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
34 * Demuxes a .mov file into raw or compressed audio and/or video streams.
36 * This element supports both push and pull-based scheduling, depending on the
37 * capabilities of the upstream elements.
39 * ## Example launch line
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/base/base.h>
57 #include <gst/tag/tag.h>
58 #include <gst/audio/audio.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"
70 #include "qtdemux_tags.h"
71 #include "qtdemux_tree.h"
77 #include <gst/math-compat.h>
83 /* max. size considered 'sane' for non-mdat atoms */
84 #define QTDEMUX_MAX_ATOM_SIZE (32*1024*1024)
86 /* if the sample index is larger than this, something is likely wrong */
87 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
89 /* For converting qt creation times to unix epoch times */
90 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
91 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
92 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
93 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
95 #define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))
97 #define STREAM_IS_EOS(s) ((s)->time_position == GST_CLOCK_TIME_NONE)
99 #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
101 #define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s))
102 #define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
103 #define QTDEMUX_NTH_STREAM(demux,idx) \
104 QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
105 #define QTDEMUX_NTH_OLD_STREAM(demux,idx) \
106 QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
108 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
110 GST_DEBUG_CATEGORY (qtdemux_debug);
111 #define GST_CAT_DEFAULT qtdemux_debug
113 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
115 /* Macros for converting to/from timescale */
116 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
117 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
119 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
120 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
122 /* timestamp is the DTS */
123 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
124 /* timestamp + offset + cslg_shift is the outgoing PTS */
125 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
126 /* timestamp + offset is the PTS used for internal seek calculations */
127 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
128 /* timestamp + duration - dts is the duration */
129 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
131 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
133 #define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
134 #define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
135 GST_TRACE("Locking from thread %p", g_thread_self()); \
136 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
137 GST_TRACE("Locked from thread %p", g_thread_self()); \
140 #define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
141 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
142 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
146 * Quicktime has tracks and segments. A track is a continuous piece of
147 * multimedia content. The track is not always played from start to finish but
148 * instead, pieces of the track are 'cut out' and played in sequence. This is
149 * what the segments do.
151 * Inside the track we have keyframes (K) and delta frames. The track has its
152 * own timing, which starts from 0 and extends to end. The position in the track
153 * is called the media_time.
155 * The segments now describe the pieces that should be played from this track
156 * and are basically tuples of media_time/duration/rate entries. We can have
157 * multiple segments and they are all played after one another. An example:
159 * segment 1: media_time: 1 second, duration: 1 second, rate 1
160 * segment 2: media_time: 3 second, duration: 2 second, rate 2
162 * To correctly play back this track, one must play: 1 second of media starting
163 * from media_time 1 followed by 2 seconds of media starting from media_time 3
166 * Each of the segments will be played at a specific time, the first segment at
167 * time 0, the second one after the duration of the first one, etc.. Note that
168 * the time in resulting playback is not identical to the media_time of the
171 * Visually, assuming the track has 4 second of media_time:
174 * .-----------------------------------------------------------.
175 * track: | K.....K.........K........K.......K.......K...........K... |
176 * '-----------------------------------------------------------'
178 * .------------^ ^ .----------^ ^
179 * / .-------------' / .------------------'
181 * .--------------. .--------------.
182 * | segment 1 | | segment 2 |
183 * '--------------' '--------------'
185 * The challenge here is to cut out the right pieces of the track for each of
186 * the playback segments. This fortunately can easily be done with the SEGMENT
187 * events of GStreamer.
189 * For playback of segment 1, we need to provide the decoder with the keyframe
190 * (a), in the above figure, but we must instruct it only to output the decoded
191 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
192 * position set to the time of the segment: 0.
194 * We then proceed to push data from keyframe (a) to frame (b). The decoder
195 * decodes but clips all before media_time 1.
197 * After finishing a segment, we push out a new SEGMENT event with the clipping
198 * boundaries of the new data.
200 * This is a good usecase for the GStreamer accumulated SEGMENT events.
203 struct _QtDemuxSegment
205 /* global time and duration, all gst time */
207 GstClockTime stop_time;
208 GstClockTime duration;
209 /* media time of trak, all gst time */
210 GstClockTime media_start;
211 GstClockTime media_stop;
213 /* Media start time in trak timescale units */
214 guint32 trak_media_start;
217 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
219 /* Used with fragmented MP4 files (mfra atom) */
220 struct _QtDemuxRandomAccessEntry
227 /* Contains properties and cryptographic info for a set of samples from a
228 * track protected using Common Encryption (cenc) */
229 struct _QtDemuxCencSampleSetInfo
231 GstStructure *default_properties;
233 /* @crypto_info holds one GstStructure per sample */
234 GPtrArray *crypto_info;
238 qt_demux_state_string (enum QtDemuxState state)
241 case QTDEMUX_STATE_INITIAL:
243 case QTDEMUX_STATE_HEADER:
245 case QTDEMUX_STATE_MOVIE:
247 case QTDEMUX_STATE_BUFFER_MDAT:
248 return "<BUFFER_MDAT>";
254 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
256 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
258 static GstStaticPadTemplate gst_qtdemux_sink_template =
259 GST_STATIC_PAD_TEMPLATE ("sink",
262 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
266 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
267 GST_STATIC_PAD_TEMPLATE ("video_%u",
270 GST_STATIC_CAPS_ANY);
272 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
273 GST_STATIC_PAD_TEMPLATE ("audio_%u",
276 GST_STATIC_CAPS_ANY);
278 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
279 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
282 GST_STATIC_CAPS_ANY);
284 #define gst_qtdemux_parent_class parent_class
285 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
287 static void gst_qtdemux_dispose (GObject * object);
288 static void gst_qtdemux_finalize (GObject * object);
291 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
292 GstClockTime media_time);
294 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
295 QtDemuxStream * str, gint64 media_offset);
298 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
299 static GstIndex *gst_qtdemux_get_index (GstElement * element);
301 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
302 GstStateChange transition);
303 static void gst_qtdemux_set_context (GstElement * element,
304 GstContext * context);
305 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
306 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
307 GstObject * parent, GstPadMode mode, gboolean active);
309 static void gst_qtdemux_loop (GstPad * pad);
310 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
312 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
314 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
316 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
317 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
318 QtDemuxStream * stream);
319 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
320 QtDemuxStream * stream);
321 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
324 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
326 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
327 const guint8 * buffer, guint length);
328 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
329 const guint8 * buffer, guint length);
330 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
332 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
333 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
335 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
336 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
337 const guint8 * stsd_entry_data, gchar ** codec_name);
338 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
339 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
340 const guint8 * data, int len, gchar ** codec_name);
341 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
342 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
343 gchar ** codec_name);
344 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
345 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
346 const guint8 * stsd_entry_data, gchar ** codec_name);
348 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
349 QtDemuxStream * stream, guint32 n);
350 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
351 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
352 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
353 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
354 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
355 static void qtdemux_do_allocation (QtDemuxStream * stream,
356 GstQTDemux * qtdemux);
357 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
358 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
359 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
360 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
361 GstClockTime * _start, GstClockTime * _stop);
362 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
363 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
365 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
366 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
368 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
370 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
371 QtDemuxStream * stream, guint sample_index);
372 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
374 static void qtdemux_gst_structure_free (GstStructure * gststructure);
375 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
378 gst_qtdemux_class_init (GstQTDemuxClass * klass)
380 GObjectClass *gobject_class;
381 GstElementClass *gstelement_class;
383 gobject_class = (GObjectClass *) klass;
384 gstelement_class = (GstElementClass *) klass;
386 parent_class = g_type_class_peek_parent (klass);
388 gobject_class->dispose = gst_qtdemux_dispose;
389 gobject_class->finalize = gst_qtdemux_finalize;
391 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
393 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
394 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
396 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
398 gst_tag_register_musicbrainz_tags ();
400 gst_element_class_add_static_pad_template (gstelement_class,
401 &gst_qtdemux_sink_template);
402 gst_element_class_add_static_pad_template (gstelement_class,
403 &gst_qtdemux_videosrc_template);
404 gst_element_class_add_static_pad_template (gstelement_class,
405 &gst_qtdemux_audiosrc_template);
406 gst_element_class_add_static_pad_template (gstelement_class,
407 &gst_qtdemux_subsrc_template);
408 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
410 "Demultiplex a QuickTime file into audio and video streams",
411 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
413 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
418 gst_qtdemux_init (GstQTDemux * qtdemux)
421 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
422 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
423 gst_pad_set_activatemode_function (qtdemux->sinkpad,
424 qtdemux_sink_activate_mode);
425 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
426 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
427 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
428 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
430 qtdemux->adapter = gst_adapter_new ();
431 g_queue_init (&qtdemux->protection_event_queue);
432 qtdemux->flowcombiner = gst_flow_combiner_new ();
433 g_mutex_init (&qtdemux->expose_lock);
435 qtdemux->active_streams = g_ptr_array_new_with_free_func
436 ((GDestroyNotify) gst_qtdemux_stream_unref);
437 qtdemux->old_streams = g_ptr_array_new_with_free_func
438 ((GDestroyNotify) gst_qtdemux_stream_unref);
440 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
442 gst_qtdemux_reset (qtdemux, TRUE);
446 gst_qtdemux_finalize (GObject * object)
448 GstQTDemux *qtdemux = GST_QTDEMUX (object);
450 g_free (qtdemux->redirect_location);
452 G_OBJECT_CLASS (parent_class)->finalize (object);
456 gst_qtdemux_dispose (GObject * object)
458 GstQTDemux *qtdemux = GST_QTDEMUX (object);
460 if (qtdemux->adapter) {
461 g_object_unref (G_OBJECT (qtdemux->adapter));
462 qtdemux->adapter = NULL;
464 gst_tag_list_unref (qtdemux->tag_list);
465 gst_flow_combiner_free (qtdemux->flowcombiner);
466 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
468 g_queue_clear (&qtdemux->protection_event_queue);
470 g_free (qtdemux->cenc_aux_info_sizes);
471 qtdemux->cenc_aux_info_sizes = NULL;
472 g_mutex_clear (&qtdemux->expose_lock);
474 g_ptr_array_free (qtdemux->active_streams, TRUE);
475 g_ptr_array_free (qtdemux->old_streams, TRUE);
477 G_OBJECT_CLASS (parent_class)->dispose (object);
481 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
483 if (qtdemux->redirect_location) {
484 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
485 (_("This file contains no playable streams.")),
486 ("no known streams found, a redirect message has been posted"),
487 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
489 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
490 (_("This file contains no playable streams.")),
491 ("no known streams found"));
496 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
498 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
499 mem, size, 0, size, mem, free_func);
503 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
510 if (G_UNLIKELY (size == 0)) {
512 GstBuffer *tmp = NULL;
514 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
515 if (ret != GST_FLOW_OK)
518 gst_buffer_map (tmp, &map, GST_MAP_READ);
519 size = QT_UINT32 (map.data);
520 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
522 gst_buffer_unmap (tmp, &map);
523 gst_buffer_unref (tmp);
526 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
527 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
528 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
529 /* we're pulling header but already got most interesting bits,
530 * so never mind the rest (e.g. tags) (that much) */
531 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
535 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
536 (_("This file is invalid and cannot be played.")),
537 ("atom has bogus size %" G_GUINT64_FORMAT, size));
538 return GST_FLOW_ERROR;
542 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
544 if (G_UNLIKELY (flow != GST_FLOW_OK))
547 bsize = gst_buffer_get_size (*buf);
548 /* Catch short reads - we don't want any partial atoms */
549 if (G_UNLIKELY (bsize < size)) {
550 GST_WARNING_OBJECT (qtdemux,
551 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
552 gst_buffer_unref (*buf);
562 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
563 GstFormat src_format, gint64 src_value, GstFormat dest_format,
567 QtDemuxStream *stream = gst_pad_get_element_private (pad);
570 if (stream->subtype != FOURCC_vide) {
575 switch (src_format) {
576 case GST_FORMAT_TIME:
577 switch (dest_format) {
578 case GST_FORMAT_BYTES:{
579 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
585 *dest_value = stream->samples[index].offset;
587 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
588 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
589 GST_TIME_ARGS (src_value), *dest_value);
597 case GST_FORMAT_BYTES:
598 switch (dest_format) {
599 case GST_FORMAT_TIME:{
601 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
610 QTSTREAMTIME_TO_GSTTIME (stream,
611 stream->samples[index].timestamp);
612 GST_DEBUG_OBJECT (qtdemux,
613 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
614 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
633 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
635 gboolean res = FALSE;
637 *duration = GST_CLOCK_TIME_NONE;
639 if (qtdemux->duration != 0 &&
640 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
641 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
644 *duration = GST_CLOCK_TIME_NONE;
651 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
654 gboolean res = FALSE;
655 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
657 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
659 switch (GST_QUERY_TYPE (query)) {
660 case GST_QUERY_POSITION:{
663 gst_query_parse_position (query, &fmt, NULL);
664 if (fmt == GST_FORMAT_TIME
665 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
666 gst_query_set_position (query, GST_FORMAT_TIME,
667 qtdemux->segment.position);
672 case GST_QUERY_DURATION:{
675 gst_query_parse_duration (query, &fmt, NULL);
676 if (fmt == GST_FORMAT_TIME) {
677 /* First try to query upstream */
678 res = gst_pad_query_default (pad, parent, query);
680 GstClockTime duration;
681 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
682 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
689 case GST_QUERY_CONVERT:{
690 GstFormat src_fmt, dest_fmt;
691 gint64 src_value, dest_value = 0;
693 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
695 res = gst_qtdemux_src_convert (qtdemux, pad,
696 src_fmt, src_value, dest_fmt, &dest_value);
698 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
702 case GST_QUERY_FORMATS:
703 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
706 case GST_QUERY_SEEKING:{
710 /* try upstream first */
711 res = gst_pad_query_default (pad, parent, query);
714 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
715 if (fmt == GST_FORMAT_TIME) {
716 GstClockTime duration;
718 gst_qtdemux_get_duration (qtdemux, &duration);
720 if (!qtdemux->pullbased) {
723 /* we might be able with help from upstream */
725 q = gst_query_new_seeking (GST_FORMAT_BYTES);
726 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
727 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
728 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
732 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
738 case GST_QUERY_SEGMENT:
743 format = qtdemux->segment.format;
746 gst_segment_to_stream_time (&qtdemux->segment, format,
747 qtdemux->segment.start);
748 if ((stop = qtdemux->segment.stop) == -1)
749 stop = qtdemux->segment.duration;
751 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
753 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
758 res = gst_pad_query_default (pad, parent, query);
766 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
768 if (G_LIKELY (stream->pad)) {
769 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
770 GST_DEBUG_PAD_NAME (stream->pad));
772 if (!gst_tag_list_is_empty (stream->stream_tags)) {
773 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
774 stream->stream_tags);
775 gst_pad_push_event (stream->pad,
776 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
779 if (G_UNLIKELY (stream->send_global_tags)) {
780 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
782 gst_pad_push_event (stream->pad,
783 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
784 stream->send_global_tags = FALSE;
789 /* push event on all source pads; takes ownership of the event */
791 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
793 gboolean has_valid_stream = FALSE;
794 GstEventType etype = GST_EVENT_TYPE (event);
797 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
798 GST_EVENT_TYPE_NAME (event));
800 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
802 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
803 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
805 if ((pad = stream->pad)) {
806 has_valid_stream = TRUE;
808 if (etype == GST_EVENT_EOS) {
809 /* let's not send twice */
810 if (stream->sent_eos)
812 stream->sent_eos = TRUE;
815 gst_pad_push_event (pad, gst_event_ref (event));
819 gst_event_unref (event);
821 /* if it is EOS and there are no pads, post an error */
822 if (!has_valid_stream && etype == GST_EVENT_EOS) {
823 gst_qtdemux_post_no_playable_stream_error (qtdemux);
833 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
835 if ((gint64) s1->timestamp > *media_time)
837 if ((gint64) s1->timestamp == *media_time)
843 /* find the index of the sample that includes the data for @media_time using a
844 * binary search. Only to be called in optimized cases of linear search below.
846 * Returns the index of the sample with the corresponding *DTS*.
849 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
852 QtDemuxSample *result;
855 /* convert media_time to mov format */
857 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
859 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
860 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
861 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
863 if (G_LIKELY (result))
864 index = result - str->samples;
873 /* find the index of the sample that includes the data for @media_offset using a
876 * Returns the index of the sample.
879 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
880 QtDemuxStream * str, gint64 media_offset)
882 QtDemuxSample *result = str->samples;
885 if (result == NULL || str->n_samples == 0)
888 if (media_offset == result->offset)
892 while (index < str->n_samples - 1) {
893 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
896 if (media_offset < result->offset)
907 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
912 /* find the index of the sample that includes the data for @media_time using a
913 * linear search, and keeping in mind that not all samples may have been parsed
914 * yet. If possible, it will delegate to binary search.
916 * Returns the index of the sample.
919 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
920 GstClockTime media_time)
924 QtDemuxSample *sample;
926 /* convert media_time to mov format */
928 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
930 sample = str->samples;
931 if (mov_time == sample->timestamp + sample->pts_offset)
934 /* use faster search if requested time in already parsed range */
935 sample = str->samples + str->stbl_index;
936 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
937 index = gst_qtdemux_find_index (qtdemux, str, media_time);
938 sample = str->samples + index;
940 while (index < str->n_samples - 1) {
941 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
944 sample = str->samples + index + 1;
945 if (mov_time < sample->timestamp) {
946 sample = str->samples + index;
954 /* sample->timestamp is now <= media_time, need to find the corresponding
955 * PTS now by looking backwards */
956 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
958 sample = str->samples + index;
966 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
971 /* find the index of the keyframe needed to decode the sample at @index
972 * of stream @str, or of a subsequent keyframe (depending on @next)
974 * Returns the index of the keyframe.
977 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
978 guint32 index, gboolean next)
980 guint32 new_index = index;
982 if (index >= str->n_samples) {
983 new_index = str->n_samples;
987 /* all keyframes, return index */
988 if (str->all_keyframe) {
993 /* else search until we have a keyframe */
994 while (new_index < str->n_samples) {
995 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
998 if (str->samples[new_index].keyframe)
1010 if (new_index == str->n_samples) {
1011 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1016 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1017 "gave %u", next ? "after" : "before", index, new_index);
1024 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1029 /* find the segment for @time_position for @stream
1031 * Returns the index of the segment containing @time_position.
1032 * Returns the last segment and sets the @eos variable to TRUE
1033 * if the time is beyond the end. @eos may be NULL
1036 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1037 GstClockTime time_position)
1042 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1043 GST_TIME_ARGS (time_position));
1046 for (i = 0; i < stream->n_segments; i++) {
1047 QtDemuxSegment *segment = &stream->segments[i];
1049 GST_LOG_OBJECT (stream->pad,
1050 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1051 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1053 /* For the last segment we include stop_time in the last segment */
1054 if (i < stream->n_segments - 1) {
1055 if (segment->time <= time_position && time_position < segment->stop_time) {
1056 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1061 /* Last segment always matches */
1069 /* move the stream @str to the sample position @index.
1071 * Updates @str->sample_index and marks discontinuity if needed.
1074 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1077 /* no change needed */
1078 if (index == str->sample_index)
1081 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1084 /* position changed, we have a discont */
1085 str->sample_index = index;
1086 str->offset_in_sample = 0;
1087 /* Each time we move in the stream we store the position where we are
1089 str->from_sample = index;
1090 str->discont = TRUE;
1094 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1095 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1098 gint64 min_byte_offset = -1;
1101 min_offset = desired_time;
1103 /* for each stream, find the index of the sample in the segment
1104 * and move back to the previous keyframe. */
1105 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1107 guint32 index, kindex;
1109 GstClockTime media_start;
1110 GstClockTime media_time;
1111 GstClockTime seg_time;
1112 QtDemuxSegment *seg;
1113 gboolean empty_segment = FALSE;
1115 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1117 if (CUR_STREAM (str)->sparse && !use_sparse)
1120 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1121 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1123 /* get segment and time in the segment */
1124 seg = &str->segments[seg_idx];
1125 seg_time = (desired_time - seg->time) * seg->rate;
1127 while (QTSEGMENT_IS_EMPTY (seg)) {
1129 empty_segment = TRUE;
1130 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1133 if (seg_idx == str->n_segments)
1135 seg = &str->segments[seg_idx];
1138 if (seg_idx == str->n_segments) {
1139 /* FIXME track shouldn't have the last segment as empty, but if it
1140 * happens we better handle it */
1144 /* get the media time in the segment */
1145 media_start = seg->media_start + seg_time;
1147 /* get the index of the sample with media time */
1148 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1149 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1150 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1151 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1154 /* shift to next frame if we are looking for next keyframe */
1155 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1156 && index < str->stbl_index)
1159 if (!empty_segment) {
1160 /* find previous keyframe */
1161 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1163 /* we will settle for one before if none found after */
1164 if (next && kindex == -1)
1165 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1167 /* Update the requested time whenever a keyframe was found, to make it
1168 * accurate and avoid having the first buffer fall outside of the segment
1173 /* get timestamp of keyframe */
1174 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1175 GST_DEBUG_OBJECT (qtdemux,
1176 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1177 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1178 str->samples[kindex].offset);
1180 /* keyframes in the segment get a chance to change the
1181 * desired_offset. keyframes out of the segment are
1183 if (media_time >= seg->media_start) {
1184 GstClockTime seg_time;
1186 /* this keyframe is inside the segment, convert back to
1188 seg_time = (media_time - seg->media_start) + seg->time;
1189 if ((!next && (seg_time < min_offset)) ||
1190 (next && (seg_time > min_offset)))
1191 min_offset = seg_time;
1196 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1197 min_byte_offset = str->samples[index].offset;
1201 *key_time = min_offset;
1203 *key_offset = min_byte_offset;
1207 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1208 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1212 g_return_val_if_fail (format != NULL, FALSE);
1213 g_return_val_if_fail (cur != NULL, FALSE);
1214 g_return_val_if_fail (stop != NULL, FALSE);
1216 if (*format == GST_FORMAT_TIME)
1220 if (cur_type != GST_SEEK_TYPE_NONE)
1221 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1222 if (res && stop_type != GST_SEEK_TYPE_NONE)
1223 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1226 *format = GST_FORMAT_TIME;
1231 /* perform seek in push based mode:
1232 find BYTE position to move to based on time and delegate to upstream
1235 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1240 GstSeekType cur_type, stop_type;
1241 gint64 cur, stop, key_cur;
1244 gint64 original_stop;
1247 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1249 gst_event_parse_seek (event, &rate, &format, &flags,
1250 &cur_type, &cur, &stop_type, &stop);
1251 seqnum = gst_event_get_seqnum (event);
1253 /* Directly send the instant-rate-change event here before taking the
1254 * stream-lock so that it can be applied as soon as possible */
1255 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1258 /* instant rate change only supported if direction does not change. All
1259 * other requirements are already checked before creating the seek event
1260 * but let's double-check here to be sure */
1261 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1262 (qtdemux->segment.rate < 0 && rate > 0) ||
1263 cur_type != GST_SEEK_TYPE_NONE ||
1264 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1265 GST_ERROR_OBJECT (qtdemux,
1266 "Instant rate change seeks only supported in the "
1267 "same direction, without flushing and position change");
1271 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1272 (GstSegmentFlags) flags);
1273 gst_event_set_seqnum (ev, seqnum);
1274 gst_qtdemux_push_event (qtdemux, ev);
1278 /* only forward streaming and seeking is possible */
1280 goto unsupported_seek;
1282 /* convert to TIME if needed and possible */
1283 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1287 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1288 * the original stop position to use when upstream pushes the new segment
1290 original_stop = stop;
1293 /* find reasonable corresponding BYTE position,
1294 * also try to mind about keyframes, since we can not go back a bit for them
1296 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1297 * mostly just work, but let's not yet boldly go there ... */
1298 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1303 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1304 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1307 GST_OBJECT_LOCK (qtdemux);
1308 qtdemux->seek_offset = byte_cur;
1309 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1310 qtdemux->push_seek_start = cur;
1312 qtdemux->push_seek_start = key_cur;
1315 if (stop_type == GST_SEEK_TYPE_NONE) {
1316 qtdemux->push_seek_stop = qtdemux->segment.stop;
1318 qtdemux->push_seek_stop = original_stop;
1320 GST_OBJECT_UNLOCK (qtdemux);
1322 qtdemux->segment_seqnum = seqnum;
1323 /* BYTE seek event */
1324 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1326 gst_event_set_seqnum (event, seqnum);
1327 res = gst_pad_push_event (qtdemux->sinkpad, event);
1334 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1340 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1345 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1350 /* perform the seek.
1352 * We set all segment_indexes in the streams to unknown and
1353 * adjust the time_position to the desired position. this is enough
1354 * to trigger a segment switch in the streaming thread to start
1355 * streaming from the desired position.
1357 * Keyframe seeking is a little more complicated when dealing with
1358 * segments. Ideally we want to move to the previous keyframe in
1359 * the segment but there might not be a keyframe in the segment. In
1360 * fact, none of the segments could contain a keyframe. We take a
1361 * practical approach: seek to the previous keyframe in the segment,
1362 * if there is none, seek to the beginning of the segment.
1364 * Called with STREAM_LOCK
1367 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1368 guint32 seqnum, GstSeekFlags flags)
1370 gint64 desired_offset;
1373 desired_offset = segment->position;
1375 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1376 GST_TIME_ARGS (desired_offset));
1378 /* may not have enough fragmented info to do this adjustment,
1379 * and we can't scan (and probably should not) at this time with
1380 * possibly flushing upstream */
1381 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1383 gboolean next, before, after;
1385 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1386 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1387 next = after && !before;
1388 if (segment->rate < 0)
1391 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1393 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1394 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1395 desired_offset = min_offset;
1398 /* and set all streams to the final position */
1399 GST_OBJECT_LOCK (qtdemux);
1400 gst_flow_combiner_reset (qtdemux->flowcombiner);
1401 GST_OBJECT_UNLOCK (qtdemux);
1402 qtdemux->segment_seqnum = seqnum;
1403 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1404 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1406 stream->time_position = desired_offset;
1407 stream->accumulated_base = 0;
1408 stream->sample_index = -1;
1409 stream->offset_in_sample = 0;
1410 stream->segment_index = -1;
1411 stream->sent_eos = FALSE;
1412 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1414 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1415 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1417 segment->position = desired_offset;
1418 if (segment->rate >= 0) {
1419 segment->start = desired_offset;
1420 /* We need to update time as we update start in that direction */
1421 segment->time = desired_offset;
1423 /* we stop at the end */
1424 if (segment->stop == -1)
1425 segment->stop = segment->duration;
1427 segment->stop = desired_offset;
1430 if (qtdemux->fragmented)
1431 qtdemux->fragmented_seek_pending = TRUE;
1436 /* do a seek in pull based mode */
1438 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1443 GstSeekType cur_type, stop_type;
1445 gboolean flush, instant_rate_change;
1447 GstSegment seeksegment;
1448 guint32 seqnum = GST_SEQNUM_INVALID;
1449 GstEvent *flush_event;
1452 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1454 gst_event_parse_seek (event, &rate, &format, &flags,
1455 &cur_type, &cur, &stop_type, &stop);
1456 seqnum = gst_event_get_seqnum (event);
1458 /* we have to have a format as the segment format. Try to convert
1460 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1464 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1466 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1467 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1469 /* Directly send the instant-rate-change event here before taking the
1470 * stream-lock so that it can be applied as soon as possible */
1471 if (instant_rate_change) {
1474 /* instant rate change only supported if direction does not change. All
1475 * other requirements are already checked before creating the seek event
1476 * but let's double-check here to be sure */
1477 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1478 (qtdemux->segment.rate < 0 && rate > 0) ||
1479 cur_type != GST_SEEK_TYPE_NONE ||
1480 stop_type != GST_SEEK_TYPE_NONE || flush) {
1481 GST_ERROR_OBJECT (qtdemux,
1482 "Instant rate change seeks only supported in the "
1483 "same direction, without flushing and position change");
1487 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1488 (GstSegmentFlags) flags);
1489 gst_event_set_seqnum (ev, seqnum);
1490 gst_qtdemux_push_event (qtdemux, ev);
1494 /* stop streaming, either by flushing or by pausing the task */
1496 flush_event = gst_event_new_flush_start ();
1497 if (seqnum != GST_SEQNUM_INVALID)
1498 gst_event_set_seqnum (flush_event, seqnum);
1499 /* unlock upstream pull_range */
1500 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1501 /* make sure out loop function exits */
1502 gst_qtdemux_push_event (qtdemux, flush_event);
1504 /* non flushing seek, pause the task */
1505 gst_pad_pause_task (qtdemux->sinkpad);
1508 /* wait for streaming to finish */
1509 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1511 /* copy segment, we need this because we still need the old
1512 * segment when we close the current segment. */
1513 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1515 /* configure the segment with the seek variables */
1516 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1517 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1518 cur_type, cur, stop_type, stop, &update)) {
1520 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1522 /* now do the seek */
1523 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1526 /* prepare for streaming again */
1528 flush_event = gst_event_new_flush_stop (TRUE);
1529 if (seqnum != GST_SEQNUM_INVALID)
1530 gst_event_set_seqnum (flush_event, seqnum);
1532 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1533 gst_qtdemux_push_event (qtdemux, flush_event);
1536 /* commit the new segment */
1537 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1539 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1540 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1541 qtdemux->segment.format, qtdemux->segment.position);
1542 if (seqnum != GST_SEQNUM_INVALID)
1543 gst_message_set_seqnum (msg, seqnum);
1544 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1547 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1548 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1549 qtdemux->sinkpad, NULL);
1551 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1558 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1564 qtdemux_ensure_index (GstQTDemux * qtdemux)
1568 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1570 /* Build complete index */
1571 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1572 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1574 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1575 GST_LOG_OBJECT (qtdemux,
1576 "Building complete index of track-id %u for seeking failed!",
1586 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1589 gboolean res = TRUE;
1590 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1592 switch (GST_EVENT_TYPE (event)) {
1593 case GST_EVENT_RECONFIGURE:
1594 GST_OBJECT_LOCK (qtdemux);
1595 gst_flow_combiner_reset (qtdemux->flowcombiner);
1596 GST_OBJECT_UNLOCK (qtdemux);
1597 res = gst_pad_event_default (pad, parent, event);
1599 case GST_EVENT_SEEK:
1601 GstSeekFlags flags = 0;
1602 gboolean instant_rate_change;
1604 #ifndef GST_DISABLE_GST_DEBUG
1605 GstClockTime ts = gst_util_get_timestamp ();
1607 guint32 seqnum = gst_event_get_seqnum (event);
1609 qtdemux->received_seek = TRUE;
1611 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1612 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1614 if (seqnum == qtdemux->segment_seqnum) {
1615 GST_LOG_OBJECT (pad,
1616 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1617 gst_event_unref (event);
1621 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1622 /* seek should be handled by upstream, we might need to re-download fragments */
1623 GST_DEBUG_OBJECT (qtdemux,
1624 "let upstream handle seek for fragmented playback");
1628 gst_event_parse_seek_trickmode_interval (event,
1629 &qtdemux->trickmode_interval);
1631 /* Build complete index for seeking;
1632 * if not a fragmented file at least and we're really doing a seek,
1633 * not just an instant-rate-change */
1634 if (!qtdemux->fragmented && !instant_rate_change) {
1635 if (!qtdemux_ensure_index (qtdemux))
1638 #ifndef GST_DISABLE_GST_DEBUG
1639 ts = gst_util_get_timestamp () - ts;
1640 GST_INFO_OBJECT (qtdemux,
1641 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1643 if (qtdemux->pullbased) {
1644 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1645 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1646 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1648 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1649 && QTDEMUX_N_STREAMS (qtdemux)
1650 && !qtdemux->fragmented) {
1651 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1653 GST_DEBUG_OBJECT (qtdemux,
1654 "ignoring seek in push mode in current state");
1657 gst_event_unref (event);
1662 res = gst_pad_event_default (pad, parent, event);
1672 GST_ERROR_OBJECT (qtdemux, "Index failed");
1673 gst_event_unref (event);
1679 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1681 * If @fw is false, the coding order is explored backwards.
1683 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1684 * sample is found for that track.
1686 * The stream and sample index of the sample with the minimum offset in the direction explored
1687 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1689 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1690 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1691 * @_stream and @_index. */
1693 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1694 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1697 gint64 time, min_time;
1698 QtDemuxStream *stream;
1705 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1708 gboolean set_sample;
1710 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1717 i = str->n_samples - 1;
1721 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1722 if (str->samples[i].size == 0)
1725 if (fw && (str->samples[i].offset < byte_pos))
1728 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1731 /* move stream to first available sample */
1733 gst_qtdemux_move_stream (qtdemux, str, i);
1737 /* avoid index from sparse streams since they might be far away */
1738 if (!CUR_STREAM (str)->sparse) {
1739 /* determine min/max time */
1740 time = QTSAMPLE_PTS (str, &str->samples[i]);
1741 if (min_time == -1 || (!fw && time > min_time) ||
1742 (fw && time < min_time)) {
1746 /* determine stream with leading sample, to get its position */
1748 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1749 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1757 /* no sample for this stream, mark eos */
1759 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1770 /* Copied from mpegtsbase code */
1771 /* FIXME: replace this function when we add new util function for stream-id creation */
1773 _get_upstream_id (GstQTDemux * demux)
1775 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1778 /* Try to create one from the upstream URI, else use a randome number */
1782 /* Try to generate one from the URI query and
1783 * if it fails take a random number instead */
1784 query = gst_query_new_uri ();
1785 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1786 gst_query_parse_uri (query, &uri);
1792 /* And then generate an SHA256 sum of the URI */
1793 cs = g_checksum_new (G_CHECKSUM_SHA256);
1794 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1796 upstream_id = g_strdup (g_checksum_get_string (cs));
1797 g_checksum_free (cs);
1799 /* Just get some random number if the URI query fails */
1800 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1801 "implementing a deterministic way of creating a stream-id");
1803 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1804 g_random_int (), g_random_int ());
1807 gst_query_unref (query);
1812 static QtDemuxStream *
1813 _create_stream (GstQTDemux * demux, guint32 track_id)
1815 QtDemuxStream *stream;
1818 stream = g_new0 (QtDemuxStream, 1);
1819 stream->demux = demux;
1820 stream->track_id = track_id;
1821 upstream_id = _get_upstream_id (demux);
1822 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1823 g_free (upstream_id);
1824 /* new streams always need a discont */
1825 stream->discont = TRUE;
1826 /* we enable clipping for raw audio/video streams */
1827 stream->need_clip = FALSE;
1828 stream->need_process = FALSE;
1829 stream->segment_index = -1;
1830 stream->time_position = 0;
1831 stream->sample_index = -1;
1832 stream->offset_in_sample = 0;
1833 stream->new_stream = TRUE;
1834 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1835 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1836 stream->protected = FALSE;
1837 stream->protection_scheme_type = 0;
1838 stream->protection_scheme_version = 0;
1839 stream->protection_scheme_info = NULL;
1840 stream->n_samples_moof = 0;
1841 stream->duration_moof = 0;
1842 stream->duration_last_moof = 0;
1843 stream->alignment = 1;
1844 stream->stream_tags = gst_tag_list_new_empty ();
1845 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1846 g_queue_init (&stream->protection_scheme_event_queue);
1847 stream->ref_count = 1;
1848 /* consistent default for push based mode */
1849 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1854 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1856 GstStructure *structure;
1857 const gchar *variant;
1858 const GstCaps *mediacaps = NULL;
1860 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1862 structure = gst_caps_get_structure (caps, 0);
1863 variant = gst_structure_get_string (structure, "variant");
1865 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1866 QtDemuxStream *stream;
1867 const GValue *value;
1869 demux->fragmented = TRUE;
1870 demux->mss_mode = TRUE;
1872 if (QTDEMUX_N_STREAMS (demux) > 1) {
1873 /* can't do this, we can only renegotiate for another mss format */
1877 value = gst_structure_get_value (structure, "media-caps");
1880 const GValue *timescale_v;
1882 /* TODO update when stream changes during playback */
1884 if (QTDEMUX_N_STREAMS (demux) == 0) {
1885 stream = _create_stream (demux, 1);
1886 g_ptr_array_add (demux->active_streams, stream);
1887 /* mss has no stsd/stsd entry, use id 0 as default */
1888 stream->stsd_entries_length = 1;
1889 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
1890 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
1892 stream = QTDEMUX_NTH_STREAM (demux, 0);
1895 timescale_v = gst_structure_get_value (structure, "timescale");
1897 stream->timescale = g_value_get_uint64 (timescale_v);
1899 /* default mss timescale */
1900 stream->timescale = 10000000;
1902 demux->timescale = stream->timescale;
1904 mediacaps = gst_value_get_caps (value);
1905 if (!CUR_STREAM (stream)->caps
1906 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
1907 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1909 stream->new_caps = TRUE;
1911 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
1912 structure = gst_caps_get_structure (mediacaps, 0);
1913 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
1914 stream->subtype = FOURCC_vide;
1916 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
1917 gst_structure_get_int (structure, "height",
1918 &CUR_STREAM (stream)->height);
1919 gst_structure_get_fraction (structure, "framerate",
1920 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
1921 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1923 stream->subtype = FOURCC_soun;
1924 gst_structure_get_int (structure, "channels",
1925 &CUR_STREAM (stream)->n_channels);
1926 gst_structure_get_int (structure, "rate", &rate);
1927 CUR_STREAM (stream)->rate = rate;
1930 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1932 demux->mss_mode = FALSE;
1939 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
1943 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
1944 gst_pad_stop_task (qtdemux->sinkpad);
1946 if (hard || qtdemux->upstream_format_is_time) {
1947 qtdemux->state = QTDEMUX_STATE_INITIAL;
1948 qtdemux->neededbytes = 16;
1949 qtdemux->todrop = 0;
1950 qtdemux->pullbased = FALSE;
1951 g_clear_pointer (&qtdemux->redirect_location, g_free);
1952 qtdemux->first_mdat = -1;
1953 qtdemux->header_size = 0;
1954 qtdemux->mdatoffset = -1;
1955 qtdemux->restoredata_offset = -1;
1956 if (qtdemux->mdatbuffer)
1957 gst_buffer_unref (qtdemux->mdatbuffer);
1958 if (qtdemux->restoredata_buffer)
1959 gst_buffer_unref (qtdemux->restoredata_buffer);
1960 qtdemux->mdatbuffer = NULL;
1961 qtdemux->restoredata_buffer = NULL;
1962 qtdemux->mdatleft = 0;
1963 qtdemux->mdatsize = 0;
1964 if (qtdemux->comp_brands)
1965 gst_buffer_unref (qtdemux->comp_brands);
1966 qtdemux->comp_brands = NULL;
1967 qtdemux->last_moov_offset = -1;
1968 if (qtdemux->moov_node_compressed) {
1969 g_node_destroy (qtdemux->moov_node_compressed);
1970 if (qtdemux->moov_node)
1971 g_free (qtdemux->moov_node->data);
1973 qtdemux->moov_node_compressed = NULL;
1974 if (qtdemux->moov_node)
1975 g_node_destroy (qtdemux->moov_node);
1976 qtdemux->moov_node = NULL;
1977 if (qtdemux->tag_list)
1978 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
1979 qtdemux->tag_list = gst_tag_list_new_empty ();
1980 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
1982 if (qtdemux->element_index)
1983 gst_object_unref (qtdemux->element_index);
1984 qtdemux->element_index = NULL;
1986 qtdemux->major_brand = 0;
1987 qtdemux->upstream_format_is_time = FALSE;
1988 qtdemux->upstream_seekable = FALSE;
1989 qtdemux->upstream_size = 0;
1991 qtdemux->fragment_start = -1;
1992 qtdemux->fragment_start_offset = -1;
1993 qtdemux->duration = 0;
1994 qtdemux->moof_offset = 0;
1995 qtdemux->chapters_track_id = 0;
1996 qtdemux->have_group_id = FALSE;
1997 qtdemux->group_id = G_MAXUINT;
1999 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2001 g_queue_clear (&qtdemux->protection_event_queue);
2003 qtdemux->received_seek = FALSE;
2004 qtdemux->first_moof_already_parsed = FALSE;
2006 qtdemux->offset = 0;
2007 gst_adapter_clear (qtdemux->adapter);
2008 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2009 qtdemux->need_segment = TRUE;
2012 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2013 qtdemux->trickmode_interval = 0;
2014 g_ptr_array_set_size (qtdemux->active_streams, 0);
2015 g_ptr_array_set_size (qtdemux->old_streams, 0);
2016 qtdemux->n_video_streams = 0;
2017 qtdemux->n_audio_streams = 0;
2018 qtdemux->n_sub_streams = 0;
2019 qtdemux->exposed = FALSE;
2020 qtdemux->fragmented = FALSE;
2021 qtdemux->mss_mode = FALSE;
2022 gst_caps_replace (&qtdemux->media_caps, NULL);
2023 qtdemux->timescale = 0;
2024 qtdemux->got_moov = FALSE;
2025 qtdemux->cenc_aux_info_offset = 0;
2026 qtdemux->cenc_aux_info_sizes = NULL;
2027 qtdemux->cenc_aux_sample_count = 0;
2028 if (qtdemux->protection_system_ids) {
2029 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2030 qtdemux->protection_system_ids = NULL;
2032 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2033 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2034 GST_BIN_FLAG_STREAMS_AWARE);
2036 if (qtdemux->preferred_protection_system_id) {
2037 g_free (qtdemux->preferred_protection_system_id);
2038 qtdemux->preferred_protection_system_id = NULL;
2040 } else if (qtdemux->mss_mode) {
2041 gst_flow_combiner_reset (qtdemux->flowcombiner);
2042 g_ptr_array_foreach (qtdemux->active_streams,
2043 (GFunc) gst_qtdemux_stream_clear, NULL);
2045 gst_flow_combiner_reset (qtdemux->flowcombiner);
2046 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2047 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2048 stream->sent_eos = FALSE;
2049 stream->time_position = 0;
2050 stream->accumulated_base = 0;
2051 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2057 /* Maps the @segment to the qt edts internal segments and pushes
2058 * the corresponding segment event.
2060 * If it ends up being at a empty segment, a gap will be pushed and the next
2061 * edts segment will be activated in sequence.
2063 * To be used in push-mode only */
2065 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2069 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2070 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2072 stream->time_position = segment->start;
2074 /* in push mode we should be guaranteed that we will have empty segments
2075 * at the beginning and then one segment after, other scenarios are not
2076 * supported and are discarded when parsing the edts */
2077 for (i = 0; i < stream->n_segments; i++) {
2078 if (stream->segments[i].stop_time > segment->start) {
2079 /* push the empty segment and move to the next one */
2080 gst_qtdemux_activate_segment (qtdemux, stream, i,
2081 stream->time_position);
2082 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2083 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2084 stream->time_position);
2086 /* accumulate previous segments */
2087 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2088 stream->accumulated_base +=
2089 (stream->segment.stop -
2090 stream->segment.start) / ABS (stream->segment.rate);
2094 g_assert (i == stream->n_segments - 1);
2101 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2112 for (i = 0; i < len; i++) {
2113 QtDemuxStream *stream = g_ptr_array_index (src, i);
2115 #ifndef GST_DISABLE_GST_DEBUG
2116 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2117 stream, GST_STR_NULL (stream->stream_id), dest);
2119 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2122 g_ptr_array_set_size (src, 0);
2126 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2129 GstQTDemux *demux = GST_QTDEMUX (parent);
2130 gboolean res = TRUE;
2132 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2134 switch (GST_EVENT_TYPE (event)) {
2135 case GST_EVENT_SEGMENT:
2138 QtDemuxStream *stream;
2142 /* some debug output */
2143 gst_event_copy_segment (event, &segment);
2144 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2147 if (segment.format == GST_FORMAT_TIME) {
2148 demux->upstream_format_is_time = TRUE;
2149 demux->segment_seqnum = gst_event_get_seqnum (event);
2151 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2152 "not in time format");
2154 /* chain will send initial newsegment after pads have been added */
2155 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2156 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2161 /* check if this matches a time seek we received previously
2162 * FIXME for backwards compatibility reasons we use the
2163 * seek_offset here to compare. In the future we might want to
2164 * change this to use the seqnum as it uniquely should identify
2165 * the segment that corresponds to the seek. */
2166 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2167 ", received segment offset %" G_GINT64_FORMAT,
2168 demux->seek_offset, segment.start);
2169 if (segment.format == GST_FORMAT_BYTES
2170 && demux->seek_offset == segment.start) {
2171 GST_OBJECT_LOCK (demux);
2172 offset = segment.start;
2174 segment.format = GST_FORMAT_TIME;
2175 segment.start = demux->push_seek_start;
2176 segment.stop = demux->push_seek_stop;
2177 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2178 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2179 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2180 GST_OBJECT_UNLOCK (demux);
2183 /* we only expect a BYTE segment, e.g. following a seek */
2184 if (segment.format == GST_FORMAT_BYTES) {
2185 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2186 offset = segment.start;
2188 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2189 NULL, (gint64 *) & segment.start);
2190 if ((gint64) segment.start < 0)
2193 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2194 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2195 NULL, (gint64 *) & segment.stop);
2196 /* keyframe seeking should already arrange for start >= stop,
2197 * but make sure in other rare cases */
2198 segment.stop = MAX (segment.stop, segment.start);
2200 } else if (segment.format == GST_FORMAT_TIME) {
2201 /* push all data on the adapter before starting this
2203 gst_qtdemux_process_adapter (demux, TRUE);
2205 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2209 /* We shouldn't modify upstream driven TIME FORMAT segment */
2210 if (!demux->upstream_format_is_time) {
2211 /* accept upstream's notion of segment and distribute along */
2212 segment.format = GST_FORMAT_TIME;
2213 segment.position = segment.time = segment.start;
2214 segment.duration = demux->segment.duration;
2215 segment.base = gst_segment_to_running_time (&demux->segment,
2216 GST_FORMAT_TIME, demux->segment.position);
2219 gst_segment_copy_into (&segment, &demux->segment);
2220 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2222 /* map segment to internal qt segments and push on each stream */
2223 if (QTDEMUX_N_STREAMS (demux)) {
2224 demux->need_segment = TRUE;
2225 gst_qtdemux_check_send_pending_segment (demux);
2228 /* clear leftover in current segment, if any */
2229 gst_adapter_clear (demux->adapter);
2231 /* set up streaming thread */
2232 demux->offset = offset;
2233 if (demux->upstream_format_is_time) {
2234 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2235 "set values to restart reading from a new atom");
2236 demux->neededbytes = 16;
2239 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2242 demux->todrop = stream->samples[idx].offset - offset;
2243 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2245 /* set up for EOS */
2246 demux->neededbytes = -1;
2251 gst_event_unref (event);
2255 case GST_EVENT_FLUSH_START:
2257 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2258 gst_event_unref (event);
2261 QTDEMUX_EXPOSE_LOCK (demux);
2262 res = gst_pad_event_default (demux->sinkpad, parent, event);
2263 QTDEMUX_EXPOSE_UNLOCK (demux);
2266 case GST_EVENT_FLUSH_STOP:
2270 dur = demux->segment.duration;
2271 gst_qtdemux_reset (demux, FALSE);
2272 demux->segment.duration = dur;
2274 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2275 gst_event_unref (event);
2281 /* If we are in push mode, and get an EOS before we've seen any streams,
2282 * then error out - we have nowhere to send the EOS */
2283 if (!demux->pullbased) {
2285 gboolean has_valid_stream = FALSE;
2286 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2287 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2288 has_valid_stream = TRUE;
2292 if (!has_valid_stream)
2293 gst_qtdemux_post_no_playable_stream_error (demux);
2295 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2296 (guint) gst_adapter_available (demux->adapter));
2297 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2303 case GST_EVENT_CAPS:{
2304 GstCaps *caps = NULL;
2306 gst_event_parse_caps (event, &caps);
2307 gst_qtdemux_setcaps (demux, caps);
2309 gst_event_unref (event);
2312 case GST_EVENT_PROTECTION:
2314 const gchar *system_id = NULL;
2316 gst_event_parse_protection (event, &system_id, NULL, NULL);
2317 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2319 gst_qtdemux_append_protection_system_id (demux, system_id);
2320 /* save the event for later, for source pads that have not been created */
2321 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2322 /* send it to all pads that already exist */
2323 gst_qtdemux_push_event (demux, event);
2327 case GST_EVENT_STREAM_START:
2330 gst_event_unref (event);
2332 /* Drain all the buffers */
2333 gst_qtdemux_process_adapter (demux, TRUE);
2334 gst_qtdemux_reset (demux, FALSE);
2335 /* We expect new moov box after new stream-start event */
2336 if (demux->exposed) {
2337 gst_qtdemux_stream_concat (demux,
2338 demux->old_streams, demux->active_streams);
2347 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2354 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2357 GstQTDemux *demux = GST_QTDEMUX (parent);
2358 gboolean res = FALSE;
2360 switch (GST_QUERY_TYPE (query)) {
2361 case GST_QUERY_BITRATE:
2363 GstClockTime duration;
2365 /* populate demux->upstream_size if not done yet */
2366 gst_qtdemux_check_seekability (demux);
2368 if (demux->upstream_size != -1
2369 && gst_qtdemux_get_duration (demux, &duration)) {
2371 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2374 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2375 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2376 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2378 /* TODO: better results based on ranges/index tables */
2379 gst_query_set_bitrate (query, bitrate);
2385 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2395 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2397 GstQTDemux *demux = GST_QTDEMUX (element);
2399 GST_OBJECT_LOCK (demux);
2400 if (demux->element_index)
2401 gst_object_unref (demux->element_index);
2403 demux->element_index = gst_object_ref (index);
2405 demux->element_index = NULL;
2407 GST_OBJECT_UNLOCK (demux);
2408 /* object lock might be taken again */
2410 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2411 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2412 demux->element_index, demux->index_id);
2416 gst_qtdemux_get_index (GstElement * element)
2418 GstIndex *result = NULL;
2419 GstQTDemux *demux = GST_QTDEMUX (element);
2421 GST_OBJECT_LOCK (demux);
2422 if (demux->element_index)
2423 result = gst_object_ref (demux->element_index);
2424 GST_OBJECT_UNLOCK (demux);
2426 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2433 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2435 g_free ((gpointer) stream->stco.data);
2436 stream->stco.data = NULL;
2437 g_free ((gpointer) stream->stsz.data);
2438 stream->stsz.data = NULL;
2439 g_free ((gpointer) stream->stsc.data);
2440 stream->stsc.data = NULL;
2441 g_free ((gpointer) stream->stts.data);
2442 stream->stts.data = NULL;
2443 g_free ((gpointer) stream->stss.data);
2444 stream->stss.data = NULL;
2445 g_free ((gpointer) stream->stps.data);
2446 stream->stps.data = NULL;
2447 g_free ((gpointer) stream->ctts.data);
2448 stream->ctts.data = NULL;
2452 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2454 g_free (stream->segments);
2455 stream->segments = NULL;
2456 stream->segment_index = -1;
2457 stream->accumulated_base = 0;
2461 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2463 g_free (stream->samples);
2464 stream->samples = NULL;
2465 gst_qtdemux_stbl_free (stream);
2468 g_free (stream->ra_entries);
2469 stream->ra_entries = NULL;
2470 stream->n_ra_entries = 0;
2472 stream->sample_index = -1;
2473 stream->stbl_index = -1;
2474 stream->n_samples = 0;
2475 stream->time_position = 0;
2477 stream->n_samples_moof = 0;
2478 stream->duration_moof = 0;
2479 stream->duration_last_moof = 0;
2483 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2486 if (stream->allocator)
2487 gst_object_unref (stream->allocator);
2488 while (stream->buffers) {
2489 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2490 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2492 for (i = 0; i < stream->stsd_entries_length; i++) {
2493 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2494 if (entry->rgb8_palette) {
2495 gst_memory_unref (entry->rgb8_palette);
2496 entry->rgb8_palette = NULL;
2498 entry->sparse = FALSE;
2501 if (stream->stream_tags)
2502 gst_tag_list_unref (stream->stream_tags);
2504 stream->stream_tags = gst_tag_list_new_empty ();
2505 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2506 g_free (stream->redirect_uri);
2507 stream->redirect_uri = NULL;
2508 stream->sent_eos = FALSE;
2509 stream->protected = FALSE;
2510 if (stream->protection_scheme_info) {
2511 if (stream->protection_scheme_type == FOURCC_cenc) {
2512 QtDemuxCencSampleSetInfo *info =
2513 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2514 if (info->default_properties)
2515 gst_structure_free (info->default_properties);
2516 if (info->crypto_info)
2517 g_ptr_array_free (info->crypto_info, TRUE);
2519 g_free (stream->protection_scheme_info);
2520 stream->protection_scheme_info = NULL;
2522 stream->protection_scheme_type = 0;
2523 stream->protection_scheme_version = 0;
2524 g_queue_foreach (&stream->protection_scheme_event_queue,
2525 (GFunc) gst_event_unref, NULL);
2526 g_queue_clear (&stream->protection_scheme_event_queue);
2527 gst_qtdemux_stream_flush_segments_data (stream);
2528 gst_qtdemux_stream_flush_samples_data (stream);
2532 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2535 gst_qtdemux_stream_clear (stream);
2536 for (i = 0; i < stream->stsd_entries_length; i++) {
2537 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2539 gst_caps_unref (entry->caps);
2543 g_free (stream->stsd_entries);
2544 stream->stsd_entries = NULL;
2545 stream->stsd_entries_length = 0;
2548 static QtDemuxStream *
2549 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2551 g_atomic_int_add (&stream->ref_count, 1);
2557 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2559 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2560 gst_qtdemux_stream_reset (stream);
2561 gst_tag_list_unref (stream->stream_tags);
2563 GstQTDemux *demux = stream->demux;
2564 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2565 GST_OBJECT_LOCK (demux);
2566 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2567 GST_OBJECT_UNLOCK (demux);
2569 g_free (stream->stream_id);
2574 static GstStateChangeReturn
2575 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2577 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2578 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2580 switch (transition) {
2581 case GST_STATE_CHANGE_READY_TO_PAUSED:
2582 gst_qtdemux_reset (qtdemux, TRUE);
2588 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2590 switch (transition) {
2591 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2592 gst_qtdemux_reset (qtdemux, TRUE);
2603 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2605 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2607 g_return_if_fail (GST_IS_CONTEXT (context));
2609 if (gst_context_has_context_type (context,
2610 "drm-preferred-decryption-system-id")) {
2611 const GstStructure *s;
2613 s = gst_context_get_structure (context);
2614 g_free (qtdemux->preferred_protection_system_id);
2615 qtdemux->preferred_protection_system_id =
2616 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2617 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2618 qtdemux->preferred_protection_system_id);
2621 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2625 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2627 /* counts as header data */
2628 qtdemux->header_size += length;
2630 /* only consider at least a sufficiently complete ftyp atom */
2634 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2635 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2636 GST_FOURCC_ARGS (qtdemux->major_brand));
2637 if (qtdemux->comp_brands)
2638 gst_buffer_unref (qtdemux->comp_brands);
2639 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2640 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2645 qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
2646 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
2649 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2650 gst_buffer_fill (kid_buf, 0, kid, 16);
2651 if (info->default_properties)
2652 gst_structure_free (info->default_properties);
2653 info->default_properties =
2654 gst_structure_new ("application/x-cenc",
2655 "iv_size", G_TYPE_UINT, iv_size,
2656 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2657 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2658 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
2659 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
2660 gst_buffer_unref (kid_buf);
2664 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
2665 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
2667 guint32 algorithm_id = 0;
2669 gboolean is_encrypted = TRUE;
2672 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2673 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2678 if (algorithm_id == 0) {
2679 is_encrypted = FALSE;
2680 } else if (algorithm_id == 1) {
2681 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2682 } else if (algorithm_id == 2) {
2683 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2686 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2689 if (!gst_byte_reader_get_data (br, 16, &kid))
2692 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
2693 is_encrypted, iv_size, kid);
2694 gst_structure_set (info->default_properties, "piff_algorithm_id",
2695 G_TYPE_UINT, algorithm_id, NULL);
2701 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
2709 QtDemuxStream *stream;
2710 GstStructure *structure;
2711 QtDemuxCencSampleSetInfo *ss_info = NULL;
2712 const gchar *system_id;
2713 gboolean uses_sub_sample_encryption = FALSE;
2714 guint32 sample_count;
2716 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
2719 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
2721 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
2722 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2723 GST_WARNING_OBJECT (qtdemux,
2724 "Attempting PIFF box parsing on an unencrypted stream.");
2728 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2729 G_TYPE_STRING, &system_id, NULL);
2730 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
2732 stream->protected = TRUE;
2733 stream->protection_scheme_type = FOURCC_cenc;
2735 if (!stream->protection_scheme_info)
2736 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
2738 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2739 if (!ss_info->default_properties) {
2740 ss_info->default_properties =
2741 gst_structure_new ("application/x-cenc",
2742 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2747 if (ss_info->crypto_info) {
2748 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2749 g_ptr_array_free (ss_info->crypto_info, TRUE);
2750 ss_info->crypto_info = NULL;
2754 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2756 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2757 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2761 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2762 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2766 if ((flags & 0x000001)) {
2767 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
2770 } else if ((flags & 0x000002)) {
2771 uses_sub_sample_encryption = TRUE;
2774 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2776 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2780 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2781 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2785 ss_info->crypto_info =
2786 g_ptr_array_new_full (sample_count,
2787 (GDestroyNotify) qtdemux_gst_structure_free);
2789 for (i = 0; i < sample_count; ++i) {
2790 GstStructure *properties;
2794 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
2795 if (properties == NULL) {
2796 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2797 qtdemux->cenc_aux_sample_count = i;
2801 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2802 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2803 gst_structure_free (properties);
2804 qtdemux->cenc_aux_sample_count = i;
2807 buf = gst_buffer_new_wrapped (data, iv_size);
2808 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2809 gst_buffer_unref (buf);
2811 if (uses_sub_sample_encryption) {
2812 guint16 n_subsamples;
2813 const GValue *kid_buf_value;
2815 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2816 || n_subsamples == 0) {
2817 GST_ERROR_OBJECT (qtdemux,
2818 "failed to get subsample count for sample %u", i);
2819 gst_structure_free (properties);
2820 qtdemux->cenc_aux_sample_count = i;
2823 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
2824 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
2825 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
2827 gst_structure_free (properties);
2828 qtdemux->cenc_aux_sample_count = i;
2831 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
2834 gst_structure_get_value (ss_info->default_properties, "kid");
2836 gst_structure_set (properties,
2837 "subsample_count", G_TYPE_UINT, n_subsamples,
2838 "subsamples", GST_TYPE_BUFFER, buf, NULL);
2839 gst_structure_set_value (properties, "kid", kid_buf_value);
2840 gst_buffer_unref (buf);
2842 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
2845 g_ptr_array_add (ss_info->crypto_info, properties);
2848 qtdemux->cenc_aux_sample_count = sample_count;
2852 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2854 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2855 0x97, 0xA9, 0x42, 0xE8,
2856 0x9C, 0x71, 0x99, 0x94,
2857 0x91, 0xE3, 0xAF, 0xAC
2859 static const guint8 playready_uuid[] = {
2860 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2861 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2864 static const guint8 piff_sample_encryption_uuid[] = {
2865 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
2866 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
2871 /* counts as header data */
2872 qtdemux->header_size += length;
2874 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2876 if (length <= offset + 16) {
2877 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2881 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2883 GstTagList *taglist;
2885 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
2886 length - offset - 16, NULL);
2887 taglist = gst_tag_list_from_xmp_buffer (buf);
2888 gst_buffer_unref (buf);
2890 /* make sure we have a usable taglist */
2891 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
2893 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
2895 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
2897 const gunichar2 *s_utf16;
2900 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
2901 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
2902 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
2903 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
2907 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
2908 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
2910 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
2911 qtdemux_parse_piff (qtdemux, buffer, length, offset);
2913 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
2914 GST_READ_UINT32_LE (buffer + offset),
2915 GST_READ_UINT32_LE (buffer + offset + 4),
2916 GST_READ_UINT32_LE (buffer + offset + 8),
2917 GST_READ_UINT32_LE (buffer + offset + 12));
2922 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2924 GstSidxParser sidx_parser;
2925 GstIsoffParserResult res;
2928 gst_isoff_qt_sidx_parser_init (&sidx_parser);
2931 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
2933 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
2934 if (res == GST_ISOFF_QT_PARSER_DONE) {
2935 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
2937 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
2940 /* caller verifies at least 8 bytes in buf */
2942 extract_initial_length_and_fourcc (const guint8 * data, guint size,
2943 guint64 * plength, guint32 * pfourcc)
2948 length = QT_UINT32 (data);
2949 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2950 fourcc = QT_FOURCC (data + 4);
2951 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
2954 length = G_MAXUINT64;
2955 } else if (length == 1 && size >= 16) {
2956 /* this means we have an extended size, which is the 64 bit value of
2957 * the next 8 bytes */
2958 length = QT_UINT64 (data + 8);
2959 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2969 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
2971 guint32 version = 0;
2972 GstClockTime duration = 0;
2974 if (!gst_byte_reader_get_uint32_be (br, &version))
2979 if (!gst_byte_reader_get_uint64_be (br, &duration))
2984 if (!gst_byte_reader_get_uint32_be (br, &dur))
2989 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
2990 qtdemux->duration = duration;
2996 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3002 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3003 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3005 if (!stream->parsed_trex && qtdemux->moov_node) {
3007 GstByteReader trex_data;
3009 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3011 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3014 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3016 /* skip version/flags */
3017 if (!gst_byte_reader_skip (&trex_data, 4))
3019 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3021 if (id != stream->track_id)
3023 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3025 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3027 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3029 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3032 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3033 "duration %d, size %d, flags 0x%x", stream->track_id,
3036 stream->parsed_trex = TRUE;
3037 stream->def_sample_description_index = sdi;
3038 stream->def_sample_duration = dur;
3039 stream->def_sample_size = size;
3040 stream->def_sample_flags = flags;
3043 /* iterate all siblings */
3044 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3050 *ds_duration = stream->def_sample_duration;
3051 *ds_size = stream->def_sample_size;
3052 *ds_flags = stream->def_sample_flags;
3054 /* even then, above values are better than random ... */
3055 if (G_UNLIKELY (!stream->parsed_trex)) {
3056 GST_WARNING_OBJECT (qtdemux,
3057 "failed to find fragment defaults for stream %d", stream->track_id);
3064 /* This method should be called whenever a more accurate duration might
3065 * have been found. It will update all relevant variables if/where needed
3068 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3072 GstClockTime prevdur;
3074 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3076 if (movdur > qtdemux->duration) {
3077 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3078 GST_DEBUG_OBJECT (qtdemux,
3079 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3080 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3081 qtdemux->duration = movdur;
3082 GST_DEBUG_OBJECT (qtdemux,
3083 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3084 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3085 GST_TIME_ARGS (qtdemux->segment.stop));
3086 if (qtdemux->segment.duration == prevdur) {
3087 /* If the current segment has duration/stop identical to previous duration
3088 * update them also (because they were set at that point in time with
3089 * the wrong duration */
3090 /* We convert the value *from* the timescale version to avoid rounding errors */
3091 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3092 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3093 qtdemux->segment.duration = fixeddur;
3094 qtdemux->segment.stop = fixeddur;
3098 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3099 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3101 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3102 if (movdur > stream->duration) {
3103 GST_DEBUG_OBJECT (qtdemux,
3104 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3105 GST_TIME_ARGS (duration));
3106 stream->duration = movdur;
3107 /* internal duration tracking state has been updated above, so */
3108 /* preserve an open-ended dummy segment rather than repeatedly updating
3109 * it and spamming downstream accordingly with segment events */
3110 if (stream->dummy_segment &&
3111 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3112 /* Update all dummy values to new duration */
3113 stream->segments[0].stop_time = duration;
3114 stream->segments[0].duration = duration;
3115 stream->segments[0].media_stop = duration;
3117 /* let downstream know we possibly have a new stop time */
3118 if (stream->segment_index != -1) {
3121 if (qtdemux->segment.rate >= 0) {
3122 pos = stream->segment.start;
3124 pos = stream->segment.stop;
3127 gst_qtdemux_stream_update_segment (qtdemux, stream,
3128 stream->segment_index, pos, NULL, NULL);
3136 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3137 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3138 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3139 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3142 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3144 gint32 data_offset = 0;
3145 guint32 flags = 0, first_flags = 0, samples_count = 0;
3148 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3149 QtDemuxSample *sample;
3150 gboolean ismv = FALSE;
3151 gint64 initial_offset;
3153 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3154 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3155 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3156 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3158 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3159 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3163 /* presence of stss or not can't really tell us much,
3164 * and flags and so on tend to be marginally reliable in these files */
3165 if (stream->subtype == FOURCC_soun) {
3166 GST_DEBUG_OBJECT (qtdemux,
3167 "sound track in fragmented file; marking all keyframes");
3168 stream->all_keyframe = TRUE;
3171 if (!gst_byte_reader_skip (trun, 1) ||
3172 !gst_byte_reader_get_uint24_be (trun, &flags))
3175 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3178 if (flags & TR_DATA_OFFSET) {
3179 /* note this is really signed */
3180 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3182 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3183 /* default base offset = first byte of moof */
3184 if (*base_offset == -1) {
3185 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3186 *base_offset = moof_offset;
3188 *running_offset = *base_offset + data_offset;
3190 /* if no offset at all, that would mean data starts at moof start,
3191 * which is a bit wrong and is ismv crappy way, so compensate
3192 * assuming data is in mdat following moof */
3193 if (*base_offset == -1) {
3194 *base_offset = moof_offset + moof_length + 8;
3195 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3198 if (*running_offset == -1)
3199 *running_offset = *base_offset;
3202 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3204 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3205 data_offset, flags, samples_count);
3207 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3208 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3209 GST_DEBUG_OBJECT (qtdemux,
3210 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3211 flags ^= TR_FIRST_SAMPLE_FLAGS;
3213 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3215 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3219 /* FIXME ? spec says other bits should also be checked to determine
3220 * entry size (and prefix size for that matter) */
3222 dur_offset = size_offset = 0;
3223 if (flags & TR_SAMPLE_DURATION) {
3224 GST_LOG_OBJECT (qtdemux, "entry duration present");
3225 dur_offset = entry_size;
3228 if (flags & TR_SAMPLE_SIZE) {
3229 GST_LOG_OBJECT (qtdemux, "entry size present");
3230 size_offset = entry_size;
3233 if (flags & TR_SAMPLE_FLAGS) {
3234 GST_LOG_OBJECT (qtdemux, "entry flags present");
3235 flags_offset = entry_size;
3238 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3239 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3240 ct_offset = entry_size;
3244 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3246 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3248 if (stream->n_samples + samples_count >=
3249 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3252 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3253 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3254 (stream->n_samples + samples_count) *
3255 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3257 /* create a new array of samples if it's the first sample parsed */
3258 if (stream->n_samples == 0) {
3259 g_assert (stream->samples == NULL);
3260 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3261 /* or try to reallocate it with space enough to insert the new samples */
3263 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3264 stream->n_samples + samples_count);
3265 if (stream->samples == NULL)
3268 if (qtdemux->fragment_start != -1) {
3269 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3270 qtdemux->fragment_start = -1;
3272 if (stream->n_samples == 0) {
3273 if (decode_ts > 0) {
3274 timestamp = decode_ts;
3275 } else if (stream->pending_seek != NULL) {
3276 /* if we don't have a timestamp from a tfdt box, we'll use the one
3277 * from the mfra seek table */
3278 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3279 GST_TIME_ARGS (stream->pending_seek->ts));
3281 /* FIXME: this is not fully correct, the timestamp refers to the random
3282 * access sample refered to in the tfra entry, which may not necessarily
3283 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3284 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3289 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3290 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3291 GST_TIME_ARGS (gst_ts));
3293 /* subsequent fragments extend stream */
3295 stream->samples[stream->n_samples - 1].timestamp +
3296 stream->samples[stream->n_samples - 1].duration;
3298 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3299 * difference (1 sec.) between decode_ts and timestamp, prefer the
3301 if (has_tfdt && !qtdemux->upstream_format_is_time
3302 && ABSDIFF (decode_ts, timestamp) >
3303 MAX (stream->duration_last_moof / 2,
3304 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3305 GST_INFO_OBJECT (qtdemux,
3306 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3307 ") are significantly different (more than %" GST_TIME_FORMAT
3308 "), using decode_ts",
3309 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3310 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3311 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3312 MAX (stream->duration_last_moof / 2,
3313 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3314 timestamp = decode_ts;
3317 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3318 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3319 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3323 initial_offset = *running_offset;
3325 sample = stream->samples + stream->n_samples;
3326 for (i = 0; i < samples_count; i++) {
3327 guint32 dur, size, sflags, ct;
3329 /* first read sample data */
3330 if (flags & TR_SAMPLE_DURATION) {
3331 dur = QT_UINT32 (data + dur_offset);
3333 dur = d_sample_duration;
3335 if (flags & TR_SAMPLE_SIZE) {
3336 size = QT_UINT32 (data + size_offset);
3338 size = d_sample_size;
3340 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3342 sflags = first_flags;
3344 sflags = d_sample_flags;
3346 } else if (flags & TR_SAMPLE_FLAGS) {
3347 sflags = QT_UINT32 (data + flags_offset);
3349 sflags = d_sample_flags;
3351 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3352 ct = QT_UINT32 (data + ct_offset);
3358 /* fill the sample information */
3359 sample->offset = *running_offset;
3360 sample->pts_offset = ct;
3361 sample->size = size;
3362 sample->timestamp = timestamp;
3363 sample->duration = dur;
3364 /* sample-is-difference-sample */
3365 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3366 * now idea how it relates to bitfield other than massive LE/BE confusion */
3367 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3368 *running_offset += size;
3370 stream->duration_moof += dur;
3374 /* Update total duration if needed */
3375 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3377 /* Pre-emptively figure out size of mdat based on trun information.
3378 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3379 * size, else we will still be able to use this when dealing with gap'ed
3381 qtdemux->mdatleft = *running_offset - initial_offset;
3382 qtdemux->mdatoffset = initial_offset;
3383 qtdemux->mdatsize = qtdemux->mdatleft;
3385 stream->n_samples += samples_count;
3386 stream->n_samples_moof += samples_count;
3388 if (stream->pending_seek != NULL)
3389 stream->pending_seek = NULL;
3395 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3400 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3406 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3407 "be larger than %uMB (broken file?)", stream->n_samples,
3408 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3413 /* find stream with @id */
3414 static inline QtDemuxStream *
3415 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3417 QtDemuxStream *stream;
3421 if (G_UNLIKELY (!id)) {
3422 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3426 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3427 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3428 if (stream->track_id == id)
3431 if (qtdemux->mss_mode) {
3432 /* mss should have only 1 stream anyway */
3433 return QTDEMUX_NTH_STREAM (qtdemux, 0);
3440 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3441 guint32 * fragment_number)
3443 if (!gst_byte_reader_skip (mfhd, 4))
3445 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3450 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3456 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3457 QtDemuxStream ** stream, guint32 * default_sample_duration,
3458 guint32 * default_sample_size, guint32 * default_sample_flags,
3459 gint64 * base_offset)
3462 guint32 track_id = 0;
3464 if (!gst_byte_reader_skip (tfhd, 1) ||
3465 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3468 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3471 *stream = qtdemux_find_stream (qtdemux, track_id);
3472 if (G_UNLIKELY (!*stream))
3473 goto unknown_stream;
3475 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3476 *base_offset = qtdemux->moof_offset;
3478 if (flags & TF_BASE_DATA_OFFSET)
3479 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3482 /* obtain stream defaults */
3483 qtdemux_parse_trex (qtdemux, *stream,
3484 default_sample_duration, default_sample_size, default_sample_flags);
3486 (*stream)->stsd_sample_description_id =
3487 (*stream)->def_sample_description_index - 1;
3489 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
3490 guint32 sample_description_index;
3491 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3493 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3496 if (qtdemux->mss_mode) {
3497 /* mss has no stsd entry */
3498 (*stream)->stsd_sample_description_id = 0;
3501 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3502 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3505 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3506 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3509 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3510 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3517 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3522 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
3528 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3529 guint64 * decode_time)
3531 guint32 version = 0;
3533 if (!gst_byte_reader_get_uint32_be (br, &version))
3538 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3541 guint32 dec_time = 0;
3542 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3544 *decode_time = dec_time;
3547 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3554 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3559 /* Returns a pointer to a GstStructure containing the properties of
3560 * the stream sample identified by @sample_index. The caller must unref
3561 * the returned object after use. Returns NULL if unsuccessful. */
3562 static GstStructure *
3563 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3564 QtDemuxStream * stream, guint sample_index)
3566 QtDemuxCencSampleSetInfo *info = NULL;
3568 g_return_val_if_fail (stream != NULL, NULL);
3569 g_return_val_if_fail (stream->protected, NULL);
3570 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3572 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3574 /* Currently, cenc properties for groups of samples are not supported, so
3575 * simply return a copy of the default sample properties */
3576 return gst_structure_copy (info->default_properties);
3579 /* Parses the sizes of sample auxiliary information contained within a stream,
3580 * as given in a saiz box. Returns array of sample_count guint8 size values,
3581 * or NULL on failure */
3583 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3584 GstByteReader * br, guint32 * sample_count)
3588 guint8 default_info_size;
3590 g_return_val_if_fail (qtdemux != NULL, NULL);
3591 g_return_val_if_fail (stream != NULL, NULL);
3592 g_return_val_if_fail (br != NULL, NULL);
3593 g_return_val_if_fail (sample_count != NULL, NULL);
3595 if (!gst_byte_reader_get_uint32_be (br, &flags))
3599 /* aux_info_type and aux_info_type_parameter are ignored */
3600 if (!gst_byte_reader_skip (br, 8))
3604 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3606 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3608 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3610 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3613 if (default_info_size == 0) {
3614 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3618 info_sizes = g_new (guint8, *sample_count);
3619 memset (info_sizes, default_info_size, *sample_count);
3625 /* Parses the offset of sample auxiliary information contained within a stream,
3626 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3628 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3629 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3634 guint32 aux_info_type = 0;
3635 guint32 aux_info_type_parameter = 0;
3636 guint32 entry_count;
3639 const guint8 *aux_info_type_data = NULL;
3641 g_return_val_if_fail (qtdemux != NULL, FALSE);
3642 g_return_val_if_fail (stream != NULL, FALSE);
3643 g_return_val_if_fail (br != NULL, FALSE);
3644 g_return_val_if_fail (offset != NULL, FALSE);
3646 if (!gst_byte_reader_get_uint8 (br, &version))
3649 if (!gst_byte_reader_get_uint24_be (br, &flags))
3654 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3656 aux_info_type = QT_FOURCC (aux_info_type_data);
3658 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3660 } else if (stream->protected) {
3661 aux_info_type = stream->protection_scheme_type;
3663 aux_info_type = CUR_STREAM (stream)->fourcc;
3667 *info_type = aux_info_type;
3668 if (info_type_parameter)
3669 *info_type_parameter = aux_info_type_parameter;
3671 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3672 "aux_info_type_parameter: %#06x",
3673 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3675 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3678 if (entry_count != 1) {
3679 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3684 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3686 *offset = (guint64) off_32;
3688 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3693 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3698 qtdemux_gst_structure_free (GstStructure * gststructure)
3701 gst_structure_free (gststructure);
3705 /* Parses auxiliary information relating to samples protected using Common
3706 * Encryption (cenc); the format of this information is defined in
3707 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
3709 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3710 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3712 QtDemuxCencSampleSetInfo *ss_info = NULL;
3715 GPtrArray *old_crypto_info = NULL;
3716 guint old_entries = 0;
3718 g_return_val_if_fail (qtdemux != NULL, FALSE);
3719 g_return_val_if_fail (stream != NULL, FALSE);
3720 g_return_val_if_fail (br != NULL, FALSE);
3721 g_return_val_if_fail (stream->protected, FALSE);
3722 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3724 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3726 if (ss_info->crypto_info) {
3727 old_crypto_info = ss_info->crypto_info;
3728 /* Count number of non-null entries remaining at the tail end */
3729 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3730 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3736 ss_info->crypto_info =
3737 g_ptr_array_new_full (sample_count + old_entries,
3738 (GDestroyNotify) qtdemux_gst_structure_free);
3740 /* We preserve old entries because we parse the next moof in advance
3741 * of consuming all samples from the previous moof, and otherwise
3742 * we'd discard the corresponding crypto info for the samples
3743 * from the previous fragment. */
3745 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3747 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3748 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3750 g_ptr_array_index (old_crypto_info, i) = NULL;
3754 if (old_crypto_info) {
3755 /* Everything now belongs to the new array */
3756 g_ptr_array_free (old_crypto_info, TRUE);
3759 for (i = 0; i < sample_count; ++i) {
3760 GstStructure *properties;
3761 guint16 n_subsamples = 0;
3766 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3767 if (properties == NULL) {
3768 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3771 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3772 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3773 gst_structure_free (properties);
3776 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
3777 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3778 gst_structure_free (properties);
3781 buf = gst_buffer_new_wrapped (data, iv_size);
3782 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3783 gst_buffer_unref (buf);
3784 size = info_sizes[i];
3785 if (size > iv_size) {
3786 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3787 || !(n_subsamples > 0)) {
3788 gst_structure_free (properties);
3789 GST_ERROR_OBJECT (qtdemux,
3790 "failed to get subsample count for sample %u", i);
3793 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3794 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3795 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3797 gst_structure_free (properties);
3800 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3802 gst_structure_free (properties);
3805 gst_structure_set (properties,
3806 "subsample_count", G_TYPE_UINT, n_subsamples,
3807 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3808 gst_buffer_unref (buf);
3810 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3812 g_ptr_array_add (ss_info->crypto_info, properties);
3817 /* Converts a UUID in raw byte form to a string representation, as defined in
3818 * RFC 4122. The caller takes ownership of the returned string and is
3819 * responsible for freeing it after use. */
3821 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
3823 const guint8 *uuid = (const guint8 *) uuid_bytes;
3825 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3826 "%02x%02x-%02x%02x%02x%02x%02x%02x",
3827 uuid[0], uuid[1], uuid[2], uuid[3],
3828 uuid[4], uuid[5], uuid[6], uuid[7],
3829 uuid[8], uuid[9], uuid[10], uuid[11],
3830 uuid[12], uuid[13], uuid[14], uuid[15]);
3833 /* Parses a Protection System Specific Header box (pssh), as defined in the
3834 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
3835 * information needed by a specific content protection system in order to
3836 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
3839 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
3841 gchar *sysid_string;
3842 guint32 pssh_size = QT_UINT32 (node->data);
3843 GstBuffer *pssh = NULL;
3844 GstEvent *event = NULL;
3845 guint32 parent_box_type;
3848 if (G_UNLIKELY (pssh_size < 32U)) {
3849 GST_ERROR_OBJECT (qtdemux, "invalid box size");
3854 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
3856 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
3858 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
3859 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
3860 gst_buffer_get_size (pssh));
3862 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
3864 /* Push an event containing the pssh box onto the queues of all streams. */
3865 event = gst_event_new_protection (sysid_string, pssh,
3866 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
3867 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3868 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3869 GST_TRACE_OBJECT (qtdemux,
3870 "adding protection event for stream %s and system %s",
3871 stream->stream_id, sysid_string);
3872 g_queue_push_tail (&stream->protection_scheme_event_queue,
3873 gst_event_ref (event));
3875 g_free (sysid_string);
3876 gst_event_unref (event);
3877 gst_buffer_unref (pssh);
3882 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
3883 guint64 moof_offset, QtDemuxStream * stream)
3885 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
3887 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
3888 GNode *saiz_node, *saio_node, *pssh_node;
3889 GstByteReader saiz_data, saio_data;
3890 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
3891 gint64 base_offset, running_offset;
3893 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
3895 /* NOTE @stream ignored */
3897 moof_node = g_node_new ((guint8 *) buffer);
3898 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
3899 qtdemux_node_dump (qtdemux, moof_node);
3901 /* Get fragment number from mfhd and check it's valid */
3903 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
3904 if (mfhd_node == NULL)
3906 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
3908 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
3910 /* unknown base_offset to start with */
3911 base_offset = running_offset = -1;
3912 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
3914 guint64 decode_time = 0;
3916 /* Fragment Header node */
3918 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
3922 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
3923 &ds_size, &ds_flags, &base_offset))
3926 /* The following code assumes at most a single set of sample auxiliary
3927 * data in the fragment (consisting of a saiz box and a corresponding saio
3928 * box); in theory, however, there could be multiple sets of sample
3929 * auxiliary data in a fragment. */
3931 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
3934 guint32 info_type = 0;
3936 guint32 info_type_parameter = 0;
3938 g_free (qtdemux->cenc_aux_info_sizes);
3940 qtdemux->cenc_aux_info_sizes =
3941 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
3942 &qtdemux->cenc_aux_sample_count);
3943 if (qtdemux->cenc_aux_info_sizes == NULL) {
3944 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
3948 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
3951 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
3952 g_free (qtdemux->cenc_aux_info_sizes);
3953 qtdemux->cenc_aux_info_sizes = NULL;
3957 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
3958 &info_type, &info_type_parameter, &offset))) {
3959 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
3960 g_free (qtdemux->cenc_aux_info_sizes);
3961 qtdemux->cenc_aux_info_sizes = NULL;
3964 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
3965 offset += (guint64) (base_offset - qtdemux->moof_offset);
3966 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
3968 if (offset > length) {
3969 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
3970 qtdemux->cenc_aux_info_offset = offset;
3972 gst_byte_reader_init (&br, buffer + offset, length - offset);
3973 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
3974 qtdemux->cenc_aux_info_sizes,
3975 qtdemux->cenc_aux_sample_count)) {
3976 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
3977 g_free (qtdemux->cenc_aux_info_sizes);
3978 qtdemux->cenc_aux_info_sizes = NULL;
3986 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
3989 /* We'll use decode_time to interpolate timestamps
3990 * in case the input timestamps are missing */
3991 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
3993 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
3994 " (%" GST_TIME_FORMAT ")", decode_time,
3995 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
3996 decode_time) : GST_CLOCK_TIME_NONE));
3998 /* Discard the fragment buffer timestamp info to avoid using it.
3999 * Rely on tfdt instead as it is more accurate than the timestamp
4000 * that is fetched from a manifest/playlist and is usually
4002 qtdemux->fragment_start = -1;
4005 if (G_UNLIKELY (!stream)) {
4006 /* we lost track of offset, we'll need to regain it,
4007 * but can delay complaining until later or avoid doing so altogether */
4011 if (G_UNLIKELY (base_offset < -1))
4014 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4016 if (!qtdemux->pullbased) {
4017 /* Sample tables can grow enough to be problematic if the system memory
4018 * is very low (e.g. embedded devices) and the videos very long
4019 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4020 * Fortunately, we can easily discard them for each new fragment when
4021 * we know qtdemux will not receive seeks outside of the current fragment.
4022 * adaptivedemux honors this assumption.
4023 * This optimization is also useful for applications that use qtdemux as
4024 * a push-based simple demuxer, like Media Source Extensions. */
4025 gst_qtdemux_stream_flush_samples_data (stream);
4028 /* initialise moof sample data */
4029 stream->n_samples_moof = 0;
4030 stream->duration_last_moof = stream->duration_moof;
4031 stream->duration_moof = 0;
4033 /* Track Run node */
4035 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4038 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4039 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4040 &running_offset, decode_time, (tfdt_node != NULL));
4041 /* iterate all siblings */
4042 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4046 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4048 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4049 guint32 box_length = QT_UINT32 (uuid_buffer);
4051 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4054 /* if no new base_offset provided for next traf,
4055 * base is end of current traf */
4056 base_offset = running_offset;
4057 running_offset = -1;
4059 if (stream->n_samples_moof && stream->duration_moof)
4060 stream->new_caps = TRUE;
4063 /* iterate all siblings */
4064 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4067 /* parse any protection system info */
4068 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4070 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4071 qtdemux_parse_pssh (qtdemux, pssh_node);
4072 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4075 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4076 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4078 /* Unless the user has explicitly requested another seek, perform an
4079 * internal seek to the time specified in the tfdt.
4081 * This way if the user opens a file where the first tfdt is 1 hour
4082 * into the presentation, they will not have to wait 1 hour for run
4083 * time to catch up and actual playback to start. */
4086 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4087 "performing an internal seek to %" GST_TIME_FORMAT,
4088 GST_TIME_ARGS (min_dts));
4090 qtdemux->segment.start = min_dts;
4091 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4093 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4094 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4095 stream->time_position = min_dts;
4098 /* Before this code was run a segment was already sent when the moov was
4099 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4100 * be emitted after a moov, and we can emit a second segment anyway for
4101 * special cases like this. */
4102 qtdemux->need_segment = TRUE;
4105 qtdemux->first_moof_already_parsed = TRUE;
4107 g_node_destroy (moof_node);
4112 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4117 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4122 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4127 g_node_destroy (moof_node);
4128 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4129 (_("This file is corrupt and cannot be played.")), (NULL));
4135 /* might be used if some day we actually use mfra & co
4136 * for random access to fragments,
4137 * but that will require quite some modifications and much less relying
4138 * on a sample array */
4142 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4144 QtDemuxStream *stream;
4145 guint32 ver_flags, track_id, len, num_entries, i;
4146 guint value_size, traf_size, trun_size, sample_size;
4147 guint64 time = 0, moof_offset = 0;
4149 GstBuffer *buf = NULL;
4154 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4156 if (!gst_byte_reader_skip (&tfra, 8))
4159 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4162 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4163 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4164 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4167 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4169 stream = qtdemux_find_stream (qtdemux, track_id);
4171 goto unknown_trackid;
4173 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4174 sample_size = (len & 3) + 1;
4175 trun_size = ((len & 12) >> 2) + 1;
4176 traf_size = ((len & 48) >> 4) + 1;
4178 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4179 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4181 if (num_entries == 0)
4184 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4185 value_size + value_size + traf_size + trun_size + sample_size))
4188 g_free (stream->ra_entries);
4189 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4190 stream->n_ra_entries = num_entries;
4192 for (i = 0; i < num_entries; i++) {
4193 qt_atom_parser_get_offset (&tfra, value_size, &time);
4194 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4195 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4196 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4197 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4199 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4201 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4202 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4204 stream->ra_entries[i].ts = time;
4205 stream->ra_entries[i].moof_offset = moof_offset;
4207 /* don't want to go through the entire file and read all moofs at startup */
4209 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4210 if (ret != GST_FLOW_OK)
4212 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4213 moof_offset, stream);
4214 gst_buffer_unref (buf);
4218 check_update_duration (qtdemux, time);
4225 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4230 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4235 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4241 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4243 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4244 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4245 GstBuffer *mfro = NULL, *mfra = NULL;
4247 gboolean ret = FALSE;
4248 GNode *mfra_node, *tfra_node;
4249 guint64 mfra_offset = 0;
4250 guint32 fourcc, mfra_size;
4253 /* query upstream size in bytes */
4254 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4255 goto size_query_failed;
4257 /* mfro box should be at the very end of the file */
4258 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4259 if (flow != GST_FLOW_OK)
4262 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4264 fourcc = QT_FOURCC (mfro_map.data + 4);
4265 if (fourcc != FOURCC_mfro)
4268 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4269 if (mfro_map.size < 16)
4270 goto invalid_mfro_size;
4272 mfra_size = QT_UINT32 (mfro_map.data + 12);
4273 if (mfra_size >= len)
4274 goto invalid_mfra_size;
4276 mfra_offset = len - mfra_size;
4278 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4279 mfra_offset, mfra_size);
4281 /* now get and parse mfra box */
4282 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4283 if (flow != GST_FLOW_OK)
4286 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4288 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4289 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4291 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4294 qtdemux_parse_tfra (qtdemux, tfra_node);
4295 /* iterate all siblings */
4296 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4298 g_node_destroy (mfra_node);
4300 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4306 if (mfro_map.memory != NULL)
4307 gst_buffer_unmap (mfro, &mfro_map);
4308 gst_buffer_unref (mfro);
4311 if (mfra_map.memory != NULL)
4312 gst_buffer_unmap (mfra, &mfra_map);
4313 gst_buffer_unref (mfra);
4320 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4325 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4330 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4335 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4341 add_offset (guint64 offset, guint64 advance)
4343 /* Avoid 64-bit overflow by clamping */
4344 if (offset > G_MAXUINT64 - advance)
4346 return offset + advance;
4349 static GstFlowReturn
4350 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4354 GstBuffer *buf = NULL;
4355 GstFlowReturn ret = GST_FLOW_OK;
4356 guint64 cur_offset = qtdemux->offset;
4359 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4360 if (G_UNLIKELY (ret != GST_FLOW_OK))
4362 gst_buffer_map (buf, &map, GST_MAP_READ);
4363 if (G_LIKELY (map.size >= 8))
4364 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4365 gst_buffer_unmap (buf, &map);
4366 gst_buffer_unref (buf);
4368 /* maybe we already got most we needed, so only consider this eof */
4369 if (G_UNLIKELY (length == 0)) {
4370 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4371 (_("Invalid atom size.")),
4372 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4373 GST_FOURCC_ARGS (fourcc)));
4380 /* record for later parsing when needed */
4381 if (!qtdemux->moof_offset) {
4382 qtdemux->moof_offset = qtdemux->offset;
4384 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4387 qtdemux->offset += length; /* skip moof and keep going */
4389 if (qtdemux->got_moov) {
4390 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4402 GST_LOG_OBJECT (qtdemux,
4403 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4404 GST_FOURCC_ARGS (fourcc), cur_offset);
4405 qtdemux->offset = add_offset (qtdemux->offset, length);
4410 GstBuffer *moov = NULL;
4412 if (qtdemux->got_moov) {
4413 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4414 qtdemux->offset = add_offset (qtdemux->offset, length);
4418 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4419 if (ret != GST_FLOW_OK)
4421 gst_buffer_map (moov, &map, GST_MAP_READ);
4423 if (length != map.size) {
4424 /* Some files have a 'moov' atom at the end of the file which contains
4425 * a terminal 'free' atom where the body of the atom is missing.
4426 * Check for, and permit, this special case.
4428 if (map.size >= 8) {
4429 guint8 *final_data = map.data + (map.size - 8);
4430 guint32 final_length = QT_UINT32 (final_data);
4431 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4433 if (final_fourcc == FOURCC_free
4434 && map.size + final_length - 8 == length) {
4435 /* Ok, we've found that special case. Allocate a new buffer with
4436 * that free atom actually present. */
4437 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4438 gst_buffer_fill (newmoov, 0, map.data, map.size);
4439 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4440 gst_buffer_unmap (moov, &map);
4441 gst_buffer_unref (moov);
4443 gst_buffer_map (moov, &map, GST_MAP_READ);
4448 if (length != map.size) {
4449 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4450 (_("This file is incomplete and cannot be played.")),
4451 ("We got less than expected (received %" G_GSIZE_FORMAT
4452 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4453 (guint) length, cur_offset));
4454 gst_buffer_unmap (moov, &map);
4455 gst_buffer_unref (moov);
4456 ret = GST_FLOW_ERROR;
4459 qtdemux->offset += length;
4461 qtdemux_parse_moov (qtdemux, map.data, length);
4462 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4464 qtdemux_parse_tree (qtdemux);
4465 if (qtdemux->moov_node_compressed) {
4466 g_node_destroy (qtdemux->moov_node_compressed);
4467 g_free (qtdemux->moov_node->data);
4469 qtdemux->moov_node_compressed = NULL;
4470 g_node_destroy (qtdemux->moov_node);
4471 qtdemux->moov_node = NULL;
4472 gst_buffer_unmap (moov, &map);
4473 gst_buffer_unref (moov);
4474 qtdemux->got_moov = TRUE;
4480 GstBuffer *ftyp = NULL;
4482 /* extract major brand; might come in handy for ISO vs QT issues */
4483 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4484 if (ret != GST_FLOW_OK)
4486 qtdemux->offset += length;
4487 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4488 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4489 gst_buffer_unmap (ftyp, &map);
4490 gst_buffer_unref (ftyp);
4495 GstBuffer *uuid = NULL;
4497 /* uuid are extension atoms */
4498 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4499 if (ret != GST_FLOW_OK)
4501 qtdemux->offset += length;
4502 gst_buffer_map (uuid, &map, GST_MAP_READ);
4503 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4504 gst_buffer_unmap (uuid, &map);
4505 gst_buffer_unref (uuid);
4510 GstBuffer *sidx = NULL;
4511 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4512 if (ret != GST_FLOW_OK)
4514 qtdemux->offset += length;
4515 gst_buffer_map (sidx, &map, GST_MAP_READ);
4516 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4517 gst_buffer_unmap (sidx, &map);
4518 gst_buffer_unref (sidx);
4523 GstBuffer *unknown = NULL;
4525 GST_LOG_OBJECT (qtdemux,
4526 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4527 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4529 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4530 if (ret != GST_FLOW_OK)
4532 gst_buffer_map (unknown, &map, GST_MAP_READ);
4533 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4534 gst_buffer_unmap (unknown, &map);
4535 gst_buffer_unref (unknown);
4536 qtdemux->offset += length;
4542 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4543 /* digested all data, show what we have */
4544 qtdemux_prepare_streams (qtdemux);
4545 QTDEMUX_EXPOSE_LOCK (qtdemux);
4546 ret = qtdemux_expose_streams (qtdemux);
4547 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
4549 qtdemux->state = QTDEMUX_STATE_MOVIE;
4550 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4557 /* Seeks to the previous keyframe of the indexed stream and
4558 * aligns other streams with respect to the keyframe timestamp
4559 * of indexed stream. Only called in case of Reverse Playback
4561 static GstFlowReturn
4562 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4564 guint32 seg_idx = 0, k_index = 0;
4565 guint32 ref_seg_idx, ref_k_index;
4566 GstClockTime k_pos = 0, last_stop = 0;
4567 QtDemuxSegment *seg = NULL;
4568 QtDemuxStream *ref_str = NULL;
4569 guint64 seg_media_start_mov; /* segment media start time in mov format */
4573 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4574 * and finally align all the other streams on that timestamp with their
4575 * respective keyframes */
4576 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4577 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4579 /* No candidate yet, take the first stream */
4585 /* So that stream has a segment, we prefer video streams */
4586 if (str->subtype == FOURCC_vide) {
4592 if (G_UNLIKELY (!ref_str)) {
4593 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4597 if (G_UNLIKELY (!ref_str->from_sample)) {
4598 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4602 /* So that stream has been playing from from_sample to to_sample. We will
4603 * get the timestamp of the previous sample and search for a keyframe before
4604 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4605 if (ref_str->subtype == FOURCC_vide) {
4606 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4607 ref_str->from_sample - 1, FALSE);
4609 if (ref_str->from_sample >= 10)
4610 k_index = ref_str->from_sample - 10;
4616 ref_str->samples[k_index].timestamp +
4617 ref_str->samples[k_index].pts_offset;
4619 /* get current segment for that stream */
4620 seg = &ref_str->segments[ref_str->segment_index];
4621 /* Use segment start in original timescale for comparisons */
4622 seg_media_start_mov = seg->trak_media_start;
4624 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4625 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4626 k_index, target_ts, seg_media_start_mov,
4627 GST_TIME_ARGS (seg->media_start));
4629 /* Crawl back through segments to find the one containing this I frame */
4630 while (target_ts < seg_media_start_mov) {
4631 GST_DEBUG_OBJECT (qtdemux,
4632 "keyframe position (sample %u) is out of segment %u " " target %"
4633 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4634 ref_str->segment_index, target_ts, seg_media_start_mov);
4636 if (G_UNLIKELY (!ref_str->segment_index)) {
4637 /* Reached first segment, let's consider it's EOS */
4640 ref_str->segment_index--;
4641 seg = &ref_str->segments[ref_str->segment_index];
4642 /* Use segment start in original timescale for comparisons */
4643 seg_media_start_mov = seg->trak_media_start;
4645 /* Calculate time position of the keyframe and where we should stop */
4647 QTSTREAMTIME_TO_GSTTIME (ref_str,
4648 target_ts - seg->trak_media_start) + seg->time;
4650 QTSTREAMTIME_TO_GSTTIME (ref_str,
4651 ref_str->samples[ref_str->from_sample].timestamp -
4652 seg->trak_media_start) + seg->time;
4654 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4655 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4656 k_index, GST_TIME_ARGS (k_pos));
4658 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4659 qtdemux->segment.position = last_stop;
4660 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4661 GST_TIME_ARGS (last_stop));
4663 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4664 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4668 ref_seg_idx = ref_str->segment_index;
4669 ref_k_index = k_index;
4671 /* Align them all on this */
4672 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4674 GstClockTime seg_time = 0;
4675 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4677 /* aligning reference stream again might lead to backing up to yet another
4678 * keyframe (due to timestamp rounding issues),
4679 * potentially putting more load on downstream; so let's try to avoid */
4680 if (str == ref_str) {
4681 seg_idx = ref_seg_idx;
4682 seg = &str->segments[seg_idx];
4683 k_index = ref_k_index;
4684 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
4685 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
4687 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4688 GST_DEBUG_OBJECT (qtdemux,
4689 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
4690 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
4692 /* get segment and time in the segment */
4693 seg = &str->segments[seg_idx];
4694 seg_time = k_pos - seg->time;
4696 /* get the media time in the segment.
4697 * No adjustment for empty "filler" segments */
4698 if (seg->media_start != GST_CLOCK_TIME_NONE)
4699 seg_time += seg->media_start;
4701 /* get the index of the sample with media time */
4702 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4703 GST_DEBUG_OBJECT (qtdemux,
4704 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
4705 GST_TIME_ARGS (seg_time), index);
4707 /* find previous keyframe */
4708 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
4711 /* Remember until where we want to go */
4712 str->to_sample = str->from_sample - 1;
4713 /* Define our time position */
4715 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4716 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4717 if (seg->media_start != GST_CLOCK_TIME_NONE)
4718 str->time_position -= seg->media_start;
4720 /* Now seek back in time */
4721 gst_qtdemux_move_stream (qtdemux, str, k_index);
4722 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
4723 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
4724 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4730 return GST_FLOW_EOS;
4734 * Gets the current qt segment start, stop and position for the
4735 * given time offset. This is used in update_segment()
4738 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
4739 QtDemuxStream * stream, GstClockTime offset,
4740 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
4742 GstClockTime seg_time;
4743 GstClockTime start, stop, time;
4744 QtDemuxSegment *segment;
4746 segment = &stream->segments[stream->segment_index];
4748 /* get time in this segment */
4749 seg_time = (offset - segment->time) * segment->rate;
4751 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4752 GST_TIME_ARGS (seg_time));
4754 if (G_UNLIKELY (seg_time > segment->duration)) {
4755 GST_LOG_OBJECT (stream->pad,
4756 "seg_time > segment->duration %" GST_TIME_FORMAT,
4757 GST_TIME_ARGS (segment->duration));
4758 seg_time = segment->duration;
4761 /* qtdemux->segment.stop is in outside-time-realm, whereas
4762 * segment->media_stop is in track-time-realm.
4764 * In order to compare the two, we need to bring segment.stop
4765 * into the track-time-realm
4767 * FIXME - does this comment still hold? Don't see any conversion here */
4769 stop = qtdemux->segment.stop;
4770 if (stop == GST_CLOCK_TIME_NONE)
4771 stop = qtdemux->segment.duration;
4772 if (stop == GST_CLOCK_TIME_NONE)
4773 stop = segment->media_stop;
4776 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4778 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4779 start = segment->time + seg_time;
4781 stop = start - seg_time + segment->duration;
4782 } else if (qtdemux->segment.rate >= 0) {
4783 start = MIN (segment->media_start + seg_time, stop);
4786 if (segment->media_start >= qtdemux->segment.start) {
4787 time = segment->time;
4789 time = segment->time + (qtdemux->segment.start - segment->media_start);
4792 start = MAX (segment->media_start, qtdemux->segment.start);
4793 stop = MIN (segment->media_start + seg_time, stop);
4802 * Updates the qt segment used for the stream and pushes a new segment event
4803 * downstream on this stream's pad.
4806 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4807 gint seg_idx, GstClockTime offset, GstClockTime * _start,
4808 GstClockTime * _stop)
4810 QtDemuxSegment *segment;
4811 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
4815 /* update the current segment */
4816 stream->segment_index = seg_idx;
4818 /* get the segment */
4819 segment = &stream->segments[seg_idx];
4821 if (G_UNLIKELY (offset < segment->time)) {
4822 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
4823 GST_TIME_ARGS (segment->time));
4827 /* segment lies beyond total indicated duration */
4828 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
4829 segment->time > qtdemux->segment.duration)) {
4830 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
4831 " < segment->time %" GST_TIME_FORMAT,
4832 GST_TIME_ARGS (qtdemux->segment.duration),
4833 GST_TIME_ARGS (segment->time));
4837 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
4838 &start, &stop, &time);
4840 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
4841 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
4842 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
4844 /* combine global rate with that of the segment */
4845 rate = segment->rate * qtdemux->segment.rate;
4847 /* Copy flags from main segment */
4848 stream->segment.flags = qtdemux->segment.flags;
4850 /* update the segment values used for clipping */
4851 stream->segment.offset = qtdemux->segment.offset;
4852 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
4853 stream->segment.applied_rate = qtdemux->segment.applied_rate;
4854 stream->segment.rate = rate;
4855 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
4856 stream->cslg_shift);
4857 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
4858 stream->cslg_shift);
4859 stream->segment.time = time;
4860 stream->segment.position = stream->segment.start;
4862 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
4865 /* now prepare and send the segment */
4867 event = gst_event_new_segment (&stream->segment);
4868 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
4869 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
4871 gst_pad_push_event (stream->pad, event);
4872 /* assume we can send more data now */
4873 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
4874 /* clear to send tags on this pad now */
4875 gst_qtdemux_push_tags (qtdemux, stream);
4886 /* activate the given segment number @seg_idx of @stream at time @offset.
4887 * @offset is an absolute global position over all the segments.
4889 * This will push out a NEWSEGMENT event with the right values and
4890 * position the stream index to the first decodable sample before
4894 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4895 guint32 seg_idx, GstClockTime offset)
4897 QtDemuxSegment *segment;
4898 guint32 index, kf_index;
4899 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
4901 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
4902 seg_idx, GST_TIME_ARGS (offset));
4904 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
4908 segment = &stream->segments[stream->segment_index];
4910 /* in the fragmented case, we pick a fragment that starts before our
4911 * desired position and rely on downstream to wait for a keyframe
4912 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
4913 * tfra entries tells us which trun/sample the key unit is in, but we don't
4914 * make use of this additional information at the moment) */
4915 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
4916 stream->to_sample = G_MAXUINT32;
4919 /* well, it will be taken care of below */
4920 qtdemux->fragmented_seek_pending = FALSE;
4921 /* FIXME ideally the do_fragmented_seek can be done right here,
4922 * rather than at loop level
4923 * (which might even allow handling edit lists in a fragmented file) */
4926 /* We don't need to look for a sample in push-based */
4927 if (!qtdemux->pullbased)
4930 /* and move to the keyframe before the indicated media time of the
4932 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
4933 if (qtdemux->segment.rate >= 0) {
4934 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
4935 stream->to_sample = G_MAXUINT32;
4936 GST_DEBUG_OBJECT (stream->pad,
4937 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4938 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
4939 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4941 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
4942 stream->to_sample = index;
4943 GST_DEBUG_OBJECT (stream->pad,
4944 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4945 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
4946 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4949 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
4950 "this is an empty segment");
4954 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
4955 * encountered an error and printed a message so we return appropriately */
4959 /* we're at the right spot */
4960 if (index == stream->sample_index) {
4961 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
4965 /* find keyframe of the target index */
4966 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
4968 /* go back two frames to provide lead-in for non-raw audio decoders */
4969 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
4970 guint32 lead_in = 2;
4971 guint32 old_index = kf_index;
4972 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
4974 if (gst_structure_has_name (s, "audio/mpeg")) {
4976 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
4977 && mpegversion == 1) {
4978 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
4983 kf_index = MAX (kf_index, lead_in) - lead_in;
4984 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
4985 GST_DEBUG_OBJECT (stream->pad,
4986 "Moving backwards %u frames to ensure sufficient sound lead-in",
4987 old_index - kf_index);
4989 kf_index = old_index;
4993 /* if we move forwards, we don't have to go back to the previous
4994 * keyframe since we already sent that. We can also just jump to
4995 * the keyframe right before the target index if there is one. */
4996 if (index > stream->sample_index) {
4997 /* moving forwards check if we move past a keyframe */
4998 if (kf_index > stream->sample_index) {
4999 GST_DEBUG_OBJECT (stream->pad,
5000 "moving forwards to keyframe at %u "
5001 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5003 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5004 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5005 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5007 GST_DEBUG_OBJECT (stream->pad,
5008 "moving forwards, keyframe at %u "
5009 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5011 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5012 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5015 GST_DEBUG_OBJECT (stream->pad,
5016 "moving backwards to %sframe at %u "
5017 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5018 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5019 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5020 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5021 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5027 /* prepare to get the current sample of @stream, getting essential values.
5029 * This function will also prepare and send the segment when needed.
5031 * Return FALSE if the stream is EOS.
5036 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5037 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5038 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5039 gboolean * keyframe)
5041 QtDemuxSample *sample;
5042 GstClockTime time_position;
5045 g_return_val_if_fail (stream != NULL, FALSE);
5047 time_position = stream->time_position;
5048 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5051 seg_idx = stream->segment_index;
5052 if (G_UNLIKELY (seg_idx == -1)) {
5053 /* find segment corresponding to time_position if we are looking
5055 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5058 /* different segment, activate it, sample_index will be set. */
5059 if (G_UNLIKELY (stream->segment_index != seg_idx))
5060 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5062 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5063 segments[stream->segment_index]))) {
5064 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5066 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5067 " prepare empty sample");
5070 *pts = *dts = time_position;
5071 *duration = seg->duration - (time_position - seg->time);
5078 if (stream->sample_index == -1)
5079 stream->sample_index = 0;
5081 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5082 stream->sample_index, stream->n_samples);
5084 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5085 if (!qtdemux->fragmented)
5088 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5092 GST_OBJECT_LOCK (qtdemux);
5093 flow = qtdemux_add_fragmented_samples (qtdemux);
5094 GST_OBJECT_UNLOCK (qtdemux);
5096 if (flow != GST_FLOW_OK)
5099 while (stream->sample_index >= stream->n_samples);
5102 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5103 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5104 stream->sample_index);
5108 /* now get the info for the sample we're at */
5109 sample = &stream->samples[stream->sample_index];
5111 *dts = QTSAMPLE_DTS (stream, sample);
5112 *pts = QTSAMPLE_PTS (stream, sample);
5113 *offset = sample->offset;
5114 *size = sample->size;
5115 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5116 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5123 stream->time_position = GST_CLOCK_TIME_NONE;
5128 /* move to the next sample in @stream.
5130 * Moves to the next segment when needed.
5133 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5135 QtDemuxSample *sample;
5136 QtDemuxSegment *segment;
5138 /* get current segment */
5139 segment = &stream->segments[stream->segment_index];
5141 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5142 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5146 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5147 /* Mark the stream as EOS */
5148 GST_DEBUG_OBJECT (qtdemux,
5149 "reached max allowed sample %u, mark EOS", stream->to_sample);
5150 stream->time_position = GST_CLOCK_TIME_NONE;
5154 /* move to next sample */
5155 stream->sample_index++;
5156 stream->offset_in_sample = 0;
5158 /* reached the last sample, we need the next segment */
5159 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5162 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5163 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5164 stream->sample_index);
5168 /* get next sample */
5169 sample = &stream->samples[stream->sample_index];
5171 /* see if we are past the segment */
5172 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5175 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5176 /* inside the segment, update time_position, looks very familiar to
5177 * GStreamer segments, doesn't it? */
5178 stream->time_position =
5179 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5181 /* not yet in segment, time does not yet increment. This means
5182 * that we are still prerolling keyframes to the decoder so it can
5183 * decode the first sample of the segment. */
5184 stream->time_position = segment->time;
5188 /* move to the next segment */
5191 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5193 if (stream->segment_index == stream->n_segments - 1) {
5194 /* are we at the end of the last segment, we're EOS */
5195 stream->time_position = GST_CLOCK_TIME_NONE;
5197 /* else we're only at the end of the current segment */
5198 stream->time_position = segment->stop_time;
5200 /* make sure we select a new segment */
5202 /* accumulate previous segments */
5203 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5204 stream->accumulated_base +=
5205 (stream->segment.stop -
5206 stream->segment.start) / ABS (stream->segment.rate);
5208 stream->segment_index = -1;
5213 gst_qtdemux_sync_streams (GstQTDemux * demux)
5217 if (QTDEMUX_N_STREAMS (demux) <= 1)
5220 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5221 QtDemuxStream *stream;
5222 GstClockTime end_time;
5224 stream = QTDEMUX_NTH_STREAM (demux, i);
5229 /* TODO advance time on subtitle streams here, if any some day */
5231 /* some clips/trailers may have unbalanced streams at the end,
5232 * so send EOS on shorter stream to prevent stalling others */
5234 /* do not mess with EOS if SEGMENT seeking */
5235 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5238 if (demux->pullbased) {
5239 /* loop mode is sample time based */
5240 if (!STREAM_IS_EOS (stream))
5243 /* push mode is byte position based */
5244 if (stream->n_samples &&
5245 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5249 if (stream->sent_eos)
5252 /* only act if some gap */
5253 end_time = stream->segments[stream->n_segments - 1].stop_time;
5254 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5255 ", stream end: %" GST_TIME_FORMAT,
5256 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5257 if (GST_CLOCK_TIME_IS_VALID (end_time)
5258 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5261 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5262 GST_PAD_NAME (stream->pad));
5263 stream->sent_eos = TRUE;
5264 event = gst_event_new_eos ();
5265 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5266 gst_event_set_seqnum (event, demux->segment_seqnum);
5267 gst_pad_push_event (stream->pad, event);
5272 /* EOS and NOT_LINKED need to be combined. This means that we return:
5274 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5275 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5277 static GstFlowReturn
5278 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5281 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5284 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5287 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5289 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5293 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5294 * completely clipped
5296 * Should be used only with raw buffers */
5298 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5301 guint64 start, stop, cstart, cstop, diff;
5302 GstClockTime pts, duration;
5304 gint num_rate, denom_rate;
5309 osize = size = gst_buffer_get_size (buf);
5312 /* depending on the type, setup the clip parameters */
5313 if (stream->subtype == FOURCC_soun) {
5314 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5315 num_rate = GST_SECOND;
5316 denom_rate = (gint) CUR_STREAM (stream)->rate;
5318 } else if (stream->subtype == FOURCC_vide) {
5320 num_rate = CUR_STREAM (stream)->fps_n;
5321 denom_rate = CUR_STREAM (stream)->fps_d;
5326 if (frame_size <= 0)
5327 goto bad_frame_size;
5329 /* we can only clip if we have a valid pts */
5330 pts = GST_BUFFER_PTS (buf);
5331 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5334 duration = GST_BUFFER_DURATION (buf);
5336 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5338 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5342 stop = start + duration;
5344 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5345 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5348 /* see if some clipping happened */
5349 diff = cstart - start;
5355 /* bring clipped time to samples and to bytes */
5356 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5359 GST_DEBUG_OBJECT (qtdemux,
5360 "clipping start to %" GST_TIME_FORMAT " %"
5361 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5367 diff = stop - cstop;
5372 /* bring clipped time to samples and then to bytes */
5373 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5375 GST_DEBUG_OBJECT (qtdemux,
5376 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5377 " bytes", GST_TIME_ARGS (cstop), diff);
5382 if (offset != 0 || size != osize)
5383 gst_buffer_resize (buf, offset, size);
5385 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5386 GST_BUFFER_PTS (buf) = pts;
5387 GST_BUFFER_DURATION (buf) = duration;
5391 /* dropped buffer */
5394 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5399 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5404 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5409 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5410 gst_buffer_unref (buf);
5416 gst_qtdemux_align_buffer (GstQTDemux * demux,
5417 GstBuffer * buffer, gsize alignment)
5421 gst_buffer_map (buffer, &map, GST_MAP_READ);
5423 if (map.size < sizeof (guintptr)) {
5424 gst_buffer_unmap (buffer, &map);
5428 if (((guintptr) map.data) & (alignment - 1)) {
5429 GstBuffer *new_buffer;
5430 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5432 new_buffer = gst_buffer_new_allocate (NULL,
5433 gst_buffer_get_size (buffer), ¶ms);
5435 /* Copy data "by hand", so ensure alignment is kept: */
5436 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5438 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5439 GST_DEBUG_OBJECT (demux,
5440 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5443 gst_buffer_unmap (buffer, &map);
5444 gst_buffer_unref (buffer);
5449 gst_buffer_unmap (buffer, &map);
5454 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
5460 /* We are converting from pairs to triplets */
5461 *res = ccpair_size / 2 * 3;
5462 storage = g_malloc (*res);
5463 for (i = 0; i * 2 < ccpair_size; i += 1) {
5464 /* FIXME: Use line offset 0 as we simply can't know here */
5466 storage[i * 3] = 0x80 | 0x00;
5468 storage[i * 3] = 0x00 | 0x00;
5469 storage[i * 3 + 1] = ccpair[i * 2];
5470 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5477 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
5481 guint32 atom_length, fourcc;
5482 QtDemuxStreamStsdEntry *stsd_entry;
5484 GST_MEMDUMP ("caption atom", data, size);
5486 /* There might be multiple atoms */
5491 atom_length = QT_UINT32 (data);
5492 fourcc = QT_FOURCC (data + 4);
5493 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5496 GST_DEBUG_OBJECT (stream->pad, "here");
5498 /* Check if we have something compatible */
5499 stsd_entry = CUR_STREAM (stream);
5500 switch (stsd_entry->fourcc) {
5502 guint8 *cdat = NULL, *cdt2 = NULL;
5503 gsize cdat_size = 0, cdt2_size = 0;
5504 /* Should be cdat or cdt2 */
5505 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
5506 GST_WARNING_OBJECT (stream->pad,
5507 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5508 GST_FOURCC_ARGS (fourcc));
5512 /* Convert to S334-1 Annex A byte triplet */
5513 if (fourcc == FOURCC_cdat)
5514 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
5516 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
5517 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5520 /* Check for another atom ? */
5521 if (size > atom_length + 8) {
5522 guint32 new_atom_length = QT_UINT32 (data + atom_length);
5523 if (size >= atom_length + new_atom_length) {
5524 fourcc = QT_FOURCC (data + atom_length + 4);
5525 if (fourcc == FOURCC_cdat) {
5528 convert_to_s334_1a (data + atom_length + 8,
5529 new_atom_length - 8, 1, &cdat_size);
5531 GST_WARNING_OBJECT (stream->pad,
5532 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5536 convert_to_s334_1a (data + atom_length + 8,
5537 new_atom_length - 8, 2, &cdt2_size);
5539 GST_WARNING_OBJECT (stream->pad,
5540 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5545 *cclen = cdat_size + cdt2_size;
5546 res = g_malloc (*cclen);
5548 memcpy (res, cdat, cdat_size);
5550 memcpy (res + cdat_size, cdt2, cdt2_size);
5556 if (fourcc != FOURCC_ccdp) {
5557 GST_WARNING_OBJECT (stream->pad,
5558 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5559 GST_FOURCC_ARGS (fourcc));
5562 *cclen = atom_length - 8;
5563 res = g_memdup (data + 8, *cclen);
5566 /* Keep this here in case other closed caption formats are added */
5567 g_assert_not_reached ();
5571 GST_MEMDUMP ("Output", res, *cclen);
5576 GST_WARNING ("[cdat] atom is too small or invalid");
5580 /* the input buffer metadata must be writable,
5581 * but time/duration etc not yet set and need not be preserved */
5583 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5590 /* not many cases for now */
5591 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
5592 /* send a one time dvd clut event */
5593 if (stream->pending_event && stream->pad)
5594 gst_pad_push_event (stream->pad, stream->pending_event);
5595 stream->pending_event = NULL;
5598 if (G_UNLIKELY (stream->subtype != FOURCC_text
5599 && stream->subtype != FOURCC_sbtl &&
5600 stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
5604 gst_buffer_map (buf, &map, GST_MAP_READ);
5606 /* empty buffer is sent to terminate previous subtitle */
5607 if (map.size <= 2) {
5608 gst_buffer_unmap (buf, &map);
5609 gst_buffer_unref (buf);
5612 if (stream->subtype == FOURCC_subp) {
5613 /* That's all the processing needed for subpictures */
5614 gst_buffer_unmap (buf, &map);
5618 if (stream->subtype == FOURCC_clcp) {
5621 /* For closed caption, we need to extract the information from the
5622 * [cdat],[cdt2] or [ccdp] atom */
5623 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
5624 gst_buffer_unmap (buf, &map);
5625 gst_buffer_unref (buf);
5627 buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
5629 /* Conversion failed or there's nothing */
5635 nsize = GST_READ_UINT16_BE (map.data);
5636 nsize = MIN (nsize, map.size - 2);
5638 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5641 /* takes care of UTF-8 validation or UTF-16 recognition,
5642 * no other encoding expected */
5643 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5644 gst_buffer_unmap (buf, &map);
5646 gst_buffer_unref (buf);
5647 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5649 /* this should not really happen unless the subtitle is corrupted */
5650 gst_buffer_unref (buf);
5654 /* FIXME ? convert optional subsequent style info to markup */
5659 static GstFlowReturn
5660 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5663 GstFlowReturn ret = GST_FLOW_OK;
5664 GstClockTime pts, duration;
5666 if (stream->need_clip)
5667 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5669 if (G_UNLIKELY (buf == NULL))
5672 if (G_UNLIKELY (stream->discont)) {
5673 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5674 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5675 stream->discont = FALSE;
5677 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5680 GST_LOG_OBJECT (qtdemux,
5681 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
5682 ", duration %" GST_TIME_FORMAT " on pad %s",
5683 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
5684 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
5685 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
5687 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
5688 GstStructure *crypto_info;
5689 QtDemuxCencSampleSetInfo *info =
5690 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5694 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5695 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
5696 GST_PTR_FORMAT, event);
5697 gst_pad_push_event (stream->pad, event);
5700 if (info->crypto_info == NULL) {
5701 GST_DEBUG_OBJECT (qtdemux,
5702 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
5704 /* The end of the crypto_info array matches our n_samples position,
5705 * so count backward from there */
5706 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
5707 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5708 /* steal structure from array */
5709 crypto_info = g_ptr_array_index (info->crypto_info, index);
5710 g_ptr_array_index (info->crypto_info, index) = NULL;
5711 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
5712 info->crypto_info->len);
5713 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5714 GST_ERROR_OBJECT (qtdemux,
5715 "failed to attach cenc metadata to buffer");
5717 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
5718 index, stream->sample_index);
5723 if (stream->alignment > 1)
5724 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
5726 pts = GST_BUFFER_PTS (buf);
5727 duration = GST_BUFFER_DURATION (buf);
5729 ret = gst_pad_push (stream->pad, buf);
5731 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
5732 /* mark position in stream, we'll need this to know when to send GAP event */
5733 stream->segment.position = pts + duration;
5741 static GstFlowReturn
5742 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5745 GstFlowReturn ret = GST_FLOW_OK;
5747 if (stream->subtype == FOURCC_clcp
5748 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
5750 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
5751 guint n_triplets, i;
5752 guint field1_off = 0, field2_off = 0;
5754 /* We have to split CEA608 buffers so that each outgoing buffer contains
5755 * one byte pair per field according to the framerate of the video track.
5757 * If there is only a single byte pair per field we don't have to do
5761 gst_buffer_map (buf, &map, GST_MAP_READ);
5763 n_triplets = map.size / 3;
5764 for (i = 0; i < n_triplets; i++) {
5765 if (map.data[3 * i] & 0x80)
5771 g_assert (n_field1 || n_field2);
5773 /* If there's more than 1 frame we have to split, otherwise we can just
5775 if (n_field1 > 1 || n_field2 > 1) {
5777 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
5778 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
5780 for (i = 0; i < n_output_buffers; i++) {
5782 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
5786 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
5787 outptr = outmap.data;
5790 gboolean found = FALSE;
5792 while (map.data + field1_off < map.data + map.size) {
5793 if (map.data[field1_off] & 0x80) {
5794 memcpy (outptr, &map.data[field1_off], 3);
5803 const guint8 empty[] = { 0x80, 0x80, 0x80 };
5805 memcpy (outptr, empty, 3);
5812 gboolean found = FALSE;
5814 while (map.data + field2_off < map.data + map.size) {
5815 if ((map.data[field2_off] & 0x80) == 0) {
5816 memcpy (outptr, &map.data[field2_off], 3);
5825 const guint8 empty[] = { 0x00, 0x80, 0x80 };
5827 memcpy (outptr, empty, 3);
5833 gst_buffer_unmap (outbuf, &outmap);
5835 GST_BUFFER_PTS (outbuf) =
5836 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
5837 GST_SECOND * CUR_STREAM (stream)->fps_d,
5838 CUR_STREAM (stream)->fps_n);
5839 GST_BUFFER_DURATION (outbuf) =
5840 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
5841 CUR_STREAM (stream)->fps_n);
5842 GST_BUFFER_OFFSET (outbuf) = -1;
5843 GST_BUFFER_OFFSET_END (outbuf) = -1;
5845 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
5847 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
5850 gst_buffer_unmap (buf, &map);
5851 gst_buffer_unref (buf);
5853 gst_buffer_unmap (buf, &map);
5854 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
5857 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
5863 /* Sets a buffer's attributes properly and pushes it downstream.
5864 * Also checks for additional actions and custom processing that may
5865 * need to be done first.
5867 static GstFlowReturn
5868 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
5869 QtDemuxStream * stream, GstBuffer * buf,
5870 GstClockTime dts, GstClockTime pts, GstClockTime duration,
5871 gboolean keyframe, GstClockTime position, guint64 byte_position)
5873 GstFlowReturn ret = GST_FLOW_OK;
5875 /* offset the timestamps according to the edit list */
5877 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
5881 gst_buffer_map (buf, &map, GST_MAP_READ);
5882 url = g_strndup ((gchar *) map.data, map.size);
5883 gst_buffer_unmap (buf, &map);
5884 if (url != NULL && strlen (url) != 0) {
5885 /* we have RTSP redirect now */
5886 g_free (qtdemux->redirect_location);
5887 qtdemux->redirect_location = g_strdup (url);
5888 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5889 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
5890 gst_structure_new ("redirect",
5891 "new-location", G_TYPE_STRING, url, NULL)));
5893 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
5899 /* position reporting */
5900 if (qtdemux->segment.rate >= 0) {
5901 qtdemux->segment.position = position;
5902 gst_qtdemux_sync_streams (qtdemux);
5905 if (G_UNLIKELY (!stream->pad)) {
5906 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
5907 gst_buffer_unref (buf);
5911 /* send out pending buffers */
5912 while (stream->buffers) {
5913 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
5915 if (G_UNLIKELY (stream->discont)) {
5916 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5917 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
5918 stream->discont = FALSE;
5920 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5923 if (stream->alignment > 1)
5924 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
5925 gst_pad_push (stream->pad, buffer);
5927 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
5930 /* we're going to modify the metadata */
5931 buf = gst_buffer_make_writable (buf);
5933 if (G_UNLIKELY (stream->need_process))
5934 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
5940 GST_BUFFER_DTS (buf) = dts;
5941 GST_BUFFER_PTS (buf) = pts;
5942 GST_BUFFER_DURATION (buf) = duration;
5943 GST_BUFFER_OFFSET (buf) = -1;
5944 GST_BUFFER_OFFSET_END (buf) = -1;
5947 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5948 stream->on_keyframe = FALSE;
5950 stream->on_keyframe = TRUE;
5953 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
5954 gst_buffer_append_memory (buf,
5955 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
5957 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
5958 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
5961 if (G_UNLIKELY (qtdemux->element_index)) {
5962 GstClockTime stream_time;
5965 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
5967 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
5968 GST_LOG_OBJECT (qtdemux,
5969 "adding association %" GST_TIME_FORMAT "-> %"
5970 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
5971 gst_index_add_association (qtdemux->element_index,
5973 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
5974 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
5975 GST_FORMAT_BYTES, byte_position, NULL);
5980 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
5986 static const QtDemuxRandomAccessEntry *
5987 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5988 GstClockTime pos, gboolean after)
5990 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
5991 guint n_entries = stream->n_ra_entries;
5994 /* we assume the table is sorted */
5995 for (i = 0; i < n_entries; ++i) {
5996 if (entries[i].ts > pos)
6000 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6001 * probably okay to assume that the index lists the very first fragment */
6008 return &entries[i - 1];
6012 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6014 const QtDemuxRandomAccessEntry *best_entry = NULL;
6017 GST_OBJECT_LOCK (qtdemux);
6019 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6021 /* first see if we can determine where to go to using mfra,
6022 * before we start clearing things */
6023 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6024 const QtDemuxRandomAccessEntry *entry;
6025 QtDemuxStream *stream;
6026 gboolean is_audio_or_video;
6028 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6030 if (stream->ra_entries == NULL)
6033 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6034 is_audio_or_video = TRUE;
6036 is_audio_or_video = FALSE;
6039 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6040 stream->time_position, !is_audio_or_video);
6042 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6043 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6045 stream->pending_seek = entry;
6047 /* decide position to jump to just based on audio/video tracks, not subs */
6048 if (!is_audio_or_video)
6051 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6055 /* no luck, will handle seek otherwise */
6056 if (best_entry == NULL) {
6057 GST_OBJECT_UNLOCK (qtdemux);
6061 /* ok, now we can prepare for processing as of located moof */
6062 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6063 QtDemuxStream *stream;
6065 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6067 g_free (stream->samples);
6068 stream->samples = NULL;
6069 stream->n_samples = 0;
6070 stream->stbl_index = -1; /* no samples have yet been parsed */
6071 stream->sample_index = -1;
6073 if (stream->protection_scheme_info) {
6074 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6075 if (stream->protection_scheme_type == FOURCC_cenc) {
6076 QtDemuxCencSampleSetInfo *info =
6077 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6078 if (info->crypto_info) {
6079 g_ptr_array_free (info->crypto_info, TRUE);
6080 info->crypto_info = NULL;
6086 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6087 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6088 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6089 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6091 qtdemux->moof_offset = best_entry->moof_offset;
6093 qtdemux_add_fragmented_samples (qtdemux);
6095 GST_OBJECT_UNLOCK (qtdemux);
6099 static GstFlowReturn
6100 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6102 GstFlowReturn ret = GST_FLOW_OK;
6103 GstBuffer *buf = NULL;
6104 QtDemuxStream *stream, *target_stream = NULL;
6105 GstClockTime min_time;
6107 GstClockTime dts = GST_CLOCK_TIME_NONE;
6108 GstClockTime pts = GST_CLOCK_TIME_NONE;
6109 GstClockTime duration = 0;
6110 gboolean keyframe = FALSE;
6111 guint sample_size = 0;
6112 guint num_samples = 1;
6117 if (qtdemux->fragmented_seek_pending) {
6118 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6119 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6120 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6121 qtdemux->fragmented_seek_pending = FALSE;
6123 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6127 /* Figure out the next stream sample to output, min_time is expressed in
6128 * global time and runs over the edit list segments. */
6129 min_time = G_MAXUINT64;
6130 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6131 GstClockTime position;
6133 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6134 position = stream->time_position;
6136 if (!GST_CLOCK_TIME_IS_VALID (position))
6139 if (stream->segment_index != -1) {
6140 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6141 position += segment->media_start;
6144 /* position of -1 is EOS */
6145 if (position < min_time) {
6146 min_time = position;
6147 target_stream = stream;
6151 if (G_UNLIKELY (target_stream == NULL)) {
6152 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6156 /* check for segment end */
6157 if (G_UNLIKELY (qtdemux->segment.stop != -1
6158 && qtdemux->segment.rate >= 0
6159 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
6160 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6161 target_stream->time_position = GST_CLOCK_TIME_NONE;
6165 /* gap events for subtitle streams */
6166 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6167 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6169 GstClockTime gap_threshold;
6171 /* Only send gap events on non-subtitle streams if lagging way behind. */
6172 if (stream->subtype == FOURCC_subp
6173 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)
6174 gap_threshold = 1 * GST_SECOND;
6176 gap_threshold = 3 * GST_SECOND;
6178 /* send gap events until the stream catches up */
6179 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6180 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6181 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6182 stream->segment.position + gap_threshold < min_time) {
6184 gst_event_new_gap (stream->segment.position, gap_threshold);
6185 gst_pad_push_event (stream->pad, gap);
6186 stream->segment.position += gap_threshold;
6191 stream = target_stream;
6192 /* fetch info for the current sample of this stream */
6193 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6194 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6197 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6198 if (stream->new_caps) {
6199 gst_qtdemux_configure_stream (qtdemux, stream);
6200 qtdemux_do_allocation (stream, qtdemux);
6203 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6204 if (G_UNLIKELY (qtdemux->segment.
6205 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6206 if (stream->subtype == FOURCC_vide) {
6208 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6211 } else if (qtdemux->trickmode_interval > 0) {
6212 GstClockTimeDiff interval;
6214 if (qtdemux->segment.rate > 0)
6215 interval = stream->time_position - stream->last_keyframe_dts;
6217 interval = stream->last_keyframe_dts - stream->time_position;
6219 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
6220 && interval < qtdemux->trickmode_interval) {
6221 GST_LOG_OBJECT (qtdemux,
6222 "Skipping keyframe within interval on track-id %u",
6226 stream->last_keyframe_dts = stream->time_position;
6232 GST_DEBUG_OBJECT (qtdemux,
6233 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6234 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6235 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6236 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6237 GST_TIME_ARGS (duration));
6239 if (G_UNLIKELY (empty)) {
6240 /* empty segment, push a gap if there's a second or more
6241 * difference and move to the next one */
6242 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6243 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6244 stream->segment.position = pts + duration;
6248 /* hmm, empty sample, skip and move to next sample */
6249 if (G_UNLIKELY (sample_size <= 0))
6252 /* last pushed sample was out of boundary, goto next sample */
6253 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6256 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
6257 GST_DEBUG_OBJECT (qtdemux,
6258 "size %d larger than stream max_buffer_size %d, trimming",
6259 sample_size, stream->max_buffer_size);
6261 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6262 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
6263 && sample_size < stream->min_buffer_size) {
6264 guint start_sample_index = stream->sample_index;
6265 guint accumulated_size = sample_size;
6266 guint64 expected_next_offset = offset + sample_size;
6268 GST_DEBUG_OBJECT (qtdemux,
6269 "size %d smaller than stream min_buffer_size %d, combining with the next",
6270 sample_size, stream->min_buffer_size);
6272 while (stream->sample_index < stream->to_sample
6273 && stream->sample_index + 1 < stream->n_samples) {
6274 const QtDemuxSample *next_sample;
6276 /* Increment temporarily */
6277 stream->sample_index++;
6279 /* Failed to parse sample so let's go back to the previous one that was
6280 * still successful */
6281 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6282 stream->sample_index--;
6286 next_sample = &stream->samples[stream->sample_index];
6288 /* Not contiguous with the previous sample so let's go back to the
6289 * previous one that was still successful */
6290 if (next_sample->offset != expected_next_offset) {
6291 stream->sample_index--;
6295 accumulated_size += next_sample->size;
6296 expected_next_offset += next_sample->size;
6297 if (accumulated_size >= stream->min_buffer_size)
6301 num_samples = stream->sample_index + 1 - start_sample_index;
6302 stream->sample_index = start_sample_index;
6303 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
6304 num_samples, accumulated_size);
6305 size = accumulated_size;
6310 if (qtdemux->cenc_aux_info_offset > 0) {
6313 GstBuffer *aux_info = NULL;
6315 /* pull the data stored before the sample */
6317 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6318 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6319 if (G_UNLIKELY (ret != GST_FLOW_OK))
6321 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6322 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6323 gst_byte_reader_init (&br, map.data + 8, map.size);
6324 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6325 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6326 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6327 gst_buffer_unmap (aux_info, &map);
6328 gst_buffer_unref (aux_info);
6329 ret = GST_FLOW_ERROR;
6332 gst_buffer_unmap (aux_info, &map);
6333 gst_buffer_unref (aux_info);
6336 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6339 if (stream->use_allocator) {
6340 /* if we have a per-stream allocator, use it */
6341 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6344 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6346 if (G_UNLIKELY (ret != GST_FLOW_OK))
6349 /* Update for both splitting and combining of samples */
6350 if (size != sample_size) {
6351 pts += gst_util_uint64_scale_int (GST_SECOND,
6352 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6355 gst_util_uint64_scale_int (GST_SECOND,
6356 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6359 gst_util_uint64_scale_int (GST_SECOND,
6360 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6363 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6364 dts, pts, duration, keyframe, min_time, offset);
6366 if (size < sample_size) {
6367 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6368 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6370 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6372 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6373 if (time_position >= segment->media_start) {
6374 /* inside the segment, update time_position, looks very familiar to
6375 * GStreamer segments, doesn't it? */
6376 stream->time_position = (time_position - segment->media_start) +
6379 /* not yet in segment, time does not yet increment. This means
6380 * that we are still prerolling keyframes to the decoder so it can
6381 * decode the first sample of the segment. */
6382 stream->time_position = segment->time;
6384 } else if (size > sample_size) {
6385 /* Increase to the last sample we already pulled so that advancing
6386 * below brings us to the next sample we need to pull */
6387 stream->sample_index += num_samples - 1;
6391 GST_OBJECT_LOCK (qtdemux);
6392 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6393 GST_OBJECT_UNLOCK (qtdemux);
6394 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6395 * we have no more data for the pad to push */
6396 if (ret == GST_FLOW_EOS)
6399 stream->offset_in_sample += size;
6400 if (stream->offset_in_sample >= sample_size) {
6401 gst_qtdemux_advance_sample (qtdemux, stream);
6406 gst_qtdemux_advance_sample (qtdemux, stream);
6414 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6420 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6421 /* EOS will be raised if all are EOS */
6428 gst_qtdemux_loop (GstPad * pad)
6430 GstQTDemux *qtdemux;
6434 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6436 cur_offset = qtdemux->offset;
6437 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6438 cur_offset, qt_demux_state_string (qtdemux->state));
6440 switch (qtdemux->state) {
6441 case QTDEMUX_STATE_INITIAL:
6442 case QTDEMUX_STATE_HEADER:
6443 ret = gst_qtdemux_loop_state_header (qtdemux);
6445 case QTDEMUX_STATE_MOVIE:
6446 ret = gst_qtdemux_loop_state_movie (qtdemux);
6447 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6448 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6456 /* if something went wrong, pause */
6457 if (ret != GST_FLOW_OK)
6461 gst_object_unref (qtdemux);
6467 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6468 (NULL), ("streaming stopped, invalid state"));
6469 gst_pad_pause_task (pad);
6470 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6475 const gchar *reason = gst_flow_get_name (ret);
6477 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6479 gst_pad_pause_task (pad);
6481 /* fatal errors need special actions */
6483 if (ret == GST_FLOW_EOS) {
6484 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
6485 /* we have no streams, post an error */
6486 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6488 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6491 if ((stop = qtdemux->segment.stop) == -1)
6492 stop = qtdemux->segment.duration;
6494 if (qtdemux->segment.rate >= 0) {
6495 GstMessage *message;
6498 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6499 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6500 GST_FORMAT_TIME, stop);
6501 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6502 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6503 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6504 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6506 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6507 gst_qtdemux_push_event (qtdemux, event);
6509 GstMessage *message;
6512 /* For Reverse Playback */
6513 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6514 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6515 GST_FORMAT_TIME, qtdemux->segment.start);
6516 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6517 qtdemux->segment.start);
6518 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6519 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6520 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6522 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6523 gst_qtdemux_push_event (qtdemux, event);
6528 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6529 event = gst_event_new_eos ();
6530 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
6531 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6532 gst_qtdemux_push_event (qtdemux, event);
6534 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6535 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6536 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6545 * Returns if there are samples to be played.
6548 has_next_entry (GstQTDemux * demux)
6550 QtDemuxStream *stream;
6553 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6555 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6556 stream = QTDEMUX_NTH_STREAM (demux, i);
6558 if (stream->sample_index == -1) {
6559 stream->sample_index = 0;
6560 stream->offset_in_sample = 0;
6563 if (stream->sample_index >= stream->n_samples) {
6564 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6567 GST_DEBUG_OBJECT (demux, "Found a sample");
6571 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6578 * Returns the size of the first entry at the current offset.
6579 * If -1, there are none (which means EOS or empty file).
6582 next_entry_size (GstQTDemux * demux)
6584 QtDemuxStream *stream, *target_stream = NULL;
6585 guint64 smalloffs = (guint64) - 1;
6586 QtDemuxSample *sample;
6589 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6592 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6593 stream = QTDEMUX_NTH_STREAM (demux, i);
6595 if (stream->sample_index == -1) {
6596 stream->sample_index = 0;
6597 stream->offset_in_sample = 0;
6600 if (stream->sample_index >= stream->n_samples) {
6601 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6605 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6606 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6607 stream->sample_index);
6611 sample = &stream->samples[stream->sample_index];
6613 GST_LOG_OBJECT (demux,
6614 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
6615 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
6616 stream->sample_index, sample->offset, sample->size);
6618 if (((smalloffs == -1)
6619 || (sample->offset < smalloffs)) && (sample->size)) {
6620 smalloffs = sample->offset;
6621 target_stream = stream;
6628 GST_LOG_OBJECT (demux,
6629 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
6630 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
6632 stream = target_stream;
6633 sample = &stream->samples[stream->sample_index];
6635 if (sample->offset >= demux->offset) {
6636 demux->todrop = sample->offset - demux->offset;
6637 return sample->size + demux->todrop;
6640 GST_DEBUG_OBJECT (demux,
6641 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6646 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6648 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6650 gst_element_post_message (GST_ELEMENT_CAST (demux),
6651 gst_message_new_element (GST_OBJECT_CAST (demux),
6652 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6656 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6661 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6664 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6665 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6666 GST_SEEK_TYPE_NONE, -1);
6668 /* store seqnum to drop flush events, they don't need to reach downstream */
6669 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6670 res = gst_pad_push_event (demux->sinkpad, event);
6671 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
6676 /* check for seekable upstream, above and beyond a mere query */
6678 gst_qtdemux_check_seekability (GstQTDemux * demux)
6681 gboolean seekable = FALSE;
6682 gint64 start = -1, stop = -1;
6684 if (demux->upstream_size)
6687 if (demux->upstream_format_is_time)
6690 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6691 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6692 GST_DEBUG_OBJECT (demux, "seeking query failed");
6696 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6698 /* try harder to query upstream size if we didn't get it the first time */
6699 if (seekable && stop == -1) {
6700 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6701 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6704 /* if upstream doesn't know the size, it's likely that it's not seekable in
6705 * practice even if it technically may be seekable */
6706 if (seekable && (start != 0 || stop <= start)) {
6707 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6712 gst_query_unref (query);
6714 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6715 G_GUINT64_FORMAT ")", seekable, start, stop);
6716 demux->upstream_seekable = seekable;
6717 demux->upstream_size = seekable ? stop : -1;
6721 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6723 g_return_if_fail (bytes <= demux->todrop);
6725 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6726 gst_adapter_flush (demux->adapter, bytes);
6727 demux->neededbytes -= bytes;
6728 demux->offset += bytes;
6729 demux->todrop -= bytes;
6732 /* PUSH-MODE only: Send a segment, if not done already. */
6734 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
6736 if (G_UNLIKELY (demux->need_segment)) {
6739 if (!demux->upstream_format_is_time) {
6740 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
6742 GstEvent *segment_event;
6743 segment_event = gst_event_new_segment (&demux->segment);
6744 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6745 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
6746 gst_qtdemux_push_event (demux, segment_event);
6749 demux->need_segment = FALSE;
6751 /* clear to send tags on all streams */
6752 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6753 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6754 gst_qtdemux_push_tags (demux, stream);
6755 if (CUR_STREAM (stream)->sparse) {
6756 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6757 gst_pad_push_event (stream->pad,
6758 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
6764 /* Used for push mode only. */
6766 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
6767 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
6769 GstClockTime ts, dur;
6773 stream->segments[segment_index].duration - (pos -
6774 stream->segments[segment_index].time);
6775 stream->time_position += dur;
6777 /* Only gaps with a duration of at least one second are propagated.
6778 * Same workaround as in pull mode.
6779 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
6780 if (dur >= GST_SECOND) {
6782 gap = gst_event_new_gap (ts, dur);
6784 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
6785 "segment: %" GST_PTR_FORMAT, gap);
6786 gst_pad_push_event (stream->pad, gap);
6790 static GstFlowReturn
6791 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6795 demux = GST_QTDEMUX (parent);
6797 GST_DEBUG_OBJECT (demux,
6798 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
6799 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
6800 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
6801 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
6802 gst_buffer_get_size (inbuf), demux->offset);
6804 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6805 gboolean is_gap_input = FALSE;
6808 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6810 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6811 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
6814 /* Check if we can land back on our feet in the case where upstream is
6815 * handling the seeking/pushing of samples with gaps in between (like
6816 * in the case of trick-mode DASH for example) */
6817 if (demux->upstream_format_is_time
6818 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
6819 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6821 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
6822 GST_LOG_OBJECT (demux,
6823 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
6824 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
6826 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
6827 stream, GST_BUFFER_OFFSET (inbuf));
6829 QtDemuxSample *sample = &stream->samples[res];
6830 GST_LOG_OBJECT (demux,
6831 "Checking if sample %d from track-id %u is valid (offset:%"
6832 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
6833 stream->track_id, sample->offset, sample->size);
6834 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
6835 GST_LOG_OBJECT (demux,
6836 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
6838 is_gap_input = TRUE;
6839 /* We can go back to standard playback mode */
6840 demux->state = QTDEMUX_STATE_MOVIE;
6841 /* Remember which sample this stream is at */
6842 stream->sample_index = res;
6843 /* Finally update all push-based values to the expected values */
6844 demux->neededbytes = stream->samples[res].size;
6845 demux->offset = GST_BUFFER_OFFSET (inbuf);
6847 demux->mdatsize - demux->offset + demux->mdatoffset;
6852 if (!is_gap_input) {
6853 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
6854 /* Reset state if it's a real discont */
6855 demux->neededbytes = 16;
6856 demux->state = QTDEMUX_STATE_INITIAL;
6857 demux->offset = GST_BUFFER_OFFSET (inbuf);
6858 gst_adapter_clear (demux->adapter);
6861 /* Reverse fragmented playback, need to flush all we have before
6862 * consuming a new fragment.
6863 * The samples array have the timestamps calculated by accumulating the
6864 * durations but this won't work for reverse playback of fragments as
6865 * the timestamps of a subsequent fragment should be smaller than the
6866 * previously received one. */
6867 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
6868 gst_qtdemux_process_adapter (demux, TRUE);
6869 g_ptr_array_foreach (demux->active_streams,
6870 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
6874 gst_adapter_push (demux->adapter, inbuf);
6876 GST_DEBUG_OBJECT (demux,
6877 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6878 demux->neededbytes, gst_adapter_available (demux->adapter));
6880 return gst_qtdemux_process_adapter (demux, FALSE);
6883 static GstFlowReturn
6884 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6886 GstFlowReturn ret = GST_FLOW_OK;
6888 /* we never really mean to buffer that much */
6889 if (demux->neededbytes == -1) {
6893 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6894 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6896 #ifndef GST_DISABLE_GST_DEBUG
6898 guint64 discont_offset, distance_from_discont;
6900 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
6901 distance_from_discont =
6902 gst_adapter_distance_from_discont (demux->adapter);
6904 GST_DEBUG_OBJECT (demux,
6905 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
6906 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
6907 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
6908 demux->offset, discont_offset, distance_from_discont);
6912 switch (demux->state) {
6913 case QTDEMUX_STATE_INITIAL:{
6918 gst_qtdemux_check_seekability (demux);
6920 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6922 /* get fourcc/length, set neededbytes */
6923 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6925 gst_adapter_unmap (demux->adapter);
6927 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
6928 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
6930 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6931 (_("This file is invalid and cannot be played.")),
6932 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
6933 GST_FOURCC_ARGS (fourcc)));
6934 ret = GST_FLOW_ERROR;
6937 if (fourcc == FOURCC_mdat) {
6938 gint next_entry = next_entry_size (demux);
6939 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
6940 || !demux->fragmented)) {
6941 /* we have the headers, start playback */
6942 demux->state = QTDEMUX_STATE_MOVIE;
6943 demux->neededbytes = next_entry;
6944 demux->mdatleft = size;
6945 demux->mdatsize = demux->mdatleft;
6947 /* no headers yet, try to get them */
6950 guint64 old, target;
6953 old = demux->offset;
6954 target = old + size;
6956 /* try to jump over the atom with a seek */
6957 /* only bother if it seems worth doing so,
6958 * and avoids possible upstream/server problems */
6959 if (demux->upstream_seekable &&
6960 demux->upstream_size > 4 * (1 << 20)) {
6961 res = qtdemux_seek_offset (demux, target);
6963 GST_DEBUG_OBJECT (demux, "skipping seek");
6968 GST_DEBUG_OBJECT (demux, "seek success");
6969 /* remember the offset fo the first mdat so we can seek back to it
6970 * after we have the headers */
6971 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
6972 demux->first_mdat = old;
6973 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
6976 /* seek worked, continue reading */
6977 demux->offset = target;
6978 demux->neededbytes = 16;
6979 demux->state = QTDEMUX_STATE_INITIAL;
6981 /* seek failed, need to buffer */
6982 demux->offset = old;
6983 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
6984 /* there may be multiple mdat (or alike) buffers */
6986 if (demux->mdatbuffer)
6987 bs = gst_buffer_get_size (demux->mdatbuffer);
6990 if (size + bs > 10 * (1 << 20))
6992 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
6993 demux->neededbytes = size;
6994 if (!demux->mdatbuffer)
6995 demux->mdatoffset = demux->offset;
6998 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
6999 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7000 (_("This file is invalid and cannot be played.")),
7001 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7002 GST_FOURCC_ARGS (fourcc), size));
7003 ret = GST_FLOW_ERROR;
7006 /* this means we already started buffering and still no moov header,
7007 * let's continue buffering everything till we get moov */
7008 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7009 || fourcc == FOURCC_moof))
7011 demux->neededbytes = size;
7012 demux->state = QTDEMUX_STATE_HEADER;
7016 case QTDEMUX_STATE_HEADER:{
7020 GST_DEBUG_OBJECT (demux, "In header");
7022 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7024 /* parse the header */
7025 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7027 if (fourcc == FOURCC_moov) {
7028 /* in usual fragmented setup we could try to scan for more
7029 * and end up at the the moov (after mdat) again */
7030 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7032 || demux->last_moov_offset == demux->offset)) {
7033 GST_DEBUG_OBJECT (demux,
7034 "Skipping moov atom as we have (this) one already");
7036 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7038 if (demux->got_moov && demux->fragmented) {
7039 GST_DEBUG_OBJECT (demux,
7040 "Got a second moov, clean up data from old one");
7041 if (demux->moov_node_compressed) {
7042 g_node_destroy (demux->moov_node_compressed);
7043 if (demux->moov_node)
7044 g_free (demux->moov_node->data);
7046 demux->moov_node_compressed = NULL;
7047 if (demux->moov_node)
7048 g_node_destroy (demux->moov_node);
7049 demux->moov_node = NULL;
7052 demux->last_moov_offset = demux->offset;
7054 /* Update streams with new moov */
7055 gst_qtdemux_stream_concat (demux,
7056 demux->old_streams, demux->active_streams);
7058 qtdemux_parse_moov (demux, data, demux->neededbytes);
7059 qtdemux_node_dump (demux, demux->moov_node);
7060 qtdemux_parse_tree (demux);
7061 qtdemux_prepare_streams (demux);
7062 QTDEMUX_EXPOSE_LOCK (demux);
7063 qtdemux_expose_streams (demux);
7064 QTDEMUX_EXPOSE_UNLOCK (demux);
7066 demux->got_moov = TRUE;
7068 gst_qtdemux_check_send_pending_segment (demux);
7070 if (demux->moov_node_compressed) {
7071 g_node_destroy (demux->moov_node_compressed);
7072 g_free (demux->moov_node->data);
7074 demux->moov_node_compressed = NULL;
7075 g_node_destroy (demux->moov_node);
7076 demux->moov_node = NULL;
7077 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7079 } else if (fourcc == FOURCC_moof) {
7080 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7082 GstClockTime prev_pts;
7083 guint64 prev_offset;
7084 guint64 adapter_discont_offset, adapter_discont_dist;
7086 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7089 * The timestamp of the moof buffer is relevant as some scenarios
7090 * won't have the initial timestamp in the atoms. Whenever a new
7091 * buffer has started, we get that buffer's PTS and use it as a base
7092 * timestamp for the trun entries.
7094 * To keep track of the current buffer timestamp and starting point
7095 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7096 * from the beginning of the buffer, with the distance and demux->offset
7097 * we know if it is still the same buffer or not.
7099 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7100 prev_offset = demux->offset - dist;
7101 if (demux->fragment_start_offset == -1
7102 || prev_offset > demux->fragment_start_offset) {
7103 demux->fragment_start_offset = prev_offset;
7104 demux->fragment_start = prev_pts;
7105 GST_DEBUG_OBJECT (demux,
7106 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7107 GST_TIME_FORMAT, demux->fragment_start_offset,
7108 GST_TIME_ARGS (demux->fragment_start));
7111 /* We can't use prev_offset() here because this would require
7112 * upstream to set consistent and correct offsets on all buffers
7113 * since the discont. Nothing ever did that in the past and we
7114 * would break backwards compatibility here then.
7115 * Instead take the offset we had at the last discont and count
7116 * the bytes from there. This works with old code as there would
7117 * be no discont between moov and moof, and also works with
7118 * adaptivedemux which correctly sets offset and will set the
7119 * DISCONT flag accordingly when needed.
7121 * We also only do this for upstream TIME segments as otherwise
7122 * there are potential backwards compatibility problems with
7123 * seeking in PUSH mode and upstream providing inconsistent
7125 adapter_discont_offset =
7126 gst_adapter_offset_at_discont (demux->adapter);
7127 adapter_discont_dist =
7128 gst_adapter_distance_from_discont (demux->adapter);
7130 GST_DEBUG_OBJECT (demux,
7131 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7132 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7133 demux->offset, adapter_discont_offset, adapter_discont_dist);
7135 if (demux->upstream_format_is_time) {
7136 demux->moof_offset = adapter_discont_offset;
7137 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7138 demux->moof_offset += adapter_discont_dist;
7139 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7140 demux->moof_offset = demux->offset;
7142 demux->moof_offset = demux->offset;
7145 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7146 demux->moof_offset, NULL)) {
7147 gst_adapter_unmap (demux->adapter);
7148 ret = GST_FLOW_ERROR;
7152 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7153 if (demux->mss_mode && !demux->exposed) {
7154 QTDEMUX_EXPOSE_LOCK (demux);
7155 qtdemux_expose_streams (demux);
7156 QTDEMUX_EXPOSE_UNLOCK (demux);
7159 gst_qtdemux_check_send_pending_segment (demux);
7161 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7163 } else if (fourcc == FOURCC_ftyp) {
7164 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7165 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7166 } else if (fourcc == FOURCC_uuid) {
7167 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7168 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7169 } else if (fourcc == FOURCC_sidx) {
7170 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7171 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7175 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7179 /* [free] and [skip] are padding atoms */
7180 GST_DEBUG_OBJECT (demux,
7181 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7182 GST_FOURCC_ARGS (fourcc));
7185 GST_WARNING_OBJECT (demux,
7186 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7187 GST_FOURCC_ARGS (fourcc));
7188 /* Let's jump that one and go back to initial state */
7192 gst_adapter_unmap (demux->adapter);
7195 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7196 gsize remaining_data_size = 0;
7198 /* the mdat was before the header */
7199 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7200 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7201 /* restore our adapter/offset view of things with upstream;
7202 * put preceding buffered data ahead of current moov data.
7203 * This should also handle evil mdat, moov, mdat cases and alike */
7204 gst_adapter_flush (demux->adapter, demux->neededbytes);
7206 /* Store any remaining data after the mdat for later usage */
7207 remaining_data_size = gst_adapter_available (demux->adapter);
7208 if (remaining_data_size > 0) {
7209 g_assert (demux->restoredata_buffer == NULL);
7210 demux->restoredata_buffer =
7211 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7212 demux->restoredata_offset = demux->offset + demux->neededbytes;
7213 GST_DEBUG_OBJECT (demux,
7214 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7215 G_GUINT64_FORMAT, remaining_data_size,
7216 demux->restoredata_offset);
7219 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7220 demux->mdatbuffer = NULL;
7221 demux->offset = demux->mdatoffset;
7222 demux->neededbytes = next_entry_size (demux);
7223 demux->state = QTDEMUX_STATE_MOVIE;
7224 demux->mdatleft = gst_adapter_available (demux->adapter);
7225 demux->mdatsize = demux->mdatleft;
7227 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7228 gst_adapter_flush (demux->adapter, demux->neededbytes);
7230 /* only go back to the mdat if there are samples to play */
7231 if (demux->got_moov && demux->first_mdat != -1
7232 && has_next_entry (demux)) {
7235 /* we need to seek back */
7236 res = qtdemux_seek_offset (demux, demux->first_mdat);
7238 demux->offset = demux->first_mdat;
7240 GST_DEBUG_OBJECT (demux, "Seek back failed");
7243 demux->offset += demux->neededbytes;
7245 demux->neededbytes = 16;
7246 demux->state = QTDEMUX_STATE_INITIAL;
7251 case QTDEMUX_STATE_BUFFER_MDAT:{
7255 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7257 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7258 gst_buffer_extract (buf, 0, fourcc, 4);
7259 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7260 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7261 if (demux->mdatbuffer)
7262 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7264 demux->mdatbuffer = buf;
7265 demux->offset += demux->neededbytes;
7266 demux->neededbytes = 16;
7267 demux->state = QTDEMUX_STATE_INITIAL;
7268 gst_qtdemux_post_progress (demux, 1, 1);
7272 case QTDEMUX_STATE_MOVIE:{
7273 QtDemuxStream *stream = NULL;
7274 QtDemuxSample *sample;
7275 GstClockTime dts, pts, duration;
7279 GST_DEBUG_OBJECT (demux,
7280 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7282 if (demux->fragmented) {
7283 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7285 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7286 /* if needed data starts within this atom,
7287 * then it should not exceed this atom */
7288 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7289 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7290 (_("This file is invalid and cannot be played.")),
7291 ("sample data crosses atom boundary"));
7292 ret = GST_FLOW_ERROR;
7295 demux->mdatleft -= demux->neededbytes;
7297 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7298 /* so we are dropping more than left in this atom */
7299 gst_qtdemux_drop_data (demux, demux->mdatleft);
7300 demux->mdatleft = 0;
7302 /* need to resume atom parsing so we do not miss any other pieces */
7303 demux->state = QTDEMUX_STATE_INITIAL;
7304 demux->neededbytes = 16;
7306 /* check if there was any stored post mdat data from previous buffers */
7307 if (demux->restoredata_buffer) {
7308 g_assert (gst_adapter_available (demux->adapter) == 0);
7310 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7311 demux->restoredata_buffer = NULL;
7312 demux->offset = demux->restoredata_offset;
7319 if (demux->todrop) {
7320 if (demux->cenc_aux_info_offset > 0) {
7324 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7325 data = gst_adapter_map (demux->adapter, demux->todrop);
7326 gst_byte_reader_init (&br, data + 8, demux->todrop);
7327 if (!qtdemux_parse_cenc_aux_info (demux,
7328 QTDEMUX_NTH_STREAM (demux, 0), &br,
7329 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7330 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7331 ret = GST_FLOW_ERROR;
7332 gst_adapter_unmap (demux->adapter);
7333 g_free (demux->cenc_aux_info_sizes);
7334 demux->cenc_aux_info_sizes = NULL;
7337 demux->cenc_aux_info_offset = 0;
7338 g_free (demux->cenc_aux_info_sizes);
7339 demux->cenc_aux_info_sizes = NULL;
7340 gst_adapter_unmap (demux->adapter);
7342 gst_qtdemux_drop_data (demux, demux->todrop);
7346 /* initial newsegment sent here after having added pads,
7347 * possible others in sink_event */
7348 gst_qtdemux_check_send_pending_segment (demux);
7350 /* Figure out which stream this packet belongs to */
7351 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7352 stream = QTDEMUX_NTH_STREAM (demux, i);
7353 if (stream->sample_index >= stream->n_samples) {
7354 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7358 GST_LOG_OBJECT (demux,
7359 "Checking track-id %u (sample_index:%d / offset:%"
7360 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7361 stream->sample_index,
7362 stream->samples[stream->sample_index].offset,
7363 stream->samples[stream->sample_index].size);
7365 if (stream->samples[stream->sample_index].offset == demux->offset)
7369 if (G_UNLIKELY (stream == NULL))
7370 goto unknown_stream;
7372 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7374 if (stream->new_caps) {
7375 gst_qtdemux_configure_stream (demux, stream);
7378 /* Put data in a buffer, set timestamps, caps, ... */
7379 sample = &stream->samples[stream->sample_index];
7381 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7382 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7383 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7385 dts = QTSAMPLE_DTS (stream, sample);
7386 pts = QTSAMPLE_PTS (stream, sample);
7387 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7388 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7390 /* check for segment end */
7391 if (G_UNLIKELY (demux->segment.stop != -1
7392 && demux->segment.stop <= pts && stream->on_keyframe)
7393 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7394 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7395 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7397 /* skip this data, stream is EOS */
7398 gst_adapter_flush (demux->adapter, demux->neededbytes);
7399 demux->offset += demux->neededbytes;
7401 /* check if all streams are eos */
7403 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7404 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
7413 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7415 /* FIXME: should either be an assert or a plain check */
7416 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7418 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7419 dts, pts, duration, keyframe, dts, demux->offset);
7423 GST_OBJECT_LOCK (demux);
7424 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7425 GST_OBJECT_UNLOCK (demux);
7427 /* skip this data, stream is EOS */
7428 gst_adapter_flush (demux->adapter, demux->neededbytes);
7431 stream->sample_index++;
7432 stream->offset_in_sample = 0;
7434 /* update current offset and figure out size of next buffer */
7435 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7436 demux->offset, demux->neededbytes);
7437 demux->offset += demux->neededbytes;
7438 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7442 if (ret == GST_FLOW_EOS) {
7443 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7444 demux->neededbytes = -1;
7448 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7449 if (demux->fragmented) {
7450 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7451 /* there may be more to follow, only finish this atom */
7452 demux->todrop = demux->mdatleft;
7453 demux->neededbytes = demux->todrop;
7458 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7459 goto non_ok_unlinked_flow;
7468 /* when buffering movie data, at least show user something is happening */
7469 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7470 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7471 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7472 demux->neededbytes);
7479 non_ok_unlinked_flow:
7481 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7482 gst_flow_get_name (ret));
7487 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7488 ret = GST_FLOW_ERROR;
7493 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7499 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7500 (NULL), ("qtdemuxer invalid state %d", demux->state));
7501 ret = GST_FLOW_ERROR;
7506 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7507 (NULL), ("no 'moov' atom within the first 10 MB"));
7508 ret = GST_FLOW_ERROR;
7514 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7519 query = gst_query_new_scheduling ();
7521 if (!gst_pad_peer_query (sinkpad, query)) {
7522 gst_query_unref (query);
7526 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7527 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7528 gst_query_unref (query);
7533 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7534 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7538 GST_DEBUG_OBJECT (sinkpad, "activating push");
7539 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7544 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7545 GstPadMode mode, gboolean active)
7548 GstQTDemux *demux = GST_QTDEMUX (parent);
7551 case GST_PAD_MODE_PUSH:
7552 demux->pullbased = FALSE;
7555 case GST_PAD_MODE_PULL:
7557 demux->pullbased = TRUE;
7558 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7561 res = gst_pad_stop_task (sinkpad);
7573 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7579 memset (&z, 0, sizeof (z));
7584 if ((ret = inflateInit (&z)) != Z_OK) {
7585 GST_ERROR ("inflateInit() returned %d", ret);
7589 z.next_in = z_buffer;
7590 z.avail_in = z_length;
7592 buffer = (guint8 *) g_malloc (*length);
7593 z.avail_out = *length;
7594 z.next_out = (Bytef *) buffer;
7596 ret = inflate (&z, Z_NO_FLUSH);
7597 if (ret == Z_STREAM_END) {
7599 } else if (ret != Z_OK) {
7600 GST_WARNING ("inflate() returned %d", ret);
7605 buffer = (guint8 *) g_realloc (buffer, *length);
7606 z.next_out = (Bytef *) (buffer + z.total_out);
7607 z.avail_out += 4096;
7608 } while (z.avail_in > 0);
7610 if (ret != Z_STREAM_END) {
7615 *length = z.total_out;
7622 #endif /* HAVE_ZLIB */
7625 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7629 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7631 /* counts as header data */
7632 qtdemux->header_size += length;
7634 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7635 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7637 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7644 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7645 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7646 if (dcom == NULL || cmvd == NULL)
7647 goto invalid_compression;
7649 dcom_len = QT_UINT32 (dcom->data);
7651 goto invalid_compression;
7653 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7657 guint uncompressed_length;
7658 guint compressed_length;
7662 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7664 goto invalid_compression;
7666 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7667 compressed_length = cmvd_len - 12;
7668 GST_LOG ("length = %u", uncompressed_length);
7671 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7672 compressed_length, &uncompressed_length);
7675 qtdemux->moov_node_compressed = qtdemux->moov_node;
7676 qtdemux->moov_node = g_node_new (buf);
7678 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7679 uncompressed_length);
7683 #endif /* HAVE_ZLIB */
7685 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7686 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7693 invalid_compression:
7695 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
7701 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
7704 while (G_UNLIKELY (buf < end)) {
7708 if (G_UNLIKELY (buf + 4 > end)) {
7709 GST_LOG_OBJECT (qtdemux, "buffer overrun");
7712 len = QT_UINT32 (buf);
7713 if (G_UNLIKELY (len == 0)) {
7714 GST_LOG_OBJECT (qtdemux, "empty container");
7717 if (G_UNLIKELY (len < 8)) {
7718 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
7721 if (G_UNLIKELY (len > (end - buf))) {
7722 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
7723 (gint) (end - buf));
7727 child = g_node_new ((guint8 *) buf);
7728 g_node_append (node, child);
7729 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
7730 qtdemux_parse_node (qtdemux, child, buf, len);
7738 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
7741 int len = QT_UINT32 (xdxt->data);
7742 guint8 *buf = xdxt->data;
7743 guint8 *end = buf + len;
7746 /* skip size and type */
7754 size = QT_UINT32 (buf);
7755 type = QT_FOURCC (buf + 4);
7757 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
7759 if (buf + size > end || size <= 0)
7765 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
7766 GST_FOURCC_ARGS (type));
7770 buffer = gst_buffer_new_and_alloc (size);
7771 gst_buffer_fill (buffer, 0, buf, size);
7772 stream->buffers = g_slist_append (stream->buffers, buffer);
7773 GST_LOG_OBJECT (qtdemux, "parsing theora header");
7776 buffer = gst_buffer_new_and_alloc (size);
7777 gst_buffer_fill (buffer, 0, buf, size);
7778 stream->buffers = g_slist_append (stream->buffers, buffer);
7779 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
7782 buffer = gst_buffer_new_and_alloc (size);
7783 gst_buffer_fill (buffer, 0, buf, size);
7784 stream->buffers = g_slist_append (stream->buffers, buffer);
7785 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
7788 GST_WARNING_OBJECT (qtdemux,
7789 "unknown theora cookie %" GST_FOURCC_FORMAT,
7790 GST_FOURCC_ARGS (type));
7799 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
7803 guint32 node_length = 0;
7804 const QtNodeType *type;
7807 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
7809 if (G_UNLIKELY (length < 8))
7810 goto not_enough_data;
7812 node_length = QT_UINT32 (buffer);
7813 fourcc = QT_FOURCC (buffer + 4);
7815 /* ignore empty nodes */
7816 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
7819 type = qtdemux_type_get (fourcc);
7821 end = buffer + length;
7823 GST_LOG_OBJECT (qtdemux,
7824 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
7825 GST_FOURCC_ARGS (fourcc), node_length, type->name);
7827 if (node_length > length)
7828 goto broken_atom_size;
7830 if (type->flags & QT_FLAG_CONTAINER) {
7831 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7836 if (node_length < 20) {
7837 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
7840 GST_DEBUG_OBJECT (qtdemux,
7841 "parsing stsd (sample table, sample description) atom");
7842 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
7843 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7854 /* also read alac (or whatever) in stead of mp4a in the following,
7855 * since a similar layout is used in other cases as well */
7856 if (fourcc == FOURCC_mp4a)
7858 else if (fourcc == FOURCC_fLaC)
7863 /* There are two things we might encounter here: a true mp4a atom, and
7864 an mp4a entry in an stsd atom. The latter is what we're interested
7865 in, and it looks like an atom, but isn't really one. The true mp4a
7866 atom is short, so we detect it based on length here. */
7867 if (length < min_size) {
7868 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7869 GST_FOURCC_ARGS (fourcc));
7873 /* 'version' here is the sound sample description version. Types 0 and
7874 1 are documented in the QTFF reference, but type 2 is not: it's
7875 described in Apple header files instead (struct SoundDescriptionV2
7877 version = QT_UINT16 (buffer + 16);
7879 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7880 GST_FOURCC_ARGS (fourcc), version);
7882 /* parse any esds descriptors */
7894 GST_WARNING_OBJECT (qtdemux,
7895 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7896 GST_FOURCC_ARGS (fourcc), version);
7901 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7927 /* codec_data is contained inside these atoms, which all have
7928 * the same format. */
7929 /* video sample description size is 86 bytes without extension.
7930 * node_length have to be bigger than 86 bytes because video sample
7931 * description can include extensions such as esds, fiel, glbl, etc. */
7932 if (node_length < 86) {
7933 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
7934 " sample description length too short (%u < 86)",
7935 GST_FOURCC_ARGS (fourcc), node_length);
7939 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
7940 GST_FOURCC_ARGS (fourcc));
7942 /* version (2 bytes) : this is set to 0, unless a compressor has changed
7944 * revision level (2 bytes) : must be set to 0. */
7945 version = QT_UINT32 (buffer + 16);
7946 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
7948 /* compressor name : PASCAL string and informative purposes
7949 * first byte : the number of bytes to be displayed.
7950 * it has to be less than 32 because it is reserved
7951 * space of 32 bytes total including itself. */
7952 str_len = QT_UINT8 (buffer + 50);
7954 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
7955 (char *) buffer + 51);
7957 GST_WARNING_OBJECT (qtdemux,
7958 "compressorname length too big (%u > 31)", str_len);
7960 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
7962 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
7967 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
7969 /* You are reading this correctly. QTFF specifies that the
7970 * metadata atom is a short atom, whereas ISO BMFF specifies
7971 * it's a full atom. But since so many people are doing things
7972 * differently, we actually peek into the atom to see which
7975 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7976 GST_FOURCC_ARGS (fourcc));
7979 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
7980 /* Variant 1: What QTFF specifies. 'meta' is a short header which
7981 * starts with a 'hdlr' atom */
7982 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7983 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
7984 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
7985 * with version/flags both set to zero */
7986 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
7988 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
7993 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
7994 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
7995 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8004 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8005 GST_FOURCC_ARGS (fourcc));
8009 version = QT_UINT32 (buffer + 12);
8010 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8017 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8022 if (length < offset) {
8023 GST_WARNING_OBJECT (qtdemux,
8024 "skipping too small %" GST_FOURCC_FORMAT " box",
8025 GST_FOURCC_ARGS (fourcc));
8028 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8034 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8039 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8044 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8048 if (!strcmp (type->name, "unknown"))
8049 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8053 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8054 GST_FOURCC_ARGS (fourcc));
8060 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8061 (_("This file is corrupt and cannot be played.")),
8062 ("Not enough data for an atom header, got only %u bytes", length));
8067 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8068 (_("This file is corrupt and cannot be played.")),
8069 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8070 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8077 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8079 /* FIXME: This can only reliably work if demuxers have a
8080 * separate streaming thread per srcpad. This should be
8081 * done in a demuxer base class, which integrates parts
8084 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8089 query = gst_query_new_allocation (stream->caps, FALSE);
8091 if (!gst_pad_peer_query (stream->pad, query)) {
8092 /* not a problem, just debug a little */
8093 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8096 if (stream->allocator)
8097 gst_object_unref (stream->allocator);
8099 if (gst_query_get_n_allocation_params (query) > 0) {
8100 /* try the allocator */
8101 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8103 stream->use_allocator = TRUE;
8105 stream->allocator = NULL;
8106 gst_allocation_params_init (&stream->params);
8107 stream->use_allocator = FALSE;
8109 gst_query_unref (query);
8114 pad_query (const GValue * item, GValue * value, gpointer user_data)
8116 GstPad *pad = g_value_get_object (item);
8117 GstQuery *query = user_data;
8120 res = gst_pad_peer_query (pad, query);
8123 g_value_set_boolean (value, TRUE);
8127 GST_INFO_OBJECT (pad, "pad peer query failed");
8132 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8133 GstPadDirection direction)
8136 GstIteratorFoldFunction func = pad_query;
8137 GValue res = { 0, };
8139 g_value_init (&res, G_TYPE_BOOLEAN);
8140 g_value_set_boolean (&res, FALSE);
8143 if (direction == GST_PAD_SRC)
8144 it = gst_element_iterate_src_pads (element);
8146 it = gst_element_iterate_sink_pads (element);
8148 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8149 gst_iterator_resync (it);
8151 gst_iterator_free (it);
8153 return g_value_get_boolean (&res);
8157 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8158 QtDemuxStream * stream)
8162 GstElement *element = GST_ELEMENT (qtdemux);
8164 gchar **filtered_sys_ids;
8165 GValue event_list = G_VALUE_INIT;
8168 /* 1. Check if we already have the context. */
8169 if (qtdemux->preferred_protection_system_id != NULL) {
8170 GST_LOG_OBJECT (element,
8171 "already have the protection context, no need to request it again");
8175 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8176 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8177 (const gchar **) qtdemux->protection_system_ids->pdata);
8179 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8180 qtdemux->protection_system_ids->len - 1);
8181 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8182 "decryptors for %u of them, running context request",
8183 qtdemux->protection_system_ids->len,
8184 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8187 if (stream->protection_scheme_event_queue.length) {
8188 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8189 stream->protection_scheme_event_queue.length);
8190 walk = stream->protection_scheme_event_queue.tail;
8192 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8193 qtdemux->protection_event_queue.length);
8194 walk = qtdemux->protection_event_queue.tail;
8197 g_value_init (&event_list, GST_TYPE_LIST);
8198 for (; walk; walk = g_list_previous (walk)) {
8199 GValue *event_value = g_new0 (GValue, 1);
8200 g_value_init (event_value, GST_TYPE_EVENT);
8201 g_value_set_boxed (event_value, walk->data);
8202 gst_value_list_append_and_take_value (&event_list, event_value);
8205 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8206 * check if downstream already has a context of the specific type
8207 * 2b) Query upstream as above.
8209 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8210 st = gst_query_writable_structure (query);
8211 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8212 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8214 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8215 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8216 gst_query_parse_context (query, &ctxt);
8217 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8218 gst_element_set_context (element, ctxt);
8219 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8220 gst_query_parse_context (query, &ctxt);
8221 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8222 gst_element_set_context (element, ctxt);
8224 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8225 * the required context type and afterwards check if a
8226 * usable context was set now as in 1). The message could
8227 * be handled by the parent bins of the element and the
8232 GST_INFO_OBJECT (element, "posting need context message");
8233 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8234 "drm-preferred-decryption-system-id");
8235 st = (GstStructure *) gst_message_get_structure (msg);
8236 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8237 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8240 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8241 gst_element_post_message (element, msg);
8244 g_strfreev (filtered_sys_ids);
8245 g_value_unset (&event_list);
8246 gst_query_unref (query);
8250 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8251 QtDemuxStream * stream)
8254 const gchar *selected_system = NULL;
8256 g_return_val_if_fail (qtdemux != NULL, FALSE);
8257 g_return_val_if_fail (stream != NULL, FALSE);
8258 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8261 if (stream->protection_scheme_type != FOURCC_cenc) {
8262 GST_ERROR_OBJECT (qtdemux,
8263 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8264 GST_FOURCC_ARGS (stream->protection_scheme_type));
8267 if (qtdemux->protection_system_ids == NULL) {
8268 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
8269 "cenc protection system information has been found");
8273 gst_qtdemux_request_protection_context (qtdemux, stream);
8274 if (qtdemux->preferred_protection_system_id != NULL) {
8275 const gchar *preferred_system_array[] =
8276 { qtdemux->preferred_protection_system_id, NULL };
8278 selected_system = gst_protection_select_system (preferred_system_array);
8280 if (selected_system) {
8281 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8282 qtdemux->preferred_protection_system_id);
8284 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8285 "because there is no available decryptor",
8286 qtdemux->preferred_protection_system_id);
8290 if (!selected_system) {
8291 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8292 selected_system = gst_protection_select_system ((const gchar **)
8293 qtdemux->protection_system_ids->pdata);
8294 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8295 qtdemux->protection_system_ids->len - 1);
8298 if (!selected_system) {
8299 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8300 "suitable decryptor element has been found");
8304 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8307 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8308 if (!gst_structure_has_name (s, "application/x-cenc")) {
8309 gst_structure_set (s,
8310 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8311 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8313 gst_structure_set_name (s, "application/x-cenc");
8319 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
8321 /* fps is calculated base on the duration of the average framerate since
8322 * qt does not have a fixed framerate. */
8323 gboolean fps_available = TRUE;
8324 guint32 first_duration = 0;
8326 if (stream->n_samples > 0)
8327 first_duration = stream->samples[0].duration;
8329 if ((stream->n_samples == 1 && first_duration == 0)
8330 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8332 CUR_STREAM (stream)->fps_n = 0;
8333 CUR_STREAM (stream)->fps_d = 1;
8335 if (stream->duration == 0 || stream->n_samples < 2) {
8336 CUR_STREAM (stream)->fps_n = stream->timescale;
8337 CUR_STREAM (stream)->fps_d = 1;
8338 fps_available = FALSE;
8340 GstClockTime avg_duration;
8344 /* duration and n_samples can be updated for fragmented format
8345 * so, framerate of fragmented format is calculated using data in a moof */
8346 if (qtdemux->fragmented && stream->n_samples_moof > 0
8347 && stream->duration_moof > 0) {
8348 n_samples = stream->n_samples_moof;
8349 duration = stream->duration_moof;
8351 n_samples = stream->n_samples;
8352 duration = stream->duration;
8355 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8356 /* stream->duration is guint64, timescale, n_samples are guint32 */
8358 gst_util_uint64_scale_round (duration -
8359 first_duration, GST_SECOND,
8360 (guint64) (stream->timescale) * (n_samples - 1));
8362 GST_LOG_OBJECT (qtdemux,
8363 "Calculating avg sample duration based on stream (or moof) duration %"
8365 " minus first sample %u, leaving %d samples gives %"
8366 GST_TIME_FORMAT, duration, first_duration,
8367 n_samples - 1, GST_TIME_ARGS (avg_duration));
8370 gst_video_guess_framerate (avg_duration,
8371 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
8373 GST_DEBUG_OBJECT (qtdemux,
8374 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8375 stream->timescale, CUR_STREAM (stream)->fps_n,
8376 CUR_STREAM (stream)->fps_d);
8380 return fps_available;
8384 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8386 if (stream->subtype == FOURCC_vide) {
8387 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8389 if (CUR_STREAM (stream)->caps) {
8390 CUR_STREAM (stream)->caps =
8391 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8393 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
8394 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8395 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8396 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8398 /* set framerate if calculated framerate is reliable */
8399 if (fps_available) {
8400 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8401 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8402 CUR_STREAM (stream)->fps_d, NULL);
8405 /* calculate pixel-aspect-ratio using display width and height */
8406 GST_DEBUG_OBJECT (qtdemux,
8407 "video size %dx%d, target display size %dx%d",
8408 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8409 stream->display_width, stream->display_height);
8410 /* qt file might have pasp atom */
8411 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8412 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8413 CUR_STREAM (stream)->par_h);
8414 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8415 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8416 CUR_STREAM (stream)->par_h, NULL);
8417 } else if (stream->display_width > 0 && stream->display_height > 0
8418 && CUR_STREAM (stream)->width > 0
8419 && CUR_STREAM (stream)->height > 0) {
8422 /* calculate the pixel aspect ratio using the display and pixel w/h */
8423 n = stream->display_width * CUR_STREAM (stream)->height;
8424 d = stream->display_height * CUR_STREAM (stream)->width;
8427 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8428 CUR_STREAM (stream)->par_w = n;
8429 CUR_STREAM (stream)->par_h = d;
8430 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8431 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8432 CUR_STREAM (stream)->par_h, NULL);
8435 if (CUR_STREAM (stream)->interlace_mode > 0) {
8436 if (CUR_STREAM (stream)->interlace_mode == 1) {
8437 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8438 G_TYPE_STRING, "progressive", NULL);
8439 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8440 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8441 G_TYPE_STRING, "interleaved", NULL);
8442 if (CUR_STREAM (stream)->field_order == 9) {
8443 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8444 G_TYPE_STRING, "top-field-first", NULL);
8445 } else if (CUR_STREAM (stream)->field_order == 14) {
8446 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8447 G_TYPE_STRING, "bottom-field-first", NULL);
8452 /* Create incomplete colorimetry here if needed */
8453 if (CUR_STREAM (stream)->colorimetry.range ||
8454 CUR_STREAM (stream)->colorimetry.matrix ||
8455 CUR_STREAM (stream)->colorimetry.transfer
8456 || CUR_STREAM (stream)->colorimetry.primaries) {
8457 gchar *colorimetry =
8458 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8459 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8460 G_TYPE_STRING, colorimetry, NULL);
8461 g_free (colorimetry);
8464 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8465 guint par_w = 1, par_h = 1;
8467 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8468 par_w = CUR_STREAM (stream)->par_w;
8469 par_h = CUR_STREAM (stream)->par_h;
8472 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8473 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8475 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8478 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8479 "multiview-mode", G_TYPE_STRING,
8480 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8481 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8482 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8487 else if (stream->subtype == FOURCC_soun) {
8488 if (CUR_STREAM (stream)->caps) {
8489 CUR_STREAM (stream)->caps =
8490 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8491 if (CUR_STREAM (stream)->rate > 0)
8492 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8493 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8494 if (CUR_STREAM (stream)->n_channels > 0)
8495 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8496 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8497 if (CUR_STREAM (stream)->n_channels > 2) {
8498 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8499 * correctly; this is just the minimum we can do - assume
8500 * we don't actually have any channel positions. */
8501 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8502 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8507 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
8508 const GstStructure *s;
8509 QtDemuxStream *fps_stream = NULL;
8510 gboolean fps_available = FALSE;
8512 /* CEA608 closed caption tracks are a bit special in that each sample
8513 * can contain CCs for multiple frames, and CCs can be omitted and have to
8514 * be inferred from the duration of the sample then.
8516 * As such we take the framerate from the (first) video track here for
8517 * CEA608 as there must be one CC byte pair for every video frame
8518 * according to the spec.
8520 * For CEA708 all is fine and there is one sample per frame.
8523 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8524 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
8527 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
8528 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
8530 if (tmp->subtype == FOURCC_vide) {
8537 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
8538 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
8539 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
8542 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8543 fps_stream = stream;
8546 CUR_STREAM (stream)->caps =
8547 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8549 /* set framerate if calculated framerate is reliable */
8550 if (fps_available) {
8551 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8552 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8553 CUR_STREAM (stream)->fps_d, NULL);
8558 GstCaps *prev_caps = NULL;
8560 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8561 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8562 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8563 gst_pad_set_active (stream->pad, TRUE);
8565 gst_pad_use_fixed_caps (stream->pad);
8567 if (stream->protected) {
8568 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8569 GST_ERROR_OBJECT (qtdemux,
8570 "Failed to configure protected stream caps.");
8575 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8576 CUR_STREAM (stream)->caps);
8577 if (stream->new_stream) {
8579 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8582 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8585 gst_event_parse_stream_flags (event, &stream_flags);
8586 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8587 qtdemux->have_group_id = TRUE;
8589 qtdemux->have_group_id = FALSE;
8590 gst_event_unref (event);
8591 } else if (!qtdemux->have_group_id) {
8592 qtdemux->have_group_id = TRUE;
8593 qtdemux->group_id = gst_util_group_id_next ();
8596 stream->new_stream = FALSE;
8597 event = gst_event_new_stream_start (stream->stream_id);
8598 if (qtdemux->have_group_id)
8599 gst_event_set_group_id (event, qtdemux->group_id);
8600 if (stream->disabled)
8601 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8602 if (CUR_STREAM (stream)->sparse) {
8603 stream_flags |= GST_STREAM_FLAG_SPARSE;
8605 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8607 gst_event_set_stream_flags (event, stream_flags);
8608 gst_pad_push_event (stream->pad, event);
8611 prev_caps = gst_pad_get_current_caps (stream->pad);
8613 if (CUR_STREAM (stream)->caps) {
8615 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8616 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8617 CUR_STREAM (stream)->caps);
8618 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8620 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8623 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8627 gst_caps_unref (prev_caps);
8628 stream->new_caps = FALSE;
8634 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8635 QtDemuxStream * stream)
8637 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8640 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8641 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8642 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8643 stream->stsd_entries_length)) {
8644 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8645 (_("This file is invalid and cannot be played.")),
8646 ("New sample description id is out of bounds (%d >= %d)",
8647 stream->stsd_sample_description_id, stream->stsd_entries_length));
8649 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8650 stream->new_caps = TRUE;
8655 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8656 QtDemuxStream * stream, GstTagList * list)
8658 gboolean ret = TRUE;
8660 if (stream->subtype == FOURCC_vide) {
8661 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8664 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8667 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8668 gst_object_unref (stream->pad);
8674 qtdemux->n_video_streams++;
8675 } else if (stream->subtype == FOURCC_soun) {
8676 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
8679 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
8681 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8682 gst_object_unref (stream->pad);
8687 qtdemux->n_audio_streams++;
8688 } else if (stream->subtype == FOURCC_strm) {
8689 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
8690 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
8691 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
8692 || stream->subtype == FOURCC_clcp) {
8693 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
8696 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
8698 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8699 gst_object_unref (stream->pad);
8704 qtdemux->n_sub_streams++;
8705 } else if (CUR_STREAM (stream)->caps) {
8706 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8709 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8711 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8712 gst_object_unref (stream->pad);
8717 qtdemux->n_video_streams++;
8719 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
8726 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
8727 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
8728 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
8729 GST_OBJECT_LOCK (qtdemux);
8730 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
8731 GST_OBJECT_UNLOCK (qtdemux);
8733 if (stream->stream_tags)
8734 gst_tag_list_unref (stream->stream_tags);
8735 stream->stream_tags = list;
8737 /* global tags go on each pad anyway */
8738 stream->send_global_tags = TRUE;
8739 /* send upstream GST_EVENT_PROTECTION events that were received before
8740 this source pad was created */
8741 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
8742 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
8746 gst_tag_list_unref (list);
8750 /* find next atom with @fourcc starting at @offset */
8751 static GstFlowReturn
8752 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
8753 guint64 * length, guint32 fourcc)
8759 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
8760 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
8766 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
8767 if (G_UNLIKELY (ret != GST_FLOW_OK))
8769 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
8772 gst_buffer_unref (buf);
8775 gst_buffer_map (buf, &map, GST_MAP_READ);
8776 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
8777 gst_buffer_unmap (buf, &map);
8778 gst_buffer_unref (buf);
8780 if (G_UNLIKELY (*length == 0)) {
8781 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
8782 ret = GST_FLOW_ERROR;
8786 if (lfourcc == fourcc) {
8787 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
8791 GST_LOG_OBJECT (qtdemux,
8792 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
8793 GST_FOURCC_ARGS (fourcc), *offset);
8802 /* might simply have had last one */
8803 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
8808 /* should only do something in pull mode */
8809 /* call with OBJECT lock */
8810 static GstFlowReturn
8811 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
8813 guint64 length, offset;
8814 GstBuffer *buf = NULL;
8815 GstFlowReturn ret = GST_FLOW_OK;
8816 GstFlowReturn res = GST_FLOW_OK;
8819 offset = qtdemux->moof_offset;
8820 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
8823 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8824 return GST_FLOW_EOS;
8827 /* best not do pull etc with lock held */
8828 GST_OBJECT_UNLOCK (qtdemux);
8830 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8831 if (ret != GST_FLOW_OK)
8834 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
8835 if (G_UNLIKELY (ret != GST_FLOW_OK))
8837 gst_buffer_map (buf, &map, GST_MAP_READ);
8838 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
8839 gst_buffer_unmap (buf, &map);
8840 gst_buffer_unref (buf);
8845 gst_buffer_unmap (buf, &map);
8846 gst_buffer_unref (buf);
8850 /* look for next moof */
8851 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8852 if (G_UNLIKELY (ret != GST_FLOW_OK))
8856 GST_OBJECT_LOCK (qtdemux);
8858 qtdemux->moof_offset = offset;
8864 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
8866 res = GST_FLOW_ERROR;
8871 /* maybe upstream temporarily flushing */
8872 if (ret != GST_FLOW_FLUSHING) {
8873 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8876 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
8877 /* resume at current position next time */
8885 qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
8889 gint32 stts_duration;
8890 GstByteWriter stsc, stts, stsz;
8892 /* Each sample has a different size, which we don't support for merging */
8893 if (stream->sample_size == 0) {
8894 GST_DEBUG_OBJECT (qtdemux,
8895 "Not all samples have the same size, not merging");
8899 /* The stream has a ctts table, we don't support that */
8900 if (stream->ctts_present) {
8901 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
8905 /* If there's a sync sample table also ignore this stream */
8906 if (stream->stps_present || stream->stss_present) {
8907 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
8911 /* If chunks are considered samples already ignore this stream */
8912 if (stream->chunks_are_samples) {
8913 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
8917 /* Require that all samples have the same duration */
8918 if (stream->n_sample_times > 1) {
8919 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
8923 /* Parse the stts to get the sample duration and number of samples */
8924 gst_byte_reader_skip_unchecked (&stream->stts, 4);
8925 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
8927 /* Parse the number of chunks from the stco manually because the
8928 * reader is already behind that */
8929 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
8931 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
8934 /* Now parse stsc, convert chunks into single samples and generate a
8935 * new stsc, stts and stsz from this information */
8936 gst_byte_writer_init (&stsc);
8937 gst_byte_writer_init (&stts);
8938 gst_byte_writer_init (&stsz);
8940 /* Note: we skip fourccs, size, version, flags and other fields of the new
8941 * atoms as the byte readers with them are already behind that position
8942 * anyway and only update the values of those inside the stream directly.
8944 stream->n_sample_times = 0;
8945 stream->n_samples = 0;
8946 for (i = 0; i < stream->n_samples_per_chunk; i++) {
8948 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
8950 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
8951 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
8952 sample_description_id =
8953 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
8955 if (i == stream->n_samples_per_chunk - 1) {
8956 /* +1 because first_chunk is 1-based */
8957 last_chunk = num_chunks + 1;
8959 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
8962 GST_DEBUG_OBJECT (qtdemux,
8963 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
8964 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
8966 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
8967 /* One sample in this chunk */
8968 gst_byte_writer_put_uint32_be (&stsc, 1);
8969 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
8971 /* For each chunk write a stts and stsz entry now */
8972 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
8973 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
8974 for (j = first_chunk; j < last_chunk; j++) {
8975 gst_byte_writer_put_uint32_be (&stsz,
8976 stream->sample_size * samples_per_chunk);
8979 stream->n_sample_times += 1;
8980 stream->n_samples += last_chunk - first_chunk;
8983 g_assert_cmpint (stream->n_samples, ==, num_chunks);
8985 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
8986 stream->n_samples, stream->n_sample_times);
8988 /* We don't have a fixed sample size anymore */
8989 stream->sample_size = 0;
8991 /* Free old data for the atoms */
8992 g_free ((gpointer) stream->stsz.data);
8993 stream->stsz.data = NULL;
8994 g_free ((gpointer) stream->stsc.data);
8995 stream->stsc.data = NULL;
8996 g_free ((gpointer) stream->stts.data);
8997 stream->stts.data = NULL;
8999 /* Store new data and replace byte readers */
9000 stream->stsz.size = gst_byte_writer_get_size (&stsz);
9001 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
9002 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
9003 stream->stts.size = gst_byte_writer_get_size (&stts);
9004 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
9005 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
9006 stream->stsc.size = gst_byte_writer_get_size (&stsc);
9007 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
9008 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
9011 /* initialise bytereaders for stbl sub-atoms */
9013 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9015 stream->stbl_index = -1; /* no samples have yet been parsed */
9016 stream->sample_index = -1;
9018 /* time-to-sample atom */
9019 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9022 /* copy atom data into a new buffer for later use */
9023 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
9025 /* skip version + flags */
9026 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9027 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9029 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9031 /* make sure there's enough data */
9032 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9033 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9034 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9035 stream->n_sample_times);
9036 if (!stream->n_sample_times)
9040 /* sync sample atom */
9041 stream->stps_present = FALSE;
9042 if ((stream->stss_present =
9043 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9044 &stream->stss) ? TRUE : FALSE) == TRUE) {
9045 /* copy atom data into a new buffer for later use */
9046 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
9048 /* skip version + flags */
9049 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9050 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9053 if (stream->n_sample_syncs) {
9054 /* make sure there's enough data */
9055 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9059 /* partial sync sample atom */
9060 if ((stream->stps_present =
9061 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9062 &stream->stps) ? TRUE : FALSE) == TRUE) {
9063 /* copy atom data into a new buffer for later use */
9064 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
9066 /* skip version + flags */
9067 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9068 !gst_byte_reader_get_uint32_be (&stream->stps,
9069 &stream->n_sample_partial_syncs))
9072 /* if there are no entries, the stss table contains the real
9074 if (stream->n_sample_partial_syncs) {
9075 /* make sure there's enough data */
9076 if (!qt_atom_parser_has_chunks (&stream->stps,
9077 stream->n_sample_partial_syncs, 4))
9084 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9087 /* copy atom data into a new buffer for later use */
9088 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
9090 /* skip version + flags */
9091 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9092 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9095 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9098 if (!stream->n_samples)
9101 /* sample-to-chunk atom */
9102 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9105 /* copy atom data into a new buffer for later use */
9106 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
9108 /* skip version + flags */
9109 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9110 !gst_byte_reader_get_uint32_be (&stream->stsc,
9111 &stream->n_samples_per_chunk))
9114 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9115 stream->n_samples_per_chunk);
9117 /* make sure there's enough data */
9118 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9124 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9125 stream->co_size = sizeof (guint32);
9126 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9128 stream->co_size = sizeof (guint64);
9132 /* copy atom data into a new buffer for later use */
9133 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
9135 /* skip version + flags */
9136 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9139 /* chunks_are_samples == TRUE means treat chunks as samples */
9140 stream->chunks_are_samples = stream->sample_size
9141 && !CUR_STREAM (stream)->sampled;
9142 if (stream->chunks_are_samples) {
9143 /* treat chunks as samples */
9144 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9147 /* skip number of entries */
9148 if (!gst_byte_reader_skip (&stream->stco, 4))
9151 /* make sure there are enough data in the stsz atom */
9152 if (!stream->sample_size) {
9153 /* different sizes for each sample */
9154 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9159 /* composition time-to-sample */
9160 if ((stream->ctts_present =
9161 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9162 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9163 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9165 /* copy atom data into a new buffer for later use */
9166 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
9168 /* skip version + flags */
9169 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9170 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9171 &stream->n_composition_times))
9174 /* make sure there's enough data */
9175 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9179 /* This is optional, if missing we iterate the ctts */
9180 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9181 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9182 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9183 g_free ((gpointer) cslg.data);
9187 gint32 cslg_least = 0;
9188 guint num_entries, pos;
9191 pos = gst_byte_reader_get_pos (&stream->ctts);
9192 num_entries = stream->n_composition_times;
9194 stream->cslg_shift = 0;
9196 for (i = 0; i < num_entries; i++) {
9199 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9200 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9201 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9202 * slightly inaccurate PTS could be more usable than corrupted one */
9203 if (G_UNLIKELY ((ABS (offset) / 2) > stream->duration)) {
9204 GST_WARNING_OBJECT (qtdemux,
9205 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9206 " larger than duration %" G_GUINT64_FORMAT,
9207 offset, stream->duration);
9209 stream->cslg_shift = 0;
9210 stream->ctts_present = FALSE;
9214 if (offset < cslg_least)
9215 cslg_least = offset;
9219 stream->cslg_shift = ABS (cslg_least);
9221 stream->cslg_shift = 0;
9223 /* reset the reader so we can generate sample table */
9224 gst_byte_reader_set_pos (&stream->ctts, pos);
9227 /* Ensure the cslg_shift value is consistent so we can use it
9228 * unconditionally to produce TS and Segment */
9229 stream->cslg_shift = 0;
9232 /* For raw audio streams especially we might want to merge the samples
9233 * to not output one audio sample per buffer. We're doing this here
9234 * before allocating the sample tables so that from this point onwards
9235 * the number of container samples are static */
9236 if (stream->min_buffer_size > 0) {
9237 qtdemux_merge_sample_table (qtdemux, stream);
9241 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9242 stream->n_samples, (guint) sizeof (QtDemuxSample),
9243 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9245 if (stream->n_samples >=
9246 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9247 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9248 "be larger than %uMB (broken file?)", stream->n_samples,
9249 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9253 g_assert (stream->samples == NULL);
9254 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9255 if (!stream->samples) {
9256 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9265 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9266 (_("This file is corrupt and cannot be played.")), (NULL));
9271 gst_qtdemux_stbl_free (stream);
9272 if (!qtdemux->fragmented) {
9273 /* not quite good */
9274 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9277 /* may pick up samples elsewhere */
9283 /* collect samples from the next sample to be parsed up to sample @n for @stream
9284 * by reading the info from @stbl
9286 * This code can be executed from both the streaming thread and the seeking
9287 * thread so it takes the object lock to protect itself
9290 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9293 QtDemuxSample *samples, *first, *cur, *last;
9294 guint32 n_samples_per_chunk;
9297 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9298 GST_FOURCC_FORMAT ", pad %s",
9299 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9300 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9302 n_samples = stream->n_samples;
9305 goto out_of_samples;
9307 GST_OBJECT_LOCK (qtdemux);
9308 if (n <= stream->stbl_index)
9309 goto already_parsed;
9311 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9313 if (!stream->stsz.data) {
9314 /* so we already parsed and passed all the moov samples;
9315 * onto fragmented ones */
9316 g_assert (qtdemux->fragmented);
9320 /* pointer to the sample table */
9321 samples = stream->samples;
9323 /* starts from -1, moves to the next sample index to parse */
9324 stream->stbl_index++;
9326 /* keep track of the first and last sample to fill */
9327 first = &samples[stream->stbl_index];
9330 if (!stream->chunks_are_samples) {
9331 /* set the sample sizes */
9332 if (stream->sample_size == 0) {
9333 /* different sizes for each sample */
9334 for (cur = first; cur <= last; cur++) {
9335 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9336 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9337 (guint) (cur - samples), cur->size);
9340 /* samples have the same size */
9341 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9342 for (cur = first; cur <= last; cur++)
9343 cur->size = stream->sample_size;
9347 n_samples_per_chunk = stream->n_samples_per_chunk;
9350 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9353 if (stream->stsc_chunk_index >= stream->last_chunk
9354 || stream->stsc_chunk_index < stream->first_chunk) {
9355 stream->first_chunk =
9356 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9357 stream->samples_per_chunk =
9358 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9360 stream->stsd_sample_description_id =
9361 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9363 /* chunk numbers are counted from 1 it seems */
9364 if (G_UNLIKELY (stream->first_chunk == 0))
9367 --stream->first_chunk;
9369 /* the last chunk of each entry is calculated by taking the first chunk
9370 * of the next entry; except if there is no next, where we fake it with
9372 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9373 stream->last_chunk = G_MAXUINT32;
9375 stream->last_chunk =
9376 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9377 if (G_UNLIKELY (stream->last_chunk == 0))
9380 --stream->last_chunk;
9383 GST_LOG_OBJECT (qtdemux,
9384 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9385 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9386 stream->samples_per_chunk, stream->stsd_sample_description_id);
9388 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9391 if (stream->last_chunk != G_MAXUINT32) {
9392 if (!qt_atom_parser_peek_sub (&stream->stco,
9393 stream->first_chunk * stream->co_size,
9394 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9399 stream->co_chunk = stream->stco;
9400 if (!gst_byte_reader_skip (&stream->co_chunk,
9401 stream->first_chunk * stream->co_size))
9405 stream->stsc_chunk_index = stream->first_chunk;
9408 last_chunk = stream->last_chunk;
9410 if (stream->chunks_are_samples) {
9411 cur = &samples[stream->stsc_chunk_index];
9413 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9416 stream->stsc_chunk_index = j;
9421 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9424 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9425 "%" G_GUINT64_FORMAT, j, cur->offset);
9427 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9428 CUR_STREAM (stream)->bytes_per_frame > 0) {
9430 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9431 CUR_STREAM (stream)->samples_per_frame *
9432 CUR_STREAM (stream)->bytes_per_frame;
9434 cur->size = stream->samples_per_chunk;
9437 GST_DEBUG_OBJECT (qtdemux,
9438 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9439 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9440 stream->stco_sample_index)), cur->size);
9442 cur->timestamp = stream->stco_sample_index;
9443 cur->duration = stream->samples_per_chunk;
9444 cur->keyframe = TRUE;
9447 stream->stco_sample_index += stream->samples_per_chunk;
9449 stream->stsc_chunk_index = j;
9451 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9452 guint32 samples_per_chunk;
9453 guint64 chunk_offset;
9455 if (!stream->stsc_sample_index
9456 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9457 &stream->chunk_offset))
9460 samples_per_chunk = stream->samples_per_chunk;
9461 chunk_offset = stream->chunk_offset;
9463 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9464 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9465 G_GUINT64_FORMAT " and size %d",
9466 (guint) (cur - samples), chunk_offset, cur->size);
9468 cur->offset = chunk_offset;
9469 chunk_offset += cur->size;
9472 if (G_UNLIKELY (cur > last)) {
9474 stream->stsc_sample_index = k + 1;
9475 stream->chunk_offset = chunk_offset;
9476 stream->stsc_chunk_index = j;
9480 stream->stsc_sample_index = 0;
9482 stream->stsc_chunk_index = j;
9484 stream->stsc_index++;
9487 if (stream->chunks_are_samples)
9491 guint32 n_sample_times;
9493 n_sample_times = stream->n_sample_times;
9496 for (i = stream->stts_index; i < n_sample_times; i++) {
9497 guint32 stts_samples;
9498 gint32 stts_duration;
9501 if (stream->stts_sample_index >= stream->stts_samples
9502 || !stream->stts_sample_index) {
9504 stream->stts_samples =
9505 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9506 stream->stts_duration =
9507 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9509 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9510 i, stream->stts_samples, stream->stts_duration);
9512 stream->stts_sample_index = 0;
9515 stts_samples = stream->stts_samples;
9516 stts_duration = stream->stts_duration;
9517 stts_time = stream->stts_time;
9519 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9520 GST_DEBUG_OBJECT (qtdemux,
9521 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9522 (guint) (cur - samples), j,
9523 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9525 cur->timestamp = stts_time;
9526 cur->duration = stts_duration;
9528 /* avoid 32-bit wrap-around,
9529 * but still mind possible 'negative' duration */
9530 stts_time += (gint64) stts_duration;
9533 if (G_UNLIKELY (cur > last)) {
9535 stream->stts_time = stts_time;
9536 stream->stts_sample_index = j + 1;
9537 if (stream->stts_sample_index >= stream->stts_samples)
9538 stream->stts_index++;
9542 stream->stts_sample_index = 0;
9543 stream->stts_time = stts_time;
9544 stream->stts_index++;
9546 /* fill up empty timestamps with the last timestamp, this can happen when
9547 * the last samples do not decode and so we don't have timestamps for them.
9548 * We however look at the last timestamp to estimate the track length so we
9549 * need something in here. */
9550 for (; cur < last; cur++) {
9551 GST_DEBUG_OBJECT (qtdemux,
9552 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9553 (guint) (cur - samples),
9554 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9555 cur->timestamp = stream->stts_time;
9561 /* sample sync, can be NULL */
9562 if (stream->stss_present == TRUE) {
9563 guint32 n_sample_syncs;
9565 n_sample_syncs = stream->n_sample_syncs;
9567 if (!n_sample_syncs) {
9568 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9569 stream->all_keyframe = TRUE;
9571 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9572 /* note that the first sample is index 1, not 0 */
9575 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9577 if (G_LIKELY (index > 0 && index <= n_samples)) {
9579 samples[index].keyframe = TRUE;
9580 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9581 /* and exit if we have enough samples */
9582 if (G_UNLIKELY (index >= n)) {
9589 stream->stss_index = i;
9592 /* stps marks partial sync frames like open GOP I-Frames */
9593 if (stream->stps_present == TRUE) {
9594 guint32 n_sample_partial_syncs;
9596 n_sample_partial_syncs = stream->n_sample_partial_syncs;
9598 /* if there are no entries, the stss table contains the real
9600 if (n_sample_partial_syncs) {
9601 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
9602 /* note that the first sample is index 1, not 0 */
9605 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
9607 if (G_LIKELY (index > 0 && index <= n_samples)) {
9609 samples[index].keyframe = TRUE;
9610 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9611 /* and exit if we have enough samples */
9612 if (G_UNLIKELY (index >= n)) {
9619 stream->stps_index = i;
9623 /* no stss, all samples are keyframes */
9624 stream->all_keyframe = TRUE;
9625 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
9630 /* composition time to sample */
9631 if (stream->ctts_present == TRUE) {
9632 guint32 n_composition_times;
9634 gint32 ctts_soffset;
9636 /* Fill in the pts_offsets */
9638 n_composition_times = stream->n_composition_times;
9640 for (i = stream->ctts_index; i < n_composition_times; i++) {
9641 if (stream->ctts_sample_index >= stream->ctts_count
9642 || !stream->ctts_sample_index) {
9643 stream->ctts_count =
9644 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
9645 stream->ctts_soffset =
9646 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9647 stream->ctts_sample_index = 0;
9650 ctts_count = stream->ctts_count;
9651 ctts_soffset = stream->ctts_soffset;
9653 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
9654 cur->pts_offset = ctts_soffset;
9657 if (G_UNLIKELY (cur > last)) {
9659 stream->ctts_sample_index = j + 1;
9663 stream->ctts_sample_index = 0;
9664 stream->ctts_index++;
9668 stream->stbl_index = n;
9669 /* if index has been completely parsed, free data that is no-longer needed */
9670 if (n + 1 == stream->n_samples) {
9671 gst_qtdemux_stbl_free (stream);
9672 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
9673 if (qtdemux->pullbased) {
9674 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
9675 while (n + 1 == stream->n_samples)
9676 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
9680 GST_OBJECT_UNLOCK (qtdemux);
9687 GST_LOG_OBJECT (qtdemux,
9688 "Tried to parse up to sample %u but this sample has already been parsed",
9690 /* if fragmented, there may be more */
9691 if (qtdemux->fragmented && n == stream->stbl_index)
9693 GST_OBJECT_UNLOCK (qtdemux);
9699 GST_LOG_OBJECT (qtdemux,
9700 "Tried to parse up to sample %u but there are only %u samples", n + 1,
9702 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9703 (_("This file is corrupt and cannot be played.")), (NULL));
9708 GST_OBJECT_UNLOCK (qtdemux);
9709 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9710 (_("This file is corrupt and cannot be played.")), (NULL));
9715 /* collect all segment info for @stream.
9718 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
9722 /* accept edts if they contain gaps at start and there is only
9723 * one media segment */
9724 gboolean allow_pushbased_edts = TRUE;
9725 gint media_segments_count = 0;
9727 /* parse and prepare segment info from the edit list */
9728 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
9729 stream->n_segments = 0;
9730 stream->segments = NULL;
9731 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
9734 gint segment_number, entry_size;
9737 const guint8 *buffer;
9741 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
9742 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
9745 buffer = elst->data;
9747 size = QT_UINT32 (buffer);
9748 /* version, flags, n_segments */
9750 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9753 version = QT_UINT8 (buffer + 8);
9754 entry_size = (version == 1) ? 20 : 12;
9756 n_segments = QT_UINT32 (buffer + 12);
9758 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
9759 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9763 /* we might allocate a bit too much, at least allocate 1 segment */
9764 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
9766 /* segments always start from 0 */
9770 for (segment_number = 0; segment_number < n_segments; segment_number++) {
9773 gboolean empty_edit = FALSE;
9774 QtDemuxSegment *segment;
9776 GstClockTime media_start = GST_CLOCK_TIME_NONE;
9779 media_time = QT_UINT64 (buffer + 8);
9780 duration = QT_UINT64 (buffer);
9781 if (media_time == G_MAXUINT64)
9784 media_time = QT_UINT32 (buffer + 4);
9785 duration = QT_UINT32 (buffer);
9786 if (media_time == G_MAXUINT32)
9791 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
9793 segment = &stream->segments[segment_number];
9795 /* time and duration expressed in global timescale */
9796 segment->time = stime;
9797 if (duration != 0 || empty_edit) {
9798 /* edge case: empty edits with duration=zero are treated here.
9799 * (files should not have these anyway). */
9801 /* add non scaled values so we don't cause roundoff errors */
9803 stime = QTTIME_TO_GSTTIME (qtdemux, time);
9804 segment->duration = stime - segment->time;
9806 /* zero duration does not imply media_start == media_stop
9807 * but, only specify media_start. The edit ends with the track. */
9808 stime = segment->duration = GST_CLOCK_TIME_NONE;
9809 /* Don't allow more edits after this one. */
9810 n_segments = segment_number + 1;
9812 segment->stop_time = stime;
9814 segment->trak_media_start = media_time;
9815 /* media_time expressed in stream timescale */
9817 segment->media_start = media_start;
9818 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
9819 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
9820 media_segments_count++;
9822 segment->media_start = GST_CLOCK_TIME_NONE;
9823 segment->media_stop = GST_CLOCK_TIME_NONE;
9825 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
9827 if (rate_int <= 1) {
9828 /* 0 is not allowed, some programs write 1 instead of the floating point
9830 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
9834 segment->rate = rate_int / 65536.0;
9837 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
9838 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
9839 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
9840 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
9841 segment_number, GST_TIME_ARGS (segment->time),
9842 GST_TIME_ARGS (segment->duration),
9843 GST_TIME_ARGS (segment->media_start), media_time,
9844 GST_TIME_ARGS (segment->media_stop),
9845 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
9847 if (segment->stop_time > qtdemux->segment.stop &&
9848 !qtdemux->upstream_format_is_time) {
9849 GST_WARNING_OBJECT (qtdemux, "Segment %d "
9850 " extends to %" GST_TIME_FORMAT
9851 " past the end of the declared movie duration %" GST_TIME_FORMAT
9852 " movie segment will be extended", segment_number,
9853 GST_TIME_ARGS (segment->stop_time),
9854 GST_TIME_ARGS (qtdemux->segment.stop));
9855 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
9858 buffer += entry_size;
9860 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
9861 stream->n_segments = n_segments;
9862 if (media_segments_count != 1)
9863 allow_pushbased_edts = FALSE;
9867 /* push based does not handle segments, so act accordingly here,
9868 * and warn if applicable */
9869 if (!qtdemux->pullbased && !allow_pushbased_edts) {
9870 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
9871 /* remove and use default one below, we stream like it anyway */
9872 g_free (stream->segments);
9873 stream->segments = NULL;
9874 stream->n_segments = 0;
9877 /* no segments, create one to play the complete trak */
9878 if (stream->n_segments == 0) {
9879 GstClockTime stream_duration =
9880 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
9882 if (stream->segments == NULL)
9883 stream->segments = g_new (QtDemuxSegment, 1);
9885 /* represent unknown our way */
9886 if (stream_duration == 0)
9887 stream_duration = GST_CLOCK_TIME_NONE;
9889 stream->segments[0].time = 0;
9890 stream->segments[0].stop_time = stream_duration;
9891 stream->segments[0].duration = stream_duration;
9892 stream->segments[0].media_start = 0;
9893 stream->segments[0].media_stop = stream_duration;
9894 stream->segments[0].rate = 1.0;
9895 stream->segments[0].trak_media_start = 0;
9897 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
9898 GST_TIME_ARGS (stream_duration));
9899 stream->n_segments = 1;
9900 stream->dummy_segment = TRUE;
9902 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
9908 * Parses the stsd atom of a svq3 trak looking for
9909 * the SMI and gama atoms.
9912 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
9913 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
9915 const guint8 *_gamma = NULL;
9916 GstBuffer *_seqh = NULL;
9917 const guint8 *stsd_data = stsd_entry_data;
9918 guint32 length = QT_UINT32 (stsd_data);
9922 GST_WARNING_OBJECT (qtdemux, "stsd too short");
9928 version = QT_UINT16 (stsd_data);
9933 while (length > 8) {
9934 guint32 fourcc, size;
9936 size = QT_UINT32 (stsd_data);
9937 fourcc = QT_FOURCC (stsd_data + 4);
9938 data = stsd_data + 8;
9941 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
9942 "svq3 atom parsing");
9951 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
9952 " for gama atom, expected 12", size);
9957 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
9959 if (_seqh != NULL) {
9960 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
9961 " found, ignoring");
9963 seqh_size = QT_UINT32 (data + 4);
9964 if (seqh_size > 0) {
9965 _seqh = gst_buffer_new_and_alloc (seqh_size);
9966 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
9973 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
9974 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
9978 if (size <= length) {
9984 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
9987 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
9988 G_GUINT16_FORMAT, version);
9999 gst_buffer_unref (_seqh);
10004 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10007 GstByteReader dref;
10011 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10012 * atom that might contain a 'data' atom with the rtsp uri.
10013 * This case was reported in bug #597497, some info about
10014 * the hndl atom can be found in TN1195
10016 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10017 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10020 guint32 dref_num_entries = 0;
10021 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10022 gst_byte_reader_skip (&dref, 4) &&
10023 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10026 /* search dref entries for hndl atom */
10027 for (i = 0; i < dref_num_entries; i++) {
10028 guint32 size = 0, type;
10029 guint8 string_len = 0;
10030 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10031 qt_atom_parser_get_fourcc (&dref, &type)) {
10032 if (type == FOURCC_hndl) {
10033 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10035 /* skip data reference handle bytes and the
10036 * following pascal string and some extra 4
10037 * bytes I have no idea what are */
10038 if (!gst_byte_reader_skip (&dref, 4) ||
10039 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10040 !gst_byte_reader_skip (&dref, string_len + 4)) {
10041 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10045 /* iterate over the atoms to find the data atom */
10046 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10050 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10051 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10052 if (atom_type == FOURCC_data) {
10053 const guint8 *uri_aux = NULL;
10055 /* found the data atom that might contain the rtsp uri */
10056 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10057 "hndl atom, interpreting it as an URI");
10058 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10060 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10061 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10063 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10064 "didn't contain a rtsp address");
10066 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10071 /* skipping to the next entry */
10072 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10075 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10082 /* skip to the next entry */
10083 if (!gst_byte_reader_skip (&dref, size - 8))
10086 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10089 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10095 #define AMR_NB_ALL_MODES 0x81ff
10096 #define AMR_WB_ALL_MODES 0x83ff
10098 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10100 /* The 'damr' atom is of the form:
10102 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10103 * 32 b 8 b 16 b 8 b 8 b
10105 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10106 * represents the highest mode used in the stream (and thus the maximum
10107 * bitrate), with a couple of special cases as seen below.
10110 /* Map of frame type ID -> bitrate */
10111 static const guint nb_bitrates[] = {
10112 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10114 static const guint wb_bitrates[] = {
10115 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10121 gst_buffer_map (buf, &map, GST_MAP_READ);
10123 if (map.size != 0x11) {
10124 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10128 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10129 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10130 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10134 mode_set = QT_UINT16 (map.data + 13);
10136 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10137 max_mode = 7 + (wb ? 1 : 0);
10139 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10140 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10142 if (max_mode == -1) {
10143 GST_DEBUG ("No mode indication was found (mode set) = %x",
10148 gst_buffer_unmap (buf, &map);
10149 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10152 gst_buffer_unmap (buf, &map);
10157 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10158 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10161 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10167 if (gst_byte_reader_get_remaining (reader) < 36)
10170 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10171 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10172 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10173 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10174 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10175 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10176 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10177 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10178 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10180 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10181 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10182 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10184 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10185 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10187 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10188 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10195 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10196 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10203 * This macro will only compare value abdegh, it expects cfi to have already
10206 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10207 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10209 /* only handle the cases where the last column has standard values */
10210 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10211 const gchar *rotation_tag = NULL;
10213 /* no rotation needed */
10214 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10216 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10217 rotation_tag = "rotate-90";
10218 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10219 rotation_tag = "rotate-180";
10220 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10221 rotation_tag = "rotate-270";
10223 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10226 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10227 GST_STR_NULL (rotation_tag));
10228 if (rotation_tag != NULL) {
10229 if (*taglist == NULL)
10230 *taglist = gst_tag_list_new_empty ();
10231 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10232 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10235 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10239 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10240 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10241 * Common Encryption (cenc), the function will also parse the tenc box (defined
10242 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10243 * (typically an enc[v|a|t|s] sample entry); the function will set
10244 * @original_fmt to the fourcc of the original unencrypted stream format.
10245 * Returns TRUE if successful; FALSE otherwise. */
10247 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10248 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10254 QtDemuxCencSampleSetInfo *info;
10256 const guint8 *tenc_data;
10258 g_return_val_if_fail (qtdemux != NULL, FALSE);
10259 g_return_val_if_fail (stream != NULL, FALSE);
10260 g_return_val_if_fail (container != NULL, FALSE);
10261 g_return_val_if_fail (original_fmt != NULL, FALSE);
10263 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10264 if (G_UNLIKELY (!sinf)) {
10265 if (stream->protection_scheme_type == FOURCC_cenc) {
10266 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10267 "mandatory for Common Encryption");
10273 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10274 if (G_UNLIKELY (!frma)) {
10275 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10279 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10280 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10281 GST_FOURCC_ARGS (*original_fmt));
10283 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10285 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10288 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10289 stream->protection_scheme_version =
10290 QT_UINT32 ((const guint8 *) schm->data + 16);
10292 GST_DEBUG_OBJECT (qtdemux,
10293 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10294 "protection_scheme_version: %#010x",
10295 GST_FOURCC_ARGS (stream->protection_scheme_type),
10296 stream->protection_scheme_version);
10298 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10300 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10303 if (stream->protection_scheme_type != FOURCC_cenc &&
10304 stream->protection_scheme_type != FOURCC_piff) {
10305 GST_ERROR_OBJECT (qtdemux,
10306 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10307 GST_FOURCC_ARGS (stream->protection_scheme_type));
10311 if (G_UNLIKELY (!stream->protection_scheme_info))
10312 stream->protection_scheme_info =
10313 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10315 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10317 if (stream->protection_scheme_type == FOURCC_cenc) {
10318 guint32 is_encrypted;
10320 const guint8 *default_kid;
10322 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10324 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10325 "which is mandatory for Common Encryption");
10328 tenc_data = (const guint8 *) tenc->data + 12;
10329 is_encrypted = QT_UINT24 (tenc_data);
10330 iv_size = QT_UINT8 (tenc_data + 3);
10331 default_kid = (tenc_data + 4);
10332 qtdemux_update_default_sample_encryption_settings (qtdemux, info,
10333 is_encrypted, iv_size, default_kid);
10334 } else if (stream->protection_scheme_type == FOURCC_piff) {
10336 static const guint8 piff_track_encryption_uuid[] = {
10337 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10338 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10341 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10343 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10344 "which is mandatory for Common Encryption");
10348 tenc_data = (const guint8 *) tenc->data + 8;
10349 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10350 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10351 GST_ERROR_OBJECT (qtdemux,
10352 "Unsupported track encryption box with uuid: %s", box_uuid);
10356 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10357 gst_byte_reader_init (&br, tenc_data, 20);
10358 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10359 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10362 stream->protection_scheme_type = FOURCC_cenc;
10369 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10370 QtDemuxStream ** stream2)
10372 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10376 qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
10381 /*parse svmi header if existing */
10382 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10384 guint len = QT_UINT32 ((guint8 *) svmi->data);
10385 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10387 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10388 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10389 guint8 frame_type, frame_layout;
10390 guint32 stereo_mono_change_count;
10395 /* MPEG-A stereo video */
10396 if (qtdemux->major_brand == FOURCC_ss02)
10397 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10399 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10400 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10401 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
10403 switch (frame_type) {
10405 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10408 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10411 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10414 /* mode 3 is primary/secondary view sequence, ie
10415 * left/right views in separate tracks. See section 7.2
10416 * of ISO/IEC 23000-11:2009 */
10417 /* In the future this might be supported using related
10418 * streams, like an enhancement track - if files like this
10420 GST_FIXME_OBJECT (qtdemux,
10421 "Implement stereo video in separate streams");
10424 if ((frame_layout & 0x1) == 0)
10425 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10427 GST_LOG_OBJECT (qtdemux,
10428 "StereoVideo: composition type: %u, is_left_first: %u",
10429 frame_type, frame_layout);
10431 if (stereo_mono_change_count > 1) {
10432 GST_FIXME_OBJECT (qtdemux,
10433 "Mixed-mono flags are not yet supported in qtdemux.");
10436 stream->multiview_mode = mode;
10437 stream->multiview_flags = flags;
10444 /* parse the traks.
10445 * With each track we associate a new QtDemuxStream that contains all the info
10447 * traks that do not decode to something (like strm traks) will not have a pad.
10450 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10452 GstByteReader tkhd;
10466 QtDemuxStream *stream = NULL;
10467 const guint8 *stsd_data;
10468 const guint8 *stsd_entry_data;
10469 guint remaining_stsd_len;
10470 guint stsd_entry_count;
10472 guint16 lang_code; /* quicktime lang code or packed iso code */
10474 guint32 tkhd_flags = 0;
10475 guint8 tkhd_version = 0;
10476 guint32 w = 0, h = 0;
10477 guint value_size, stsd_len, len;
10481 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10483 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10484 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10485 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10488 /* pick between 64 or 32 bits */
10489 value_size = tkhd_version == 1 ? 8 : 4;
10490 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10491 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10494 /* Check if current moov has duplicated track_id */
10495 if (qtdemux_find_stream (qtdemux, track_id))
10496 goto existing_stream;
10498 stream = _create_stream (qtdemux, track_id);
10499 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10501 /* need defaults for fragments */
10502 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10504 if ((tkhd_flags & 1) == 0)
10505 stream->disabled = TRUE;
10507 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10508 tkhd_version, tkhd_flags, stream->track_id);
10510 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10513 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10514 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10515 if (qtdemux->major_brand != FOURCC_mjp2 ||
10516 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
10520 len = QT_UINT32 ((guint8 *) mdhd->data);
10521 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
10522 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
10523 if (version == 0x01000000) {
10526 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
10527 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
10528 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
10532 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
10533 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
10534 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
10537 if (lang_code < 0x400) {
10538 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
10539 } else if (lang_code == 0x7fff) {
10540 stream->lang_id[0] = 0; /* unspecified */
10542 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
10543 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
10544 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
10545 stream->lang_id[3] = 0;
10548 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
10549 stream->timescale);
10550 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
10552 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
10553 lang_code, stream->lang_id);
10555 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
10558 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
10559 /* chapters track reference */
10560 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
10562 gsize length = GST_READ_UINT32_BE (chap->data);
10563 if (qtdemux->chapters_track_id)
10564 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
10566 if (length >= 12) {
10567 qtdemux->chapters_track_id =
10568 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
10573 /* fragmented files may have bogus duration in moov */
10574 if (!qtdemux->fragmented &&
10575 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
10576 guint64 tdur1, tdur2;
10578 /* don't overflow */
10579 tdur1 = stream->timescale * (guint64) qtdemux->duration;
10580 tdur2 = qtdemux->timescale * (guint64) stream->duration;
10583 * some of those trailers, nowadays, have prologue images that are
10584 * themselves video tracks as well. I haven't really found a way to
10585 * identify those yet, except for just looking at their duration. */
10586 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
10587 GST_WARNING_OBJECT (qtdemux,
10588 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
10589 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
10590 "found, assuming preview image or something; skipping track",
10591 stream->duration, stream->timescale, qtdemux->duration,
10592 qtdemux->timescale);
10593 gst_qtdemux_stream_unref (stream);
10598 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
10601 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
10602 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
10604 len = QT_UINT32 ((guint8 *) hdlr->data);
10606 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
10607 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
10608 GST_FOURCC_ARGS (stream->subtype));
10610 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
10613 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
10616 /* Parse out svmi (and later st3d/sv3d) atoms */
10617 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
10620 /* parse rest of tkhd */
10621 if (stream->subtype == FOURCC_vide) {
10624 /* version 1 uses some 64-bit ints */
10625 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
10628 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
10631 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
10632 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
10635 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
10636 &stream->stream_tags);
10640 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
10642 stsd_data = (const guint8 *) stsd->data;
10644 /* stsd should at least have one entry */
10645 stsd_len = QT_UINT32 (stsd_data);
10646 if (stsd_len < 24) {
10647 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
10648 if (stream->subtype == FOURCC_vivo) {
10649 gst_qtdemux_stream_unref (stream);
10656 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
10657 if (stream->stsd_entries_length == 0)
10659 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10660 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10661 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10663 stsd_entry_data = stsd_data + 16;
10664 remaining_stsd_len = stsd_len - 16;
10665 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10667 gchar *codec = NULL;
10668 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10670 /* and that entry should fit within stsd */
10671 len = QT_UINT32 (stsd_entry_data);
10672 if (len > remaining_stsd_len)
10675 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10676 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10677 GST_FOURCC_ARGS (entry->fourcc));
10678 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10680 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10681 goto error_encrypted;
10683 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10684 /* FIXME this looks wrong, there might be multiple children
10685 * with the same type */
10686 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10687 stream->protected = TRUE;
10688 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10689 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10692 if (stream->subtype == FOURCC_vide) {
10697 gint depth, palette_size, palette_count;
10698 guint32 *palette_data = NULL;
10700 entry->sampled = TRUE;
10702 stream->display_width = w >> 16;
10703 stream->display_height = h >> 16;
10706 if (len < 86) /* TODO verify */
10709 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10710 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10711 entry->fps_n = 0; /* this is filled in later */
10712 entry->fps_d = 0; /* this is filled in later */
10713 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10714 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10716 /* if color_table_id is 0, ctab atom must follow; however some files
10717 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10718 * if color table is not present we'll correct the value */
10719 if (entry->color_table_id == 0 &&
10721 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10722 entry->color_table_id = -1;
10725 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10726 entry->width, entry->height, entry->bits_per_sample,
10727 entry->color_table_id);
10729 depth = entry->bits_per_sample;
10731 /* more than 32 bits means grayscale */
10732 gray = (depth > 32);
10733 /* low 32 bits specify the depth */
10736 /* different number of palette entries is determined by depth. */
10738 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10739 palette_count = (1 << depth);
10740 palette_size = palette_count * 4;
10742 if (entry->color_table_id) {
10743 switch (palette_count) {
10747 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10750 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10755 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10757 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10762 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10764 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10767 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10768 (_("The video in this file might not play correctly.")),
10769 ("unsupported palette depth %d", depth));
10773 gint i, j, start, end;
10779 start = QT_UINT32 (stsd_entry_data + offset + 70);
10780 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10781 end = QT_UINT16 (stsd_entry_data + offset + 76);
10783 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10784 start, end, palette_count);
10791 if (len < 94 + (end - start) * 8)
10794 /* palette is always the same size */
10795 palette_data = g_malloc0 (256 * 4);
10796 palette_size = 256 * 4;
10798 for (j = 0, i = start; i <= end; j++, i++) {
10799 guint32 a, r, g, b;
10801 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10802 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10803 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10804 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10806 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10807 (g & 0xff00) | (b >> 8);
10812 gst_caps_unref (entry->caps);
10815 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10817 if (G_UNLIKELY (!entry->caps)) {
10818 g_free (palette_data);
10819 goto unknown_stream;
10823 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10824 GST_TAG_VIDEO_CODEC, codec, NULL);
10829 if (palette_data) {
10832 if (entry->rgb8_palette)
10833 gst_memory_unref (entry->rgb8_palette);
10834 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10835 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10837 s = gst_caps_get_structure (entry->caps, 0);
10839 /* non-raw video has a palette_data property. raw video has the palette as
10840 * an extra plane that we append to the output buffers before we push
10842 if (!gst_structure_has_name (s, "video/x-raw")) {
10843 GstBuffer *palette;
10845 palette = gst_buffer_new ();
10846 gst_buffer_append_memory (palette, entry->rgb8_palette);
10847 entry->rgb8_palette = NULL;
10849 gst_caps_set_simple (entry->caps, "palette_data",
10850 GST_TYPE_BUFFER, palette, NULL);
10851 gst_buffer_unref (palette);
10853 } else if (palette_count != 0) {
10854 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10855 (NULL), ("Unsupported palette depth %d", depth));
10858 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10859 QT_UINT16 (stsd_entry_data + offset + 32));
10865 /* pick 'the' stsd child */
10866 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10867 // We should skip parsing the stsd for non-protected streams if
10868 // the entry doesn't match the fourcc, since they don't change
10869 // format. However, for protected streams we can have partial
10870 // encryption, where parts of the stream are encrypted and parts
10871 // not. For both parts of such streams, we should ensure the
10872 // esds overrides are parsed for both from the stsd.
10873 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10874 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
10876 else if (!stream->protected)
10881 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10882 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10883 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10884 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10888 const guint8 *pasp_data = (const guint8 *) pasp->data;
10889 gint len = QT_UINT32 (pasp_data);
10892 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10893 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10895 CUR_STREAM (stream)->par_w = 0;
10896 CUR_STREAM (stream)->par_h = 0;
10899 CUR_STREAM (stream)->par_w = 0;
10900 CUR_STREAM (stream)->par_h = 0;
10904 const guint8 *fiel_data = (const guint8 *) fiel->data;
10905 gint len = QT_UINT32 (fiel_data);
10908 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10909 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10914 const guint8 *colr_data = (const guint8 *) colr->data;
10915 gint len = QT_UINT32 (colr_data);
10917 if (len == 19 || len == 18) {
10918 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10920 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10921 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10922 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10923 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10924 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10926 CUR_STREAM (stream)->colorimetry.primaries =
10927 gst_video_color_primaries_from_iso (primaries);
10928 CUR_STREAM (stream)->colorimetry.transfer =
10929 gst_video_color_transfer_from_iso (transfer_function);
10930 CUR_STREAM (stream)->colorimetry.matrix =
10931 gst_video_color_matrix_from_iso (matrix);
10932 CUR_STREAM (stream)->colorimetry.range =
10933 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10934 GST_VIDEO_COLOR_RANGE_16_235;
10936 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10939 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10944 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10945 stream->stream_tags);
10952 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10953 const guint8 *avc_data = stsd_entry_data + 0x56;
10956 while (len >= 0x8) {
10959 if (QT_UINT32 (avc_data) <= len)
10960 size = QT_UINT32 (avc_data) - 0x8;
10965 /* No real data, so break out */
10968 switch (QT_FOURCC (avc_data + 0x4)) {
10971 /* parse, if found */
10974 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10976 /* First 4 bytes are the length of the atom, the next 4 bytes
10977 * are the fourcc, the next 1 byte is the version, and the
10978 * subsequent bytes are profile_tier_level structure like data. */
10979 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
10980 avc_data + 8 + 1, size - 1);
10981 buf = gst_buffer_new_and_alloc (size);
10982 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
10983 gst_caps_set_simple (entry->caps,
10984 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10985 gst_buffer_unref (buf);
10993 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
10995 /* First 4 bytes are the length of the atom, the next 4 bytes
10996 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
10997 * next 1 byte is the version, and the
10998 * subsequent bytes are sequence parameter set like data. */
11000 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11002 gst_codec_utils_h264_caps_set_level_and_profile
11003 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11005 buf = gst_buffer_new_and_alloc (size);
11006 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11007 gst_caps_set_simple (entry->caps,
11008 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11009 gst_buffer_unref (buf);
11015 guint avg_bitrate, max_bitrate;
11017 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11021 max_bitrate = QT_UINT32 (avc_data + 0xc);
11022 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11024 if (!max_bitrate && !avg_bitrate)
11027 /* Some muxers seem to swap the average and maximum bitrates
11028 * (I'm looking at you, YouTube), so we swap for sanity. */
11029 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11030 guint temp = avg_bitrate;
11032 avg_bitrate = max_bitrate;
11033 max_bitrate = temp;
11036 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11037 gst_tag_list_add (stream->stream_tags,
11038 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11039 max_bitrate, NULL);
11041 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11042 gst_tag_list_add (stream->stream_tags,
11043 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11055 avc_data += size + 8;
11064 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11065 const guint8 *hevc_data = stsd_entry_data + 0x56;
11068 while (len >= 0x8) {
11071 if (QT_UINT32 (hevc_data) <= len)
11072 size = QT_UINT32 (hevc_data) - 0x8;
11077 /* No real data, so break out */
11080 switch (QT_FOURCC (hevc_data + 0x4)) {
11083 /* parse, if found */
11086 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11088 /* First 4 bytes are the length of the atom, the next 4 bytes
11089 * are the fourcc, the next 1 byte is the version, and the
11090 * subsequent bytes are sequence parameter set like data. */
11091 gst_codec_utils_h265_caps_set_level_tier_and_profile
11092 (entry->caps, hevc_data + 8 + 1, size - 1);
11094 buf = gst_buffer_new_and_alloc (size);
11095 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11096 gst_caps_set_simple (entry->caps,
11097 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11098 gst_buffer_unref (buf);
11105 hevc_data += size + 8;
11118 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11119 GST_FOURCC_ARGS (fourcc));
11121 /* codec data might be in glbl extension atom */
11123 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11129 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11131 len = QT_UINT32 (data);
11134 buf = gst_buffer_new_and_alloc (len);
11135 gst_buffer_fill (buf, 0, data + 8, len);
11136 gst_caps_set_simple (entry->caps,
11137 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11138 gst_buffer_unref (buf);
11145 /* see annex I of the jpeg2000 spec */
11146 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11147 const guint8 *data;
11148 const gchar *colorspace = NULL;
11150 guint32 ncomp_map = 0;
11151 gint32 *comp_map = NULL;
11152 guint32 nchan_def = 0;
11153 gint32 *chan_def = NULL;
11155 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11156 /* some required atoms */
11157 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11160 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11164 /* number of components; redundant with info in codestream, but useful
11166 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11167 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11169 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11171 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11174 GST_DEBUG_OBJECT (qtdemux, "found colr");
11175 /* extract colour space info */
11176 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11177 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11179 colorspace = "sRGB";
11182 colorspace = "GRAY";
11185 colorspace = "sYUV";
11193 /* colr is required, and only values 16, 17, and 18 are specified,
11194 so error if we have no colorspace */
11197 /* extract component mapping */
11198 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11200 guint32 cmap_len = 0;
11202 cmap_len = QT_UINT32 (cmap->data);
11203 if (cmap_len >= 8) {
11204 /* normal box, subtract off header */
11206 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11207 if (cmap_len % 4 == 0) {
11208 ncomp_map = (cmap_len / 4);
11209 comp_map = g_new0 (gint32, ncomp_map);
11210 for (i = 0; i < ncomp_map; i++) {
11213 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11214 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11215 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11216 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11221 /* extract channel definitions */
11222 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11224 guint32 cdef_len = 0;
11226 cdef_len = QT_UINT32 (cdef->data);
11227 if (cdef_len >= 10) {
11228 /* normal box, subtract off header and len */
11230 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11231 if (cdef_len % 6 == 0) {
11232 nchan_def = (cdef_len / 6);
11233 chan_def = g_new0 (gint32, nchan_def);
11234 for (i = 0; i < nchan_def; i++)
11236 for (i = 0; i < nchan_def; i++) {
11237 guint16 cn, typ, asoc;
11238 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11239 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11240 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11241 if (cn < nchan_def) {
11244 chan_def[cn] = asoc;
11247 chan_def[cn] = 0; /* alpha */
11250 chan_def[cn] = -typ;
11258 gst_caps_set_simple (entry->caps,
11259 "num-components", G_TYPE_INT, ncomp, NULL);
11260 gst_caps_set_simple (entry->caps,
11261 "colorspace", G_TYPE_STRING, colorspace, NULL);
11264 GValue arr = { 0, };
11265 GValue elt = { 0, };
11267 g_value_init (&arr, GST_TYPE_ARRAY);
11268 g_value_init (&elt, G_TYPE_INT);
11269 for (i = 0; i < ncomp_map; i++) {
11270 g_value_set_int (&elt, comp_map[i]);
11271 gst_value_array_append_value (&arr, &elt);
11273 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11274 "component-map", &arr);
11275 g_value_unset (&elt);
11276 g_value_unset (&arr);
11281 GValue arr = { 0, };
11282 GValue elt = { 0, };
11284 g_value_init (&arr, GST_TYPE_ARRAY);
11285 g_value_init (&elt, G_TYPE_INT);
11286 for (i = 0; i < nchan_def; i++) {
11287 g_value_set_int (&elt, chan_def[i]);
11288 gst_value_array_append_value (&arr, &elt);
11290 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11291 "channel-definitions", &arr);
11292 g_value_unset (&elt);
11293 g_value_unset (&arr);
11297 /* some optional atoms */
11298 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11299 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11301 /* indicate possible fields in caps */
11303 data = (guint8 *) field->data + 8;
11305 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11306 (gint) * data, NULL);
11308 /* add codec_data if provided */
11313 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11314 data = prefix->data;
11315 len = QT_UINT32 (data);
11318 buf = gst_buffer_new_and_alloc (len);
11319 gst_buffer_fill (buf, 0, data + 8, len);
11320 gst_caps_set_simple (entry->caps,
11321 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11322 gst_buffer_unref (buf);
11331 GstBuffer *seqh = NULL;
11332 const guint8 *gamma_data = NULL;
11333 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11335 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11338 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11339 QT_FP32 (gamma_data), NULL);
11342 /* sorry for the bad name, but we don't know what this is, other
11343 * than its own fourcc */
11344 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11346 gst_buffer_unref (seqh);
11349 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11350 buf = gst_buffer_new_and_alloc (len);
11351 gst_buffer_fill (buf, 0, stsd_data, len);
11352 gst_caps_set_simple (entry->caps,
11353 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11354 gst_buffer_unref (buf);
11359 /* https://developer.apple.com/standards/qtff-2001.pdf,
11360 * page 92, "Video Sample Description", under table 3.1 */
11363 const gint compressor_offset =
11364 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11365 const gint min_size = compressor_offset + 32 + 2 + 2;
11368 guint16 color_table_id = 0;
11371 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11373 /* recover information on interlaced/progressive */
11374 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11378 len = QT_UINT32 (jpeg->data);
11379 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11381 if (len >= min_size) {
11382 gst_byte_reader_init (&br, jpeg->data, len);
11384 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11385 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11386 if (color_table_id != 0) {
11387 /* the spec says there can be concatenated chunks in the data, and we want
11388 * to find one called field. Walk through them. */
11389 gint offset = min_size;
11390 while (offset + 8 < len) {
11391 guint32 size = 0, tag;
11392 ok = gst_byte_reader_get_uint32_le (&br, &size);
11393 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11394 if (!ok || size < 8) {
11395 GST_WARNING_OBJECT (qtdemux,
11396 "Failed to walk optional chunk list");
11399 GST_DEBUG_OBJECT (qtdemux,
11400 "Found optional %4.4s chunk, size %u",
11401 (const char *) &tag, size);
11402 if (tag == FOURCC_fiel) {
11403 guint8 n_fields = 0, ordering = 0;
11404 gst_byte_reader_get_uint8 (&br, &n_fields);
11405 gst_byte_reader_get_uint8 (&br, &ordering);
11406 if (n_fields == 1 || n_fields == 2) {
11407 GST_DEBUG_OBJECT (qtdemux,
11408 "Found fiel tag with %u fields, ordering %u",
11409 n_fields, ordering);
11411 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11412 "interlace-mode", G_TYPE_STRING, "interleaved",
11415 GST_WARNING_OBJECT (qtdemux,
11416 "Found fiel tag with invalid fields (%u)", n_fields);
11422 GST_DEBUG_OBJECT (qtdemux,
11423 "Color table ID is 0, not trying to get interlacedness");
11426 GST_WARNING_OBJECT (qtdemux,
11427 "Length of jpeg chunk is too small, not trying to get interlacedness");
11435 gst_caps_set_simple (entry->caps,
11436 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11442 GNode *xith, *xdxt;
11444 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11445 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11449 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11453 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11454 /* collect the headers and store them in a stream list so that we can
11455 * send them out first */
11456 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11466 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11467 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11470 ovc1_data = ovc1->data;
11471 ovc1_len = QT_UINT32 (ovc1_data);
11472 if (ovc1_len <= 198) {
11473 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11476 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11477 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11478 gst_caps_set_simple (entry->caps,
11479 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11480 gst_buffer_unref (buf);
11485 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11486 const guint8 *vc1_data = stsd_entry_data + 0x56;
11492 if (QT_UINT32 (vc1_data) <= len)
11493 size = QT_UINT32 (vc1_data) - 8;
11498 /* No real data, so break out */
11501 switch (QT_FOURCC (vc1_data + 0x4)) {
11502 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11506 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11507 buf = gst_buffer_new_and_alloc (size);
11508 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11509 gst_caps_set_simple (entry->caps,
11510 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11511 gst_buffer_unref (buf);
11518 vc1_data += size + 8;
11524 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11525 const guint8 *av1_data = stsd_entry_data + 0x56;
11528 while (len >= 0x8) {
11531 if (QT_UINT32 (av1_data) <= len)
11532 size = QT_UINT32 (av1_data) - 0x8;
11537 /* No real data, so break out */
11540 switch (QT_FOURCC (av1_data + 0x4)) {
11543 /* parse, if found */
11545 guint8 pres_delay_field;
11547 GST_DEBUG_OBJECT (qtdemux,
11548 "found av1C codec_data in stsd of size %d", size);
11550 /* not enough data, just ignore and hope for the best */
11555 * 4 bytes: atom length
11560 * 1 bits: initial_presentation_delay_present
11561 * 4 bits: initial_presentation_delay (if present else reserved
11565 if (av1_data[9] != 0) {
11566 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
11570 /* We skip initial_presentation_delay* for now */
11571 pres_delay_field = *(av1_data + 12);
11572 if (pres_delay_field & (1 << 5)) {
11573 gst_caps_set_simple (entry->caps,
11574 "presentation-delay", G_TYPE_INT,
11575 (gint) (pres_delay_field & 0x0F) + 1, NULL);
11578 buf = gst_buffer_new_and_alloc (size - 5);
11579 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
11580 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
11581 gst_caps_set_simple (entry->caps,
11582 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11583 gst_buffer_unref (buf);
11592 av1_data += size + 8;
11598 /* TODO: Need to parse vpcC for VP8 codec too.
11599 * Note that VPCodecConfigurationBox (vpcC) is defined for
11600 * vp08, vp09, and vp10 fourcc. */
11603 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11604 const guint8 *vpcc_data = stsd_entry_data + 0x56;
11607 while (len >= 0x8) {
11610 if (QT_UINT32 (vpcc_data) <= len)
11611 size = QT_UINT32 (vpcc_data) - 0x8;
11616 /* No real data, so break out */
11619 switch (QT_FOURCC (vpcc_data + 0x4)) {
11622 const gchar *profile_str = NULL;
11623 const gchar *chroma_format_str = NULL;
11626 guint8 chroma_format;
11627 GstVideoColorimetry cinfo;
11629 /* parse, if found */
11630 GST_DEBUG_OBJECT (qtdemux,
11631 "found vp codec_data in stsd of size %d", size);
11633 /* the meaning of "size" is length of the atom body, excluding
11634 * atom length and fourcc fields */
11639 * 4 bytes: atom length
11646 * 3 bits: chromaSubsampling
11647 * 1 bit: videoFullRangeFlag
11648 * 1 byte: colourPrimaries
11649 * 1 byte: transferCharacteristics
11650 * 1 byte: matrixCoefficients
11651 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
11652 * rest: codecIntializationData (not used for vp8 and vp9)
11655 if (vpcc_data[8] != 1) {
11656 GST_WARNING_OBJECT (qtdemux,
11657 "unknown vpcC version %d", vpcc_data[8]);
11661 profile = vpcc_data[12];
11680 gst_caps_set_simple (entry->caps,
11681 "profile", G_TYPE_STRING, profile_str, NULL);
11684 /* skip level, the VP9 spec v0.6 defines only one level atm,
11685 * but webm spec define various ones. Add level to caps
11686 * if we really need it then */
11688 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
11689 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
11690 gst_caps_set_simple (entry->caps,
11691 "bit-depth-luma", G_TYPE_UINT, bitdepth,
11692 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
11695 chroma_format = (vpcc_data[14] & 0xe) >> 1;
11696 switch (chroma_format) {
11699 chroma_format_str = "4:2:0";
11702 chroma_format_str = "4:2:2";
11705 chroma_format_str = "4:4:4";
11711 if (chroma_format_str) {
11712 gst_caps_set_simple (entry->caps,
11713 "chroma-format", G_TYPE_STRING, chroma_format_str,
11717 if ((vpcc_data[14] & 0x1) != 0)
11718 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
11720 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
11722 gst_video_color_primaries_from_iso (vpcc_data[15]);
11724 gst_video_color_transfer_from_iso (vpcc_data[16]);
11726 gst_video_color_matrix_from_iso (vpcc_data[17]);
11728 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
11729 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
11730 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
11731 /* set this only if all values are known, otherwise this
11732 * might overwrite valid ones parsed from other color box */
11733 CUR_STREAM (stream)->colorimetry = cinfo;
11742 vpcc_data += size + 8;
11752 GST_INFO_OBJECT (qtdemux,
11753 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11754 GST_FOURCC_ARGS (fourcc), entry->caps);
11756 } else if (stream->subtype == FOURCC_soun) {
11758 int version, samplesize;
11759 guint16 compression_id;
11760 gboolean amrwb = FALSE;
11763 /* sample description entry (16) + sound sample description v0 (20) */
11767 version = QT_UINT32 (stsd_entry_data + offset);
11768 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11769 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11770 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11771 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11773 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11774 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11775 QT_UINT32 (stsd_entry_data + offset + 4));
11776 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11777 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11778 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11779 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11780 QT_UINT16 (stsd_entry_data + offset + 14));
11781 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11783 if (compression_id == 0xfffe)
11784 entry->sampled = TRUE;
11786 /* first assume uncompressed audio */
11787 entry->bytes_per_sample = samplesize / 8;
11788 entry->samples_per_frame = entry->n_channels;
11789 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11790 entry->samples_per_packet = entry->samples_per_frame;
11791 entry->bytes_per_packet = entry->bytes_per_sample;
11795 if (version == 0x00010000) {
11796 /* sample description entry (16) + sound sample description v1 (20+16) */
11800 /* take information from here over the normal sample description */
11801 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11802 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11803 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11804 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11806 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
11807 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11808 entry->samples_per_packet);
11809 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11810 entry->bytes_per_packet);
11811 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11812 entry->bytes_per_frame);
11813 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11814 entry->bytes_per_sample);
11816 if (!entry->sampled && entry->bytes_per_packet) {
11817 entry->samples_per_frame = (entry->bytes_per_frame /
11818 entry->bytes_per_packet) * entry->samples_per_packet;
11819 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11820 entry->samples_per_frame);
11822 } else if (version == 0x00020000) {
11823 /* sample description entry (16) + sound sample description v2 (56) */
11827 /* take information from here over the normal sample description */
11828 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
11829 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11830 entry->samples_per_frame = entry->n_channels;
11831 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
11832 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
11833 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
11834 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
11836 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11837 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11838 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11839 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11840 entry->bytes_per_sample * 8);
11841 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11842 QT_UINT32 (stsd_entry_data + offset + 24));
11843 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11844 entry->bytes_per_packet);
11845 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11846 entry->samples_per_packet);
11847 } else if (version != 0x00000) {
11848 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11853 /* Yes, these have to be hard-coded */
11856 entry->samples_per_packet = 6;
11857 entry->bytes_per_packet = 1;
11858 entry->bytes_per_frame = 1 * entry->n_channels;
11859 entry->bytes_per_sample = 1;
11860 entry->samples_per_frame = 6 * entry->n_channels;
11865 entry->samples_per_packet = 3;
11866 entry->bytes_per_packet = 1;
11867 entry->bytes_per_frame = 1 * entry->n_channels;
11868 entry->bytes_per_sample = 1;
11869 entry->samples_per_frame = 3 * entry->n_channels;
11874 entry->samples_per_packet = 64;
11875 entry->bytes_per_packet = 34;
11876 entry->bytes_per_frame = 34 * entry->n_channels;
11877 entry->bytes_per_sample = 2;
11878 entry->samples_per_frame = 64 * entry->n_channels;
11884 entry->samples_per_packet = 1;
11885 entry->bytes_per_packet = 1;
11886 entry->bytes_per_frame = 1 * entry->n_channels;
11887 entry->bytes_per_sample = 1;
11888 entry->samples_per_frame = 1 * entry->n_channels;
11893 entry->samples_per_packet = 160;
11894 entry->bytes_per_packet = 33;
11895 entry->bytes_per_frame = 33 * entry->n_channels;
11896 entry->bytes_per_sample = 2;
11897 entry->samples_per_frame = 160 * entry->n_channels;
11900 /* fix up any invalid header information from above */
11905 /* Sometimes these are set to 0 in the sound sample descriptions so
11906 * let's try to infer useful values from the other information we
11907 * have available */
11908 if (entry->bytes_per_sample == 0)
11909 entry->bytes_per_sample =
11910 entry->bytes_per_frame / entry->n_channels;
11911 if (entry->bytes_per_sample == 0)
11912 entry->bytes_per_sample = samplesize / 8;
11914 if (entry->bytes_per_frame == 0)
11915 entry->bytes_per_frame =
11916 entry->bytes_per_sample * entry->n_channels;
11918 if (entry->bytes_per_packet == 0)
11919 entry->bytes_per_packet = entry->bytes_per_sample;
11921 if (entry->samples_per_frame == 0)
11922 entry->samples_per_frame = entry->n_channels;
11924 if (entry->samples_per_packet == 0)
11925 entry->samples_per_packet = entry->samples_per_frame;
11935 entry->bytes_per_sample = 3;
11939 entry->bytes_per_sample = 4;
11942 entry->bytes_per_sample = 8;
11945 entry->bytes_per_sample = 2;
11948 g_assert_not_reached ();
11951 entry->samples_per_frame = entry->n_channels;
11952 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11953 entry->samples_per_packet = entry->samples_per_frame;
11954 entry->bytes_per_packet = entry->bytes_per_sample;
11962 gst_caps_unref (entry->caps);
11964 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11965 stsd_entry_data + 32, len - 16, &codec);
11976 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
11978 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
11980 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
11982 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11985 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11986 const gchar *format_str;
11990 format_str = (enda_value) ? "S24LE" : "S24BE";
11993 format_str = (enda_value) ? "S32LE" : "S32BE";
11996 format_str = (enda_value) ? "F32LE" : "F32BE";
11999 format_str = (enda_value) ? "F64LE" : "F64BE";
12002 g_assert_not_reached ();
12005 gst_caps_set_simple (entry->caps,
12006 "format", G_TYPE_STRING, format_str, NULL);
12012 const guint8 *owma_data;
12013 const gchar *codec_name = NULL;
12017 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12018 /* FIXME this should also be gst_riff_strf_auds,
12019 * but the latter one is actually missing bits-per-sample :( */
12024 gint32 nSamplesPerSec;
12025 gint32 nAvgBytesPerSec;
12026 gint16 nBlockAlign;
12027 gint16 wBitsPerSample;
12030 WAVEFORMATEX *wfex;
12032 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12033 owma_data = stsd_entry_data;
12034 owma_len = QT_UINT32 (owma_data);
12035 if (owma_len <= 54) {
12036 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12039 wfex = (WAVEFORMATEX *) (owma_data + 36);
12040 buf = gst_buffer_new_and_alloc (owma_len - 54);
12041 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12042 if (wfex->wFormatTag == 0x0161) {
12043 codec_name = "Windows Media Audio";
12045 } else if (wfex->wFormatTag == 0x0162) {
12046 codec_name = "Windows Media Audio 9 Pro";
12048 } else if (wfex->wFormatTag == 0x0163) {
12049 codec_name = "Windows Media Audio 9 Lossless";
12050 /* is that correct? gstffmpegcodecmap.c is missing it, but
12051 * fluendo codec seems to support it */
12055 gst_caps_set_simple (entry->caps,
12056 "codec_data", GST_TYPE_BUFFER, buf,
12057 "wmaversion", G_TYPE_INT, version,
12058 "block_align", G_TYPE_INT,
12059 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12060 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
12061 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
12062 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
12063 gst_buffer_unref (buf);
12067 codec = g_strdup (codec_name);
12073 gint len = QT_UINT32 (stsd_entry_data) - offset;
12074 const guint8 *wfex_data = stsd_entry_data + offset;
12075 const gchar *codec_name = NULL;
12077 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12078 /* FIXME this should also be gst_riff_strf_auds,
12079 * but the latter one is actually missing bits-per-sample :( */
12084 gint32 nSamplesPerSec;
12085 gint32 nAvgBytesPerSec;
12086 gint16 nBlockAlign;
12087 gint16 wBitsPerSample;
12092 /* FIXME: unify with similar wavformatex parsing code above */
12093 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
12099 if (QT_UINT32 (wfex_data) <= len)
12100 size = QT_UINT32 (wfex_data) - 8;
12105 /* No real data, so break out */
12108 switch (QT_FOURCC (wfex_data + 4)) {
12109 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
12111 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
12116 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
12117 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
12118 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
12119 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
12120 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
12121 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
12122 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
12124 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
12125 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
12126 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
12127 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
12128 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
12129 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
12131 if (wfex.wFormatTag == 0x0161) {
12132 codec_name = "Windows Media Audio";
12134 } else if (wfex.wFormatTag == 0x0162) {
12135 codec_name = "Windows Media Audio 9 Pro";
12137 } else if (wfex.wFormatTag == 0x0163) {
12138 codec_name = "Windows Media Audio 9 Lossless";
12139 /* is that correct? gstffmpegcodecmap.c is missing it, but
12140 * fluendo codec seems to support it */
12144 gst_caps_set_simple (entry->caps,
12145 "wmaversion", G_TYPE_INT, version,
12146 "block_align", G_TYPE_INT, wfex.nBlockAlign,
12147 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
12148 "width", G_TYPE_INT, wfex.wBitsPerSample,
12149 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
12151 if (size > wfex.cbSize) {
12154 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
12155 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
12156 size - wfex.cbSize);
12157 gst_caps_set_simple (entry->caps,
12158 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12159 gst_buffer_unref (buf);
12161 GST_WARNING_OBJECT (qtdemux, "no codec data");
12166 codec = g_strdup (codec_name);
12174 wfex_data += size + 8;
12180 const guint8 *opus_data;
12181 guint8 *channel_mapping = NULL;
12184 guint8 channel_mapping_family;
12185 guint8 stream_count;
12186 guint8 coupled_count;
12189 opus_data = stsd_entry_data;
12191 channels = GST_READ_UINT8 (opus_data + 45);
12192 rate = GST_READ_UINT32_LE (opus_data + 48);
12193 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12194 stream_count = GST_READ_UINT8 (opus_data + 55);
12195 coupled_count = GST_READ_UINT8 (opus_data + 56);
12197 if (channels > 0) {
12198 channel_mapping = g_malloc (channels * sizeof (guint8));
12199 for (i = 0; i < channels; i++)
12200 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12203 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12204 channel_mapping_family, stream_count, coupled_count,
12216 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12217 GST_TAG_AUDIO_CODEC, codec, NULL);
12221 /* some bitrate info may have ended up in caps */
12222 s = gst_caps_get_structure (entry->caps, 0);
12223 gst_structure_get_int (s, "bitrate", &bitrate);
12225 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12226 GST_TAG_BITRATE, bitrate, NULL);
12229 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12230 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12231 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12233 else if (!stream->protected)
12240 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12242 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12244 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12248 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12249 16 bits is a byte-swapped wave-style codec identifier,
12250 and we can find a WAVE header internally to a 'wave' atom here.
12251 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12252 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12255 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12256 if (len < offset + 20) {
12257 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12259 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12260 const guint8 *data = stsd_entry_data + offset + 16;
12262 GNode *waveheadernode;
12264 wavenode = g_node_new ((guint8 *) data);
12265 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12266 const guint8 *waveheader;
12269 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12270 if (waveheadernode) {
12271 waveheader = (const guint8 *) waveheadernode->data;
12272 headerlen = QT_UINT32 (waveheader);
12274 if (headerlen > 8) {
12275 gst_riff_strf_auds *header = NULL;
12276 GstBuffer *headerbuf;
12282 headerbuf = gst_buffer_new_and_alloc (headerlen);
12283 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12285 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12286 headerbuf, &header, &extra)) {
12287 gst_caps_unref (entry->caps);
12288 /* FIXME: Need to do something with the channel reorder map */
12290 gst_riff_create_audio_caps (header->format, NULL, header,
12291 extra, NULL, NULL, NULL);
12294 gst_buffer_unref (extra);
12299 GST_DEBUG ("Didn't find waveheadernode for this codec");
12301 g_node_destroy (wavenode);
12304 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12305 stream->stream_tags);
12309 /* FIXME: what is in the chunk? */
12312 gint len = QT_UINT32 (stsd_data);
12314 /* seems to be always = 116 = 0x74 */
12320 gint len = QT_UINT32 (stsd_entry_data);
12323 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12325 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12326 gst_caps_set_simple (entry->caps,
12327 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12328 gst_buffer_unref (buf);
12330 gst_caps_set_simple (entry->caps,
12331 "samplesize", G_TYPE_INT, samplesize, NULL);
12336 GNode *alac, *wave = NULL;
12338 /* apparently, m4a has this atom appended directly in the stsd entry,
12339 * while mov has it in a wave atom */
12340 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12342 /* alac now refers to stsd entry atom */
12343 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12345 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12347 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12350 const guint8 *alac_data = alac->data;
12351 gint len = QT_UINT32 (alac->data);
12355 GST_DEBUG_OBJECT (qtdemux,
12356 "discarding alac atom with unexpected len %d", len);
12358 /* codec-data contains alac atom size and prefix,
12359 * ffmpeg likes it that way, not quite gst-ish though ...*/
12360 buf = gst_buffer_new_and_alloc (len);
12361 gst_buffer_fill (buf, 0, alac->data, len);
12362 gst_caps_set_simple (entry->caps,
12363 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12364 gst_buffer_unref (buf);
12366 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12367 entry->n_channels = QT_UINT8 (alac_data + 21);
12368 entry->rate = QT_UINT32 (alac_data + 32);
12371 gst_caps_set_simple (entry->caps,
12372 "samplesize", G_TYPE_INT, samplesize, NULL);
12377 /* The codingname of the sample entry is 'fLaC' */
12378 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12381 /* The 'dfLa' box is added to the sample entry to convey
12382 initializing information for the decoder. */
12383 const GNode *dfla =
12384 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12387 const guint32 len = QT_UINT32 (dfla->data);
12389 /* Must contain at least dfLa box header (12),
12390 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12392 GST_DEBUG_OBJECT (qtdemux,
12393 "discarding dfla atom with unexpected len %d", len);
12395 /* skip dfLa header to get the METADATA_BLOCKs */
12396 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12397 const guint32 metadata_blocks_len = len - 12;
12399 gchar *stream_marker = g_strdup ("fLaC");
12400 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12401 strlen (stream_marker));
12404 guint32 remainder = 0;
12405 guint32 block_size = 0;
12406 gboolean is_last = FALSE;
12408 GValue array = G_VALUE_INIT;
12409 GValue value = G_VALUE_INIT;
12411 g_value_init (&array, GST_TYPE_ARRAY);
12412 g_value_init (&value, GST_TYPE_BUFFER);
12414 gst_value_set_buffer (&value, block);
12415 gst_value_array_append_value (&array, &value);
12416 g_value_reset (&value);
12418 gst_buffer_unref (block);
12420 /* check there's at least one METADATA_BLOCK_HEADER's worth
12421 * of data, and we haven't already finished parsing */
12422 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12423 remainder = metadata_blocks_len - index;
12425 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12427 (metadata_blocks[index + 1] << 16) +
12428 (metadata_blocks[index + 2] << 8) +
12429 metadata_blocks[index + 3];
12431 /* be careful not to read off end of box */
12432 if (block_size > remainder) {
12436 is_last = metadata_blocks[index] >> 7;
12438 block = gst_buffer_new_and_alloc (block_size);
12440 gst_buffer_fill (block, 0, &metadata_blocks[index],
12443 gst_value_set_buffer (&value, block);
12444 gst_value_array_append_value (&array, &value);
12445 g_value_reset (&value);
12447 gst_buffer_unref (block);
12449 index += block_size;
12452 /* only append the metadata if we successfully read all of it */
12454 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12455 (stream)->caps, 0), "streamheader", &array);
12457 GST_WARNING_OBJECT (qtdemux,
12458 "discarding all METADATA_BLOCKs due to invalid "
12459 "block_size %d at idx %d, rem %d", block_size, index,
12463 g_value_unset (&value);
12464 g_value_unset (&array);
12466 /* The sample rate obtained from the stsd may not be accurate
12467 * since it cannot represent rates greater than 65535Hz, so
12468 * override that value with the sample rate from the
12469 * METADATA_BLOCK_STREAMINFO block */
12470 CUR_STREAM (stream)->rate =
12471 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12482 gint len = QT_UINT32 (stsd_entry_data);
12485 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12488 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12490 /* If we have enough data, let's try to get the 'damr' atom. See
12491 * the 3GPP container spec (26.244) for more details. */
12492 if ((len - 0x34) > 8 &&
12493 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12494 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12495 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12498 gst_caps_set_simple (entry->caps,
12499 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12500 gst_buffer_unref (buf);
12506 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12507 gint len = QT_UINT32 (stsd_entry_data);
12510 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12512 if (sound_version == 1) {
12513 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12514 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12515 guint8 codec_data[2];
12517 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12519 gint sample_rate_index =
12520 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12522 /* build AAC codec data */
12523 codec_data[0] = profile << 3;
12524 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12525 codec_data[1] = (sample_rate_index & 0x01) << 7;
12526 codec_data[1] |= (channels & 0xF) << 3;
12528 buf = gst_buffer_new_and_alloc (2);
12529 gst_buffer_fill (buf, 0, codec_data, 2);
12530 gst_caps_set_simple (entry->caps,
12531 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12532 gst_buffer_unref (buf);
12543 /* Fully handled elsewhere */
12546 GST_INFO_OBJECT (qtdemux,
12547 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12551 GST_INFO_OBJECT (qtdemux,
12552 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12553 GST_FOURCC_ARGS (fourcc), entry->caps);
12555 } else if (stream->subtype == FOURCC_strm) {
12556 if (fourcc == FOURCC_rtsp) {
12557 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12559 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12560 GST_FOURCC_ARGS (fourcc));
12561 goto unknown_stream;
12563 entry->sampled = TRUE;
12564 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12565 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12566 || stream->subtype == FOURCC_clcp) {
12568 entry->sampled = TRUE;
12569 entry->sparse = TRUE;
12572 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12575 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12576 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12581 /* hunt for sort-of codec data */
12585 GNode *mp4s = NULL;
12586 GNode *esds = NULL;
12588 /* look for palette in a stsd->mp4s->esds sub-atom */
12589 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12591 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12592 if (esds == NULL) {
12594 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12598 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12599 stream->stream_tags);
12603 GST_INFO_OBJECT (qtdemux,
12604 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12607 GST_INFO_OBJECT (qtdemux,
12608 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12609 GST_FOURCC_ARGS (fourcc), entry->caps);
12611 /* everything in 1 sample */
12612 entry->sampled = TRUE;
12615 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12618 if (entry->caps == NULL)
12619 goto unknown_stream;
12622 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12623 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12629 /* promote to sampled format */
12630 if (entry->fourcc == FOURCC_samr) {
12631 /* force mono 8000 Hz for AMR */
12632 entry->sampled = TRUE;
12633 entry->n_channels = 1;
12634 entry->rate = 8000;
12635 } else if (entry->fourcc == FOURCC_sawb) {
12636 /* force mono 16000 Hz for AMR-WB */
12637 entry->sampled = TRUE;
12638 entry->n_channels = 1;
12639 entry->rate = 16000;
12640 } else if (entry->fourcc == FOURCC_mp4a) {
12641 entry->sampled = TRUE;
12645 stsd_entry_data += len;
12646 remaining_stsd_len -= len;
12650 /* collect sample information */
12651 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12652 goto samples_failed;
12654 if (qtdemux->fragmented) {
12657 /* need all moov samples as basis; probably not many if any at all */
12658 /* prevent moof parsing taking of at this time */
12659 offset = qtdemux->moof_offset;
12660 qtdemux->moof_offset = 0;
12661 if (stream->n_samples &&
12662 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12663 qtdemux->moof_offset = offset;
12664 goto samples_failed;
12666 qtdemux->moof_offset = 0;
12667 /* movie duration more reliable in this case (e.g. mehd) */
12668 if (qtdemux->segment.duration &&
12669 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12671 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12674 /* configure segments */
12675 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12676 goto segments_failed;
12678 /* add some language tag, if useful */
12679 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12680 strcmp (stream->lang_id, "und")) {
12681 const gchar *lang_code;
12683 /* convert ISO 639-2 code to ISO 639-1 */
12684 lang_code = gst_tag_get_language_code (stream->lang_id);
12685 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12686 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12689 /* Check for UDTA tags */
12690 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12691 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12694 /* Insert and sort new stream in track-id order.
12695 * This will help in comparing old/new streams during stream update check */
12696 g_ptr_array_add (qtdemux->active_streams, stream);
12697 g_ptr_array_sort (qtdemux->active_streams,
12698 (GCompareFunc) qtdemux_track_id_compare_func);
12699 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
12700 QTDEMUX_N_STREAMS (qtdemux));
12707 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12708 (_("This file is corrupt and cannot be played.")), (NULL));
12710 gst_qtdemux_stream_unref (stream);
12715 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12716 gst_qtdemux_stream_unref (stream);
12722 /* we posted an error already */
12723 /* free stbl sub-atoms */
12724 gst_qtdemux_stbl_free (stream);
12725 gst_qtdemux_stream_unref (stream);
12730 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12736 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12737 GST_FOURCC_ARGS (stream->subtype));
12738 gst_qtdemux_stream_unref (stream);
12743 /* If we can estimate the overall bitrate, and don't have information about the
12744 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12745 * the overall bitrate minus the sum of the bitrates of all other streams. This
12746 * should be useful for the common case where we have one audio and one video
12747 * stream and can estimate the bitrate of one, but not the other. */
12749 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12751 QtDemuxStream *stream = NULL;
12752 gint64 size, sys_bitrate, sum_bitrate = 0;
12753 GstClockTime duration;
12757 if (qtdemux->fragmented)
12760 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12762 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12764 GST_DEBUG_OBJECT (qtdemux,
12765 "Size in bytes of the stream not known - bailing");
12769 /* Subtract the header size */
12770 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12771 size, qtdemux->header_size);
12773 if (size < qtdemux->header_size)
12776 size = size - qtdemux->header_size;
12778 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12779 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12783 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12784 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
12785 switch (str->subtype) {
12788 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12789 CUR_STREAM (str)->caps);
12790 /* retrieve bitrate, prefer avg then max */
12792 if (str->stream_tags) {
12793 if (gst_tag_list_get_uint (str->stream_tags,
12794 GST_TAG_MAXIMUM_BITRATE, &bitrate))
12795 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12796 if (gst_tag_list_get_uint (str->stream_tags,
12797 GST_TAG_NOMINAL_BITRATE, &bitrate))
12798 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12799 if (gst_tag_list_get_uint (str->stream_tags,
12800 GST_TAG_BITRATE, &bitrate))
12801 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12804 sum_bitrate += bitrate;
12807 GST_DEBUG_OBJECT (qtdemux,
12808 ">1 stream with unknown bitrate - bailing");
12815 /* For other subtypes, we assume no significant impact on bitrate */
12821 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12825 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12827 if (sys_bitrate < sum_bitrate) {
12828 /* This can happen, since sum_bitrate might be derived from maximum
12829 * bitrates and not average bitrates */
12830 GST_DEBUG_OBJECT (qtdemux,
12831 "System bitrate less than sum bitrate - bailing");
12835 bitrate = sys_bitrate - sum_bitrate;
12836 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12837 ", Stream bitrate = %u", sys_bitrate, bitrate);
12839 if (!stream->stream_tags)
12840 stream->stream_tags = gst_tag_list_new_empty ();
12842 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12844 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12845 GST_TAG_BITRATE, bitrate, NULL);
12848 static GstFlowReturn
12849 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12851 GstFlowReturn ret = GST_FLOW_OK;
12854 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12856 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12857 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12858 guint32 sample_num = 0;
12860 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12861 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12863 if (qtdemux->fragmented) {
12864 /* need all moov samples first */
12865 GST_OBJECT_LOCK (qtdemux);
12866 while (stream->n_samples == 0)
12867 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12869 GST_OBJECT_UNLOCK (qtdemux);
12871 /* discard any stray moof */
12872 qtdemux->moof_offset = 0;
12875 /* prepare braking */
12876 if (ret != GST_FLOW_ERROR)
12879 /* in pull mode, we should have parsed some sample info by now;
12880 * and quite some code will not handle no samples.
12881 * in push mode, we'll just have to deal with it */
12882 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12883 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12884 g_ptr_array_remove_index (qtdemux->active_streams, i);
12887 } else if (stream->track_id == qtdemux->chapters_track_id &&
12888 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
12889 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12890 so that it doesn't look like a subtitle track */
12891 g_ptr_array_remove_index (qtdemux->active_streams, i);
12896 /* parse the initial sample for use in setting the frame rate cap */
12897 while (sample_num == 0 && sample_num < stream->n_samples) {
12898 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12908 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
12910 return g_strcmp0 (stream->stream_id, stream_id) == 0;
12914 qtdemux_is_streams_update (GstQTDemux * qtdemux)
12918 /* Different length, updated */
12919 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
12922 /* streams in list are sorted in track-id order */
12923 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12924 /* Different stream-id, updated */
12925 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
12926 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
12934 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
12935 QtDemuxStream * oldstream, QtDemuxStream * newstream)
12937 /* Connect old stream's srcpad to new stream */
12938 newstream->pad = oldstream->pad;
12939 oldstream->pad = NULL;
12941 /* unset new_stream to prevent stream-start event */
12942 newstream->new_stream = FALSE;
12944 return gst_qtdemux_configure_stream (qtdemux, newstream);
12947 /* g_ptr_array_find_with_equal_func is available since 2.54,
12948 * replacement until we can depend unconditionally on the real one in GLib */
12949 #if !GLIB_CHECK_VERSION(2,54,0)
12950 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
12952 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
12953 gconstpointer needle, GEqualFunc equal_func, guint * index_)
12957 g_return_val_if_fail (haystack != NULL, FALSE);
12959 if (equal_func == NULL)
12960 equal_func = g_direct_equal;
12962 for (i = 0; i < haystack->len; i++) {
12963 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
12964 if (index_ != NULL)
12975 qtdemux_update_streams (GstQTDemux * qtdemux)
12978 g_assert (qtdemux->streams_aware);
12980 /* At below, figure out which stream in active_streams has identical stream-id
12981 * with that of in old_streams. If there is matching stream-id,
12982 * corresponding newstream will not be exposed again,
12983 * but demux will reuse srcpad of matched old stream
12985 * active_streams : newly created streams from the latest moov
12986 * old_streams : existing streams (belong to previous moov)
12989 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12990 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12991 QtDemuxStream *oldstream = NULL;
12994 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12995 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12997 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
12998 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
12999 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
13001 /* null pad stream cannot be reused */
13002 if (oldstream->pad == NULL)
13007 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13009 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13012 /* we don't need to preserve order of old streams */
13013 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13017 /* now we have all info and can expose */
13018 list = stream->stream_tags;
13019 stream->stream_tags = NULL;
13020 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13028 /* Must be called with expose lock */
13029 static GstFlowReturn
13030 qtdemux_expose_streams (GstQTDemux * qtdemux)
13034 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13036 if (!qtdemux_is_streams_update (qtdemux)) {
13037 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13038 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13039 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13040 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13041 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13042 return GST_FLOW_ERROR;
13045 g_ptr_array_set_size (qtdemux->old_streams, 0);
13046 qtdemux->need_segment = TRUE;
13048 return GST_FLOW_OK;
13051 if (qtdemux->streams_aware) {
13052 if (!qtdemux_update_streams (qtdemux))
13053 return GST_FLOW_ERROR;
13055 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13056 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13059 /* now we have all info and can expose */
13060 list = stream->stream_tags;
13061 stream->stream_tags = NULL;
13062 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13063 return GST_FLOW_ERROR;
13068 gst_qtdemux_guess_bitrate (qtdemux);
13070 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
13072 /* If we have still old_streams, it's no more used stream */
13073 for (i = 0; i < qtdemux->old_streams->len; i++) {
13074 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13079 event = gst_event_new_eos ();
13080 if (qtdemux->segment_seqnum)
13081 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
13083 gst_pad_push_event (stream->pad, event);
13087 g_ptr_array_set_size (qtdemux->old_streams, 0);
13089 /* check if we should post a redirect in case there is a single trak
13090 * and it is a redirecting trak */
13091 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
13092 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
13095 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
13096 "an external content");
13097 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
13098 gst_structure_new ("redirect",
13099 "new-location", G_TYPE_STRING,
13100 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
13101 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
13102 g_free (qtdemux->redirect_location);
13103 qtdemux->redirect_location =
13104 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
13107 g_ptr_array_foreach (qtdemux->active_streams,
13108 (GFunc) qtdemux_do_allocation, qtdemux);
13110 qtdemux->need_segment = TRUE;
13112 qtdemux->exposed = TRUE;
13113 return GST_FLOW_OK;
13118 GstStructure *structure; /* helper for sort function */
13120 guint min_req_bitrate;
13121 guint min_req_qt_version;
13125 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13127 GstQtReference *ref_a = (GstQtReference *) a;
13128 GstQtReference *ref_b = (GstQtReference *) b;
13130 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13131 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13133 /* known bitrates go before unknown; higher bitrates go first */
13134 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13137 /* sort the redirects and post a message for the application.
13140 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13142 GstQtReference *best;
13145 GValue list_val = { 0, };
13148 g_assert (references != NULL);
13150 references = g_list_sort (references, qtdemux_redirects_sort_func);
13152 best = (GstQtReference *) references->data;
13154 g_value_init (&list_val, GST_TYPE_LIST);
13156 for (l = references; l != NULL; l = l->next) {
13157 GstQtReference *ref = (GstQtReference *) l->data;
13158 GValue struct_val = { 0, };
13160 ref->structure = gst_structure_new ("redirect",
13161 "new-location", G_TYPE_STRING, ref->location, NULL);
13163 if (ref->min_req_bitrate > 0) {
13164 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13165 ref->min_req_bitrate, NULL);
13168 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13169 g_value_set_boxed (&struct_val, ref->structure);
13170 gst_value_list_append_value (&list_val, &struct_val);
13171 g_value_unset (&struct_val);
13172 /* don't free anything here yet, since we need best->structure below */
13175 g_assert (best != NULL);
13176 s = gst_structure_copy (best->structure);
13178 if (g_list_length (references) > 1) {
13179 gst_structure_set_value (s, "locations", &list_val);
13182 g_value_unset (&list_val);
13184 for (l = references; l != NULL; l = l->next) {
13185 GstQtReference *ref = (GstQtReference *) l->data;
13187 gst_structure_free (ref->structure);
13188 g_free (ref->location);
13191 g_list_free (references);
13193 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13194 g_free (qtdemux->redirect_location);
13195 qtdemux->redirect_location =
13196 g_strdup (gst_structure_get_string (s, "new-location"));
13197 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13198 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13201 /* look for redirect nodes, collect all redirect information and
13205 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13207 GNode *rmra, *rmda, *rdrf;
13209 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13211 GList *redirects = NULL;
13213 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13215 GstQtReference ref = { NULL, NULL, 0, 0 };
13216 GNode *rmdr, *rmvc;
13218 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13219 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13220 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13221 ref.min_req_bitrate);
13224 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13225 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13226 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
13228 #ifndef GST_DISABLE_GST_DEBUG
13229 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
13231 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
13233 GST_LOG_OBJECT (qtdemux,
13234 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
13235 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
13236 bitmask, check_type);
13237 if (package == FOURCC_qtim && check_type == 0) {
13238 ref.min_req_qt_version = version;
13242 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
13248 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
13249 if (ref_len > 20) {
13250 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
13251 ref_data = (guint8 *) rdrf->data + 20;
13252 if (ref_type == FOURCC_alis) {
13253 guint record_len, record_version, fn_len;
13255 if (ref_len > 70) {
13256 /* MacOSX alias record, google for alias-layout.txt */
13257 record_len = QT_UINT16 (ref_data + 4);
13258 record_version = QT_UINT16 (ref_data + 4 + 2);
13259 fn_len = QT_UINT8 (ref_data + 50);
13260 if (record_len > 50 && record_version == 2 && fn_len > 0) {
13261 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
13264 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
13267 } else if (ref_type == FOURCC_url_) {
13268 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
13270 GST_DEBUG_OBJECT (qtdemux,
13271 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
13272 GST_FOURCC_ARGS (ref_type));
13274 if (ref.location != NULL) {
13275 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
13277 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
13279 GST_WARNING_OBJECT (qtdemux,
13280 "Failed to extract redirect location from rdrf atom");
13283 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
13287 /* look for others */
13288 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
13291 if (redirects != NULL) {
13292 qtdemux_process_redirects (qtdemux, redirects);
13298 static GstTagList *
13299 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
13303 if (tags == NULL) {
13304 tags = gst_tag_list_new_empty ();
13305 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
13308 if (qtdemux->major_brand == FOURCC_mjp2)
13309 fmt = "Motion JPEG 2000";
13310 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
13312 else if (qtdemux->major_brand == FOURCC_qt__)
13314 else if (qtdemux->fragmented)
13317 fmt = "ISO MP4/M4A";
13319 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
13320 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
13322 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
13328 /* we have read the complete moov node now.
13329 * This function parses all of the relevant info, creates the traks and
13330 * prepares all data structures for playback
13333 qtdemux_parse_tree (GstQTDemux * qtdemux)
13340 guint64 creation_time;
13341 GstDateTime *datetime = NULL;
13344 /* make sure we have a usable taglist */
13345 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13347 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
13348 if (mvhd == NULL) {
13349 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
13350 return qtdemux_parse_redirects (qtdemux);
13353 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
13354 if (version == 1) {
13355 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
13356 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
13357 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
13358 } else if (version == 0) {
13359 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
13360 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
13361 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
13363 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
13367 /* Moving qt creation time (secs since 1904) to unix time */
13368 if (creation_time != 0) {
13369 /* Try to use epoch first as it should be faster and more commonly found */
13370 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
13373 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
13374 /* some data cleansing sanity */
13375 now_s = g_get_real_time () / G_USEC_PER_SEC;
13376 if (now_s + 24 * 3600 < creation_time) {
13377 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
13379 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
13382 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
13383 GDateTime *dt, *dt_local;
13385 dt = g_date_time_add_seconds (base_dt, creation_time);
13386 dt_local = g_date_time_to_local (dt);
13387 datetime = gst_date_time_new_from_g_date_time (dt_local);
13389 g_date_time_unref (base_dt);
13390 g_date_time_unref (dt);
13394 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
13395 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
13397 gst_date_time_unref (datetime);
13400 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
13401 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
13403 /* check for fragmented file and get some (default) data */
13404 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
13407 GstByteReader mehd_data;
13409 /* let track parsing or anyone know weird stuff might happen ... */
13410 qtdemux->fragmented = TRUE;
13412 /* compensate for total duration */
13413 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
13415 qtdemux_parse_mehd (qtdemux, &mehd_data);
13418 /* Update the movie segment duration, unless it was directly given to us
13419 * by upstream. Otherwise let it as is, as we don't want to mangle the
13420 * duration provided by upstream that may come e.g. from a MPD file. */
13421 if (!qtdemux->upstream_format_is_time) {
13422 GstClockTime duration;
13423 /* set duration in the segment info */
13424 gst_qtdemux_get_duration (qtdemux, &duration);
13425 qtdemux->segment.duration = duration;
13426 /* also do not exceed duration; stop is set that way post seek anyway,
13427 * and segment activation falls back to duration,
13428 * whereas loop only checks stop, so let's align this here as well */
13429 qtdemux->segment.stop = duration;
13432 /* parse all traks */
13433 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
13435 qtdemux_parse_trak (qtdemux, trak);
13436 /* iterate all siblings */
13437 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
13440 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13443 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
13445 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13447 GST_LOG_OBJECT (qtdemux, "No udta node found.");
13450 /* maybe also some tags in meta box */
13451 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
13453 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
13454 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13456 GST_LOG_OBJECT (qtdemux, "No meta node found.");
13459 /* parse any protection system info */
13460 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
13462 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
13463 qtdemux_parse_pssh (qtdemux, pssh);
13464 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
13467 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
13472 /* taken from ffmpeg */
13474 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
13486 len = (len << 7) | (c & 0x7f);
13495 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
13496 gsize codec_data_size)
13498 GList *list = NULL;
13499 guint8 *p = codec_data;
13500 gint i, offset, num_packets;
13501 guint *length, last;
13503 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
13505 if (codec_data == NULL || codec_data_size == 0)
13508 /* start of the stream and vorbis audio or theora video, need to
13509 * send the codec_priv data as first three packets */
13510 num_packets = p[0] + 1;
13511 GST_DEBUG_OBJECT (qtdemux,
13512 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
13513 (guint) num_packets, codec_data_size);
13515 /* Let's put some limits, Don't think there even is a xiph codec
13516 * with more than 3-4 headers */
13517 if (G_UNLIKELY (num_packets > 16)) {
13518 GST_WARNING_OBJECT (qtdemux,
13519 "Unlikely number of xiph headers, most likely not valid");
13523 length = g_alloca (num_packets * sizeof (guint));
13527 /* first packets, read length values */
13528 for (i = 0; i < num_packets - 1; i++) {
13530 while (offset < codec_data_size) {
13531 length[i] += p[offset];
13532 if (p[offset++] != 0xff)
13537 if (offset + last > codec_data_size)
13540 /* last packet is the remaining size */
13541 length[i] = codec_data_size - offset - last;
13543 for (i = 0; i < num_packets; i++) {
13546 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
13548 if (offset + length[i] > codec_data_size)
13551 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
13552 list = g_list_append (list, hdr);
13554 offset += length[i];
13563 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
13569 /* this can change the codec originally present in @list */
13571 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
13572 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
13574 int len = QT_UINT32 (esds->data);
13575 guint8 *ptr = esds->data;
13576 guint8 *end = ptr + len;
13578 guint8 *data_ptr = NULL;
13580 guint8 object_type_id = 0;
13581 guint8 stream_type = 0;
13582 const char *codec_name = NULL;
13583 GstCaps *caps = NULL;
13585 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
13587 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
13589 while (ptr + 1 < end) {
13590 tag = QT_UINT8 (ptr);
13591 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
13593 len = read_descr_size (ptr, end, &ptr);
13594 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
13596 /* Check the stated amount of data is available for reading */
13597 if (len < 0 || ptr + len > end)
13601 case ES_DESCRIPTOR_TAG:
13602 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
13603 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
13606 case DECODER_CONFIG_DESC_TAG:{
13607 guint max_bitrate, avg_bitrate;
13609 object_type_id = QT_UINT8 (ptr);
13610 stream_type = QT_UINT8 (ptr + 1) >> 2;
13611 max_bitrate = QT_UINT32 (ptr + 5);
13612 avg_bitrate = QT_UINT32 (ptr + 9);
13613 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
13614 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
13615 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
13616 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
13617 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
13618 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
13619 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13620 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
13622 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
13623 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
13624 avg_bitrate, NULL);
13629 case DECODER_SPECIFIC_INFO_TAG:
13630 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
13631 if (object_type_id == 0xe0 && len == 0x40) {
13637 GST_DEBUG_OBJECT (qtdemux,
13638 "Have VOBSUB palette. Creating palette event");
13639 /* move to decConfigDescr data and read palette */
13641 for (i = 0; i < 16; i++) {
13642 clut[i] = QT_UINT32 (data);
13646 s = gst_structure_new ("application/x-gst-dvd", "event",
13647 G_TYPE_STRING, "dvd-spu-clut-change",
13648 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
13649 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
13650 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
13651 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
13652 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
13653 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
13654 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
13655 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
13658 /* store event and trigger custom processing */
13659 stream->pending_event =
13660 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
13662 /* Generic codec_data handler puts it on the caps */
13669 case SL_CONFIG_DESC_TAG:
13670 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
13674 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
13676 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
13682 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
13683 * in use, and should also be used to override some other parameters for some
13685 switch (object_type_id) {
13686 case 0x20: /* MPEG-4 */
13687 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
13688 * profile_and_level_indication */
13689 if (data_ptr != NULL && data_len >= 5 &&
13690 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
13691 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
13692 data_ptr + 4, data_len - 4);
13694 break; /* Nothing special needed here */
13695 case 0x21: /* H.264 */
13696 codec_name = "H.264 / AVC";
13697 caps = gst_caps_new_simple ("video/x-h264",
13698 "stream-format", G_TYPE_STRING, "avc",
13699 "alignment", G_TYPE_STRING, "au", NULL);
13701 case 0x40: /* AAC (any) */
13702 case 0x66: /* AAC Main */
13703 case 0x67: /* AAC LC */
13704 case 0x68: /* AAC SSR */
13705 /* Override channels and rate based on the codec_data, as it's often
13707 /* Only do so for basic setup without HE-AAC extension */
13708 if (data_ptr && data_len == 2) {
13709 guint channels, rate;
13711 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
13713 entry->n_channels = channels;
13715 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
13717 entry->rate = rate;
13720 /* Set level and profile if possible */
13721 if (data_ptr != NULL && data_len >= 2) {
13722 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
13723 data_ptr, data_len);
13725 const gchar *profile_str = NULL;
13728 guint8 *codec_data;
13729 gint rate_idx, profile;
13731 /* No codec_data, let's invent something.
13732 * FIXME: This is wrong for SBR! */
13734 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
13736 buffer = gst_buffer_new_and_alloc (2);
13737 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
13738 codec_data = map.data;
13741 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
13744 switch (object_type_id) {
13746 profile_str = "main";
13750 profile_str = "lc";
13754 profile_str = "ssr";
13762 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
13764 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
13766 gst_buffer_unmap (buffer, &map);
13767 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
13768 GST_TYPE_BUFFER, buffer, NULL);
13769 gst_buffer_unref (buffer);
13772 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
13773 G_TYPE_STRING, profile_str, NULL);
13777 case 0x60: /* MPEG-2, various profiles */
13783 codec_name = "MPEG-2 video";
13784 caps = gst_caps_new_simple ("video/mpeg",
13785 "mpegversion", G_TYPE_INT, 2,
13786 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13788 case 0x69: /* MPEG-2 BC audio */
13789 case 0x6B: /* MPEG-1 audio */
13790 caps = gst_caps_new_simple ("audio/mpeg",
13791 "mpegversion", G_TYPE_INT, 1, NULL);
13792 codec_name = "MPEG-1 audio";
13794 case 0x6A: /* MPEG-1 */
13795 codec_name = "MPEG-1 video";
13796 caps = gst_caps_new_simple ("video/mpeg",
13797 "mpegversion", G_TYPE_INT, 1,
13798 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13800 case 0x6C: /* MJPEG */
13802 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13804 codec_name = "Motion-JPEG";
13806 case 0x6D: /* PNG */
13808 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
13810 codec_name = "PNG still images";
13812 case 0x6E: /* JPEG2000 */
13813 codec_name = "JPEG-2000";
13814 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
13816 case 0xA4: /* Dirac */
13817 codec_name = "Dirac";
13818 caps = gst_caps_new_empty_simple ("video/x-dirac");
13820 case 0xA5: /* AC3 */
13821 codec_name = "AC-3 audio";
13822 caps = gst_caps_new_simple ("audio/x-ac3",
13823 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13825 case 0xA9: /* AC3 */
13826 codec_name = "DTS audio";
13827 caps = gst_caps_new_simple ("audio/x-dts",
13828 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13831 if (stream_type == 0x05 && data_ptr) {
13833 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
13836 GValue arr_val = G_VALUE_INIT;
13837 GValue buf_val = G_VALUE_INIT;
13840 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
13841 codec_name = "Vorbis";
13842 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
13843 g_value_init (&arr_val, GST_TYPE_ARRAY);
13844 g_value_init (&buf_val, GST_TYPE_BUFFER);
13845 for (tmp = headers; tmp; tmp = tmp->next) {
13846 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
13847 gst_value_array_append_value (&arr_val, &buf_val);
13849 s = gst_caps_get_structure (caps, 0);
13850 gst_structure_take_value (s, "streamheader", &arr_val);
13851 g_value_unset (&buf_val);
13852 g_list_free (headers);
13859 case 0xE1: /* QCELP */
13860 /* QCELP, the codec_data is a riff tag (little endian) with
13861 * 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). */
13862 caps = gst_caps_new_empty_simple ("audio/qcelp");
13863 codec_name = "QCELP";
13869 /* If we have a replacement caps, then change our caps for this stream */
13871 gst_caps_unref (entry->caps);
13872 entry->caps = caps;
13875 if (codec_name && list)
13876 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13877 GST_TAG_AUDIO_CODEC, codec_name, NULL);
13879 /* Add the codec_data attribute to caps, if we have it */
13883 buffer = gst_buffer_new_and_alloc (data_len);
13884 gst_buffer_fill (buffer, 0, data_ptr, data_len);
13886 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
13887 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
13889 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
13891 gst_buffer_unref (buffer);
13896 static inline GstCaps *
13897 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
13901 char *s, fourstr[5];
13903 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13904 for (i = 0; i < 4; i++) {
13905 if (!g_ascii_isalnum (fourstr[i]))
13908 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
13909 caps = gst_caps_new_empty_simple (s);
13914 #define _codec(name) \
13916 if (codec_name) { \
13917 *codec_name = g_strdup (name); \
13922 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
13923 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
13924 const guint8 * stsd_entry_data, gchar ** codec_name)
13926 GstCaps *caps = NULL;
13927 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
13931 _codec ("PNG still images");
13932 caps = gst_caps_new_empty_simple ("image/png");
13935 _codec ("JPEG still images");
13937 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13940 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
13941 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
13942 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
13943 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
13944 _codec ("Motion-JPEG");
13946 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13949 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
13950 _codec ("Motion-JPEG format B");
13951 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
13954 _codec ("JPEG-2000");
13955 /* override to what it should be according to spec, avoid palette_data */
13956 entry->bits_per_sample = 24;
13957 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
13960 _codec ("Sorensen video v.3");
13961 caps = gst_caps_new_simple ("video/x-svq",
13962 "svqversion", G_TYPE_INT, 3, NULL);
13964 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
13965 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
13966 _codec ("Sorensen video v.1");
13967 caps = gst_caps_new_simple ("video/x-svq",
13968 "svqversion", G_TYPE_INT, 1, NULL);
13970 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
13971 caps = gst_caps_new_empty_simple ("video/x-raw");
13972 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
13973 _codec ("Windows Raw RGB");
13974 stream->alignment = 32;
13980 bps = QT_UINT16 (stsd_entry_data + 82);
13983 format = GST_VIDEO_FORMAT_RGB15;
13986 format = GST_VIDEO_FORMAT_RGB16;
13989 format = GST_VIDEO_FORMAT_RGB;
13992 format = GST_VIDEO_FORMAT_ARGB;
14000 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14001 format = GST_VIDEO_FORMAT_I420;
14003 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14004 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14005 format = GST_VIDEO_FORMAT_I420;
14008 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14009 format = GST_VIDEO_FORMAT_UYVY;
14011 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14012 format = GST_VIDEO_FORMAT_v308;
14014 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14015 format = GST_VIDEO_FORMAT_v216;
14018 format = GST_VIDEO_FORMAT_v210;
14020 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14021 format = GST_VIDEO_FORMAT_r210;
14023 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14024 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14025 format = GST_VIDEO_FORMAT_v410;
14028 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14029 * but different order than AYUV
14030 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14031 format = GST_VIDEO_FORMAT_v408;
14034 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14035 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14036 _codec ("MPEG-1 video");
14037 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14038 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14040 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14041 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14042 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14043 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14044 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14045 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14046 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14047 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14048 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14049 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14050 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14051 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14052 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14053 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14054 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14055 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14056 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14057 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14058 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14059 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14060 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14061 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14062 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14063 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14064 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14065 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14066 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14067 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14068 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14069 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14070 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14071 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14072 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14073 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14074 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14075 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14076 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14077 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14078 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14079 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14080 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14081 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14082 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14083 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14084 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14085 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14086 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14087 _codec ("MPEG-2 video");
14088 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14089 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14091 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14092 _codec ("GIF still images");
14093 caps = gst_caps_new_empty_simple ("image/gif");
14096 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14098 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14100 /* ffmpeg uses the height/width props, don't know why */
14101 caps = gst_caps_new_simple ("video/x-h263",
14102 "variant", G_TYPE_STRING, "itu", NULL);
14106 _codec ("MPEG-4 video");
14107 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14108 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14110 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14111 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14112 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14113 caps = gst_caps_new_simple ("video/x-msmpeg",
14114 "msmpegversion", G_TYPE_INT, 43, NULL);
14116 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14118 caps = gst_caps_new_simple ("video/x-divx",
14119 "divxversion", G_TYPE_INT, 3, NULL);
14121 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14122 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14124 caps = gst_caps_new_simple ("video/x-divx",
14125 "divxversion", G_TYPE_INT, 4, NULL);
14127 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14129 caps = gst_caps_new_simple ("video/x-divx",
14130 "divxversion", G_TYPE_INT, 5, NULL);
14133 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14135 caps = gst_caps_new_simple ("video/x-ffv",
14136 "ffvversion", G_TYPE_INT, 1, NULL);
14139 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14140 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14145 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14146 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14147 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14151 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14152 _codec ("Cinepak");
14153 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14155 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14156 _codec ("Apple QuickDraw");
14157 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14159 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14160 _codec ("Apple video");
14161 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14165 _codec ("H.264 / AVC");
14166 caps = gst_caps_new_simple ("video/x-h264",
14167 "stream-format", G_TYPE_STRING, "avc",
14168 "alignment", G_TYPE_STRING, "au", NULL);
14171 _codec ("H.264 / AVC");
14172 caps = gst_caps_new_simple ("video/x-h264",
14173 "stream-format", G_TYPE_STRING, "avc3",
14174 "alignment", G_TYPE_STRING, "au", NULL);
14178 _codec ("H.265 / HEVC");
14179 caps = gst_caps_new_simple ("video/x-h265",
14180 "stream-format", G_TYPE_STRING, "hvc1",
14181 "alignment", G_TYPE_STRING, "au", NULL);
14184 _codec ("H.265 / HEVC");
14185 caps = gst_caps_new_simple ("video/x-h265",
14186 "stream-format", G_TYPE_STRING, "hev1",
14187 "alignment", G_TYPE_STRING, "au", NULL);
14190 _codec ("Run-length encoding");
14191 caps = gst_caps_new_simple ("video/x-rle",
14192 "layout", G_TYPE_STRING, "quicktime", NULL);
14195 _codec ("Run-length encoding");
14196 caps = gst_caps_new_simple ("video/x-rle",
14197 "layout", G_TYPE_STRING, "microsoft", NULL);
14199 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14200 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14201 _codec ("Indeo Video 3");
14202 caps = gst_caps_new_simple ("video/x-indeo",
14203 "indeoversion", G_TYPE_INT, 3, NULL);
14205 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14206 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14207 _codec ("Intel Video 4");
14208 caps = gst_caps_new_simple ("video/x-indeo",
14209 "indeoversion", G_TYPE_INT, 4, NULL);
14213 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14214 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14215 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14216 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14217 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14218 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14219 _codec ("DV Video");
14220 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14221 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14223 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14224 case FOURCC_dv5p: /* DVCPRO50 PAL */
14225 _codec ("DVCPro50 Video");
14226 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14227 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14229 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
14230 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
14231 _codec ("DVCProHD Video");
14232 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
14233 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14235 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
14236 _codec ("Apple Graphics (SMC)");
14237 caps = gst_caps_new_empty_simple ("video/x-smc");
14239 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
14241 caps = gst_caps_new_empty_simple ("video/x-vp3");
14243 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
14244 _codec ("VP6 Flash");
14245 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
14249 caps = gst_caps_new_empty_simple ("video/x-theora");
14250 /* theora uses one byte of padding in the data stream because it does not
14251 * allow 0 sized packets while theora does */
14252 entry->padding = 1;
14256 caps = gst_caps_new_empty_simple ("video/x-dirac");
14258 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
14259 _codec ("TIFF still images");
14260 caps = gst_caps_new_empty_simple ("image/tiff");
14262 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
14263 _codec ("Apple Intermediate Codec");
14264 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
14266 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
14267 _codec ("AVID DNxHD");
14268 caps = gst_caps_from_string ("video/x-dnxhd");
14272 _codec ("On2 VP8");
14273 caps = gst_caps_from_string ("video/x-vp8");
14276 _codec ("Google VP9");
14277 caps = gst_caps_from_string ("video/x-vp9");
14280 _codec ("Apple ProRes LT");
14282 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
14286 _codec ("Apple ProRes HQ");
14288 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
14292 _codec ("Apple ProRes");
14294 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14298 _codec ("Apple ProRes Proxy");
14300 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14304 _codec ("Apple ProRes 4444");
14306 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14310 _codec ("Apple ProRes 4444 XQ");
14312 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14316 _codec ("GoPro CineForm");
14317 caps = gst_caps_from_string ("video/x-cineform");
14322 caps = gst_caps_new_simple ("video/x-wmv",
14323 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
14327 caps = gst_caps_new_empty_simple ("video/x-av1");
14329 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
14332 caps = _get_unknown_codec_name ("video", fourcc);
14337 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
14340 gst_video_info_init (&info);
14341 gst_video_info_set_format (&info, format, entry->width, entry->height);
14343 caps = gst_video_info_to_caps (&info);
14344 *codec_name = gst_pb_utils_get_codec_description (caps);
14346 /* enable clipping for raw video streams */
14347 stream->need_clip = TRUE;
14348 stream->alignment = 32;
14355 round_up_pow2 (guint n)
14367 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14368 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
14369 int len, gchar ** codec_name)
14372 const GstStructure *s;
14375 GstAudioFormat format = 0;
14378 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14380 depth = entry->bytes_per_packet * 8;
14383 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
14385 /* 8-bit audio is unsigned */
14387 format = GST_AUDIO_FORMAT_U8;
14388 /* otherwise it's signed and big-endian just like 'twos' */
14390 endian = G_BIG_ENDIAN;
14397 endian = G_LITTLE_ENDIAN;
14400 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
14402 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
14406 caps = gst_caps_new_simple ("audio/x-raw",
14407 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14408 "layout", G_TYPE_STRING, "interleaved", NULL);
14409 stream->alignment = GST_ROUND_UP_8 (depth);
14410 stream->alignment = round_up_pow2 (stream->alignment);
14414 _codec ("Raw 64-bit floating-point audio");
14415 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14417 caps = gst_caps_new_simple ("audio/x-raw",
14418 "format", G_TYPE_STRING, "F64BE",
14419 "layout", G_TYPE_STRING, "interleaved", NULL);
14420 stream->alignment = 8;
14423 _codec ("Raw 32-bit floating-point audio");
14424 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14426 caps = gst_caps_new_simple ("audio/x-raw",
14427 "format", G_TYPE_STRING, "F32BE",
14428 "layout", G_TYPE_STRING, "interleaved", NULL);
14429 stream->alignment = 4;
14432 _codec ("Raw 24-bit PCM audio");
14433 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14435 caps = gst_caps_new_simple ("audio/x-raw",
14436 "format", G_TYPE_STRING, "S24BE",
14437 "layout", G_TYPE_STRING, "interleaved", NULL);
14438 stream->alignment = 4;
14441 _codec ("Raw 32-bit PCM audio");
14442 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14444 caps = gst_caps_new_simple ("audio/x-raw",
14445 "format", G_TYPE_STRING, "S32BE",
14446 "layout", G_TYPE_STRING, "interleaved", NULL);
14447 stream->alignment = 4;
14450 _codec ("Raw 16-bit PCM audio");
14451 caps = gst_caps_new_simple ("audio/x-raw",
14452 "format", G_TYPE_STRING, "S16LE",
14453 "layout", G_TYPE_STRING, "interleaved", NULL);
14454 stream->alignment = 2;
14457 _codec ("Mu-law audio");
14458 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
14461 _codec ("A-law audio");
14462 caps = gst_caps_new_empty_simple ("audio/x-alaw");
14466 _codec ("Microsoft ADPCM");
14467 /* Microsoft ADPCM-ACM code 2 */
14468 caps = gst_caps_new_simple ("audio/x-adpcm",
14469 "layout", G_TYPE_STRING, "microsoft", NULL);
14473 _codec ("DVI/IMA ADPCM");
14474 caps = gst_caps_new_simple ("audio/x-adpcm",
14475 "layout", G_TYPE_STRING, "dvi", NULL);
14479 _codec ("DVI/Intel IMA ADPCM");
14480 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
14481 caps = gst_caps_new_simple ("audio/x-adpcm",
14482 "layout", G_TYPE_STRING, "quicktime", NULL);
14486 /* MPEG layer 3, CBR only (pre QT4.1) */
14489 _codec ("MPEG-1 layer 3");
14490 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
14491 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
14492 "mpegversion", G_TYPE_INT, 1, NULL);
14494 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
14495 _codec ("MPEG-1 layer 2");
14497 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
14498 "mpegversion", G_TYPE_INT, 1, NULL);
14501 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
14502 _codec ("EAC-3 audio");
14503 caps = gst_caps_new_simple ("audio/x-eac3",
14504 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14505 entry->sampled = TRUE;
14507 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
14509 _codec ("AC-3 audio");
14510 caps = gst_caps_new_simple ("audio/x-ac3",
14511 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14512 entry->sampled = TRUE;
14514 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
14515 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
14516 _codec ("DTS audio");
14517 caps = gst_caps_new_simple ("audio/x-dts",
14518 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14519 entry->sampled = TRUE;
14521 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
14522 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
14523 _codec ("DTS-HD audio");
14524 caps = gst_caps_new_simple ("audio/x-dts",
14525 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14526 entry->sampled = TRUE;
14530 caps = gst_caps_new_simple ("audio/x-mace",
14531 "maceversion", G_TYPE_INT, 3, NULL);
14535 caps = gst_caps_new_simple ("audio/x-mace",
14536 "maceversion", G_TYPE_INT, 6, NULL);
14538 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
14540 caps = gst_caps_new_empty_simple ("application/ogg");
14542 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
14543 _codec ("DV audio");
14544 caps = gst_caps_new_empty_simple ("audio/x-dv");
14547 _codec ("MPEG-4 AAC audio");
14548 caps = gst_caps_new_simple ("audio/mpeg",
14549 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
14550 "stream-format", G_TYPE_STRING, "raw", NULL);
14552 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
14553 _codec ("QDesign Music");
14554 caps = gst_caps_new_empty_simple ("audio/x-qdm");
14557 _codec ("QDesign Music v.2");
14558 /* FIXME: QDesign music version 2 (no constant) */
14559 if (FALSE && data) {
14560 caps = gst_caps_new_simple ("audio/x-qdm2",
14561 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
14562 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
14563 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
14565 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
14569 _codec ("GSM audio");
14570 caps = gst_caps_new_empty_simple ("audio/x-gsm");
14573 _codec ("AMR audio");
14574 caps = gst_caps_new_empty_simple ("audio/AMR");
14577 _codec ("AMR-WB audio");
14578 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
14581 _codec ("Quicktime IMA ADPCM");
14582 caps = gst_caps_new_simple ("audio/x-adpcm",
14583 "layout", G_TYPE_STRING, "quicktime", NULL);
14586 _codec ("Apple lossless audio");
14587 caps = gst_caps_new_empty_simple ("audio/x-alac");
14590 _codec ("Free Lossless Audio Codec");
14591 caps = gst_caps_new_simple ("audio/x-flac",
14592 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14594 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
14595 _codec ("QualComm PureVoice");
14596 caps = gst_caps_from_string ("audio/qcelp");
14601 caps = gst_caps_new_empty_simple ("audio/x-wma");
14605 caps = gst_caps_new_empty_simple ("audio/x-opus");
14612 GstAudioFormat format;
14615 FLAG_IS_FLOAT = 0x1,
14616 FLAG_IS_BIG_ENDIAN = 0x2,
14617 FLAG_IS_SIGNED = 0x4,
14618 FLAG_IS_PACKED = 0x8,
14619 FLAG_IS_ALIGNED_HIGH = 0x10,
14620 FLAG_IS_NON_INTERLEAVED = 0x20
14622 _codec ("Raw LPCM audio");
14624 if (data && len >= 36) {
14625 depth = QT_UINT32 (data + 24);
14626 flags = QT_UINT32 (data + 28);
14627 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
14629 if ((flags & FLAG_IS_FLOAT) == 0) {
14634 if ((flags & FLAG_IS_ALIGNED_HIGH))
14637 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
14638 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
14639 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
14640 caps = gst_caps_new_simple ("audio/x-raw",
14641 "format", G_TYPE_STRING,
14643 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
14644 "UNKNOWN", "layout", G_TYPE_STRING,
14645 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
14646 "interleaved", NULL);
14647 stream->alignment = GST_ROUND_UP_8 (depth);
14648 stream->alignment = round_up_pow2 (stream->alignment);
14653 if (flags & FLAG_IS_BIG_ENDIAN)
14654 format = GST_AUDIO_FORMAT_F64BE;
14656 format = GST_AUDIO_FORMAT_F64LE;
14658 if (flags & FLAG_IS_BIG_ENDIAN)
14659 format = GST_AUDIO_FORMAT_F32BE;
14661 format = GST_AUDIO_FORMAT_F32LE;
14663 caps = gst_caps_new_simple ("audio/x-raw",
14664 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14665 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14666 "non-interleaved" : "interleaved", NULL);
14667 stream->alignment = width / 8;
14671 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
14674 caps = gst_caps_new_empty_simple ("audio/x-ac4");
14677 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
14681 caps = _get_unknown_codec_name ("audio", fourcc);
14687 GstCaps *templ_caps =
14688 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
14689 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
14690 gst_caps_unref (caps);
14691 gst_caps_unref (templ_caps);
14692 caps = intersection;
14695 /* enable clipping for raw audio streams */
14696 s = gst_caps_get_structure (caps, 0);
14697 name = gst_structure_get_name (s);
14698 if (g_str_has_prefix (name, "audio/x-raw")) {
14699 stream->need_clip = TRUE;
14700 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
14701 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
14702 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
14703 stream->max_buffer_size);
14709 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14710 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14711 const guint8 * stsd_entry_data, gchar ** codec_name)
14715 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14719 _codec ("DVD subtitle");
14720 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
14721 stream->need_process = TRUE;
14724 _codec ("Quicktime timed text");
14727 _codec ("3GPP timed text");
14729 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
14731 /* actual text piece needs to be extracted */
14732 stream->need_process = TRUE;
14735 _codec ("XML subtitles");
14736 caps = gst_caps_new_empty_simple ("application/ttml+xml");
14739 _codec ("CEA 608 Closed Caption");
14741 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
14742 G_TYPE_STRING, "s334-1a", NULL);
14743 stream->need_process = TRUE;
14744 stream->need_split = TRUE;
14747 _codec ("CEA 708 Closed Caption");
14749 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
14750 G_TYPE_STRING, "cdp", NULL);
14751 stream->need_process = TRUE;
14756 caps = _get_unknown_codec_name ("text", fourcc);
14764 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14765 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14766 const guint8 * stsd_entry_data, gchar ** codec_name)
14772 _codec ("MPEG 1 video");
14773 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14774 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14784 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
14785 const gchar * system_id)
14789 if (!qtdemux->protection_system_ids)
14790 qtdemux->protection_system_ids =
14791 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
14792 /* Check whether we already have an entry for this system ID. */
14793 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
14794 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
14795 if (g_ascii_strcasecmp (system_id, id) == 0) {
14799 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
14800 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,