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 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10658 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10659 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10661 stsd_entry_data = stsd_data + 16;
10662 remaining_stsd_len = stsd_len - 16;
10663 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10665 gchar *codec = NULL;
10666 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10668 /* and that entry should fit within stsd */
10669 len = QT_UINT32 (stsd_entry_data);
10670 if (len > remaining_stsd_len)
10673 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10674 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10675 GST_FOURCC_ARGS (entry->fourcc));
10676 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10678 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10679 goto error_encrypted;
10681 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10682 /* FIXME this looks wrong, there might be multiple children
10683 * with the same type */
10684 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10685 stream->protected = TRUE;
10686 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10687 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10690 if (stream->subtype == FOURCC_vide) {
10695 gint depth, palette_size, palette_count;
10696 guint32 *palette_data = NULL;
10698 entry->sampled = TRUE;
10700 stream->display_width = w >> 16;
10701 stream->display_height = h >> 16;
10704 if (len < 86) /* TODO verify */
10707 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10708 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10709 entry->fps_n = 0; /* this is filled in later */
10710 entry->fps_d = 0; /* this is filled in later */
10711 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10712 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10714 /* if color_table_id is 0, ctab atom must follow; however some files
10715 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10716 * if color table is not present we'll correct the value */
10717 if (entry->color_table_id == 0 &&
10719 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10720 entry->color_table_id = -1;
10723 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10724 entry->width, entry->height, entry->bits_per_sample,
10725 entry->color_table_id);
10727 depth = entry->bits_per_sample;
10729 /* more than 32 bits means grayscale */
10730 gray = (depth > 32);
10731 /* low 32 bits specify the depth */
10734 /* different number of palette entries is determined by depth. */
10736 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10737 palette_count = (1 << depth);
10738 palette_size = palette_count * 4;
10740 if (entry->color_table_id) {
10741 switch (palette_count) {
10745 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10748 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10753 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10755 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10760 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10762 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10765 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10766 (_("The video in this file might not play correctly.")),
10767 ("unsupported palette depth %d", depth));
10771 gint i, j, start, end;
10777 start = QT_UINT32 (stsd_entry_data + offset + 70);
10778 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10779 end = QT_UINT16 (stsd_entry_data + offset + 76);
10781 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10782 start, end, palette_count);
10789 if (len < 94 + (end - start) * 8)
10792 /* palette is always the same size */
10793 palette_data = g_malloc0 (256 * 4);
10794 palette_size = 256 * 4;
10796 for (j = 0, i = start; i <= end; j++, i++) {
10797 guint32 a, r, g, b;
10799 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10800 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10801 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10802 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10804 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10805 (g & 0xff00) | (b >> 8);
10810 gst_caps_unref (entry->caps);
10813 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10815 if (G_UNLIKELY (!entry->caps)) {
10816 g_free (palette_data);
10817 goto unknown_stream;
10821 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10822 GST_TAG_VIDEO_CODEC, codec, NULL);
10827 if (palette_data) {
10830 if (entry->rgb8_palette)
10831 gst_memory_unref (entry->rgb8_palette);
10832 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10833 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10835 s = gst_caps_get_structure (entry->caps, 0);
10837 /* non-raw video has a palette_data property. raw video has the palette as
10838 * an extra plane that we append to the output buffers before we push
10840 if (!gst_structure_has_name (s, "video/x-raw")) {
10841 GstBuffer *palette;
10843 palette = gst_buffer_new ();
10844 gst_buffer_append_memory (palette, entry->rgb8_palette);
10845 entry->rgb8_palette = NULL;
10847 gst_caps_set_simple (entry->caps, "palette_data",
10848 GST_TYPE_BUFFER, palette, NULL);
10849 gst_buffer_unref (palette);
10851 } else if (palette_count != 0) {
10852 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10853 (NULL), ("Unsupported palette depth %d", depth));
10856 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10857 QT_UINT16 (stsd_entry_data + offset + 32));
10863 /* pick 'the' stsd child */
10864 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10865 // We should skip parsing the stsd for non-protected streams if
10866 // the entry doesn't match the fourcc, since they don't change
10867 // format. However, for protected streams we can have partial
10868 // encryption, where parts of the stream are encrypted and parts
10869 // not. For both parts of such streams, we should ensure the
10870 // esds overrides are parsed for both from the stsd.
10871 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10872 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
10874 else if (!stream->protected)
10879 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10880 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10881 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10882 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10886 const guint8 *pasp_data = (const guint8 *) pasp->data;
10887 gint len = QT_UINT32 (pasp_data);
10890 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10891 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10893 CUR_STREAM (stream)->par_w = 0;
10894 CUR_STREAM (stream)->par_h = 0;
10897 CUR_STREAM (stream)->par_w = 0;
10898 CUR_STREAM (stream)->par_h = 0;
10902 const guint8 *fiel_data = (const guint8 *) fiel->data;
10903 gint len = QT_UINT32 (fiel_data);
10906 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10907 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10912 const guint8 *colr_data = (const guint8 *) colr->data;
10913 gint len = QT_UINT32 (colr_data);
10915 if (len == 19 || len == 18) {
10916 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10918 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10919 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10920 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10921 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10922 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10924 CUR_STREAM (stream)->colorimetry.primaries =
10925 gst_video_color_primaries_from_iso (primaries);
10926 CUR_STREAM (stream)->colorimetry.transfer =
10927 gst_video_color_transfer_from_iso (transfer_function);
10928 CUR_STREAM (stream)->colorimetry.matrix =
10929 gst_video_color_matrix_from_iso (matrix);
10930 CUR_STREAM (stream)->colorimetry.range =
10931 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10932 GST_VIDEO_COLOR_RANGE_16_235;
10934 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10937 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10942 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10943 stream->stream_tags);
10950 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10951 const guint8 *avc_data = stsd_entry_data + 0x56;
10954 while (len >= 0x8) {
10957 if (QT_UINT32 (avc_data) <= len)
10958 size = QT_UINT32 (avc_data) - 0x8;
10963 /* No real data, so break out */
10966 switch (QT_FOURCC (avc_data + 0x4)) {
10969 /* parse, if found */
10972 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10974 /* First 4 bytes are the length of the atom, the next 4 bytes
10975 * are the fourcc, the next 1 byte is the version, and the
10976 * subsequent bytes are profile_tier_level structure like data. */
10977 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
10978 avc_data + 8 + 1, size - 1);
10979 buf = gst_buffer_new_and_alloc (size);
10980 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
10981 gst_caps_set_simple (entry->caps,
10982 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10983 gst_buffer_unref (buf);
10991 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
10993 /* First 4 bytes are the length of the atom, the next 4 bytes
10994 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
10995 * next 1 byte is the version, and the
10996 * subsequent bytes are sequence parameter set like data. */
10998 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11000 gst_codec_utils_h264_caps_set_level_and_profile
11001 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11003 buf = gst_buffer_new_and_alloc (size);
11004 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11005 gst_caps_set_simple (entry->caps,
11006 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11007 gst_buffer_unref (buf);
11013 guint avg_bitrate, max_bitrate;
11015 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11019 max_bitrate = QT_UINT32 (avc_data + 0xc);
11020 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11022 if (!max_bitrate && !avg_bitrate)
11025 /* Some muxers seem to swap the average and maximum bitrates
11026 * (I'm looking at you, YouTube), so we swap for sanity. */
11027 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11028 guint temp = avg_bitrate;
11030 avg_bitrate = max_bitrate;
11031 max_bitrate = temp;
11034 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11035 gst_tag_list_add (stream->stream_tags,
11036 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11037 max_bitrate, NULL);
11039 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11040 gst_tag_list_add (stream->stream_tags,
11041 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11053 avc_data += size + 8;
11062 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11063 const guint8 *hevc_data = stsd_entry_data + 0x56;
11066 while (len >= 0x8) {
11069 if (QT_UINT32 (hevc_data) <= len)
11070 size = QT_UINT32 (hevc_data) - 0x8;
11075 /* No real data, so break out */
11078 switch (QT_FOURCC (hevc_data + 0x4)) {
11081 /* parse, if found */
11084 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11086 /* First 4 bytes are the length of the atom, the next 4 bytes
11087 * are the fourcc, the next 1 byte is the version, and the
11088 * subsequent bytes are sequence parameter set like data. */
11089 gst_codec_utils_h265_caps_set_level_tier_and_profile
11090 (entry->caps, hevc_data + 8 + 1, size - 1);
11092 buf = gst_buffer_new_and_alloc (size);
11093 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11094 gst_caps_set_simple (entry->caps,
11095 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11096 gst_buffer_unref (buf);
11103 hevc_data += size + 8;
11116 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11117 GST_FOURCC_ARGS (fourcc));
11119 /* codec data might be in glbl extension atom */
11121 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11127 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11129 len = QT_UINT32 (data);
11132 buf = gst_buffer_new_and_alloc (len);
11133 gst_buffer_fill (buf, 0, data + 8, len);
11134 gst_caps_set_simple (entry->caps,
11135 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11136 gst_buffer_unref (buf);
11143 /* see annex I of the jpeg2000 spec */
11144 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11145 const guint8 *data;
11146 const gchar *colorspace = NULL;
11148 guint32 ncomp_map = 0;
11149 gint32 *comp_map = NULL;
11150 guint32 nchan_def = 0;
11151 gint32 *chan_def = NULL;
11153 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11154 /* some required atoms */
11155 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11158 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11162 /* number of components; redundant with info in codestream, but useful
11164 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11165 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11167 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11169 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11172 GST_DEBUG_OBJECT (qtdemux, "found colr");
11173 /* extract colour space info */
11174 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11175 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11177 colorspace = "sRGB";
11180 colorspace = "GRAY";
11183 colorspace = "sYUV";
11191 /* colr is required, and only values 16, 17, and 18 are specified,
11192 so error if we have no colorspace */
11195 /* extract component mapping */
11196 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11198 guint32 cmap_len = 0;
11200 cmap_len = QT_UINT32 (cmap->data);
11201 if (cmap_len >= 8) {
11202 /* normal box, subtract off header */
11204 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11205 if (cmap_len % 4 == 0) {
11206 ncomp_map = (cmap_len / 4);
11207 comp_map = g_new0 (gint32, ncomp_map);
11208 for (i = 0; i < ncomp_map; i++) {
11211 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11212 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11213 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11214 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11219 /* extract channel definitions */
11220 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11222 guint32 cdef_len = 0;
11224 cdef_len = QT_UINT32 (cdef->data);
11225 if (cdef_len >= 10) {
11226 /* normal box, subtract off header and len */
11228 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11229 if (cdef_len % 6 == 0) {
11230 nchan_def = (cdef_len / 6);
11231 chan_def = g_new0 (gint32, nchan_def);
11232 for (i = 0; i < nchan_def; i++)
11234 for (i = 0; i < nchan_def; i++) {
11235 guint16 cn, typ, asoc;
11236 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11237 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11238 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11239 if (cn < nchan_def) {
11242 chan_def[cn] = asoc;
11245 chan_def[cn] = 0; /* alpha */
11248 chan_def[cn] = -typ;
11256 gst_caps_set_simple (entry->caps,
11257 "num-components", G_TYPE_INT, ncomp, NULL);
11258 gst_caps_set_simple (entry->caps,
11259 "colorspace", G_TYPE_STRING, colorspace, NULL);
11262 GValue arr = { 0, };
11263 GValue elt = { 0, };
11265 g_value_init (&arr, GST_TYPE_ARRAY);
11266 g_value_init (&elt, G_TYPE_INT);
11267 for (i = 0; i < ncomp_map; i++) {
11268 g_value_set_int (&elt, comp_map[i]);
11269 gst_value_array_append_value (&arr, &elt);
11271 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11272 "component-map", &arr);
11273 g_value_unset (&elt);
11274 g_value_unset (&arr);
11279 GValue arr = { 0, };
11280 GValue elt = { 0, };
11282 g_value_init (&arr, GST_TYPE_ARRAY);
11283 g_value_init (&elt, G_TYPE_INT);
11284 for (i = 0; i < nchan_def; i++) {
11285 g_value_set_int (&elt, chan_def[i]);
11286 gst_value_array_append_value (&arr, &elt);
11288 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11289 "channel-definitions", &arr);
11290 g_value_unset (&elt);
11291 g_value_unset (&arr);
11295 /* some optional atoms */
11296 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11297 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11299 /* indicate possible fields in caps */
11301 data = (guint8 *) field->data + 8;
11303 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11304 (gint) * data, NULL);
11306 /* add codec_data if provided */
11311 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11312 data = prefix->data;
11313 len = QT_UINT32 (data);
11316 buf = gst_buffer_new_and_alloc (len);
11317 gst_buffer_fill (buf, 0, data + 8, len);
11318 gst_caps_set_simple (entry->caps,
11319 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11320 gst_buffer_unref (buf);
11329 GstBuffer *seqh = NULL;
11330 const guint8 *gamma_data = NULL;
11331 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11333 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11336 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11337 QT_FP32 (gamma_data), NULL);
11340 /* sorry for the bad name, but we don't know what this is, other
11341 * than its own fourcc */
11342 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11344 gst_buffer_unref (seqh);
11347 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11348 buf = gst_buffer_new_and_alloc (len);
11349 gst_buffer_fill (buf, 0, stsd_data, len);
11350 gst_caps_set_simple (entry->caps,
11351 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11352 gst_buffer_unref (buf);
11357 /* https://developer.apple.com/standards/qtff-2001.pdf,
11358 * page 92, "Video Sample Description", under table 3.1 */
11361 const gint compressor_offset =
11362 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11363 const gint min_size = compressor_offset + 32 + 2 + 2;
11366 guint16 color_table_id = 0;
11369 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11371 /* recover information on interlaced/progressive */
11372 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11376 len = QT_UINT32 (jpeg->data);
11377 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11379 if (len >= min_size) {
11380 gst_byte_reader_init (&br, jpeg->data, len);
11382 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11383 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11384 if (color_table_id != 0) {
11385 /* the spec says there can be concatenated chunks in the data, and we want
11386 * to find one called field. Walk through them. */
11387 gint offset = min_size;
11388 while (offset + 8 < len) {
11389 guint32 size = 0, tag;
11390 ok = gst_byte_reader_get_uint32_le (&br, &size);
11391 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11392 if (!ok || size < 8) {
11393 GST_WARNING_OBJECT (qtdemux,
11394 "Failed to walk optional chunk list");
11397 GST_DEBUG_OBJECT (qtdemux,
11398 "Found optional %4.4s chunk, size %u",
11399 (const char *) &tag, size);
11400 if (tag == FOURCC_fiel) {
11401 guint8 n_fields = 0, ordering = 0;
11402 gst_byte_reader_get_uint8 (&br, &n_fields);
11403 gst_byte_reader_get_uint8 (&br, &ordering);
11404 if (n_fields == 1 || n_fields == 2) {
11405 GST_DEBUG_OBJECT (qtdemux,
11406 "Found fiel tag with %u fields, ordering %u",
11407 n_fields, ordering);
11409 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11410 "interlace-mode", G_TYPE_STRING, "interleaved",
11413 GST_WARNING_OBJECT (qtdemux,
11414 "Found fiel tag with invalid fields (%u)", n_fields);
11420 GST_DEBUG_OBJECT (qtdemux,
11421 "Color table ID is 0, not trying to get interlacedness");
11424 GST_WARNING_OBJECT (qtdemux,
11425 "Length of jpeg chunk is too small, not trying to get interlacedness");
11433 gst_caps_set_simple (entry->caps,
11434 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11440 GNode *xith, *xdxt;
11442 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11443 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11447 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11451 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11452 /* collect the headers and store them in a stream list so that we can
11453 * send them out first */
11454 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11464 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11465 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11468 ovc1_data = ovc1->data;
11469 ovc1_len = QT_UINT32 (ovc1_data);
11470 if (ovc1_len <= 198) {
11471 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11474 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11475 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11476 gst_caps_set_simple (entry->caps,
11477 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11478 gst_buffer_unref (buf);
11483 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11484 const guint8 *vc1_data = stsd_entry_data + 0x56;
11490 if (QT_UINT32 (vc1_data) <= len)
11491 size = QT_UINT32 (vc1_data) - 8;
11496 /* No real data, so break out */
11499 switch (QT_FOURCC (vc1_data + 0x4)) {
11500 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11504 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11505 buf = gst_buffer_new_and_alloc (size);
11506 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11507 gst_caps_set_simple (entry->caps,
11508 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11509 gst_buffer_unref (buf);
11516 vc1_data += size + 8;
11522 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11523 const guint8 *av1_data = stsd_entry_data + 0x56;
11526 while (len >= 0x8) {
11529 if (QT_UINT32 (av1_data) <= len)
11530 size = QT_UINT32 (av1_data) - 0x8;
11535 /* No real data, so break out */
11538 switch (QT_FOURCC (av1_data + 0x4)) {
11541 /* parse, if found */
11543 guint8 pres_delay_field;
11545 GST_DEBUG_OBJECT (qtdemux,
11546 "found av1C codec_data in stsd of size %d", size);
11548 /* not enough data, just ignore and hope for the best */
11553 * 4 bytes: atom length
11558 * 1 bits: initial_presentation_delay_present
11559 * 4 bits: initial_presentation_delay (if present else reserved
11563 if (av1_data[9] != 0) {
11564 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
11568 /* We skip initial_presentation_delay* for now */
11569 pres_delay_field = *(av1_data + 12);
11570 if (pres_delay_field & (1 << 5)) {
11571 gst_caps_set_simple (entry->caps,
11572 "presentation-delay", G_TYPE_INT,
11573 (gint) (pres_delay_field & 0x0F) + 1, NULL);
11576 buf = gst_buffer_new_and_alloc (size - 5);
11577 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
11578 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
11579 gst_caps_set_simple (entry->caps,
11580 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11581 gst_buffer_unref (buf);
11590 av1_data += size + 8;
11596 /* TODO: Need to parse vpcC for VP8 codec too.
11597 * Note that VPCodecConfigurationBox (vpcC) is defined for
11598 * vp08, vp09, and vp10 fourcc. */
11601 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11602 const guint8 *vpcc_data = stsd_entry_data + 0x56;
11605 while (len >= 0x8) {
11608 if (QT_UINT32 (vpcc_data) <= len)
11609 size = QT_UINT32 (vpcc_data) - 0x8;
11614 /* No real data, so break out */
11617 switch (QT_FOURCC (vpcc_data + 0x4)) {
11620 const gchar *profile_str = NULL;
11621 const gchar *chroma_format_str = NULL;
11624 guint8 chroma_format;
11625 GstVideoColorimetry cinfo;
11627 /* parse, if found */
11628 GST_DEBUG_OBJECT (qtdemux,
11629 "found vp codec_data in stsd of size %d", size);
11631 /* the meaning of "size" is length of the atom body, excluding
11632 * atom length and fourcc fields */
11637 * 4 bytes: atom length
11644 * 3 bits: chromaSubsampling
11645 * 1 bit: videoFullRangeFlag
11646 * 1 byte: colourPrimaries
11647 * 1 byte: transferCharacteristics
11648 * 1 byte: matrixCoefficients
11649 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
11650 * rest: codecIntializationData (not used for vp8 and vp9)
11653 if (vpcc_data[8] != 1) {
11654 GST_WARNING_OBJECT (qtdemux,
11655 "unknown vpcC version %d", vpcc_data[8]);
11659 profile = vpcc_data[12];
11678 gst_caps_set_simple (entry->caps,
11679 "profile", G_TYPE_STRING, profile_str, NULL);
11682 /* skip level, the VP9 spec v0.6 defines only one level atm,
11683 * but webm spec define various ones. Add level to caps
11684 * if we really need it then */
11686 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
11687 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
11688 gst_caps_set_simple (entry->caps,
11689 "bit-depth-luma", G_TYPE_UINT, bitdepth,
11690 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
11693 chroma_format = (vpcc_data[14] & 0xe) >> 1;
11694 switch (chroma_format) {
11697 chroma_format_str = "4:2:0";
11700 chroma_format_str = "4:2:2";
11703 chroma_format_str = "4:4:4";
11709 if (chroma_format_str) {
11710 gst_caps_set_simple (entry->caps,
11711 "chroma-format", G_TYPE_STRING, chroma_format_str,
11715 if ((vpcc_data[14] & 0x1) != 0)
11716 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
11718 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
11720 gst_video_color_primaries_from_iso (vpcc_data[15]);
11722 gst_video_color_transfer_from_iso (vpcc_data[16]);
11724 gst_video_color_matrix_from_iso (vpcc_data[17]);
11726 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
11727 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
11728 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
11729 /* set this only if all values are known, otherwise this
11730 * might overwrite valid ones parsed from other color box */
11731 CUR_STREAM (stream)->colorimetry = cinfo;
11740 vpcc_data += size + 8;
11750 GST_INFO_OBJECT (qtdemux,
11751 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11752 GST_FOURCC_ARGS (fourcc), entry->caps);
11754 } else if (stream->subtype == FOURCC_soun) {
11756 int version, samplesize;
11757 guint16 compression_id;
11758 gboolean amrwb = FALSE;
11761 /* sample description entry (16) + sound sample description v0 (20) */
11765 version = QT_UINT32 (stsd_entry_data + offset);
11766 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11767 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11768 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11769 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11771 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11772 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11773 QT_UINT32 (stsd_entry_data + offset + 4));
11774 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11775 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11776 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11777 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11778 QT_UINT16 (stsd_entry_data + offset + 14));
11779 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11781 if (compression_id == 0xfffe)
11782 entry->sampled = TRUE;
11784 /* first assume uncompressed audio */
11785 entry->bytes_per_sample = samplesize / 8;
11786 entry->samples_per_frame = entry->n_channels;
11787 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11788 entry->samples_per_packet = entry->samples_per_frame;
11789 entry->bytes_per_packet = entry->bytes_per_sample;
11793 if (version == 0x00010000) {
11794 /* sample description entry (16) + sound sample description v1 (20+16) */
11798 /* take information from here over the normal sample description */
11799 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11800 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11801 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11802 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11804 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
11805 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11806 entry->samples_per_packet);
11807 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11808 entry->bytes_per_packet);
11809 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11810 entry->bytes_per_frame);
11811 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11812 entry->bytes_per_sample);
11814 if (!entry->sampled && entry->bytes_per_packet) {
11815 entry->samples_per_frame = (entry->bytes_per_frame /
11816 entry->bytes_per_packet) * entry->samples_per_packet;
11817 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11818 entry->samples_per_frame);
11820 } else if (version == 0x00020000) {
11821 /* sample description entry (16) + sound sample description v2 (56) */
11825 /* take information from here over the normal sample description */
11826 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
11827 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11828 entry->samples_per_frame = entry->n_channels;
11829 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
11830 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
11831 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
11832 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
11834 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11835 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11836 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11837 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11838 entry->bytes_per_sample * 8);
11839 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11840 QT_UINT32 (stsd_entry_data + offset + 24));
11841 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11842 entry->bytes_per_packet);
11843 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11844 entry->samples_per_packet);
11845 } else if (version != 0x00000) {
11846 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11851 /* Yes, these have to be hard-coded */
11854 entry->samples_per_packet = 6;
11855 entry->bytes_per_packet = 1;
11856 entry->bytes_per_frame = 1 * entry->n_channels;
11857 entry->bytes_per_sample = 1;
11858 entry->samples_per_frame = 6 * entry->n_channels;
11863 entry->samples_per_packet = 3;
11864 entry->bytes_per_packet = 1;
11865 entry->bytes_per_frame = 1 * entry->n_channels;
11866 entry->bytes_per_sample = 1;
11867 entry->samples_per_frame = 3 * entry->n_channels;
11872 entry->samples_per_packet = 64;
11873 entry->bytes_per_packet = 34;
11874 entry->bytes_per_frame = 34 * entry->n_channels;
11875 entry->bytes_per_sample = 2;
11876 entry->samples_per_frame = 64 * entry->n_channels;
11882 entry->samples_per_packet = 1;
11883 entry->bytes_per_packet = 1;
11884 entry->bytes_per_frame = 1 * entry->n_channels;
11885 entry->bytes_per_sample = 1;
11886 entry->samples_per_frame = 1 * entry->n_channels;
11891 entry->samples_per_packet = 160;
11892 entry->bytes_per_packet = 33;
11893 entry->bytes_per_frame = 33 * entry->n_channels;
11894 entry->bytes_per_sample = 2;
11895 entry->samples_per_frame = 160 * entry->n_channels;
11898 /* fix up any invalid header information from above */
11903 /* Sometimes these are set to 0 in the sound sample descriptions so
11904 * let's try to infer useful values from the other information we
11905 * have available */
11906 if (entry->bytes_per_sample == 0)
11907 entry->bytes_per_sample =
11908 entry->bytes_per_frame / entry->n_channels;
11909 if (entry->bytes_per_sample == 0)
11910 entry->bytes_per_sample = samplesize / 8;
11912 if (entry->bytes_per_frame == 0)
11913 entry->bytes_per_frame =
11914 entry->bytes_per_sample * entry->n_channels;
11916 if (entry->bytes_per_packet == 0)
11917 entry->bytes_per_packet = entry->bytes_per_sample;
11919 if (entry->samples_per_frame == 0)
11920 entry->samples_per_frame = entry->n_channels;
11922 if (entry->samples_per_packet == 0)
11923 entry->samples_per_packet = entry->samples_per_frame;
11933 entry->bytes_per_sample = 3;
11937 entry->bytes_per_sample = 4;
11940 entry->bytes_per_sample = 8;
11943 entry->bytes_per_sample = 2;
11946 g_assert_not_reached ();
11949 entry->samples_per_frame = entry->n_channels;
11950 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11951 entry->samples_per_packet = entry->samples_per_frame;
11952 entry->bytes_per_packet = entry->bytes_per_sample;
11960 gst_caps_unref (entry->caps);
11962 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11963 stsd_entry_data + 32, len - 16, &codec);
11974 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
11976 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
11978 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
11980 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11983 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11984 const gchar *format_str;
11988 format_str = (enda_value) ? "S24LE" : "S24BE";
11991 format_str = (enda_value) ? "S32LE" : "S32BE";
11994 format_str = (enda_value) ? "F32LE" : "F32BE";
11997 format_str = (enda_value) ? "F64LE" : "F64BE";
12000 g_assert_not_reached ();
12003 gst_caps_set_simple (entry->caps,
12004 "format", G_TYPE_STRING, format_str, NULL);
12010 const guint8 *owma_data;
12011 const gchar *codec_name = NULL;
12015 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12016 /* FIXME this should also be gst_riff_strf_auds,
12017 * but the latter one is actually missing bits-per-sample :( */
12022 gint32 nSamplesPerSec;
12023 gint32 nAvgBytesPerSec;
12024 gint16 nBlockAlign;
12025 gint16 wBitsPerSample;
12028 WAVEFORMATEX *wfex;
12030 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12031 owma_data = stsd_entry_data;
12032 owma_len = QT_UINT32 (owma_data);
12033 if (owma_len <= 54) {
12034 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12037 wfex = (WAVEFORMATEX *) (owma_data + 36);
12038 buf = gst_buffer_new_and_alloc (owma_len - 54);
12039 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12040 if (wfex->wFormatTag == 0x0161) {
12041 codec_name = "Windows Media Audio";
12043 } else if (wfex->wFormatTag == 0x0162) {
12044 codec_name = "Windows Media Audio 9 Pro";
12046 } else if (wfex->wFormatTag == 0x0163) {
12047 codec_name = "Windows Media Audio 9 Lossless";
12048 /* is that correct? gstffmpegcodecmap.c is missing it, but
12049 * fluendo codec seems to support it */
12053 gst_caps_set_simple (entry->caps,
12054 "codec_data", GST_TYPE_BUFFER, buf,
12055 "wmaversion", G_TYPE_INT, version,
12056 "block_align", G_TYPE_INT,
12057 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12058 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
12059 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
12060 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
12061 gst_buffer_unref (buf);
12065 codec = g_strdup (codec_name);
12071 gint len = QT_UINT32 (stsd_entry_data) - offset;
12072 const guint8 *wfex_data = stsd_entry_data + offset;
12073 const gchar *codec_name = NULL;
12075 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12076 /* FIXME this should also be gst_riff_strf_auds,
12077 * but the latter one is actually missing bits-per-sample :( */
12082 gint32 nSamplesPerSec;
12083 gint32 nAvgBytesPerSec;
12084 gint16 nBlockAlign;
12085 gint16 wBitsPerSample;
12090 /* FIXME: unify with similar wavformatex parsing code above */
12091 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
12097 if (QT_UINT32 (wfex_data) <= len)
12098 size = QT_UINT32 (wfex_data) - 8;
12103 /* No real data, so break out */
12106 switch (QT_FOURCC (wfex_data + 4)) {
12107 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
12109 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
12114 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
12115 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
12116 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
12117 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
12118 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
12119 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
12120 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
12122 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
12123 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
12124 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
12125 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
12126 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
12127 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
12129 if (wfex.wFormatTag == 0x0161) {
12130 codec_name = "Windows Media Audio";
12132 } else if (wfex.wFormatTag == 0x0162) {
12133 codec_name = "Windows Media Audio 9 Pro";
12135 } else if (wfex.wFormatTag == 0x0163) {
12136 codec_name = "Windows Media Audio 9 Lossless";
12137 /* is that correct? gstffmpegcodecmap.c is missing it, but
12138 * fluendo codec seems to support it */
12142 gst_caps_set_simple (entry->caps,
12143 "wmaversion", G_TYPE_INT, version,
12144 "block_align", G_TYPE_INT, wfex.nBlockAlign,
12145 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
12146 "width", G_TYPE_INT, wfex.wBitsPerSample,
12147 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
12149 if (size > wfex.cbSize) {
12152 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
12153 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
12154 size - wfex.cbSize);
12155 gst_caps_set_simple (entry->caps,
12156 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12157 gst_buffer_unref (buf);
12159 GST_WARNING_OBJECT (qtdemux, "no codec data");
12164 codec = g_strdup (codec_name);
12172 wfex_data += size + 8;
12178 const guint8 *opus_data;
12179 guint8 *channel_mapping = NULL;
12182 guint8 channel_mapping_family;
12183 guint8 stream_count;
12184 guint8 coupled_count;
12187 opus_data = stsd_entry_data;
12189 channels = GST_READ_UINT8 (opus_data + 45);
12190 rate = GST_READ_UINT32_LE (opus_data + 48);
12191 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
12192 stream_count = GST_READ_UINT8 (opus_data + 55);
12193 coupled_count = GST_READ_UINT8 (opus_data + 56);
12195 if (channels > 0) {
12196 channel_mapping = g_malloc (channels * sizeof (guint8));
12197 for (i = 0; i < channels; i++)
12198 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
12201 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12202 channel_mapping_family, stream_count, coupled_count,
12214 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12215 GST_TAG_AUDIO_CODEC, codec, NULL);
12219 /* some bitrate info may have ended up in caps */
12220 s = gst_caps_get_structure (entry->caps, 0);
12221 gst_structure_get_int (s, "bitrate", &bitrate);
12223 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12224 GST_TAG_BITRATE, bitrate, NULL);
12227 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12228 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12229 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
12231 else if (!stream->protected)
12238 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12240 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12242 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12246 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12247 16 bits is a byte-swapped wave-style codec identifier,
12248 and we can find a WAVE header internally to a 'wave' atom here.
12249 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12250 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12253 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12254 if (len < offset + 20) {
12255 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12257 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12258 const guint8 *data = stsd_entry_data + offset + 16;
12260 GNode *waveheadernode;
12262 wavenode = g_node_new ((guint8 *) data);
12263 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12264 const guint8 *waveheader;
12267 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12268 if (waveheadernode) {
12269 waveheader = (const guint8 *) waveheadernode->data;
12270 headerlen = QT_UINT32 (waveheader);
12272 if (headerlen > 8) {
12273 gst_riff_strf_auds *header = NULL;
12274 GstBuffer *headerbuf;
12280 headerbuf = gst_buffer_new_and_alloc (headerlen);
12281 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12283 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12284 headerbuf, &header, &extra)) {
12285 gst_caps_unref (entry->caps);
12286 /* FIXME: Need to do something with the channel reorder map */
12288 gst_riff_create_audio_caps (header->format, NULL, header,
12289 extra, NULL, NULL, NULL);
12292 gst_buffer_unref (extra);
12297 GST_DEBUG ("Didn't find waveheadernode for this codec");
12299 g_node_destroy (wavenode);
12302 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12303 stream->stream_tags);
12307 /* FIXME: what is in the chunk? */
12310 gint len = QT_UINT32 (stsd_data);
12312 /* seems to be always = 116 = 0x74 */
12318 gint len = QT_UINT32 (stsd_entry_data);
12321 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12323 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12324 gst_caps_set_simple (entry->caps,
12325 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12326 gst_buffer_unref (buf);
12328 gst_caps_set_simple (entry->caps,
12329 "samplesize", G_TYPE_INT, samplesize, NULL);
12334 GNode *alac, *wave = NULL;
12336 /* apparently, m4a has this atom appended directly in the stsd entry,
12337 * while mov has it in a wave atom */
12338 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12340 /* alac now refers to stsd entry atom */
12341 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12343 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12345 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12348 const guint8 *alac_data = alac->data;
12349 gint len = QT_UINT32 (alac->data);
12353 GST_DEBUG_OBJECT (qtdemux,
12354 "discarding alac atom with unexpected len %d", len);
12356 /* codec-data contains alac atom size and prefix,
12357 * ffmpeg likes it that way, not quite gst-ish though ...*/
12358 buf = gst_buffer_new_and_alloc (len);
12359 gst_buffer_fill (buf, 0, alac->data, len);
12360 gst_caps_set_simple (entry->caps,
12361 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12362 gst_buffer_unref (buf);
12364 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12365 entry->n_channels = QT_UINT8 (alac_data + 21);
12366 entry->rate = QT_UINT32 (alac_data + 32);
12369 gst_caps_set_simple (entry->caps,
12370 "samplesize", G_TYPE_INT, samplesize, NULL);
12375 /* The codingname of the sample entry is 'fLaC' */
12376 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12379 /* The 'dfLa' box is added to the sample entry to convey
12380 initializing information for the decoder. */
12381 const GNode *dfla =
12382 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12385 const guint32 len = QT_UINT32 (dfla->data);
12387 /* Must contain at least dfLa box header (12),
12388 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12390 GST_DEBUG_OBJECT (qtdemux,
12391 "discarding dfla atom with unexpected len %d", len);
12393 /* skip dfLa header to get the METADATA_BLOCKs */
12394 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12395 const guint32 metadata_blocks_len = len - 12;
12397 gchar *stream_marker = g_strdup ("fLaC");
12398 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12399 strlen (stream_marker));
12402 guint32 remainder = 0;
12403 guint32 block_size = 0;
12404 gboolean is_last = FALSE;
12406 GValue array = G_VALUE_INIT;
12407 GValue value = G_VALUE_INIT;
12409 g_value_init (&array, GST_TYPE_ARRAY);
12410 g_value_init (&value, GST_TYPE_BUFFER);
12412 gst_value_set_buffer (&value, block);
12413 gst_value_array_append_value (&array, &value);
12414 g_value_reset (&value);
12416 gst_buffer_unref (block);
12418 /* check there's at least one METADATA_BLOCK_HEADER's worth
12419 * of data, and we haven't already finished parsing */
12420 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12421 remainder = metadata_blocks_len - index;
12423 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12425 (metadata_blocks[index + 1] << 16) +
12426 (metadata_blocks[index + 2] << 8) +
12427 metadata_blocks[index + 3];
12429 /* be careful not to read off end of box */
12430 if (block_size > remainder) {
12434 is_last = metadata_blocks[index] >> 7;
12436 block = gst_buffer_new_and_alloc (block_size);
12438 gst_buffer_fill (block, 0, &metadata_blocks[index],
12441 gst_value_set_buffer (&value, block);
12442 gst_value_array_append_value (&array, &value);
12443 g_value_reset (&value);
12445 gst_buffer_unref (block);
12447 index += block_size;
12450 /* only append the metadata if we successfully read all of it */
12452 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12453 (stream)->caps, 0), "streamheader", &array);
12455 GST_WARNING_OBJECT (qtdemux,
12456 "discarding all METADATA_BLOCKs due to invalid "
12457 "block_size %d at idx %d, rem %d", block_size, index,
12461 g_value_unset (&value);
12462 g_value_unset (&array);
12464 /* The sample rate obtained from the stsd may not be accurate
12465 * since it cannot represent rates greater than 65535Hz, so
12466 * override that value with the sample rate from the
12467 * METADATA_BLOCK_STREAMINFO block */
12468 CUR_STREAM (stream)->rate =
12469 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12480 gint len = QT_UINT32 (stsd_entry_data);
12483 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
12486 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
12488 /* If we have enough data, let's try to get the 'damr' atom. See
12489 * the 3GPP container spec (26.244) for more details. */
12490 if ((len - 0x34) > 8 &&
12491 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
12492 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12493 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12496 gst_caps_set_simple (entry->caps,
12497 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12498 gst_buffer_unref (buf);
12504 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12505 gint len = QT_UINT32 (stsd_entry_data);
12508 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12510 if (sound_version == 1) {
12511 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12512 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12513 guint8 codec_data[2];
12515 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12517 gint sample_rate_index =
12518 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12520 /* build AAC codec data */
12521 codec_data[0] = profile << 3;
12522 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12523 codec_data[1] = (sample_rate_index & 0x01) << 7;
12524 codec_data[1] |= (channels & 0xF) << 3;
12526 buf = gst_buffer_new_and_alloc (2);
12527 gst_buffer_fill (buf, 0, codec_data, 2);
12528 gst_caps_set_simple (entry->caps,
12529 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12530 gst_buffer_unref (buf);
12541 /* Fully handled elsewhere */
12544 GST_INFO_OBJECT (qtdemux,
12545 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12549 GST_INFO_OBJECT (qtdemux,
12550 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12551 GST_FOURCC_ARGS (fourcc), entry->caps);
12553 } else if (stream->subtype == FOURCC_strm) {
12554 if (fourcc == FOURCC_rtsp) {
12555 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12557 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12558 GST_FOURCC_ARGS (fourcc));
12559 goto unknown_stream;
12561 entry->sampled = TRUE;
12562 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12563 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
12564 || stream->subtype == FOURCC_clcp) {
12566 entry->sampled = TRUE;
12567 entry->sparse = TRUE;
12570 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12573 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12574 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12579 /* hunt for sort-of codec data */
12583 GNode *mp4s = NULL;
12584 GNode *esds = NULL;
12586 /* look for palette in a stsd->mp4s->esds sub-atom */
12587 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12589 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12590 if (esds == NULL) {
12592 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12596 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12597 stream->stream_tags);
12601 GST_INFO_OBJECT (qtdemux,
12602 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12605 GST_INFO_OBJECT (qtdemux,
12606 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12607 GST_FOURCC_ARGS (fourcc), entry->caps);
12609 /* everything in 1 sample */
12610 entry->sampled = TRUE;
12613 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12616 if (entry->caps == NULL)
12617 goto unknown_stream;
12620 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12621 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12627 /* promote to sampled format */
12628 if (entry->fourcc == FOURCC_samr) {
12629 /* force mono 8000 Hz for AMR */
12630 entry->sampled = TRUE;
12631 entry->n_channels = 1;
12632 entry->rate = 8000;
12633 } else if (entry->fourcc == FOURCC_sawb) {
12634 /* force mono 16000 Hz for AMR-WB */
12635 entry->sampled = TRUE;
12636 entry->n_channels = 1;
12637 entry->rate = 16000;
12638 } else if (entry->fourcc == FOURCC_mp4a) {
12639 entry->sampled = TRUE;
12643 stsd_entry_data += len;
12644 remaining_stsd_len -= len;
12648 /* collect sample information */
12649 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12650 goto samples_failed;
12652 if (qtdemux->fragmented) {
12655 /* need all moov samples as basis; probably not many if any at all */
12656 /* prevent moof parsing taking of at this time */
12657 offset = qtdemux->moof_offset;
12658 qtdemux->moof_offset = 0;
12659 if (stream->n_samples &&
12660 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12661 qtdemux->moof_offset = offset;
12662 goto samples_failed;
12664 qtdemux->moof_offset = 0;
12665 /* movie duration more reliable in this case (e.g. mehd) */
12666 if (qtdemux->segment.duration &&
12667 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12669 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12672 /* configure segments */
12673 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12674 goto segments_failed;
12676 /* add some language tag, if useful */
12677 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12678 strcmp (stream->lang_id, "und")) {
12679 const gchar *lang_code;
12681 /* convert ISO 639-2 code to ISO 639-1 */
12682 lang_code = gst_tag_get_language_code (stream->lang_id);
12683 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12684 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12687 /* Check for UDTA tags */
12688 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12689 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12692 /* Insert and sort new stream in track-id order.
12693 * This will help in comparing old/new streams during stream update check */
12694 g_ptr_array_add (qtdemux->active_streams, stream);
12695 g_ptr_array_sort (qtdemux->active_streams,
12696 (GCompareFunc) qtdemux_track_id_compare_func);
12697 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
12698 QTDEMUX_N_STREAMS (qtdemux));
12705 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12706 (_("This file is corrupt and cannot be played.")), (NULL));
12708 gst_qtdemux_stream_unref (stream);
12713 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12714 gst_qtdemux_stream_unref (stream);
12720 /* we posted an error already */
12721 /* free stbl sub-atoms */
12722 gst_qtdemux_stbl_free (stream);
12723 gst_qtdemux_stream_unref (stream);
12728 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12734 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12735 GST_FOURCC_ARGS (stream->subtype));
12736 gst_qtdemux_stream_unref (stream);
12741 /* If we can estimate the overall bitrate, and don't have information about the
12742 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12743 * the overall bitrate minus the sum of the bitrates of all other streams. This
12744 * should be useful for the common case where we have one audio and one video
12745 * stream and can estimate the bitrate of one, but not the other. */
12747 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12749 QtDemuxStream *stream = NULL;
12750 gint64 size, sys_bitrate, sum_bitrate = 0;
12751 GstClockTime duration;
12755 if (qtdemux->fragmented)
12758 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12760 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12762 GST_DEBUG_OBJECT (qtdemux,
12763 "Size in bytes of the stream not known - bailing");
12767 /* Subtract the header size */
12768 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12769 size, qtdemux->header_size);
12771 if (size < qtdemux->header_size)
12774 size = size - qtdemux->header_size;
12776 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12777 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12781 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12782 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
12783 switch (str->subtype) {
12786 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12787 CUR_STREAM (str)->caps);
12788 /* retrieve bitrate, prefer avg then max */
12790 if (str->stream_tags) {
12791 if (gst_tag_list_get_uint (str->stream_tags,
12792 GST_TAG_MAXIMUM_BITRATE, &bitrate))
12793 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12794 if (gst_tag_list_get_uint (str->stream_tags,
12795 GST_TAG_NOMINAL_BITRATE, &bitrate))
12796 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12797 if (gst_tag_list_get_uint (str->stream_tags,
12798 GST_TAG_BITRATE, &bitrate))
12799 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12802 sum_bitrate += bitrate;
12805 GST_DEBUG_OBJECT (qtdemux,
12806 ">1 stream with unknown bitrate - bailing");
12813 /* For other subtypes, we assume no significant impact on bitrate */
12819 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12823 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12825 if (sys_bitrate < sum_bitrate) {
12826 /* This can happen, since sum_bitrate might be derived from maximum
12827 * bitrates and not average bitrates */
12828 GST_DEBUG_OBJECT (qtdemux,
12829 "System bitrate less than sum bitrate - bailing");
12833 bitrate = sys_bitrate - sum_bitrate;
12834 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12835 ", Stream bitrate = %u", sys_bitrate, bitrate);
12837 if (!stream->stream_tags)
12838 stream->stream_tags = gst_tag_list_new_empty ();
12840 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12842 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12843 GST_TAG_BITRATE, bitrate, NULL);
12846 static GstFlowReturn
12847 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12849 GstFlowReturn ret = GST_FLOW_OK;
12852 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12854 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12855 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12856 guint32 sample_num = 0;
12858 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12859 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12861 if (qtdemux->fragmented) {
12862 /* need all moov samples first */
12863 GST_OBJECT_LOCK (qtdemux);
12864 while (stream->n_samples == 0)
12865 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12867 GST_OBJECT_UNLOCK (qtdemux);
12869 /* discard any stray moof */
12870 qtdemux->moof_offset = 0;
12873 /* prepare braking */
12874 if (ret != GST_FLOW_ERROR)
12877 /* in pull mode, we should have parsed some sample info by now;
12878 * and quite some code will not handle no samples.
12879 * in push mode, we'll just have to deal with it */
12880 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12881 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12882 g_ptr_array_remove_index (qtdemux->active_streams, i);
12885 } else if (stream->track_id == qtdemux->chapters_track_id &&
12886 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
12887 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12888 so that it doesn't look like a subtitle track */
12889 g_ptr_array_remove_index (qtdemux->active_streams, i);
12894 /* parse the initial sample for use in setting the frame rate cap */
12895 while (sample_num == 0 && sample_num < stream->n_samples) {
12896 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12906 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
12908 return g_strcmp0 (stream->stream_id, stream_id) == 0;
12912 qtdemux_is_streams_update (GstQTDemux * qtdemux)
12916 /* Different length, updated */
12917 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
12920 /* streams in list are sorted in track-id order */
12921 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12922 /* Different stream-id, updated */
12923 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
12924 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
12932 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
12933 QtDemuxStream * oldstream, QtDemuxStream * newstream)
12935 /* Connect old stream's srcpad to new stream */
12936 newstream->pad = oldstream->pad;
12937 oldstream->pad = NULL;
12939 /* unset new_stream to prevent stream-start event */
12940 newstream->new_stream = FALSE;
12942 return gst_qtdemux_configure_stream (qtdemux, newstream);
12945 /* g_ptr_array_find_with_equal_func is available since 2.54,
12946 * replacement until we can depend unconditionally on the real one in GLib */
12947 #if !GLIB_CHECK_VERSION(2,54,0)
12948 #define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
12950 qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
12951 gconstpointer needle, GEqualFunc equal_func, guint * index_)
12955 g_return_val_if_fail (haystack != NULL, FALSE);
12957 if (equal_func == NULL)
12958 equal_func = g_direct_equal;
12960 for (i = 0; i < haystack->len; i++) {
12961 if (equal_func (g_ptr_array_index (haystack, i), needle)) {
12962 if (index_ != NULL)
12973 qtdemux_update_streams (GstQTDemux * qtdemux)
12976 g_assert (qtdemux->streams_aware);
12978 /* At below, figure out which stream in active_streams has identical stream-id
12979 * with that of in old_streams. If there is matching stream-id,
12980 * corresponding newstream will not be exposed again,
12981 * but demux will reuse srcpad of matched old stream
12983 * active_streams : newly created streams from the latest moov
12984 * old_streams : existing streams (belong to previous moov)
12987 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
12988 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
12989 QtDemuxStream *oldstream = NULL;
12992 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
12993 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12995 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
12996 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
12997 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
12999 /* null pad stream cannot be reused */
13000 if (oldstream->pad == NULL)
13005 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13007 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13010 /* we don't need to preserve order of old streams */
13011 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13015 /* now we have all info and can expose */
13016 list = stream->stream_tags;
13017 stream->stream_tags = NULL;
13018 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13026 /* Must be called with expose lock */
13027 static GstFlowReturn
13028 qtdemux_expose_streams (GstQTDemux * qtdemux)
13032 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13034 if (!qtdemux_is_streams_update (qtdemux)) {
13035 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13036 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13037 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13038 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13039 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13040 return GST_FLOW_ERROR;
13043 g_ptr_array_set_size (qtdemux->old_streams, 0);
13044 qtdemux->need_segment = TRUE;
13046 return GST_FLOW_OK;
13049 if (qtdemux->streams_aware) {
13050 if (!qtdemux_update_streams (qtdemux))
13051 return GST_FLOW_ERROR;
13053 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13054 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13057 /* now we have all info and can expose */
13058 list = stream->stream_tags;
13059 stream->stream_tags = NULL;
13060 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13061 return GST_FLOW_ERROR;
13066 gst_qtdemux_guess_bitrate (qtdemux);
13068 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
13070 /* If we have still old_streams, it's no more used stream */
13071 for (i = 0; i < qtdemux->old_streams->len; i++) {
13072 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13077 event = gst_event_new_eos ();
13078 if (qtdemux->segment_seqnum)
13079 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
13081 gst_pad_push_event (stream->pad, event);
13085 g_ptr_array_set_size (qtdemux->old_streams, 0);
13087 /* check if we should post a redirect in case there is a single trak
13088 * and it is a redirecting trak */
13089 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
13090 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
13093 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
13094 "an external content");
13095 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
13096 gst_structure_new ("redirect",
13097 "new-location", G_TYPE_STRING,
13098 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
13099 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
13100 g_free (qtdemux->redirect_location);
13101 qtdemux->redirect_location =
13102 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
13105 g_ptr_array_foreach (qtdemux->active_streams,
13106 (GFunc) qtdemux_do_allocation, qtdemux);
13108 qtdemux->need_segment = TRUE;
13110 qtdemux->exposed = TRUE;
13111 return GST_FLOW_OK;
13116 GstStructure *structure; /* helper for sort function */
13118 guint min_req_bitrate;
13119 guint min_req_qt_version;
13123 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13125 GstQtReference *ref_a = (GstQtReference *) a;
13126 GstQtReference *ref_b = (GstQtReference *) b;
13128 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13129 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13131 /* known bitrates go before unknown; higher bitrates go first */
13132 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13135 /* sort the redirects and post a message for the application.
13138 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13140 GstQtReference *best;
13143 GValue list_val = { 0, };
13146 g_assert (references != NULL);
13148 references = g_list_sort (references, qtdemux_redirects_sort_func);
13150 best = (GstQtReference *) references->data;
13152 g_value_init (&list_val, GST_TYPE_LIST);
13154 for (l = references; l != NULL; l = l->next) {
13155 GstQtReference *ref = (GstQtReference *) l->data;
13156 GValue struct_val = { 0, };
13158 ref->structure = gst_structure_new ("redirect",
13159 "new-location", G_TYPE_STRING, ref->location, NULL);
13161 if (ref->min_req_bitrate > 0) {
13162 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13163 ref->min_req_bitrate, NULL);
13166 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13167 g_value_set_boxed (&struct_val, ref->structure);
13168 gst_value_list_append_value (&list_val, &struct_val);
13169 g_value_unset (&struct_val);
13170 /* don't free anything here yet, since we need best->structure below */
13173 g_assert (best != NULL);
13174 s = gst_structure_copy (best->structure);
13176 if (g_list_length (references) > 1) {
13177 gst_structure_set_value (s, "locations", &list_val);
13180 g_value_unset (&list_val);
13182 for (l = references; l != NULL; l = l->next) {
13183 GstQtReference *ref = (GstQtReference *) l->data;
13185 gst_structure_free (ref->structure);
13186 g_free (ref->location);
13189 g_list_free (references);
13191 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13192 g_free (qtdemux->redirect_location);
13193 qtdemux->redirect_location =
13194 g_strdup (gst_structure_get_string (s, "new-location"));
13195 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13196 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13199 /* look for redirect nodes, collect all redirect information and
13203 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13205 GNode *rmra, *rmda, *rdrf;
13207 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13209 GList *redirects = NULL;
13211 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13213 GstQtReference ref = { NULL, NULL, 0, 0 };
13214 GNode *rmdr, *rmvc;
13216 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13217 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13218 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13219 ref.min_req_bitrate);
13222 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13223 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13224 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
13226 #ifndef GST_DISABLE_GST_DEBUG
13227 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
13229 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
13231 GST_LOG_OBJECT (qtdemux,
13232 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
13233 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
13234 bitmask, check_type);
13235 if (package == FOURCC_qtim && check_type == 0) {
13236 ref.min_req_qt_version = version;
13240 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
13246 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
13247 if (ref_len > 20) {
13248 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
13249 ref_data = (guint8 *) rdrf->data + 20;
13250 if (ref_type == FOURCC_alis) {
13251 guint record_len, record_version, fn_len;
13253 if (ref_len > 70) {
13254 /* MacOSX alias record, google for alias-layout.txt */
13255 record_len = QT_UINT16 (ref_data + 4);
13256 record_version = QT_UINT16 (ref_data + 4 + 2);
13257 fn_len = QT_UINT8 (ref_data + 50);
13258 if (record_len > 50 && record_version == 2 && fn_len > 0) {
13259 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
13262 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
13265 } else if (ref_type == FOURCC_url_) {
13266 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
13268 GST_DEBUG_OBJECT (qtdemux,
13269 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
13270 GST_FOURCC_ARGS (ref_type));
13272 if (ref.location != NULL) {
13273 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
13275 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
13277 GST_WARNING_OBJECT (qtdemux,
13278 "Failed to extract redirect location from rdrf atom");
13281 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
13285 /* look for others */
13286 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
13289 if (redirects != NULL) {
13290 qtdemux_process_redirects (qtdemux, redirects);
13296 static GstTagList *
13297 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
13301 if (tags == NULL) {
13302 tags = gst_tag_list_new_empty ();
13303 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
13306 if (qtdemux->major_brand == FOURCC_mjp2)
13307 fmt = "Motion JPEG 2000";
13308 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
13310 else if (qtdemux->major_brand == FOURCC_qt__)
13312 else if (qtdemux->fragmented)
13315 fmt = "ISO MP4/M4A";
13317 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
13318 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
13320 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
13326 /* we have read the complete moov node now.
13327 * This function parses all of the relevant info, creates the traks and
13328 * prepares all data structures for playback
13331 qtdemux_parse_tree (GstQTDemux * qtdemux)
13338 guint64 creation_time;
13339 GstDateTime *datetime = NULL;
13342 /* make sure we have a usable taglist */
13343 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13345 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
13346 if (mvhd == NULL) {
13347 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
13348 return qtdemux_parse_redirects (qtdemux);
13351 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
13352 if (version == 1) {
13353 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
13354 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
13355 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
13356 } else if (version == 0) {
13357 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
13358 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
13359 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
13361 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
13365 /* Moving qt creation time (secs since 1904) to unix time */
13366 if (creation_time != 0) {
13367 /* Try to use epoch first as it should be faster and more commonly found */
13368 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
13371 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
13372 /* some data cleansing sanity */
13373 now_s = g_get_real_time () / G_USEC_PER_SEC;
13374 if (now_s + 24 * 3600 < creation_time) {
13375 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
13377 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
13380 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
13381 GDateTime *dt, *dt_local;
13383 dt = g_date_time_add_seconds (base_dt, creation_time);
13384 dt_local = g_date_time_to_local (dt);
13385 datetime = gst_date_time_new_from_g_date_time (dt_local);
13387 g_date_time_unref (base_dt);
13388 g_date_time_unref (dt);
13392 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
13393 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
13395 gst_date_time_unref (datetime);
13398 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
13399 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
13401 /* check for fragmented file and get some (default) data */
13402 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
13405 GstByteReader mehd_data;
13407 /* let track parsing or anyone know weird stuff might happen ... */
13408 qtdemux->fragmented = TRUE;
13410 /* compensate for total duration */
13411 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
13413 qtdemux_parse_mehd (qtdemux, &mehd_data);
13416 /* Update the movie segment duration, unless it was directly given to us
13417 * by upstream. Otherwise let it as is, as we don't want to mangle the
13418 * duration provided by upstream that may come e.g. from a MPD file. */
13419 if (!qtdemux->upstream_format_is_time) {
13420 GstClockTime duration;
13421 /* set duration in the segment info */
13422 gst_qtdemux_get_duration (qtdemux, &duration);
13423 qtdemux->segment.duration = duration;
13424 /* also do not exceed duration; stop is set that way post seek anyway,
13425 * and segment activation falls back to duration,
13426 * whereas loop only checks stop, so let's align this here as well */
13427 qtdemux->segment.stop = duration;
13430 /* parse all traks */
13431 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
13433 qtdemux_parse_trak (qtdemux, trak);
13434 /* iterate all siblings */
13435 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
13438 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13441 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
13443 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13445 GST_LOG_OBJECT (qtdemux, "No udta node found.");
13448 /* maybe also some tags in meta box */
13449 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
13451 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
13452 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13454 GST_LOG_OBJECT (qtdemux, "No meta node found.");
13457 /* parse any protection system info */
13458 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
13460 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
13461 qtdemux_parse_pssh (qtdemux, pssh);
13462 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
13465 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
13470 /* taken from ffmpeg */
13472 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
13484 len = (len << 7) | (c & 0x7f);
13493 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
13494 gsize codec_data_size)
13496 GList *list = NULL;
13497 guint8 *p = codec_data;
13498 gint i, offset, num_packets;
13499 guint *length, last;
13501 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
13503 if (codec_data == NULL || codec_data_size == 0)
13506 /* start of the stream and vorbis audio or theora video, need to
13507 * send the codec_priv data as first three packets */
13508 num_packets = p[0] + 1;
13509 GST_DEBUG_OBJECT (qtdemux,
13510 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
13511 (guint) num_packets, codec_data_size);
13513 /* Let's put some limits, Don't think there even is a xiph codec
13514 * with more than 3-4 headers */
13515 if (G_UNLIKELY (num_packets > 16)) {
13516 GST_WARNING_OBJECT (qtdemux,
13517 "Unlikely number of xiph headers, most likely not valid");
13521 length = g_alloca (num_packets * sizeof (guint));
13525 /* first packets, read length values */
13526 for (i = 0; i < num_packets - 1; i++) {
13528 while (offset < codec_data_size) {
13529 length[i] += p[offset];
13530 if (p[offset++] != 0xff)
13535 if (offset + last > codec_data_size)
13538 /* last packet is the remaining size */
13539 length[i] = codec_data_size - offset - last;
13541 for (i = 0; i < num_packets; i++) {
13544 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
13546 if (offset + length[i] > codec_data_size)
13549 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
13550 list = g_list_append (list, hdr);
13552 offset += length[i];
13561 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
13567 /* this can change the codec originally present in @list */
13569 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
13570 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
13572 int len = QT_UINT32 (esds->data);
13573 guint8 *ptr = esds->data;
13574 guint8 *end = ptr + len;
13576 guint8 *data_ptr = NULL;
13578 guint8 object_type_id = 0;
13579 guint8 stream_type = 0;
13580 const char *codec_name = NULL;
13581 GstCaps *caps = NULL;
13583 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
13585 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
13587 while (ptr + 1 < end) {
13588 tag = QT_UINT8 (ptr);
13589 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
13591 len = read_descr_size (ptr, end, &ptr);
13592 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
13594 /* Check the stated amount of data is available for reading */
13595 if (len < 0 || ptr + len > end)
13599 case ES_DESCRIPTOR_TAG:
13600 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
13601 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
13604 case DECODER_CONFIG_DESC_TAG:{
13605 guint max_bitrate, avg_bitrate;
13607 object_type_id = QT_UINT8 (ptr);
13608 stream_type = QT_UINT8 (ptr + 1) >> 2;
13609 max_bitrate = QT_UINT32 (ptr + 5);
13610 avg_bitrate = QT_UINT32 (ptr + 9);
13611 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
13612 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
13613 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
13614 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
13615 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
13616 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
13617 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13618 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
13620 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
13621 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
13622 avg_bitrate, NULL);
13627 case DECODER_SPECIFIC_INFO_TAG:
13628 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
13629 if (object_type_id == 0xe0 && len == 0x40) {
13635 GST_DEBUG_OBJECT (qtdemux,
13636 "Have VOBSUB palette. Creating palette event");
13637 /* move to decConfigDescr data and read palette */
13639 for (i = 0; i < 16; i++) {
13640 clut[i] = QT_UINT32 (data);
13644 s = gst_structure_new ("application/x-gst-dvd", "event",
13645 G_TYPE_STRING, "dvd-spu-clut-change",
13646 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
13647 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
13648 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
13649 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
13650 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
13651 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
13652 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
13653 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
13656 /* store event and trigger custom processing */
13657 stream->pending_event =
13658 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
13660 /* Generic codec_data handler puts it on the caps */
13667 case SL_CONFIG_DESC_TAG:
13668 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
13672 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
13674 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
13680 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
13681 * in use, and should also be used to override some other parameters for some
13683 switch (object_type_id) {
13684 case 0x20: /* MPEG-4 */
13685 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
13686 * profile_and_level_indication */
13687 if (data_ptr != NULL && data_len >= 5 &&
13688 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
13689 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
13690 data_ptr + 4, data_len - 4);
13692 break; /* Nothing special needed here */
13693 case 0x21: /* H.264 */
13694 codec_name = "H.264 / AVC";
13695 caps = gst_caps_new_simple ("video/x-h264",
13696 "stream-format", G_TYPE_STRING, "avc",
13697 "alignment", G_TYPE_STRING, "au", NULL);
13699 case 0x40: /* AAC (any) */
13700 case 0x66: /* AAC Main */
13701 case 0x67: /* AAC LC */
13702 case 0x68: /* AAC SSR */
13703 /* Override channels and rate based on the codec_data, as it's often
13705 /* Only do so for basic setup without HE-AAC extension */
13706 if (data_ptr && data_len == 2) {
13707 guint channels, rate;
13709 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
13711 entry->n_channels = channels;
13713 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
13715 entry->rate = rate;
13718 /* Set level and profile if possible */
13719 if (data_ptr != NULL && data_len >= 2) {
13720 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
13721 data_ptr, data_len);
13723 const gchar *profile_str = NULL;
13726 guint8 *codec_data;
13727 gint rate_idx, profile;
13729 /* No codec_data, let's invent something.
13730 * FIXME: This is wrong for SBR! */
13732 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
13734 buffer = gst_buffer_new_and_alloc (2);
13735 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
13736 codec_data = map.data;
13739 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
13742 switch (object_type_id) {
13744 profile_str = "main";
13748 profile_str = "lc";
13752 profile_str = "ssr";
13760 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
13762 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
13764 gst_buffer_unmap (buffer, &map);
13765 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
13766 GST_TYPE_BUFFER, buffer, NULL);
13767 gst_buffer_unref (buffer);
13770 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
13771 G_TYPE_STRING, profile_str, NULL);
13775 case 0x60: /* MPEG-2, various profiles */
13781 codec_name = "MPEG-2 video";
13782 caps = gst_caps_new_simple ("video/mpeg",
13783 "mpegversion", G_TYPE_INT, 2,
13784 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13786 case 0x69: /* MPEG-2 BC audio */
13787 case 0x6B: /* MPEG-1 audio */
13788 caps = gst_caps_new_simple ("audio/mpeg",
13789 "mpegversion", G_TYPE_INT, 1, NULL);
13790 codec_name = "MPEG-1 audio";
13792 case 0x6A: /* MPEG-1 */
13793 codec_name = "MPEG-1 video";
13794 caps = gst_caps_new_simple ("video/mpeg",
13795 "mpegversion", G_TYPE_INT, 1,
13796 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13798 case 0x6C: /* MJPEG */
13800 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13802 codec_name = "Motion-JPEG";
13804 case 0x6D: /* PNG */
13806 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
13808 codec_name = "PNG still images";
13810 case 0x6E: /* JPEG2000 */
13811 codec_name = "JPEG-2000";
13812 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
13814 case 0xA4: /* Dirac */
13815 codec_name = "Dirac";
13816 caps = gst_caps_new_empty_simple ("video/x-dirac");
13818 case 0xA5: /* AC3 */
13819 codec_name = "AC-3 audio";
13820 caps = gst_caps_new_simple ("audio/x-ac3",
13821 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13823 case 0xA9: /* AC3 */
13824 codec_name = "DTS audio";
13825 caps = gst_caps_new_simple ("audio/x-dts",
13826 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13829 if (stream_type == 0x05 && data_ptr) {
13831 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
13834 GValue arr_val = G_VALUE_INIT;
13835 GValue buf_val = G_VALUE_INIT;
13838 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
13839 codec_name = "Vorbis";
13840 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
13841 g_value_init (&arr_val, GST_TYPE_ARRAY);
13842 g_value_init (&buf_val, GST_TYPE_BUFFER);
13843 for (tmp = headers; tmp; tmp = tmp->next) {
13844 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
13845 gst_value_array_append_value (&arr_val, &buf_val);
13847 s = gst_caps_get_structure (caps, 0);
13848 gst_structure_take_value (s, "streamheader", &arr_val);
13849 g_value_unset (&buf_val);
13850 g_list_free (headers);
13857 case 0xE1: /* QCELP */
13858 /* QCELP, the codec_data is a riff tag (little endian) with
13859 * 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). */
13860 caps = gst_caps_new_empty_simple ("audio/qcelp");
13861 codec_name = "QCELP";
13867 /* If we have a replacement caps, then change our caps for this stream */
13869 gst_caps_unref (entry->caps);
13870 entry->caps = caps;
13873 if (codec_name && list)
13874 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13875 GST_TAG_AUDIO_CODEC, codec_name, NULL);
13877 /* Add the codec_data attribute to caps, if we have it */
13881 buffer = gst_buffer_new_and_alloc (data_len);
13882 gst_buffer_fill (buffer, 0, data_ptr, data_len);
13884 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
13885 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
13887 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
13889 gst_buffer_unref (buffer);
13894 static inline GstCaps *
13895 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
13899 char *s, fourstr[5];
13901 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13902 for (i = 0; i < 4; i++) {
13903 if (!g_ascii_isalnum (fourstr[i]))
13906 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
13907 caps = gst_caps_new_empty_simple (s);
13912 #define _codec(name) \
13914 if (codec_name) { \
13915 *codec_name = g_strdup (name); \
13920 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
13921 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
13922 const guint8 * stsd_entry_data, gchar ** codec_name)
13924 GstCaps *caps = NULL;
13925 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
13929 _codec ("PNG still images");
13930 caps = gst_caps_new_empty_simple ("image/png");
13933 _codec ("JPEG still images");
13935 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13938 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
13939 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
13940 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
13941 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
13942 _codec ("Motion-JPEG");
13944 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
13947 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
13948 _codec ("Motion-JPEG format B");
13949 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
13952 _codec ("JPEG-2000");
13953 /* override to what it should be according to spec, avoid palette_data */
13954 entry->bits_per_sample = 24;
13955 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
13958 _codec ("Sorensen video v.3");
13959 caps = gst_caps_new_simple ("video/x-svq",
13960 "svqversion", G_TYPE_INT, 3, NULL);
13962 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
13963 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
13964 _codec ("Sorensen video v.1");
13965 caps = gst_caps_new_simple ("video/x-svq",
13966 "svqversion", G_TYPE_INT, 1, NULL);
13968 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
13969 caps = gst_caps_new_empty_simple ("video/x-raw");
13970 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
13971 _codec ("Windows Raw RGB");
13972 stream->alignment = 32;
13978 bps = QT_UINT16 (stsd_entry_data + 82);
13981 format = GST_VIDEO_FORMAT_RGB15;
13984 format = GST_VIDEO_FORMAT_RGB16;
13987 format = GST_VIDEO_FORMAT_RGB;
13990 format = GST_VIDEO_FORMAT_ARGB;
13998 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
13999 format = GST_VIDEO_FORMAT_I420;
14001 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14002 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14003 format = GST_VIDEO_FORMAT_I420;
14006 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14007 format = GST_VIDEO_FORMAT_UYVY;
14009 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14010 format = GST_VIDEO_FORMAT_v308;
14012 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14013 format = GST_VIDEO_FORMAT_v216;
14016 format = GST_VIDEO_FORMAT_v210;
14018 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14019 format = GST_VIDEO_FORMAT_r210;
14021 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14022 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14023 format = GST_VIDEO_FORMAT_v410;
14026 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14027 * but different order than AYUV
14028 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14029 format = GST_VIDEO_FORMAT_v408;
14032 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14033 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14034 _codec ("MPEG-1 video");
14035 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14036 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14038 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14039 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14040 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14041 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14042 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14043 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14044 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14045 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14046 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14047 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14048 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14049 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14050 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14051 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14052 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14053 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14054 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14055 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14056 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14057 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14058 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14059 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14060 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14061 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14062 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14063 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14064 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14065 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14066 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14067 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14068 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14069 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14070 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14071 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14072 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14073 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14074 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14075 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14076 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14077 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14078 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14079 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14080 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14081 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14082 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14083 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14084 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14085 _codec ("MPEG-2 video");
14086 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14087 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14089 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14090 _codec ("GIF still images");
14091 caps = gst_caps_new_empty_simple ("image/gif");
14094 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14096 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14098 /* ffmpeg uses the height/width props, don't know why */
14099 caps = gst_caps_new_simple ("video/x-h263",
14100 "variant", G_TYPE_STRING, "itu", NULL);
14104 _codec ("MPEG-4 video");
14105 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14106 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14108 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14109 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14110 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14111 caps = gst_caps_new_simple ("video/x-msmpeg",
14112 "msmpegversion", G_TYPE_INT, 43, NULL);
14114 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14116 caps = gst_caps_new_simple ("video/x-divx",
14117 "divxversion", G_TYPE_INT, 3, NULL);
14119 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14120 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14122 caps = gst_caps_new_simple ("video/x-divx",
14123 "divxversion", G_TYPE_INT, 4, NULL);
14125 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14127 caps = gst_caps_new_simple ("video/x-divx",
14128 "divxversion", G_TYPE_INT, 5, NULL);
14131 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14133 caps = gst_caps_new_simple ("video/x-ffv",
14134 "ffvversion", G_TYPE_INT, 1, NULL);
14137 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14138 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14143 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14144 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14145 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14149 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14150 _codec ("Cinepak");
14151 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14153 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14154 _codec ("Apple QuickDraw");
14155 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14157 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14158 _codec ("Apple video");
14159 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14163 _codec ("H.264 / AVC");
14164 caps = gst_caps_new_simple ("video/x-h264",
14165 "stream-format", G_TYPE_STRING, "avc",
14166 "alignment", G_TYPE_STRING, "au", NULL);
14169 _codec ("H.264 / AVC");
14170 caps = gst_caps_new_simple ("video/x-h264",
14171 "stream-format", G_TYPE_STRING, "avc3",
14172 "alignment", G_TYPE_STRING, "au", NULL);
14176 _codec ("H.265 / HEVC");
14177 caps = gst_caps_new_simple ("video/x-h265",
14178 "stream-format", G_TYPE_STRING, "hvc1",
14179 "alignment", G_TYPE_STRING, "au", NULL);
14182 _codec ("H.265 / HEVC");
14183 caps = gst_caps_new_simple ("video/x-h265",
14184 "stream-format", G_TYPE_STRING, "hev1",
14185 "alignment", G_TYPE_STRING, "au", NULL);
14188 _codec ("Run-length encoding");
14189 caps = gst_caps_new_simple ("video/x-rle",
14190 "layout", G_TYPE_STRING, "quicktime", NULL);
14193 _codec ("Run-length encoding");
14194 caps = gst_caps_new_simple ("video/x-rle",
14195 "layout", G_TYPE_STRING, "microsoft", NULL);
14197 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14198 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14199 _codec ("Indeo Video 3");
14200 caps = gst_caps_new_simple ("video/x-indeo",
14201 "indeoversion", G_TYPE_INT, 3, NULL);
14203 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14204 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14205 _codec ("Intel Video 4");
14206 caps = gst_caps_new_simple ("video/x-indeo",
14207 "indeoversion", G_TYPE_INT, 4, NULL);
14211 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14212 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14213 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14214 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14215 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14216 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14217 _codec ("DV Video");
14218 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14219 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14221 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14222 case FOURCC_dv5p: /* DVCPRO50 PAL */
14223 _codec ("DVCPro50 Video");
14224 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14225 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14227 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
14228 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
14229 _codec ("DVCProHD Video");
14230 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
14231 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14233 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
14234 _codec ("Apple Graphics (SMC)");
14235 caps = gst_caps_new_empty_simple ("video/x-smc");
14237 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
14239 caps = gst_caps_new_empty_simple ("video/x-vp3");
14241 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
14242 _codec ("VP6 Flash");
14243 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
14247 caps = gst_caps_new_empty_simple ("video/x-theora");
14248 /* theora uses one byte of padding in the data stream because it does not
14249 * allow 0 sized packets while theora does */
14250 entry->padding = 1;
14254 caps = gst_caps_new_empty_simple ("video/x-dirac");
14256 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
14257 _codec ("TIFF still images");
14258 caps = gst_caps_new_empty_simple ("image/tiff");
14260 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
14261 _codec ("Apple Intermediate Codec");
14262 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
14264 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
14265 _codec ("AVID DNxHD");
14266 caps = gst_caps_from_string ("video/x-dnxhd");
14270 _codec ("On2 VP8");
14271 caps = gst_caps_from_string ("video/x-vp8");
14274 _codec ("Google VP9");
14275 caps = gst_caps_from_string ("video/x-vp9");
14278 _codec ("Apple ProRes LT");
14280 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
14284 _codec ("Apple ProRes HQ");
14286 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
14290 _codec ("Apple ProRes");
14292 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14296 _codec ("Apple ProRes Proxy");
14298 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14302 _codec ("Apple ProRes 4444");
14304 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14308 _codec ("Apple ProRes 4444 XQ");
14310 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14314 _codec ("GoPro CineForm");
14315 caps = gst_caps_from_string ("video/x-cineform");
14320 caps = gst_caps_new_simple ("video/x-wmv",
14321 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
14325 caps = gst_caps_new_empty_simple ("video/x-av1");
14327 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
14330 caps = _get_unknown_codec_name ("video", fourcc);
14335 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
14338 gst_video_info_init (&info);
14339 gst_video_info_set_format (&info, format, entry->width, entry->height);
14341 caps = gst_video_info_to_caps (&info);
14342 *codec_name = gst_pb_utils_get_codec_description (caps);
14344 /* enable clipping for raw video streams */
14345 stream->need_clip = TRUE;
14346 stream->alignment = 32;
14353 round_up_pow2 (guint n)
14365 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14366 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
14367 int len, gchar ** codec_name)
14370 const GstStructure *s;
14373 GstAudioFormat format = 0;
14376 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14378 depth = entry->bytes_per_packet * 8;
14381 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
14383 /* 8-bit audio is unsigned */
14385 format = GST_AUDIO_FORMAT_U8;
14386 /* otherwise it's signed and big-endian just like 'twos' */
14388 endian = G_BIG_ENDIAN;
14395 endian = G_LITTLE_ENDIAN;
14398 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
14400 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
14404 caps = gst_caps_new_simple ("audio/x-raw",
14405 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14406 "layout", G_TYPE_STRING, "interleaved", NULL);
14407 stream->alignment = GST_ROUND_UP_8 (depth);
14408 stream->alignment = round_up_pow2 (stream->alignment);
14412 _codec ("Raw 64-bit floating-point audio");
14413 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14415 caps = gst_caps_new_simple ("audio/x-raw",
14416 "format", G_TYPE_STRING, "F64BE",
14417 "layout", G_TYPE_STRING, "interleaved", NULL);
14418 stream->alignment = 8;
14421 _codec ("Raw 32-bit floating-point audio");
14422 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14424 caps = gst_caps_new_simple ("audio/x-raw",
14425 "format", G_TYPE_STRING, "F32BE",
14426 "layout", G_TYPE_STRING, "interleaved", NULL);
14427 stream->alignment = 4;
14430 _codec ("Raw 24-bit PCM audio");
14431 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14433 caps = gst_caps_new_simple ("audio/x-raw",
14434 "format", G_TYPE_STRING, "S24BE",
14435 "layout", G_TYPE_STRING, "interleaved", NULL);
14436 stream->alignment = 4;
14439 _codec ("Raw 32-bit PCM audio");
14440 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14442 caps = gst_caps_new_simple ("audio/x-raw",
14443 "format", G_TYPE_STRING, "S32BE",
14444 "layout", G_TYPE_STRING, "interleaved", NULL);
14445 stream->alignment = 4;
14448 _codec ("Raw 16-bit PCM audio");
14449 caps = gst_caps_new_simple ("audio/x-raw",
14450 "format", G_TYPE_STRING, "S16LE",
14451 "layout", G_TYPE_STRING, "interleaved", NULL);
14452 stream->alignment = 2;
14455 _codec ("Mu-law audio");
14456 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
14459 _codec ("A-law audio");
14460 caps = gst_caps_new_empty_simple ("audio/x-alaw");
14464 _codec ("Microsoft ADPCM");
14465 /* Microsoft ADPCM-ACM code 2 */
14466 caps = gst_caps_new_simple ("audio/x-adpcm",
14467 "layout", G_TYPE_STRING, "microsoft", NULL);
14471 _codec ("DVI/IMA ADPCM");
14472 caps = gst_caps_new_simple ("audio/x-adpcm",
14473 "layout", G_TYPE_STRING, "dvi", NULL);
14477 _codec ("DVI/Intel IMA ADPCM");
14478 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
14479 caps = gst_caps_new_simple ("audio/x-adpcm",
14480 "layout", G_TYPE_STRING, "quicktime", NULL);
14484 /* MPEG layer 3, CBR only (pre QT4.1) */
14487 _codec ("MPEG-1 layer 3");
14488 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
14489 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
14490 "mpegversion", G_TYPE_INT, 1, NULL);
14492 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
14493 _codec ("MPEG-1 layer 2");
14495 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
14496 "mpegversion", G_TYPE_INT, 1, NULL);
14499 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
14500 _codec ("EAC-3 audio");
14501 caps = gst_caps_new_simple ("audio/x-eac3",
14502 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14503 entry->sampled = TRUE;
14505 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
14507 _codec ("AC-3 audio");
14508 caps = gst_caps_new_simple ("audio/x-ac3",
14509 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14510 entry->sampled = TRUE;
14512 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
14513 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
14514 _codec ("DTS audio");
14515 caps = gst_caps_new_simple ("audio/x-dts",
14516 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14517 entry->sampled = TRUE;
14519 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
14520 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
14521 _codec ("DTS-HD audio");
14522 caps = gst_caps_new_simple ("audio/x-dts",
14523 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14524 entry->sampled = TRUE;
14528 caps = gst_caps_new_simple ("audio/x-mace",
14529 "maceversion", G_TYPE_INT, 3, NULL);
14533 caps = gst_caps_new_simple ("audio/x-mace",
14534 "maceversion", G_TYPE_INT, 6, NULL);
14536 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
14538 caps = gst_caps_new_empty_simple ("application/ogg");
14540 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
14541 _codec ("DV audio");
14542 caps = gst_caps_new_empty_simple ("audio/x-dv");
14545 _codec ("MPEG-4 AAC audio");
14546 caps = gst_caps_new_simple ("audio/mpeg",
14547 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
14548 "stream-format", G_TYPE_STRING, "raw", NULL);
14550 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
14551 _codec ("QDesign Music");
14552 caps = gst_caps_new_empty_simple ("audio/x-qdm");
14555 _codec ("QDesign Music v.2");
14556 /* FIXME: QDesign music version 2 (no constant) */
14557 if (FALSE && data) {
14558 caps = gst_caps_new_simple ("audio/x-qdm2",
14559 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
14560 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
14561 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
14563 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
14567 _codec ("GSM audio");
14568 caps = gst_caps_new_empty_simple ("audio/x-gsm");
14571 _codec ("AMR audio");
14572 caps = gst_caps_new_empty_simple ("audio/AMR");
14575 _codec ("AMR-WB audio");
14576 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
14579 _codec ("Quicktime IMA ADPCM");
14580 caps = gst_caps_new_simple ("audio/x-adpcm",
14581 "layout", G_TYPE_STRING, "quicktime", NULL);
14584 _codec ("Apple lossless audio");
14585 caps = gst_caps_new_empty_simple ("audio/x-alac");
14588 _codec ("Free Lossless Audio Codec");
14589 caps = gst_caps_new_simple ("audio/x-flac",
14590 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14592 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
14593 _codec ("QualComm PureVoice");
14594 caps = gst_caps_from_string ("audio/qcelp");
14599 caps = gst_caps_new_empty_simple ("audio/x-wma");
14603 caps = gst_caps_new_empty_simple ("audio/x-opus");
14610 GstAudioFormat format;
14613 FLAG_IS_FLOAT = 0x1,
14614 FLAG_IS_BIG_ENDIAN = 0x2,
14615 FLAG_IS_SIGNED = 0x4,
14616 FLAG_IS_PACKED = 0x8,
14617 FLAG_IS_ALIGNED_HIGH = 0x10,
14618 FLAG_IS_NON_INTERLEAVED = 0x20
14620 _codec ("Raw LPCM audio");
14622 if (data && len >= 36) {
14623 depth = QT_UINT32 (data + 24);
14624 flags = QT_UINT32 (data + 28);
14625 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
14627 if ((flags & FLAG_IS_FLOAT) == 0) {
14632 if ((flags & FLAG_IS_ALIGNED_HIGH))
14635 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
14636 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
14637 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
14638 caps = gst_caps_new_simple ("audio/x-raw",
14639 "format", G_TYPE_STRING,
14641 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
14642 "UNKNOWN", "layout", G_TYPE_STRING,
14643 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
14644 "interleaved", NULL);
14645 stream->alignment = GST_ROUND_UP_8 (depth);
14646 stream->alignment = round_up_pow2 (stream->alignment);
14651 if (flags & FLAG_IS_BIG_ENDIAN)
14652 format = GST_AUDIO_FORMAT_F64BE;
14654 format = GST_AUDIO_FORMAT_F64LE;
14656 if (flags & FLAG_IS_BIG_ENDIAN)
14657 format = GST_AUDIO_FORMAT_F32BE;
14659 format = GST_AUDIO_FORMAT_F32LE;
14661 caps = gst_caps_new_simple ("audio/x-raw",
14662 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14663 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14664 "non-interleaved" : "interleaved", NULL);
14665 stream->alignment = width / 8;
14669 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
14672 caps = gst_caps_new_empty_simple ("audio/x-ac4");
14675 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
14679 caps = _get_unknown_codec_name ("audio", fourcc);
14685 GstCaps *templ_caps =
14686 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
14687 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
14688 gst_caps_unref (caps);
14689 gst_caps_unref (templ_caps);
14690 caps = intersection;
14693 /* enable clipping for raw audio streams */
14694 s = gst_caps_get_structure (caps, 0);
14695 name = gst_structure_get_name (s);
14696 if (g_str_has_prefix (name, "audio/x-raw")) {
14697 stream->need_clip = TRUE;
14698 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
14699 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
14700 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
14701 stream->max_buffer_size);
14707 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14708 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14709 const guint8 * stsd_entry_data, gchar ** codec_name)
14713 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14717 _codec ("DVD subtitle");
14718 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
14719 stream->need_process = TRUE;
14722 _codec ("Quicktime timed text");
14725 _codec ("3GPP timed text");
14727 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
14729 /* actual text piece needs to be extracted */
14730 stream->need_process = TRUE;
14733 _codec ("XML subtitles");
14734 caps = gst_caps_new_empty_simple ("application/ttml+xml");
14737 _codec ("CEA 608 Closed Caption");
14739 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
14740 G_TYPE_STRING, "s334-1a", NULL);
14741 stream->need_process = TRUE;
14742 stream->need_split = TRUE;
14745 _codec ("CEA 708 Closed Caption");
14747 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
14748 G_TYPE_STRING, "cdp", NULL);
14749 stream->need_process = TRUE;
14754 caps = _get_unknown_codec_name ("text", fourcc);
14762 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14763 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14764 const guint8 * stsd_entry_data, gchar ** codec_name)
14770 _codec ("MPEG 1 video");
14771 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14772 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14782 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
14783 const gchar * system_id)
14787 if (!qtdemux->protection_system_ids)
14788 qtdemux->protection_system_ids =
14789 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
14790 /* Check whether we already have an entry for this system ID. */
14791 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
14792 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
14793 if (g_ascii_strcasecmp (system_id, id) == 0) {
14797 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
14798 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,