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 <glib/gi18n-lib.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 "gstisomp4elements.h"
63 #include "qtatomparser.h"
64 #include "qtdemux_types.h"
65 #include "qtdemux_dump.h"
67 #include "descriptors.h"
68 #include "qtdemux_lang.h"
70 #include "qtpalette.h"
71 #include "qtdemux_tags.h"
72 #include "qtdemux_tree.h"
73 #include "qtdemux-webvtt.h"
79 #include <gst/math-compat.h>
85 /* max. size considered 'sane' for non-mdat atoms */
86 #define QTDEMUX_MAX_ATOM_SIZE (32*1024*1024)
88 /* if the sample index is larger than this, something is likely wrong */
89 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
91 /* For converting qt creation times to unix epoch times */
92 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
93 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
94 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
95 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
97 #define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))
99 #define STREAM_IS_EOS(s) ((s)->time_position == GST_CLOCK_TIME_NONE)
101 #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
103 #define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s))
104 #define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
105 #define QTDEMUX_NTH_STREAM(demux,idx) \
106 QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
107 #define QTDEMUX_NTH_OLD_STREAM(demux,idx) \
108 QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
110 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
112 GST_DEBUG_CATEGORY (qtdemux_debug);
113 #define GST_CAT_DEFAULT qtdemux_debug
115 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
116 typedef struct _QtDemuxAavdEncryptionInfo QtDemuxAavdEncryptionInfo;
118 /* Macros for converting to/from timescale */
119 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
120 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
122 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
123 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
125 /* timestamp is the DTS */
126 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
127 /* timestamp + offset + cslg_shift is the outgoing PTS */
128 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
129 /* timestamp + offset is the PTS used for internal seek calculations */
130 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
131 /* timestamp + duration - dts is the duration */
132 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
134 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
136 #define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
137 #define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
138 GST_TRACE("Locking from thread %p", g_thread_self()); \
139 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
140 GST_TRACE("Locked from thread %p", g_thread_self()); \
143 #define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
144 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
145 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
149 * Quicktime has tracks and segments. A track is a continuous piece of
150 * multimedia content. The track is not always played from start to finish but
151 * instead, pieces of the track are 'cut out' and played in sequence. This is
152 * what the segments do.
154 * Inside the track we have keyframes (K) and delta frames. The track has its
155 * own timing, which starts from 0 and extends to end. The position in the track
156 * is called the media_time.
158 * The segments now describe the pieces that should be played from this track
159 * and are basically tuples of media_time/duration/rate entries. We can have
160 * multiple segments and they are all played after one another. An example:
162 * segment 1: media_time: 1 second, duration: 1 second, rate 1
163 * segment 2: media_time: 3 second, duration: 2 second, rate 2
165 * To correctly play back this track, one must play: 1 second of media starting
166 * from media_time 1 followed by 2 seconds of media starting from media_time 3
169 * Each of the segments will be played at a specific time, the first segment at
170 * time 0, the second one after the duration of the first one, etc.. Note that
171 * the time in resulting playback is not identical to the media_time of the
174 * Visually, assuming the track has 4 second of media_time:
177 * .-----------------------------------------------------------.
178 * track: | K.....K.........K........K.......K.......K...........K... |
179 * '-----------------------------------------------------------'
181 * .------------^ ^ .----------^ ^
182 * / .-------------' / .------------------'
184 * .--------------. .--------------.
185 * | segment 1 | | segment 2 |
186 * '--------------' '--------------'
188 * The challenge here is to cut out the right pieces of the track for each of
189 * the playback segments. This fortunately can easily be done with the SEGMENT
190 * events of GStreamer.
192 * For playback of segment 1, we need to provide the decoder with the keyframe
193 * (a), in the above figure, but we must instruct it only to output the decoded
194 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
195 * position set to the time of the segment: 0.
197 * We then proceed to push data from keyframe (a) to frame (b). The decoder
198 * decodes but clips all before media_time 1.
200 * After finishing a segment, we push out a new SEGMENT event with the clipping
201 * boundaries of the new data.
203 * This is a good usecase for the GStreamer accumulated SEGMENT events.
206 struct _QtDemuxSegment
208 /* global time and duration, all gst time */
210 GstClockTime stop_time;
211 GstClockTime duration;
212 /* media time of trak, all gst time */
213 GstClockTime media_start;
214 GstClockTime media_stop;
216 /* Media start time in trak timescale units */
217 guint32 trak_media_start;
220 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
222 /* Used with fragmented MP4 files (mfra atom) */
223 struct _QtDemuxRandomAccessEntry
230 /* Contains properties and cryptographic info for a set of samples from a
231 * track protected using Common Encryption (cenc) */
232 struct _QtDemuxCencSampleSetInfo
234 GstStructure *default_properties;
236 /* @crypto_info holds one GstStructure per sample */
237 GPtrArray *crypto_info;
240 struct _QtDemuxAavdEncryptionInfo
242 GstStructure *default_properties;
246 qt_demux_state_string (enum QtDemuxState state)
249 case QTDEMUX_STATE_INITIAL:
251 case QTDEMUX_STATE_HEADER:
253 case QTDEMUX_STATE_MOVIE:
255 case QTDEMUX_STATE_BUFFER_MDAT:
256 return "<BUFFER_MDAT>";
262 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
264 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
266 static GstStaticPadTemplate gst_qtdemux_sink_template =
267 GST_STATIC_PAD_TEMPLATE ("sink",
270 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
274 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
275 GST_STATIC_PAD_TEMPLATE ("video_%u",
278 GST_STATIC_CAPS_ANY);
280 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
281 GST_STATIC_PAD_TEMPLATE ("audio_%u",
284 GST_STATIC_CAPS_ANY);
286 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
287 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
290 GST_STATIC_CAPS_ANY);
292 static GstStaticPadTemplate gst_qtdemux_metasrc_template =
293 GST_STATIC_PAD_TEMPLATE ("meta_%u",
296 GST_STATIC_CAPS_ANY);
298 #define gst_qtdemux_parent_class parent_class
299 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
300 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qtdemux, "qtdemux",
301 GST_RANK_PRIMARY, GST_TYPE_QTDEMUX, isomp4_element_init (plugin));
303 static void gst_qtdemux_dispose (GObject * object);
304 static void gst_qtdemux_finalize (GObject * object);
307 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
308 GstClockTime media_time);
310 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
311 QtDemuxStream * str, gint64 media_offset);
314 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
315 static GstIndex *gst_qtdemux_get_index (GstElement * element);
317 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
318 GstStateChange transition);
319 static void gst_qtdemux_set_context (GstElement * element,
320 GstContext * context);
321 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
322 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
323 GstObject * parent, GstPadMode mode, gboolean active);
325 static void gst_qtdemux_loop (GstPad * pad);
326 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
328 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
330 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
332 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
333 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
334 QtDemuxStream * stream);
335 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
336 QtDemuxStream * stream);
337 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
340 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
342 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
343 const guint8 * buffer, guint length);
344 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
345 const guint8 * buffer, guint length);
346 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
348 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
349 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
351 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
352 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
353 const guint8 * stsd_entry_data, gchar ** codec_name);
354 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
355 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
356 const guint8 * data, int len, gchar ** codec_name);
357 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
358 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
359 gchar ** codec_name);
360 static GstCaps *qtdemux_meta_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
361 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
362 gchar ** codec_name);
363 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
364 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
365 const guint8 * stsd_entry_data, gchar ** codec_name);
367 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
368 QtDemuxStream * stream, guint32 n);
369 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
370 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
371 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
372 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
373 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
374 static void qtdemux_do_allocation (QtDemuxStream * stream,
375 GstQTDemux * qtdemux);
376 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
377 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
378 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
379 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
380 GstClockTime * _start, GstClockTime * _stop);
381 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
382 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
384 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
385 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
387 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
389 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
390 QtDemuxStream * stream, guint sample_index);
391 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
393 static void qtdemux_gst_structure_free (GstStructure * gststructure);
394 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
397 gst_qtdemux_class_init (GstQTDemuxClass * klass)
399 GObjectClass *gobject_class;
400 GstElementClass *gstelement_class;
402 gobject_class = (GObjectClass *) klass;
403 gstelement_class = (GstElementClass *) klass;
405 parent_class = g_type_class_peek_parent (klass);
407 gobject_class->dispose = gst_qtdemux_dispose;
408 gobject_class->finalize = gst_qtdemux_finalize;
410 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
412 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
413 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
415 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
417 gst_tag_register_musicbrainz_tags ();
419 gst_element_class_add_static_pad_template (gstelement_class,
420 &gst_qtdemux_sink_template);
421 gst_element_class_add_static_pad_template (gstelement_class,
422 &gst_qtdemux_videosrc_template);
423 gst_element_class_add_static_pad_template (gstelement_class,
424 &gst_qtdemux_audiosrc_template);
425 gst_element_class_add_static_pad_template (gstelement_class,
426 &gst_qtdemux_subsrc_template);
427 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
429 "Demultiplex a QuickTime file into audio and video streams",
430 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
432 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
437 gst_qtdemux_init (GstQTDemux * qtdemux)
440 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
441 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
442 gst_pad_set_activatemode_function (qtdemux->sinkpad,
443 qtdemux_sink_activate_mode);
444 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
445 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
446 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
447 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
449 qtdemux->adapter = gst_adapter_new ();
450 g_queue_init (&qtdemux->protection_event_queue);
451 qtdemux->flowcombiner = gst_flow_combiner_new ();
452 g_mutex_init (&qtdemux->expose_lock);
454 qtdemux->active_streams = g_ptr_array_new_with_free_func
455 ((GDestroyNotify) gst_qtdemux_stream_unref);
456 qtdemux->old_streams = g_ptr_array_new_with_free_func
457 ((GDestroyNotify) gst_qtdemux_stream_unref);
459 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
461 gst_qtdemux_reset (qtdemux, TRUE);
465 gst_qtdemux_finalize (GObject * object)
467 GstQTDemux *qtdemux = GST_QTDEMUX (object);
469 g_free (qtdemux->redirect_location);
471 G_OBJECT_CLASS (parent_class)->finalize (object);
475 gst_qtdemux_dispose (GObject * object)
477 GstQTDemux *qtdemux = GST_QTDEMUX (object);
479 if (qtdemux->adapter) {
480 g_object_unref (G_OBJECT (qtdemux->adapter));
481 qtdemux->adapter = NULL;
483 gst_tag_list_unref (qtdemux->tag_list);
484 gst_flow_combiner_free (qtdemux->flowcombiner);
485 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
487 g_queue_clear (&qtdemux->protection_event_queue);
489 g_free (qtdemux->cenc_aux_info_sizes);
490 qtdemux->cenc_aux_info_sizes = NULL;
491 g_mutex_clear (&qtdemux->expose_lock);
493 g_ptr_array_free (qtdemux->active_streams, TRUE);
494 g_ptr_array_free (qtdemux->old_streams, TRUE);
496 G_OBJECT_CLASS (parent_class)->dispose (object);
500 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
502 if (qtdemux->redirect_location) {
503 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
504 (_("This file contains no playable streams.")),
505 ("no known streams found, a redirect message has been posted"),
506 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
508 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
509 (_("This file contains no playable streams.")),
510 ("no known streams found"));
515 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
517 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
518 mem, size, 0, size, mem, free_func);
522 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
529 if (G_UNLIKELY (size == 0)) {
531 GstBuffer *tmp = NULL;
533 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
534 if (ret != GST_FLOW_OK)
537 gst_buffer_map (tmp, &map, GST_MAP_READ);
538 size = QT_UINT32 (map.data);
539 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
541 gst_buffer_unmap (tmp, &map);
542 gst_buffer_unref (tmp);
545 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
546 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
547 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
548 /* we're pulling header but already got most interesting bits,
549 * so never mind the rest (e.g. tags) (that much) */
550 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
554 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
555 (_("This file is invalid and cannot be played.")),
556 ("atom has bogus size %" G_GUINT64_FORMAT, size));
557 return GST_FLOW_ERROR;
561 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
563 if (G_UNLIKELY (flow != GST_FLOW_OK))
566 bsize = gst_buffer_get_size (*buf);
567 /* Catch short reads - we don't want any partial atoms */
568 if (G_UNLIKELY (bsize < size)) {
569 GST_WARNING_OBJECT (qtdemux,
570 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
571 gst_buffer_unref (*buf);
581 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
582 GstFormat src_format, gint64 src_value, GstFormat dest_format,
586 QtDemuxStream *stream = gst_pad_get_element_private (pad);
589 if (stream->subtype != FOURCC_vide) {
594 switch (src_format) {
595 case GST_FORMAT_TIME:
596 switch (dest_format) {
597 case GST_FORMAT_BYTES:{
598 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
604 *dest_value = stream->samples[index].offset;
606 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
607 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
608 GST_TIME_ARGS (src_value), *dest_value);
616 case GST_FORMAT_BYTES:
617 switch (dest_format) {
618 case GST_FORMAT_TIME:{
620 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
629 QTSTREAMTIME_TO_GSTTIME (stream,
630 stream->samples[index].timestamp);
631 GST_DEBUG_OBJECT (qtdemux,
632 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
633 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
652 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
654 gboolean res = FALSE;
656 *duration = GST_CLOCK_TIME_NONE;
658 if (qtdemux->duration != 0 &&
659 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
660 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
663 *duration = GST_CLOCK_TIME_NONE;
670 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
673 gboolean res = FALSE;
674 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
676 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
678 switch (GST_QUERY_TYPE (query)) {
679 case GST_QUERY_POSITION:{
682 gst_query_parse_position (query, &fmt, NULL);
683 if (fmt == GST_FORMAT_TIME
684 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
685 gst_query_set_position (query, GST_FORMAT_TIME,
686 qtdemux->segment.position);
691 case GST_QUERY_DURATION:{
694 gst_query_parse_duration (query, &fmt, NULL);
695 if (fmt == GST_FORMAT_TIME) {
696 /* First try to query upstream */
697 res = gst_pad_query_default (pad, parent, query);
699 GstClockTime duration;
700 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
701 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
708 case GST_QUERY_CONVERT:{
709 GstFormat src_fmt, dest_fmt;
710 gint64 src_value, dest_value = 0;
712 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
714 res = gst_qtdemux_src_convert (qtdemux, pad,
715 src_fmt, src_value, dest_fmt, &dest_value);
717 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
721 case GST_QUERY_FORMATS:
722 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
725 case GST_QUERY_SEEKING:{
729 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
731 if (fmt == GST_FORMAT_BYTES) {
732 /* We always refuse BYTES seeks from downstream */
736 /* try upstream first */
737 res = gst_pad_query_default (pad, parent, query);
740 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
741 if (fmt == GST_FORMAT_TIME) {
742 GstClockTime duration;
744 gst_qtdemux_get_duration (qtdemux, &duration);
746 if (!qtdemux->pullbased) {
749 /* we might be able with help from upstream */
751 q = gst_query_new_seeking (GST_FORMAT_BYTES);
752 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
753 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
754 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
758 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
764 case GST_QUERY_SEGMENT:
769 format = qtdemux->segment.format;
772 gst_segment_to_stream_time (&qtdemux->segment, format,
773 qtdemux->segment.start);
774 if ((stop = qtdemux->segment.stop) == -1)
775 stop = qtdemux->segment.duration;
777 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
779 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
784 res = gst_pad_query_default (pad, parent, query);
792 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
794 if (G_LIKELY (stream->pad)) {
795 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
796 GST_DEBUG_PAD_NAME (stream->pad));
798 if (!gst_tag_list_is_empty (stream->stream_tags)) {
799 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
800 stream->stream_tags);
801 gst_pad_push_event (stream->pad,
802 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
805 if (G_UNLIKELY (stream->send_global_tags)) {
806 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
808 gst_pad_push_event (stream->pad,
809 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
810 stream->send_global_tags = FALSE;
815 /* push event on all source pads; takes ownership of the event */
817 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
819 gboolean has_valid_stream = FALSE;
820 GstEventType etype = GST_EVENT_TYPE (event);
823 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
824 GST_EVENT_TYPE_NAME (event));
826 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
828 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
829 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
831 if ((pad = stream->pad)) {
832 has_valid_stream = TRUE;
834 if (etype == GST_EVENT_EOS) {
835 /* let's not send twice */
836 if (stream->sent_eos)
838 stream->sent_eos = TRUE;
841 gst_pad_push_event (pad, gst_event_ref (event));
845 gst_event_unref (event);
847 /* if it is EOS and there are no pads, post an error */
848 if (!has_valid_stream && etype == GST_EVENT_EOS) {
849 gst_qtdemux_post_no_playable_stream_error (qtdemux);
859 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
861 if ((gint64) s1->timestamp > *media_time)
863 if ((gint64) s1->timestamp == *media_time)
869 /* find the index of the sample that includes the data for @media_time using a
870 * binary search. Only to be called in optimized cases of linear search below.
872 * Returns the index of the sample with the corresponding *DTS*.
875 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
878 QtDemuxSample *result;
881 /* convert media_time to mov format */
883 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
885 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
886 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
887 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
889 if (G_LIKELY (result))
890 index = result - str->samples;
899 /* find the index of the sample that includes the data for @media_offset using a
902 * Returns the index of the sample.
905 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
906 QtDemuxStream * str, gint64 media_offset)
908 QtDemuxSample *result = str->samples;
911 if (result == NULL || str->n_samples == 0)
914 if (media_offset == result->offset)
918 while (index < str->n_samples - 1) {
919 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
922 if (media_offset < result->offset)
933 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
938 /* find the index of the sample that includes the data for @media_time using a
939 * linear search, and keeping in mind that not all samples may have been parsed
940 * yet. If possible, it will delegate to binary search.
942 * Returns the index of the sample.
945 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
946 GstClockTime media_time)
950 QtDemuxSample *sample;
952 /* convert media_time to mov format */
954 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
956 sample = str->samples;
957 if (mov_time == sample->timestamp + sample->pts_offset)
960 /* use faster search if requested time in already parsed range */
961 sample = str->samples + str->stbl_index;
962 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
963 index = gst_qtdemux_find_index (qtdemux, str, media_time);
964 sample = str->samples + index;
966 while (index < str->n_samples - 1) {
967 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
970 sample = str->samples + index + 1;
971 if (mov_time < sample->timestamp) {
972 sample = str->samples + index;
980 /* sample->timestamp is now <= media_time, need to find the corresponding
981 * PTS now by looking backwards */
982 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
984 sample = str->samples + index;
992 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
997 /* find the index of the keyframe needed to decode the sample at @index
998 * of stream @str, or of a subsequent keyframe (depending on @next)
1000 * Returns the index of the keyframe.
1003 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1004 guint32 index, gboolean next)
1006 guint32 new_index = index;
1008 if (index >= str->n_samples) {
1009 new_index = str->n_samples;
1013 /* all keyframes, return index */
1014 if (str->all_keyframe) {
1019 /* else search until we have a keyframe */
1020 while (new_index < str->n_samples) {
1021 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1024 if (str->samples[new_index].keyframe)
1036 if (new_index == str->n_samples) {
1037 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1042 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1043 "gave %u", next ? "after" : "before", index, new_index);
1050 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1055 /* find the segment for @time_position for @stream
1057 * Returns the index of the segment containing @time_position.
1058 * Returns the last segment and sets the @eos variable to TRUE
1059 * if the time is beyond the end. @eos may be NULL
1062 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1063 GstClockTime time_position)
1068 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1069 GST_TIME_ARGS (time_position));
1072 for (i = 0; i < stream->n_segments; i++) {
1073 QtDemuxSegment *segment = &stream->segments[i];
1075 GST_LOG_OBJECT (stream->pad,
1076 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1077 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1079 /* For the last segment we include stop_time in the last segment */
1080 if (i < stream->n_segments - 1) {
1081 if (segment->time <= time_position && time_position < segment->stop_time) {
1082 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1087 /* Last segment always matches */
1095 /* move the stream @str to the sample position @index.
1097 * Updates @str->sample_index and marks discontinuity if needed.
1100 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1103 /* no change needed */
1104 if (index == str->sample_index)
1107 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1110 /* position changed, we have a discont */
1111 str->sample_index = index;
1112 str->offset_in_sample = 0;
1113 /* Each time we move in the stream we store the position where we are
1115 str->from_sample = index;
1116 str->discont = TRUE;
1120 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1121 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1124 gint64 min_byte_offset = -1;
1127 min_offset = desired_time;
1129 /* for each stream, find the index of the sample in the segment
1130 * and move back to the previous keyframe. */
1131 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1133 guint32 index, kindex;
1135 GstClockTime media_start;
1136 GstClockTime media_time;
1137 GstClockTime seg_time;
1138 QtDemuxSegment *seg;
1139 gboolean empty_segment = FALSE;
1141 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1143 if (CUR_STREAM (str)->sparse && !use_sparse)
1146 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1147 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1149 /* get segment and time in the segment */
1150 seg = &str->segments[seg_idx];
1151 seg_time = (desired_time - seg->time) * seg->rate;
1153 while (QTSEGMENT_IS_EMPTY (seg)) {
1155 empty_segment = TRUE;
1156 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1159 if (seg_idx == str->n_segments)
1161 seg = &str->segments[seg_idx];
1164 if (seg_idx == str->n_segments) {
1165 /* FIXME track shouldn't have the last segment as empty, but if it
1166 * happens we better handle it */
1170 /* get the media time in the segment */
1171 media_start = seg->media_start + seg_time;
1173 /* get the index of the sample with media time */
1174 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1175 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1176 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1177 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1180 /* shift to next frame if we are looking for next keyframe */
1181 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1182 && index < str->stbl_index)
1185 if (!empty_segment) {
1186 /* find previous keyframe */
1187 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1189 /* we will settle for one before if none found after */
1190 if (next && kindex == -1)
1191 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1193 /* Update the requested time whenever a keyframe was found, to make it
1194 * accurate and avoid having the first buffer fall outside of the segment
1199 /* get timestamp of keyframe */
1200 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1201 GST_DEBUG_OBJECT (qtdemux,
1202 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1203 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1204 str->samples[kindex].offset);
1206 /* keyframes in the segment get a chance to change the
1207 * desired_offset. keyframes out of the segment are
1209 if (media_time >= seg->media_start) {
1210 GstClockTime seg_time;
1212 /* this keyframe is inside the segment, convert back to
1214 seg_time = (media_time - seg->media_start) + seg->time;
1215 if ((!next && (seg_time < min_offset)) ||
1216 (next && (seg_time > min_offset)))
1217 min_offset = seg_time;
1222 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1223 min_byte_offset = str->samples[index].offset;
1227 *key_time = min_offset;
1229 *key_offset = min_byte_offset;
1233 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1234 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1238 g_return_val_if_fail (format != NULL, FALSE);
1239 g_return_val_if_fail (cur != NULL, FALSE);
1240 g_return_val_if_fail (stop != NULL, FALSE);
1242 if (*format == GST_FORMAT_TIME)
1246 if (cur_type != GST_SEEK_TYPE_NONE)
1247 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1248 if (res && stop_type != GST_SEEK_TYPE_NONE)
1249 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1252 *format = GST_FORMAT_TIME;
1257 /* perform seek in push based mode:
1258 find BYTE position to move to based on time and delegate to upstream
1261 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1266 GstSeekType cur_type, stop_type;
1267 gint64 cur, stop, key_cur;
1270 gint64 original_stop;
1273 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1275 gst_event_parse_seek (event, &rate, &format, &flags,
1276 &cur_type, &cur, &stop_type, &stop);
1277 seqnum = gst_event_get_seqnum (event);
1279 /* Directly send the instant-rate-change event here before taking the
1280 * stream-lock so that it can be applied as soon as possible */
1281 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1284 /* instant rate change only supported if direction does not change. All
1285 * other requirements are already checked before creating the seek event
1286 * but let's double-check here to be sure */
1287 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1288 (qtdemux->segment.rate < 0 && rate > 0) ||
1289 cur_type != GST_SEEK_TYPE_NONE ||
1290 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1291 GST_ERROR_OBJECT (qtdemux,
1292 "Instant rate change seeks only supported in the "
1293 "same direction, without flushing and position change");
1297 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1298 (GstSegmentFlags) flags);
1299 gst_event_set_seqnum (ev, seqnum);
1300 gst_qtdemux_push_event (qtdemux, ev);
1304 /* only forward streaming and seeking is possible */
1306 goto unsupported_seek;
1308 /* convert to TIME if needed and possible */
1309 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1313 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1314 * the original stop position to use when upstream pushes the new segment
1316 original_stop = stop;
1319 /* find reasonable corresponding BYTE position,
1320 * also try to mind about keyframes, since we can not go back a bit for them
1322 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1323 * mostly just work, but let's not yet boldly go there ... */
1324 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1329 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1330 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1333 GST_OBJECT_LOCK (qtdemux);
1334 qtdemux->seek_offset = byte_cur;
1335 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1336 qtdemux->push_seek_start = cur;
1338 qtdemux->push_seek_start = key_cur;
1341 if (stop_type == GST_SEEK_TYPE_NONE) {
1342 qtdemux->push_seek_stop = qtdemux->segment.stop;
1344 qtdemux->push_seek_stop = original_stop;
1346 GST_OBJECT_UNLOCK (qtdemux);
1348 qtdemux->segment_seqnum = seqnum;
1349 /* BYTE seek event */
1350 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1352 gst_event_set_seqnum (event, seqnum);
1353 res = gst_pad_push_event (qtdemux->sinkpad, event);
1360 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1366 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1371 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1376 /* perform the seek.
1378 * We set all segment_indexes in the streams to unknown and
1379 * adjust the time_position to the desired position. this is enough
1380 * to trigger a segment switch in the streaming thread to start
1381 * streaming from the desired position.
1383 * Keyframe seeking is a little more complicated when dealing with
1384 * segments. Ideally we want to move to the previous keyframe in
1385 * the segment but there might not be a keyframe in the segment. In
1386 * fact, none of the segments could contain a keyframe. We take a
1387 * practical approach: seek to the previous keyframe in the segment,
1388 * if there is none, seek to the beginning of the segment.
1390 * Called with STREAM_LOCK
1393 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1394 guint32 seqnum, GstSeekFlags flags)
1396 gint64 desired_offset;
1399 desired_offset = segment->position;
1401 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1402 GST_TIME_ARGS (desired_offset));
1404 /* may not have enough fragmented info to do this adjustment,
1405 * and we can't scan (and probably should not) at this time with
1406 * possibly flushing upstream */
1407 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1409 gboolean next, before, after;
1411 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1412 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1413 next = after && !before;
1414 if (segment->rate < 0)
1417 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1419 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1420 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1421 desired_offset = min_offset;
1424 /* and set all streams to the final position */
1425 GST_OBJECT_LOCK (qtdemux);
1426 gst_flow_combiner_reset (qtdemux->flowcombiner);
1427 GST_OBJECT_UNLOCK (qtdemux);
1428 qtdemux->segment_seqnum = seqnum;
1429 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1430 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1432 stream->time_position = desired_offset;
1433 stream->accumulated_base = 0;
1434 stream->sample_index = -1;
1435 stream->offset_in_sample = 0;
1436 stream->segment_index = -1;
1437 stream->sent_eos = FALSE;
1438 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1440 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1441 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1443 segment->position = desired_offset;
1444 if (segment->rate >= 0) {
1445 segment->start = desired_offset;
1446 /* We need to update time as we update start in that direction */
1447 segment->time = desired_offset;
1449 /* we stop at the end */
1450 if (segment->stop == -1)
1451 segment->stop = segment->duration;
1453 segment->stop = desired_offset;
1456 if (qtdemux->fragmented)
1457 qtdemux->fragmented_seek_pending = TRUE;
1462 /* do a seek in pull based mode */
1464 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1469 GstSeekType cur_type, stop_type;
1471 gboolean flush, instant_rate_change;
1473 GstSegment seeksegment;
1474 guint32 seqnum = GST_SEQNUM_INVALID;
1475 GstEvent *flush_event;
1478 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1480 gst_event_parse_seek (event, &rate, &format, &flags,
1481 &cur_type, &cur, &stop_type, &stop);
1482 seqnum = gst_event_get_seqnum (event);
1484 /* we have to have a format as the segment format. Try to convert
1486 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1490 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1492 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1493 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1495 /* Directly send the instant-rate-change event here before taking the
1496 * stream-lock so that it can be applied as soon as possible */
1497 if (instant_rate_change) {
1500 /* instant rate change only supported if direction does not change. All
1501 * other requirements are already checked before creating the seek event
1502 * but let's double-check here to be sure */
1503 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1504 (qtdemux->segment.rate < 0 && rate > 0) ||
1505 cur_type != GST_SEEK_TYPE_NONE ||
1506 stop_type != GST_SEEK_TYPE_NONE || flush) {
1507 GST_ERROR_OBJECT (qtdemux,
1508 "Instant rate change seeks only supported in the "
1509 "same direction, without flushing and position change");
1513 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1514 (GstSegmentFlags) flags);
1515 gst_event_set_seqnum (ev, seqnum);
1516 gst_qtdemux_push_event (qtdemux, ev);
1520 /* stop streaming, either by flushing or by pausing the task */
1522 flush_event = gst_event_new_flush_start ();
1523 if (seqnum != GST_SEQNUM_INVALID)
1524 gst_event_set_seqnum (flush_event, seqnum);
1525 /* unlock upstream pull_range */
1526 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1527 /* make sure out loop function exits */
1528 gst_qtdemux_push_event (qtdemux, flush_event);
1530 /* non flushing seek, pause the task */
1531 gst_pad_pause_task (qtdemux->sinkpad);
1534 /* wait for streaming to finish */
1535 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1537 /* copy segment, we need this because we still need the old
1538 * segment when we close the current segment. */
1539 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1541 /* configure the segment with the seek variables */
1542 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1543 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1544 cur_type, cur, stop_type, stop, &update)) {
1546 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1548 /* now do the seek */
1549 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1552 /* prepare for streaming again */
1554 flush_event = gst_event_new_flush_stop (TRUE);
1555 if (seqnum != GST_SEQNUM_INVALID)
1556 gst_event_set_seqnum (flush_event, seqnum);
1558 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1559 gst_qtdemux_push_event (qtdemux, flush_event);
1562 /* commit the new segment */
1563 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1565 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1566 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1567 qtdemux->segment.format, qtdemux->segment.position);
1568 if (seqnum != GST_SEQNUM_INVALID)
1569 gst_message_set_seqnum (msg, seqnum);
1570 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1573 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1574 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1575 qtdemux->sinkpad, NULL);
1577 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1584 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1590 qtdemux_ensure_index (GstQTDemux * qtdemux)
1594 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1596 /* Build complete index */
1597 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1598 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1600 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1601 GST_LOG_OBJECT (qtdemux,
1602 "Building complete index of track-id %u for seeking failed!",
1612 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1615 gboolean res = TRUE;
1616 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1618 switch (GST_EVENT_TYPE (event)) {
1619 case GST_EVENT_RECONFIGURE:
1620 GST_OBJECT_LOCK (qtdemux);
1621 gst_flow_combiner_reset (qtdemux->flowcombiner);
1622 GST_OBJECT_UNLOCK (qtdemux);
1623 res = gst_pad_event_default (pad, parent, event);
1625 case GST_EVENT_SEEK:
1627 GstSeekFlags flags = 0;
1628 GstFormat seek_format;
1629 gboolean instant_rate_change;
1631 #ifndef GST_DISABLE_GST_DEBUG
1632 GstClockTime ts = gst_util_get_timestamp ();
1634 guint32 seqnum = gst_event_get_seqnum (event);
1636 qtdemux->received_seek = TRUE;
1638 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1640 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1642 if (seqnum == qtdemux->segment_seqnum) {
1643 GST_LOG_OBJECT (pad,
1644 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1645 gst_event_unref (event);
1649 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1650 /* seek should be handled by upstream, we might need to re-download fragments */
1651 GST_DEBUG_OBJECT (qtdemux,
1652 "let upstream handle seek for fragmented playback");
1656 if (seek_format == GST_FORMAT_BYTES) {
1657 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1658 gst_event_unref (event);
1662 gst_event_parse_seek_trickmode_interval (event,
1663 &qtdemux->trickmode_interval);
1665 /* Build complete index for seeking;
1666 * if not a fragmented file at least and we're really doing a seek,
1667 * not just an instant-rate-change */
1668 if (!qtdemux->fragmented && !instant_rate_change) {
1669 if (!qtdemux_ensure_index (qtdemux))
1672 #ifndef GST_DISABLE_GST_DEBUG
1673 ts = gst_util_get_timestamp () - ts;
1674 GST_INFO_OBJECT (qtdemux,
1675 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1677 if (qtdemux->pullbased) {
1678 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1679 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1680 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1682 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1683 && QTDEMUX_N_STREAMS (qtdemux)
1684 && !qtdemux->fragmented) {
1685 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1687 GST_DEBUG_OBJECT (qtdemux,
1688 "ignoring seek in push mode in current state");
1691 gst_event_unref (event);
1696 res = gst_pad_event_default (pad, parent, event);
1706 GST_ERROR_OBJECT (qtdemux, "Index failed");
1707 gst_event_unref (event);
1713 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1715 * If @fw is false, the coding order is explored backwards.
1717 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1718 * sample is found for that track.
1720 * The stream and sample index of the sample with the minimum offset in the direction explored
1721 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1723 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1724 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1725 * @_stream and @_index. */
1727 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1728 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1731 gint64 time, min_time;
1732 QtDemuxStream *stream;
1739 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1742 gboolean set_sample;
1744 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1751 i = str->n_samples - 1;
1755 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1756 if (str->samples[i].size == 0)
1759 if (fw && (str->samples[i].offset < byte_pos))
1762 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1765 /* move stream to first available sample */
1767 gst_qtdemux_move_stream (qtdemux, str, i);
1771 /* avoid index from sparse streams since they might be far away */
1772 if (!CUR_STREAM (str)->sparse) {
1773 /* determine min/max time */
1774 time = QTSAMPLE_PTS (str, &str->samples[i]);
1775 if (min_time == -1 || (!fw && time > min_time) ||
1776 (fw && time < min_time)) {
1780 /* determine stream with leading sample, to get its position */
1782 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1783 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1791 /* no sample for this stream, mark eos */
1793 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1804 /* Copied from mpegtsbase code */
1805 /* FIXME: replace this function when we add new util function for stream-id creation */
1807 _get_upstream_id (GstQTDemux * demux)
1809 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1812 /* Try to create one from the upstream URI, else use a randome number */
1816 /* Try to generate one from the URI query and
1817 * if it fails take a random number instead */
1818 query = gst_query_new_uri ();
1819 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1820 gst_query_parse_uri (query, &uri);
1826 /* And then generate an SHA256 sum of the URI */
1827 cs = g_checksum_new (G_CHECKSUM_SHA256);
1828 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1830 upstream_id = g_strdup (g_checksum_get_string (cs));
1831 g_checksum_free (cs);
1833 /* Just get some random number if the URI query fails */
1834 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1835 "implementing a deterministic way of creating a stream-id");
1837 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1838 g_random_int (), g_random_int ());
1841 gst_query_unref (query);
1846 static QtDemuxStream *
1847 _create_stream (GstQTDemux * demux, guint32 track_id)
1849 QtDemuxStream *stream;
1852 stream = g_new0 (QtDemuxStream, 1);
1853 stream->demux = demux;
1854 stream->track_id = track_id;
1855 upstream_id = _get_upstream_id (demux);
1856 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1857 g_free (upstream_id);
1858 /* new streams always need a discont */
1859 stream->discont = TRUE;
1860 /* we enable clipping for raw audio/video streams */
1861 stream->need_clip = FALSE;
1862 stream->process_func = NULL;
1863 stream->segment_index = -1;
1864 stream->time_position = 0;
1865 stream->sample_index = -1;
1866 stream->offset_in_sample = 0;
1867 stream->new_stream = TRUE;
1868 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1869 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1870 stream->protected = FALSE;
1871 stream->protection_scheme_type = 0;
1872 stream->protection_scheme_version = 0;
1873 stream->protection_scheme_info = NULL;
1874 stream->n_samples_moof = 0;
1875 stream->duration_moof = 0;
1876 stream->duration_last_moof = 0;
1877 stream->alignment = 1;
1878 stream->stream_tags = gst_tag_list_new_empty ();
1879 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1880 g_queue_init (&stream->protection_scheme_event_queue);
1881 stream->ref_count = 1;
1882 /* consistent default for push based mode */
1883 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1888 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1890 GstStructure *structure;
1891 const gchar *variant;
1892 const GstCaps *mediacaps = NULL;
1894 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1896 structure = gst_caps_get_structure (caps, 0);
1897 variant = gst_structure_get_string (structure, "variant");
1899 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1900 QtDemuxStream *stream;
1901 const GValue *value;
1903 demux->fragmented = TRUE;
1904 demux->mss_mode = TRUE;
1906 if (QTDEMUX_N_STREAMS (demux) > 1) {
1907 /* can't do this, we can only renegotiate for another mss format */
1911 value = gst_structure_get_value (structure, "media-caps");
1914 const GValue *timescale_v;
1916 /* TODO update when stream changes during playback */
1918 if (QTDEMUX_N_STREAMS (demux) == 0) {
1919 stream = _create_stream (demux, 1);
1920 g_ptr_array_add (demux->active_streams, stream);
1921 /* mss has no stsd/stsd entry, use id 0 as default */
1922 stream->stsd_entries_length = 1;
1923 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
1924 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
1926 stream = QTDEMUX_NTH_STREAM (demux, 0);
1929 timescale_v = gst_structure_get_value (structure, "timescale");
1931 stream->timescale = g_value_get_uint64 (timescale_v);
1933 /* default mss timescale */
1934 stream->timescale = 10000000;
1936 demux->timescale = stream->timescale;
1938 mediacaps = gst_value_get_caps (value);
1939 if (!CUR_STREAM (stream)->caps
1940 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
1941 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1943 stream->new_caps = TRUE;
1945 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
1946 structure = gst_caps_get_structure (mediacaps, 0);
1947 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
1948 stream->subtype = FOURCC_vide;
1950 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
1951 gst_structure_get_int (structure, "height",
1952 &CUR_STREAM (stream)->height);
1953 gst_structure_get_fraction (structure, "framerate",
1954 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
1955 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1957 stream->subtype = FOURCC_soun;
1958 gst_structure_get_int (structure, "channels",
1959 &CUR_STREAM (stream)->n_channels);
1960 gst_structure_get_int (structure, "rate", &rate);
1961 CUR_STREAM (stream)->rate = rate;
1962 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
1963 if (gst_structure_has_field (structure, "original-media-type")) {
1964 const gchar *media_type =
1965 gst_structure_get_string (structure, "original-media-type");
1966 if (g_str_has_prefix (media_type, "video")) {
1967 stream->subtype = FOURCC_vide;
1968 } else if (g_str_has_prefix (media_type, "audio")) {
1969 stream->subtype = FOURCC_soun;
1974 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1976 demux->mss_mode = FALSE;
1983 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
1987 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
1988 gst_pad_stop_task (qtdemux->sinkpad);
1990 if (hard || qtdemux->upstream_format_is_time) {
1991 qtdemux->state = QTDEMUX_STATE_INITIAL;
1992 qtdemux->neededbytes = 16;
1993 qtdemux->todrop = 0;
1994 qtdemux->pullbased = FALSE;
1995 g_clear_pointer (&qtdemux->redirect_location, g_free);
1996 qtdemux->first_mdat = -1;
1997 qtdemux->header_size = 0;
1998 qtdemux->mdatoffset = -1;
1999 qtdemux->restoredata_offset = -1;
2000 if (qtdemux->mdatbuffer)
2001 gst_buffer_unref (qtdemux->mdatbuffer);
2002 if (qtdemux->restoredata_buffer)
2003 gst_buffer_unref (qtdemux->restoredata_buffer);
2004 qtdemux->mdatbuffer = NULL;
2005 qtdemux->restoredata_buffer = NULL;
2006 qtdemux->mdatleft = 0;
2007 qtdemux->mdatsize = 0;
2008 if (qtdemux->comp_brands)
2009 gst_buffer_unref (qtdemux->comp_brands);
2010 qtdemux->comp_brands = NULL;
2011 qtdemux->last_moov_offset = -1;
2012 if (qtdemux->moov_node_compressed) {
2013 g_node_destroy (qtdemux->moov_node_compressed);
2014 if (qtdemux->moov_node)
2015 g_free (qtdemux->moov_node->data);
2017 qtdemux->moov_node_compressed = NULL;
2018 if (qtdemux->moov_node)
2019 g_node_destroy (qtdemux->moov_node);
2020 qtdemux->moov_node = NULL;
2021 if (qtdemux->tag_list)
2022 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2023 qtdemux->tag_list = gst_tag_list_new_empty ();
2024 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2026 if (qtdemux->element_index)
2027 gst_object_unref (qtdemux->element_index);
2028 qtdemux->element_index = NULL;
2030 qtdemux->major_brand = 0;
2031 qtdemux->upstream_format_is_time = FALSE;
2032 qtdemux->upstream_seekable = FALSE;
2033 qtdemux->upstream_size = 0;
2035 qtdemux->fragment_start = -1;
2036 qtdemux->fragment_start_offset = -1;
2037 qtdemux->duration = 0;
2038 qtdemux->moof_offset = 0;
2039 qtdemux->chapters_track_id = 0;
2040 qtdemux->have_group_id = FALSE;
2041 qtdemux->group_id = G_MAXUINT;
2043 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2045 g_queue_clear (&qtdemux->protection_event_queue);
2047 qtdemux->received_seek = FALSE;
2048 qtdemux->first_moof_already_parsed = FALSE;
2050 qtdemux->offset = 0;
2051 gst_adapter_clear (qtdemux->adapter);
2052 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2053 qtdemux->need_segment = TRUE;
2056 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2057 qtdemux->trickmode_interval = 0;
2058 g_ptr_array_set_size (qtdemux->active_streams, 0);
2059 g_ptr_array_set_size (qtdemux->old_streams, 0);
2060 qtdemux->n_video_streams = 0;
2061 qtdemux->n_audio_streams = 0;
2062 qtdemux->n_sub_streams = 0;
2063 qtdemux->n_meta_streams = 0;
2064 qtdemux->exposed = FALSE;
2065 qtdemux->fragmented = FALSE;
2066 qtdemux->mss_mode = FALSE;
2067 gst_caps_replace (&qtdemux->media_caps, NULL);
2068 qtdemux->timescale = 0;
2069 qtdemux->got_moov = FALSE;
2070 qtdemux->cenc_aux_info_offset = 0;
2071 qtdemux->cenc_aux_info_sizes = NULL;
2072 qtdemux->cenc_aux_sample_count = 0;
2073 if (qtdemux->protection_system_ids) {
2074 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2075 qtdemux->protection_system_ids = NULL;
2077 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2078 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2079 GST_BIN_FLAG_STREAMS_AWARE);
2081 if (qtdemux->preferred_protection_system_id) {
2082 g_free (qtdemux->preferred_protection_system_id);
2083 qtdemux->preferred_protection_system_id = NULL;
2085 } else if (qtdemux->mss_mode) {
2086 gst_flow_combiner_reset (qtdemux->flowcombiner);
2087 g_ptr_array_foreach (qtdemux->active_streams,
2088 (GFunc) gst_qtdemux_stream_clear, NULL);
2090 gst_flow_combiner_reset (qtdemux->flowcombiner);
2091 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2092 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2093 stream->sent_eos = FALSE;
2094 stream->time_position = 0;
2095 stream->accumulated_base = 0;
2096 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2102 /* Maps the @segment to the qt edts internal segments and pushes
2103 * the corresponding segment event.
2105 * If it ends up being at a empty segment, a gap will be pushed and the next
2106 * edts segment will be activated in sequence.
2108 * To be used in push-mode only */
2110 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2114 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2115 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2117 stream->time_position = segment->start;
2119 /* in push mode we should be guaranteed that we will have empty segments
2120 * at the beginning and then one segment after, other scenarios are not
2121 * supported and are discarded when parsing the edts */
2122 for (i = 0; i < stream->n_segments; i++) {
2123 if (stream->segments[i].stop_time > segment->start) {
2124 /* push the empty segment and move to the next one */
2125 gst_qtdemux_activate_segment (qtdemux, stream, i,
2126 stream->time_position);
2127 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2128 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2129 stream->time_position);
2131 /* accumulate previous segments */
2132 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2133 stream->accumulated_base +=
2134 (stream->segment.stop -
2135 stream->segment.start) / ABS (stream->segment.rate);
2139 g_assert (i == stream->n_segments - 1);
2146 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2157 for (i = 0; i < len; i++) {
2158 QtDemuxStream *stream = g_ptr_array_index (src, i);
2160 #ifndef GST_DISABLE_GST_DEBUG
2161 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2162 stream, GST_STR_NULL (stream->stream_id), dest);
2164 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2167 g_ptr_array_set_size (src, 0);
2171 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2174 GstQTDemux *demux = GST_QTDEMUX (parent);
2175 gboolean res = TRUE;
2177 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2179 switch (GST_EVENT_TYPE (event)) {
2180 case GST_EVENT_SEGMENT:
2183 QtDemuxStream *stream;
2187 /* some debug output */
2188 gst_event_copy_segment (event, &segment);
2189 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2192 if (segment.format == GST_FORMAT_TIME) {
2193 demux->upstream_format_is_time = TRUE;
2194 demux->segment_seqnum = gst_event_get_seqnum (event);
2196 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2197 "not in time format");
2199 /* chain will send initial newsegment after pads have been added */
2200 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2201 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2206 /* check if this matches a time seek we received previously
2207 * FIXME for backwards compatibility reasons we use the
2208 * seek_offset here to compare. In the future we might want to
2209 * change this to use the seqnum as it uniquely should identify
2210 * the segment that corresponds to the seek. */
2211 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2212 ", received segment offset %" G_GINT64_FORMAT,
2213 demux->seek_offset, segment.start);
2214 if (segment.format == GST_FORMAT_BYTES
2215 && demux->seek_offset == segment.start) {
2216 GST_OBJECT_LOCK (demux);
2217 offset = segment.start;
2219 segment.format = GST_FORMAT_TIME;
2220 segment.start = demux->push_seek_start;
2221 segment.stop = demux->push_seek_stop;
2222 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2223 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2224 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2225 GST_OBJECT_UNLOCK (demux);
2228 /* we only expect a BYTE segment, e.g. following a seek */
2229 if (segment.format == GST_FORMAT_BYTES) {
2230 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2231 offset = segment.start;
2233 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2234 NULL, (gint64 *) & segment.start);
2235 if ((gint64) segment.start < 0)
2238 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2239 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2240 NULL, (gint64 *) & segment.stop);
2241 /* keyframe seeking should already arrange for start >= stop,
2242 * but make sure in other rare cases */
2243 segment.stop = MAX (segment.stop, segment.start);
2245 } else if (segment.format == GST_FORMAT_TIME) {
2246 /* push all data on the adapter before starting this
2248 gst_qtdemux_process_adapter (demux, TRUE);
2250 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2254 /* We shouldn't modify upstream driven TIME FORMAT segment */
2255 if (!demux->upstream_format_is_time) {
2256 /* accept upstream's notion of segment and distribute along */
2257 segment.format = GST_FORMAT_TIME;
2258 segment.position = segment.time = segment.start;
2259 segment.duration = demux->segment.duration;
2260 segment.base = gst_segment_to_running_time (&demux->segment,
2261 GST_FORMAT_TIME, demux->segment.position);
2264 gst_segment_copy_into (&segment, &demux->segment);
2265 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2267 /* map segment to internal qt segments and push on each stream */
2268 if (QTDEMUX_N_STREAMS (demux)) {
2269 demux->need_segment = TRUE;
2270 gst_qtdemux_check_send_pending_segment (demux);
2273 /* clear leftover in current segment, if any */
2274 gst_adapter_clear (demux->adapter);
2276 /* set up streaming thread */
2277 demux->offset = offset;
2278 if (demux->upstream_format_is_time) {
2279 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2280 "set values to restart reading from a new atom");
2281 demux->neededbytes = 16;
2284 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2287 demux->todrop = stream->samples[idx].offset - offset;
2288 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2290 /* set up for EOS */
2291 demux->neededbytes = -1;
2296 gst_event_unref (event);
2300 case GST_EVENT_FLUSH_START:
2302 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2303 gst_event_unref (event);
2306 QTDEMUX_EXPOSE_LOCK (demux);
2307 res = gst_pad_event_default (demux->sinkpad, parent, event);
2308 QTDEMUX_EXPOSE_UNLOCK (demux);
2311 case GST_EVENT_FLUSH_STOP:
2315 dur = demux->segment.duration;
2316 gst_qtdemux_reset (demux, FALSE);
2317 demux->segment.duration = dur;
2319 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2320 gst_event_unref (event);
2326 /* If we are in push mode, and get an EOS before we've seen any streams,
2327 * then error out - we have nowhere to send the EOS */
2328 if (!demux->pullbased) {
2330 gboolean has_valid_stream = FALSE;
2331 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2332 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2333 has_valid_stream = TRUE;
2337 if (!has_valid_stream)
2338 gst_qtdemux_post_no_playable_stream_error (demux);
2340 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2341 (guint) gst_adapter_available (demux->adapter));
2342 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2348 case GST_EVENT_CAPS:{
2349 GstCaps *caps = NULL;
2351 gst_event_parse_caps (event, &caps);
2352 gst_qtdemux_setcaps (demux, caps);
2354 gst_event_unref (event);
2357 case GST_EVENT_PROTECTION:
2359 const gchar *system_id = NULL;
2361 gst_event_parse_protection (event, &system_id, NULL, NULL);
2362 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2364 gst_qtdemux_append_protection_system_id (demux, system_id);
2365 /* save the event for later, for source pads that have not been created */
2366 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2367 /* send it to all pads that already exist */
2368 gst_qtdemux_push_event (demux, event);
2372 case GST_EVENT_STREAM_START:
2375 gst_event_unref (event);
2377 /* Drain all the buffers */
2378 gst_qtdemux_process_adapter (demux, TRUE);
2379 gst_qtdemux_reset (demux, FALSE);
2380 /* We expect new moov box after new stream-start event */
2381 if (demux->exposed) {
2382 gst_qtdemux_stream_concat (demux,
2383 demux->old_streams, demux->active_streams);
2392 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2399 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2402 GstQTDemux *demux = GST_QTDEMUX (parent);
2403 gboolean res = FALSE;
2405 switch (GST_QUERY_TYPE (query)) {
2406 case GST_QUERY_BITRATE:
2408 GstClockTime duration;
2410 /* populate demux->upstream_size if not done yet */
2411 gst_qtdemux_check_seekability (demux);
2413 if (demux->upstream_size != -1
2414 && gst_qtdemux_get_duration (demux, &duration)) {
2416 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2419 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2420 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2421 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2423 /* TODO: better results based on ranges/index tables */
2424 gst_query_set_bitrate (query, bitrate);
2430 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2440 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2442 GstQTDemux *demux = GST_QTDEMUX (element);
2444 GST_OBJECT_LOCK (demux);
2445 if (demux->element_index)
2446 gst_object_unref (demux->element_index);
2448 demux->element_index = gst_object_ref (index);
2450 demux->element_index = NULL;
2452 GST_OBJECT_UNLOCK (demux);
2453 /* object lock might be taken again */
2455 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2456 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2457 demux->element_index, demux->index_id);
2461 gst_qtdemux_get_index (GstElement * element)
2463 GstIndex *result = NULL;
2464 GstQTDemux *demux = GST_QTDEMUX (element);
2466 GST_OBJECT_LOCK (demux);
2467 if (demux->element_index)
2468 result = gst_object_ref (demux->element_index);
2469 GST_OBJECT_UNLOCK (demux);
2471 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2478 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2480 g_free ((gpointer) stream->stco.data);
2481 stream->stco.data = NULL;
2482 g_free ((gpointer) stream->stsz.data);
2483 stream->stsz.data = NULL;
2484 g_free ((gpointer) stream->stsc.data);
2485 stream->stsc.data = NULL;
2486 g_free ((gpointer) stream->stts.data);
2487 stream->stts.data = NULL;
2488 g_free ((gpointer) stream->stss.data);
2489 stream->stss.data = NULL;
2490 g_free ((gpointer) stream->stps.data);
2491 stream->stps.data = NULL;
2492 g_free ((gpointer) stream->ctts.data);
2493 stream->ctts.data = NULL;
2497 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2499 g_free (stream->segments);
2500 stream->segments = NULL;
2501 stream->segment_index = -1;
2502 stream->accumulated_base = 0;
2506 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2508 g_free (stream->samples);
2509 stream->samples = NULL;
2510 gst_qtdemux_stbl_free (stream);
2513 g_free (stream->ra_entries);
2514 stream->ra_entries = NULL;
2515 stream->n_ra_entries = 0;
2517 stream->sample_index = -1;
2518 stream->stbl_index = -1;
2519 stream->n_samples = 0;
2520 stream->time_position = 0;
2522 stream->n_samples_moof = 0;
2523 stream->duration_moof = 0;
2524 stream->duration_last_moof = 0;
2528 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2531 if (stream->allocator)
2532 gst_object_unref (stream->allocator);
2533 while (stream->buffers) {
2534 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2535 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2537 for (i = 0; i < stream->stsd_entries_length; i++) {
2538 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2539 if (entry->rgb8_palette) {
2540 gst_memory_unref (entry->rgb8_palette);
2541 entry->rgb8_palette = NULL;
2543 entry->sparse = FALSE;
2546 if (stream->stream_tags)
2547 gst_tag_list_unref (stream->stream_tags);
2549 stream->stream_tags = gst_tag_list_new_empty ();
2550 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2551 g_free (stream->redirect_uri);
2552 stream->redirect_uri = NULL;
2553 stream->sent_eos = FALSE;
2554 stream->protected = FALSE;
2555 if (stream->protection_scheme_info) {
2556 if (stream->protection_scheme_type == FOURCC_cenc
2557 || stream->protection_scheme_type == FOURCC_cbcs) {
2558 QtDemuxCencSampleSetInfo *info =
2559 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2560 if (info->default_properties)
2561 gst_structure_free (info->default_properties);
2562 if (info->crypto_info)
2563 g_ptr_array_free (info->crypto_info, TRUE);
2565 if (stream->protection_scheme_type == FOURCC_aavd) {
2566 QtDemuxAavdEncryptionInfo *info =
2567 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
2568 if (info->default_properties)
2569 gst_structure_free (info->default_properties);
2571 g_free (stream->protection_scheme_info);
2572 stream->protection_scheme_info = NULL;
2574 stream->protection_scheme_type = 0;
2575 stream->protection_scheme_version = 0;
2576 g_queue_foreach (&stream->protection_scheme_event_queue,
2577 (GFunc) gst_event_unref, NULL);
2578 g_queue_clear (&stream->protection_scheme_event_queue);
2579 gst_qtdemux_stream_flush_segments_data (stream);
2580 gst_qtdemux_stream_flush_samples_data (stream);
2584 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2587 gst_qtdemux_stream_clear (stream);
2588 for (i = 0; i < stream->stsd_entries_length; i++) {
2589 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2591 gst_caps_unref (entry->caps);
2595 g_free (stream->stsd_entries);
2596 stream->stsd_entries = NULL;
2597 stream->stsd_entries_length = 0;
2600 static QtDemuxStream *
2601 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2603 g_atomic_int_add (&stream->ref_count, 1);
2609 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2611 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2612 gst_qtdemux_stream_reset (stream);
2613 gst_tag_list_unref (stream->stream_tags);
2615 GstQTDemux *demux = stream->demux;
2616 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2617 GST_OBJECT_LOCK (demux);
2618 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2619 GST_OBJECT_UNLOCK (demux);
2621 g_free (stream->stream_id);
2626 static GstStateChangeReturn
2627 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2629 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2630 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2632 switch (transition) {
2633 case GST_STATE_CHANGE_READY_TO_PAUSED:
2634 gst_qtdemux_reset (qtdemux, TRUE);
2640 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2642 switch (transition) {
2643 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2644 gst_qtdemux_reset (qtdemux, TRUE);
2655 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2657 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2659 g_return_if_fail (GST_IS_CONTEXT (context));
2661 if (gst_context_has_context_type (context,
2662 "drm-preferred-decryption-system-id")) {
2663 const GstStructure *s;
2665 s = gst_context_get_structure (context);
2666 g_free (qtdemux->preferred_protection_system_id);
2667 qtdemux->preferred_protection_system_id =
2668 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2669 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2670 qtdemux->preferred_protection_system_id);
2673 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2677 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2679 /* counts as header data */
2680 qtdemux->header_size += length;
2682 /* only consider at least a sufficiently complete ftyp atom */
2685 guint32 minor_version;
2688 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2689 GST_DEBUG_OBJECT (qtdemux, "ftyp major brand: %" GST_FOURCC_FORMAT,
2690 GST_FOURCC_ARGS (qtdemux->major_brand));
2691 minor_version = QT_UINT32 (buffer + 12);
2692 GST_DEBUG_OBJECT (qtdemux, "ftyp minor version: %u", minor_version);
2693 if (qtdemux->comp_brands)
2694 gst_buffer_unref (qtdemux->comp_brands);
2695 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2696 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2699 length = length - 16;
2700 while (length > 0) {
2701 GST_DEBUG_OBJECT (qtdemux, "ftyp compatible brand: %" GST_FOURCC_FORMAT,
2702 GST_FOURCC_ARGS (QT_FOURCC (p)));
2710 qtdemux_parse_styp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2712 /* only consider at least a sufficiently complete styp atom */
2715 guint32 major_brand;
2716 guint32 minor_version;
2719 major_brand = QT_FOURCC (buffer + 8);
2720 GST_DEBUG_OBJECT (qtdemux, "styp major brand: %" GST_FOURCC_FORMAT,
2721 GST_FOURCC_ARGS (major_brand));
2722 minor_version = QT_UINT32 (buffer + 12);
2723 GST_DEBUG_OBJECT (qtdemux, "styp minor version: %u", minor_version);
2724 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2725 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2728 length = length - 16;
2729 while (length > 0) {
2730 GST_DEBUG_OBJECT (qtdemux, "styp compatible brand: %" GST_FOURCC_FORMAT,
2731 GST_FOURCC_ARGS (QT_FOURCC (p)));
2739 qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
2740 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
2741 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
2742 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
2743 const guint8 * constant_iv)
2745 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2746 gst_buffer_fill (kid_buf, 0, kid, 16);
2747 if (info->default_properties)
2748 gst_structure_free (info->default_properties);
2749 info->default_properties =
2750 gst_structure_new ("application/x-cenc",
2751 "iv_size", G_TYPE_UINT, iv_size,
2752 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2753 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2754 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
2755 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
2756 gst_buffer_unref (kid_buf);
2757 if (protection_scheme_type == FOURCC_cbcs) {
2758 if (crypt_byte_block != 0 || skip_byte_block != 0) {
2759 gst_structure_set (info->default_properties, "crypt_byte_block",
2760 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
2761 skip_byte_block, NULL);
2763 if (constant_iv != NULL) {
2764 GstBuffer *constant_iv_buf =
2765 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
2766 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
2767 gst_structure_set (info->default_properties, "constant_iv_size",
2768 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
2770 gst_buffer_unref (constant_iv_buf);
2772 gst_structure_set (info->default_properties, "cipher-mode",
2773 G_TYPE_STRING, "cbcs", NULL);
2775 gst_structure_set (info->default_properties, "cipher-mode",
2776 G_TYPE_STRING, "cenc", NULL);
2781 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
2782 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
2784 guint32 algorithm_id = 0;
2786 gboolean is_encrypted = TRUE;
2789 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2790 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2795 if (algorithm_id == 0) {
2796 is_encrypted = FALSE;
2797 } else if (algorithm_id == 1) {
2798 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2799 } else if (algorithm_id == 2) {
2800 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2803 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2806 if (!gst_byte_reader_get_data (br, 16, &kid))
2809 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
2810 is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
2811 gst_structure_set (info->default_properties, "piff_algorithm_id",
2812 G_TYPE_UINT, algorithm_id, NULL);
2818 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
2826 QtDemuxStream *stream;
2827 GstStructure *structure;
2828 QtDemuxCencSampleSetInfo *ss_info = NULL;
2829 const gchar *system_id;
2830 gboolean uses_sub_sample_encryption = FALSE;
2831 guint32 sample_count;
2833 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
2836 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
2838 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
2839 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2840 GST_WARNING_OBJECT (qtdemux,
2841 "Attempting PIFF box parsing on an unencrypted stream.");
2845 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2846 G_TYPE_STRING, &system_id, NULL);
2847 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
2849 stream->protected = TRUE;
2850 stream->protection_scheme_type = FOURCC_cenc;
2852 if (!stream->protection_scheme_info)
2853 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
2855 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2856 if (!ss_info->default_properties) {
2857 ss_info->default_properties =
2858 gst_structure_new ("application/x-cenc",
2859 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2864 if (ss_info->crypto_info) {
2865 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2866 g_ptr_array_free (ss_info->crypto_info, TRUE);
2867 ss_info->crypto_info = NULL;
2871 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2873 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2874 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2878 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2879 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2883 if ((flags & 0x000001)) {
2884 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
2887 } else if ((flags & 0x000002)) {
2888 uses_sub_sample_encryption = TRUE;
2891 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2893 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2897 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2898 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2902 ss_info->crypto_info =
2903 g_ptr_array_new_full (sample_count,
2904 (GDestroyNotify) qtdemux_gst_structure_free);
2906 for (i = 0; i < sample_count; ++i) {
2907 GstStructure *properties;
2911 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
2912 if (properties == NULL) {
2913 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2914 qtdemux->cenc_aux_sample_count = i;
2918 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2919 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2920 gst_structure_free (properties);
2921 qtdemux->cenc_aux_sample_count = i;
2924 buf = gst_buffer_new_wrapped (data, iv_size);
2925 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2926 gst_buffer_unref (buf);
2928 if (uses_sub_sample_encryption) {
2929 guint16 n_subsamples;
2930 const GValue *kid_buf_value;
2932 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2933 || n_subsamples == 0) {
2934 GST_ERROR_OBJECT (qtdemux,
2935 "failed to get subsample count for sample %u", i);
2936 gst_structure_free (properties);
2937 qtdemux->cenc_aux_sample_count = i;
2940 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
2941 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
2942 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
2944 gst_structure_free (properties);
2945 qtdemux->cenc_aux_sample_count = i;
2948 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
2951 gst_structure_get_value (ss_info->default_properties, "kid");
2953 gst_structure_set (properties,
2954 "subsample_count", G_TYPE_UINT, n_subsamples,
2955 "subsamples", GST_TYPE_BUFFER, buf, NULL);
2956 gst_structure_set_value (properties, "kid", kid_buf_value);
2957 gst_buffer_unref (buf);
2959 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
2962 g_ptr_array_add (ss_info->crypto_info, properties);
2965 qtdemux->cenc_aux_sample_count = sample_count;
2969 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2971 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2972 0x97, 0xA9, 0x42, 0xE8,
2973 0x9C, 0x71, 0x99, 0x94,
2974 0x91, 0xE3, 0xAF, 0xAC
2976 static const guint8 playready_uuid[] = {
2977 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2978 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2981 static const guint8 piff_sample_encryption_uuid[] = {
2982 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
2983 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
2988 /* counts as header data */
2989 qtdemux->header_size += length;
2991 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2993 if (length <= offset + 16) {
2994 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2998 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3000 GstTagList *taglist;
3002 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3003 length - offset - 16, NULL);
3004 taglist = gst_tag_list_from_xmp_buffer (buf);
3005 gst_buffer_unref (buf);
3007 /* make sure we have a usable taglist */
3008 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3010 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3012 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3014 const gunichar2 *s_utf16;
3017 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3018 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3019 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3020 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3024 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3025 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3027 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3028 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3030 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3031 GST_READ_UINT32_LE (buffer + offset),
3032 GST_READ_UINT32_LE (buffer + offset + 4),
3033 GST_READ_UINT32_LE (buffer + offset + 8),
3034 GST_READ_UINT32_LE (buffer + offset + 12));
3039 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3041 GstSidxParser sidx_parser;
3042 GstIsoffParserResult res;
3045 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3048 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3050 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3051 if (res == GST_ISOFF_QT_PARSER_DONE) {
3052 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3054 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3057 /* caller verifies at least 8 bytes in buf */
3059 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3060 guint64 * plength, guint32 * pfourcc)
3065 length = QT_UINT32 (data);
3066 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3067 fourcc = QT_FOURCC (data + 4);
3068 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3071 length = G_MAXUINT64;
3072 } else if (length == 1 && size >= 16) {
3073 /* this means we have an extended size, which is the 64 bit value of
3074 * the next 8 bytes */
3075 length = QT_UINT64 (data + 8);
3076 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3086 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3088 guint32 version = 0;
3089 GstClockTime duration = 0;
3091 if (!gst_byte_reader_get_uint32_be (br, &version))
3096 if (!gst_byte_reader_get_uint64_be (br, &duration))
3101 if (!gst_byte_reader_get_uint32_be (br, &dur))
3106 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3107 qtdemux->duration = duration;
3113 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3119 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3120 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3122 if (!stream->parsed_trex && qtdemux->moov_node) {
3124 GstByteReader trex_data;
3126 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3128 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3131 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3133 /* skip version/flags */
3134 if (!gst_byte_reader_skip (&trex_data, 4))
3136 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3138 if (id != stream->track_id)
3140 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3142 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3144 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3146 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3149 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3150 "duration %d, size %d, flags 0x%x", stream->track_id,
3153 stream->parsed_trex = TRUE;
3154 stream->def_sample_description_index = sdi;
3155 stream->def_sample_duration = dur;
3156 stream->def_sample_size = size;
3157 stream->def_sample_flags = flags;
3160 /* iterate all siblings */
3161 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3167 *ds_duration = stream->def_sample_duration;
3168 *ds_size = stream->def_sample_size;
3169 *ds_flags = stream->def_sample_flags;
3171 /* even then, above values are better than random ... */
3172 if (G_UNLIKELY (!stream->parsed_trex)) {
3173 GST_WARNING_OBJECT (qtdemux,
3174 "failed to find fragment defaults for stream %d", stream->track_id);
3181 /* This method should be called whenever a more accurate duration might
3182 * have been found. It will update all relevant variables if/where needed
3185 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3189 GstClockTime prevdur;
3191 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3193 if (movdur > qtdemux->duration) {
3194 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3195 GST_DEBUG_OBJECT (qtdemux,
3196 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3197 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3198 qtdemux->duration = movdur;
3199 GST_DEBUG_OBJECT (qtdemux,
3200 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3201 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3202 GST_TIME_ARGS (qtdemux->segment.stop));
3203 if (qtdemux->segment.duration == prevdur) {
3204 /* If the current segment has duration/stop identical to previous duration
3205 * update them also (because they were set at that point in time with
3206 * the wrong duration */
3207 /* We convert the value *from* the timescale version to avoid rounding errors */
3208 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3209 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3210 qtdemux->segment.duration = fixeddur;
3211 qtdemux->segment.stop = fixeddur;
3215 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3216 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3218 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3219 if (movdur > stream->duration) {
3220 GST_DEBUG_OBJECT (qtdemux,
3221 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3222 GST_TIME_ARGS (duration));
3223 stream->duration = movdur;
3224 /* internal duration tracking state has been updated above, so */
3225 /* preserve an open-ended dummy segment rather than repeatedly updating
3226 * it and spamming downstream accordingly with segment events */
3227 /* also mangle the edit list end time when fragmented with a single edit
3228 * list that may only cover any non-fragmented data */
3229 if ((stream->dummy_segment ||
3230 (qtdemux->fragmented && stream->n_segments == 1)) &&
3231 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3232 /* Update all dummy values to new duration */
3233 stream->segments[0].stop_time = duration;
3234 stream->segments[0].duration = duration;
3235 stream->segments[0].media_stop = duration;
3237 /* let downstream know we possibly have a new stop time */
3238 if (stream->segment_index != -1) {
3241 if (qtdemux->segment.rate >= 0) {
3242 pos = stream->segment.start;
3244 pos = stream->segment.stop;
3247 gst_qtdemux_stream_update_segment (qtdemux, stream,
3248 stream->segment_index, pos, NULL, NULL);
3256 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3257 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3258 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3259 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3262 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3264 gint32 data_offset = 0;
3266 guint32 flags = 0, first_flags = 0, samples_count = 0;
3269 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3270 QtDemuxSample *sample;
3271 gboolean ismv = FALSE;
3272 gint64 initial_offset;
3275 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3276 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3277 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3278 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3280 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3281 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3285 /* presence of stss or not can't really tell us much,
3286 * and flags and so on tend to be marginally reliable in these files */
3287 if (stream->subtype == FOURCC_soun) {
3288 GST_DEBUG_OBJECT (qtdemux,
3289 "sound track in fragmented file; marking all keyframes");
3290 stream->all_keyframe = TRUE;
3293 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3294 !gst_byte_reader_get_uint24_be (trun, &flags))
3297 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3300 if (flags & TR_DATA_OFFSET) {
3301 /* note this is really signed */
3302 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3304 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3305 /* default base offset = first byte of moof */
3306 if (*base_offset == -1) {
3307 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3308 *base_offset = moof_offset;
3310 *running_offset = *base_offset + data_offset;
3312 /* if no offset at all, that would mean data starts at moof start,
3313 * which is a bit wrong and is ismv crappy way, so compensate
3314 * assuming data is in mdat following moof */
3315 if (*base_offset == -1) {
3316 *base_offset = moof_offset + moof_length + 8;
3317 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3320 if (*running_offset == -1)
3321 *running_offset = *base_offset;
3324 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3326 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3327 data_offset, flags, samples_count);
3329 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3330 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3331 GST_DEBUG_OBJECT (qtdemux,
3332 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3333 flags ^= TR_FIRST_SAMPLE_FLAGS;
3335 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3337 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3341 /* FIXME ? spec says other bits should also be checked to determine
3342 * entry size (and prefix size for that matter) */
3344 dur_offset = size_offset = 0;
3345 if (flags & TR_SAMPLE_DURATION) {
3346 GST_LOG_OBJECT (qtdemux, "entry duration present");
3347 dur_offset = entry_size;
3350 if (flags & TR_SAMPLE_SIZE) {
3351 GST_LOG_OBJECT (qtdemux, "entry size present");
3352 size_offset = entry_size;
3355 if (flags & TR_SAMPLE_FLAGS) {
3356 GST_LOG_OBJECT (qtdemux, "entry flags present");
3357 flags_offset = entry_size;
3360 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3361 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3362 ct_offset = entry_size;
3366 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3368 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3370 if (stream->n_samples + samples_count >=
3371 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3374 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3375 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3376 (stream->n_samples + samples_count) *
3377 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3379 /* create a new array of samples if it's the first sample parsed */
3380 if (stream->n_samples == 0) {
3381 g_assert (stream->samples == NULL);
3382 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3383 /* or try to reallocate it with space enough to insert the new samples */
3385 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3386 stream->n_samples + samples_count);
3387 if (stream->samples == NULL)
3390 if (qtdemux->fragment_start != -1) {
3391 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3392 qtdemux->fragment_start = -1;
3394 if (stream->n_samples == 0) {
3395 if (decode_ts > 0) {
3396 timestamp = decode_ts;
3397 } else if (stream->pending_seek != NULL) {
3398 /* if we don't have a timestamp from a tfdt box, we'll use the one
3399 * from the mfra seek table */
3400 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3401 GST_TIME_ARGS (stream->pending_seek->ts));
3403 /* FIXME: this is not fully correct, the timestamp refers to the random
3404 * access sample refered to in the tfra entry, which may not necessarily
3405 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3406 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3411 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3412 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3413 GST_TIME_ARGS (gst_ts));
3415 /* subsequent fragments extend stream */
3417 stream->samples[stream->n_samples - 1].timestamp +
3418 stream->samples[stream->n_samples - 1].duration;
3420 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3421 * difference (1 sec.) between decode_ts and timestamp, prefer the
3423 if (has_tfdt && !qtdemux->upstream_format_is_time
3424 && ABSDIFF (decode_ts, timestamp) >
3425 MAX (stream->duration_last_moof / 2,
3426 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3427 GST_INFO_OBJECT (qtdemux,
3428 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3429 ") are significantly different (more than %" GST_TIME_FORMAT
3430 "), using decode_ts",
3431 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3432 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3433 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3434 MAX (stream->duration_last_moof / 2,
3435 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3436 timestamp = decode_ts;
3439 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3440 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3441 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3445 initial_offset = *running_offset;
3447 sample = stream->samples + stream->n_samples;
3448 for (i = 0; i < samples_count; i++) {
3449 guint32 dur, size, sflags;
3452 /* first read sample data */
3453 if (flags & TR_SAMPLE_DURATION) {
3454 dur = QT_UINT32 (data + dur_offset);
3456 dur = d_sample_duration;
3458 if (flags & TR_SAMPLE_SIZE) {
3459 size = QT_UINT32 (data + size_offset);
3461 size = d_sample_size;
3463 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3465 sflags = first_flags;
3467 sflags = d_sample_flags;
3469 } else if (flags & TR_SAMPLE_FLAGS) {
3470 sflags = QT_UINT32 (data + flags_offset);
3472 sflags = d_sample_flags;
3475 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3476 /* Read offsets as signed numbers regardless of trun version as very
3477 * high offsets are unlikely and there are files out there that use
3478 * version=0 truns with negative offsets */
3479 ct = QT_UINT32 (data + ct_offset);
3481 /* FIXME: Set offset to 0 for "no decode samples". This needs
3482 * to be handled in a codec specific manner ideally. */
3483 if (ct == G_MININT32)
3490 /* fill the sample information */
3491 sample->offset = *running_offset;
3492 sample->pts_offset = ct;
3493 sample->size = size;
3494 sample->timestamp = timestamp;
3495 sample->duration = dur;
3496 /* sample-is-difference-sample */
3497 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3498 * now idea how it relates to bitfield other than massive LE/BE confusion */
3499 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3500 *running_offset += size;
3502 stream->duration_moof += dur;
3509 /* Shift PTS/DTS to allow for negative composition offsets while keeping
3510 * A/V sync in place. This is similar to the code handling ctts/cslg in the
3511 * non-fragmented case.
3514 stream->cslg_shift = -min_ct;
3516 stream->cslg_shift = 0;
3518 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
3519 stream->cslg_shift);
3521 /* Update total duration if needed */
3522 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3524 /* Pre-emptively figure out size of mdat based on trun information.
3525 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3526 * size, else we will still be able to use this when dealing with gap'ed
3528 qtdemux->mdatleft = *running_offset - initial_offset;
3529 qtdemux->mdatoffset = initial_offset;
3530 qtdemux->mdatsize = qtdemux->mdatleft;
3532 stream->n_samples += samples_count;
3533 stream->n_samples_moof += samples_count;
3535 if (stream->pending_seek != NULL)
3536 stream->pending_seek = NULL;
3542 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3547 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3553 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3554 "be larger than %uMB (broken file?)", stream->n_samples,
3555 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3560 /* find stream with @id */
3561 static inline QtDemuxStream *
3562 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3564 QtDemuxStream *stream;
3568 if (G_UNLIKELY (!id)) {
3569 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3573 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3574 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3575 if (stream->track_id == id)
3578 if (qtdemux->mss_mode) {
3579 /* mss should have only 1 stream anyway */
3580 return QTDEMUX_NTH_STREAM (qtdemux, 0);
3587 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3588 guint32 * fragment_number)
3590 if (!gst_byte_reader_skip (mfhd, 4))
3592 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3597 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3603 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3604 QtDemuxStream ** stream, guint32 * default_sample_duration,
3605 guint32 * default_sample_size, guint32 * default_sample_flags,
3606 gint64 * base_offset)
3609 guint32 track_id = 0;
3611 if (!gst_byte_reader_skip (tfhd, 1) ||
3612 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3615 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3618 *stream = qtdemux_find_stream (qtdemux, track_id);
3619 if (G_UNLIKELY (!*stream))
3620 goto unknown_stream;
3622 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3623 *base_offset = qtdemux->moof_offset;
3625 if (flags & TF_BASE_DATA_OFFSET)
3626 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3629 /* obtain stream defaults */
3630 qtdemux_parse_trex (qtdemux, *stream,
3631 default_sample_duration, default_sample_size, default_sample_flags);
3633 (*stream)->stsd_sample_description_id =
3634 (*stream)->def_sample_description_index - 1;
3636 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
3637 guint32 sample_description_index;
3638 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3640 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3643 if (qtdemux->mss_mode) {
3644 /* mss has no stsd entry */
3645 (*stream)->stsd_sample_description_id = 0;
3648 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3649 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3652 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3653 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3656 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3657 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3664 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3669 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
3675 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3676 guint64 * decode_time)
3678 guint32 version = 0;
3680 if (!gst_byte_reader_get_uint32_be (br, &version))
3685 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3688 guint32 dec_time = 0;
3689 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3691 *decode_time = dec_time;
3694 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3701 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3706 /* Returns a pointer to a GstStructure containing the properties of
3707 * the stream sample identified by @sample_index. The caller must unref
3708 * the returned object after use. Returns NULL if unsuccessful. */
3709 static GstStructure *
3710 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3711 QtDemuxStream * stream, guint sample_index)
3713 QtDemuxCencSampleSetInfo *info = NULL;
3715 g_return_val_if_fail (stream != NULL, NULL);
3716 g_return_val_if_fail (stream->protected, NULL);
3717 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3719 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3721 /* Currently, cenc properties for groups of samples are not supported, so
3722 * simply return a copy of the default sample properties */
3723 return gst_structure_copy (info->default_properties);
3726 /* Parses the sizes of sample auxiliary information contained within a stream,
3727 * as given in a saiz box. Returns array of sample_count guint8 size values,
3728 * or NULL on failure */
3730 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3731 GstByteReader * br, guint32 * sample_count)
3735 guint8 default_info_size;
3737 g_return_val_if_fail (qtdemux != NULL, NULL);
3738 g_return_val_if_fail (stream != NULL, NULL);
3739 g_return_val_if_fail (br != NULL, NULL);
3740 g_return_val_if_fail (sample_count != NULL, NULL);
3742 if (!gst_byte_reader_get_uint32_be (br, &flags))
3746 /* aux_info_type and aux_info_type_parameter are ignored */
3747 if (!gst_byte_reader_skip (br, 8))
3751 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3753 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3755 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3757 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3760 if (default_info_size == 0) {
3761 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3765 info_sizes = g_new (guint8, *sample_count);
3766 memset (info_sizes, default_info_size, *sample_count);
3772 /* Parses the offset of sample auxiliary information contained within a stream,
3773 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3775 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3776 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3781 guint32 aux_info_type = 0;
3782 guint32 aux_info_type_parameter = 0;
3783 guint32 entry_count;
3786 const guint8 *aux_info_type_data = NULL;
3788 g_return_val_if_fail (qtdemux != NULL, FALSE);
3789 g_return_val_if_fail (stream != NULL, FALSE);
3790 g_return_val_if_fail (br != NULL, FALSE);
3791 g_return_val_if_fail (offset != NULL, FALSE);
3793 if (!gst_byte_reader_get_uint8 (br, &version))
3796 if (!gst_byte_reader_get_uint24_be (br, &flags))
3801 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3803 aux_info_type = QT_FOURCC (aux_info_type_data);
3805 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3807 } else if (stream->protected) {
3808 aux_info_type = stream->protection_scheme_type;
3810 aux_info_type = CUR_STREAM (stream)->fourcc;
3814 *info_type = aux_info_type;
3815 if (info_type_parameter)
3816 *info_type_parameter = aux_info_type_parameter;
3818 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3819 "aux_info_type_parameter: %#06x",
3820 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3822 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3825 if (entry_count != 1) {
3826 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3831 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3833 *offset = (guint64) off_32;
3835 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3840 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3845 qtdemux_gst_structure_free (GstStructure * gststructure)
3848 gst_structure_free (gststructure);
3852 /* Parses auxiliary information relating to samples protected using
3853 * Common Encryption (cenc); the format of this information
3854 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
3857 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3858 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3860 QtDemuxCencSampleSetInfo *ss_info = NULL;
3863 GPtrArray *old_crypto_info = NULL;
3864 guint old_entries = 0;
3866 g_return_val_if_fail (qtdemux != NULL, FALSE);
3867 g_return_val_if_fail (stream != NULL, FALSE);
3868 g_return_val_if_fail (br != NULL, FALSE);
3869 g_return_val_if_fail (stream->protected, FALSE);
3870 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3872 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3874 if (ss_info->crypto_info) {
3875 old_crypto_info = ss_info->crypto_info;
3876 /* Count number of non-null entries remaining at the tail end */
3877 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3878 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3884 ss_info->crypto_info =
3885 g_ptr_array_new_full (sample_count + old_entries,
3886 (GDestroyNotify) qtdemux_gst_structure_free);
3888 /* We preserve old entries because we parse the next moof in advance
3889 * of consuming all samples from the previous moof, and otherwise
3890 * we'd discard the corresponding crypto info for the samples
3891 * from the previous fragment. */
3893 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3895 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3896 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3898 g_ptr_array_index (old_crypto_info, i) = NULL;
3902 if (old_crypto_info) {
3903 /* Everything now belongs to the new array */
3904 g_ptr_array_free (old_crypto_info, TRUE);
3907 for (i = 0; i < sample_count; ++i) {
3908 GstStructure *properties;
3909 guint16 n_subsamples = 0;
3913 gboolean could_read_iv;
3915 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3916 if (properties == NULL) {
3917 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3920 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3921 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3922 gst_structure_free (properties);
3926 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
3927 if (could_read_iv) {
3928 buf = gst_buffer_new_wrapped (data, iv_size);
3929 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3930 gst_buffer_unref (buf);
3931 } else if (stream->protection_scheme_type == FOURCC_cbcs) {
3932 const GValue *constant_iv_size_value =
3933 gst_structure_get_value (properties, "constant_iv_size");
3934 const GValue *constant_iv_value =
3935 gst_structure_get_value (properties, "iv");
3936 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
3937 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
3938 gst_structure_free (properties);
3941 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
3942 gst_structure_remove_field (properties, "constant_iv_size");
3943 } else if (stream->protection_scheme_type == FOURCC_cenc) {
3944 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3945 gst_structure_free (properties);
3948 size = info_sizes[i];
3949 if (size > iv_size) {
3950 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3951 || !(n_subsamples > 0)) {
3952 gst_structure_free (properties);
3953 GST_ERROR_OBJECT (qtdemux,
3954 "failed to get subsample count for sample %u", i);
3957 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3958 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3959 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3961 gst_structure_free (properties);
3964 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3966 gst_structure_free (properties);
3969 gst_structure_set (properties,
3970 "subsample_count", G_TYPE_UINT, n_subsamples,
3971 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3972 gst_buffer_unref (buf);
3974 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3976 g_ptr_array_add (ss_info->crypto_info, properties);
3981 /* Converts a UUID in raw byte form to a string representation, as defined in
3982 * RFC 4122. The caller takes ownership of the returned string and is
3983 * responsible for freeing it after use. */
3985 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
3987 const guint8 *uuid = (const guint8 *) uuid_bytes;
3989 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3990 "%02x%02x-%02x%02x%02x%02x%02x%02x",
3991 uuid[0], uuid[1], uuid[2], uuid[3],
3992 uuid[4], uuid[5], uuid[6], uuid[7],
3993 uuid[8], uuid[9], uuid[10], uuid[11],
3994 uuid[12], uuid[13], uuid[14], uuid[15]);
3997 /* Parses a Protection System Specific Header box (pssh), as defined in the
3998 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
3999 * information needed by a specific content protection system in order to
4000 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4003 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4005 gchar *sysid_string;
4006 guint32 pssh_size = QT_UINT32 (node->data);
4007 GstBuffer *pssh = NULL;
4008 GstEvent *event = NULL;
4009 guint32 parent_box_type;
4012 if (G_UNLIKELY (pssh_size < 32U)) {
4013 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4018 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4020 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4022 pssh = gst_buffer_new_memdup (node->data, pssh_size);
4023 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4024 gst_buffer_get_size (pssh));
4026 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4028 /* Push an event containing the pssh box onto the queues of all streams. */
4029 event = gst_event_new_protection (sysid_string, pssh,
4030 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4031 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4032 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4033 GST_TRACE_OBJECT (qtdemux,
4034 "adding protection event for stream %s and system %s",
4035 stream->stream_id, sysid_string);
4036 g_queue_push_tail (&stream->protection_scheme_event_queue,
4037 gst_event_ref (event));
4039 g_free (sysid_string);
4040 gst_event_unref (event);
4041 gst_buffer_unref (pssh);
4046 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4047 guint64 moof_offset, QtDemuxStream * stream)
4049 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4051 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4052 GNode *saiz_node, *saio_node, *pssh_node;
4053 GstByteReader saiz_data, saio_data;
4054 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4055 gint64 base_offset, running_offset;
4057 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4059 /* NOTE @stream ignored */
4061 moof_node = g_node_new ((guint8 *) buffer);
4062 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4063 qtdemux_node_dump (qtdemux, moof_node);
4065 /* Get fragment number from mfhd and check it's valid */
4067 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4068 if (mfhd_node == NULL)
4070 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4072 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4074 /* unknown base_offset to start with */
4075 base_offset = running_offset = -1;
4076 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4078 guint64 decode_time = 0;
4080 /* Fragment Header node */
4082 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4086 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4087 &ds_size, &ds_flags, &base_offset))
4090 /* The following code assumes at most a single set of sample auxiliary
4091 * data in the fragment (consisting of a saiz box and a corresponding saio
4092 * box); in theory, however, there could be multiple sets of sample
4093 * auxiliary data in a fragment. */
4095 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4098 guint32 info_type = 0;
4100 guint32 info_type_parameter = 0;
4102 g_free (qtdemux->cenc_aux_info_sizes);
4104 qtdemux->cenc_aux_info_sizes =
4105 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4106 &qtdemux->cenc_aux_sample_count);
4107 if (qtdemux->cenc_aux_info_sizes == NULL) {
4108 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4112 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4115 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4116 g_free (qtdemux->cenc_aux_info_sizes);
4117 qtdemux->cenc_aux_info_sizes = NULL;
4121 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4122 &info_type, &info_type_parameter, &offset))) {
4123 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4124 g_free (qtdemux->cenc_aux_info_sizes);
4125 qtdemux->cenc_aux_info_sizes = NULL;
4128 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4129 offset += (guint64) (base_offset - qtdemux->moof_offset);
4130 if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs)
4131 && info_type_parameter == 0U) {
4133 if (offset > length) {
4134 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4135 qtdemux->cenc_aux_info_offset = offset;
4137 gst_byte_reader_init (&br, buffer + offset, length - offset);
4138 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4139 qtdemux->cenc_aux_info_sizes,
4140 qtdemux->cenc_aux_sample_count)) {
4141 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4142 g_free (qtdemux->cenc_aux_info_sizes);
4143 qtdemux->cenc_aux_info_sizes = NULL;
4151 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4154 /* We'll use decode_time to interpolate timestamps
4155 * in case the input timestamps are missing */
4156 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4158 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4159 " (%" GST_TIME_FORMAT ")", decode_time,
4160 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4161 decode_time) : GST_CLOCK_TIME_NONE));
4163 /* Discard the fragment buffer timestamp info to avoid using it.
4164 * Rely on tfdt instead as it is more accurate than the timestamp
4165 * that is fetched from a manifest/playlist and is usually
4167 qtdemux->fragment_start = -1;
4170 if (G_UNLIKELY (!stream)) {
4171 /* we lost track of offset, we'll need to regain it,
4172 * but can delay complaining until later or avoid doing so altogether */
4176 if (G_UNLIKELY (base_offset < -1))
4179 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4181 if (!qtdemux->pullbased) {
4182 /* Sample tables can grow enough to be problematic if the system memory
4183 * is very low (e.g. embedded devices) and the videos very long
4184 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4185 * Fortunately, we can easily discard them for each new fragment when
4186 * we know qtdemux will not receive seeks outside of the current fragment.
4187 * adaptivedemux honors this assumption.
4188 * This optimization is also useful for applications that use qtdemux as
4189 * a push-based simple demuxer, like Media Source Extensions. */
4190 gst_qtdemux_stream_flush_samples_data (stream);
4193 /* initialise moof sample data */
4194 stream->n_samples_moof = 0;
4195 stream->duration_last_moof = stream->duration_moof;
4196 stream->duration_moof = 0;
4198 /* Track Run node */
4200 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4203 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4204 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4205 &running_offset, decode_time, (tfdt_node != NULL));
4206 /* iterate all siblings */
4207 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4209 /* don't use tfdt for subsequent trun as it only refers to the first */
4213 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4215 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4216 guint32 box_length = QT_UINT32 (uuid_buffer);
4218 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4221 /* if no new base_offset provided for next traf,
4222 * base is end of current traf */
4223 base_offset = running_offset;
4224 running_offset = -1;
4226 if (stream->n_samples_moof && stream->duration_moof)
4227 stream->new_caps = TRUE;
4230 /* iterate all siblings */
4231 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4234 /* parse any protection system info */
4235 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4237 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4238 qtdemux_parse_pssh (qtdemux, pssh_node);
4239 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4242 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4243 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4245 /* Unless the user has explicitly requested another seek, perform an
4246 * internal seek to the time specified in the tfdt.
4248 * This way if the user opens a file where the first tfdt is 1 hour
4249 * into the presentation, they will not have to wait 1 hour for run
4250 * time to catch up and actual playback to start. */
4253 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4254 "performing an internal seek to %" GST_TIME_FORMAT,
4255 GST_TIME_ARGS (min_dts));
4257 qtdemux->segment.start = min_dts;
4258 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4260 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4261 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4262 stream->time_position = min_dts;
4265 /* Before this code was run a segment was already sent when the moov was
4266 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4267 * be emitted after a moov, and we can emit a second segment anyway for
4268 * special cases like this. */
4269 qtdemux->need_segment = TRUE;
4272 qtdemux->first_moof_already_parsed = TRUE;
4274 g_node_destroy (moof_node);
4279 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4284 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4289 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4294 g_node_destroy (moof_node);
4295 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4296 (_("This file is corrupt and cannot be played.")), (NULL));
4302 /* might be used if some day we actually use mfra & co
4303 * for random access to fragments,
4304 * but that will require quite some modifications and much less relying
4305 * on a sample array */
4309 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4311 QtDemuxStream *stream;
4312 guint32 ver_flags, track_id, len, num_entries, i;
4313 guint value_size, traf_size, trun_size, sample_size;
4314 guint64 time = 0, moof_offset = 0;
4316 GstBuffer *buf = NULL;
4321 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4323 if (!gst_byte_reader_skip (&tfra, 8))
4326 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4329 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4330 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4331 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4334 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4336 stream = qtdemux_find_stream (qtdemux, track_id);
4338 goto unknown_trackid;
4340 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4341 sample_size = (len & 3) + 1;
4342 trun_size = ((len & 12) >> 2) + 1;
4343 traf_size = ((len & 48) >> 4) + 1;
4345 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4346 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4348 if (num_entries == 0)
4351 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4352 value_size + value_size + traf_size + trun_size + sample_size))
4355 g_free (stream->ra_entries);
4356 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4357 stream->n_ra_entries = num_entries;
4359 for (i = 0; i < num_entries; i++) {
4360 qt_atom_parser_get_offset (&tfra, value_size, &time);
4361 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4362 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4363 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4364 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4366 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4368 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4369 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4371 stream->ra_entries[i].ts = time;
4372 stream->ra_entries[i].moof_offset = moof_offset;
4374 /* don't want to go through the entire file and read all moofs at startup */
4376 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4377 if (ret != GST_FLOW_OK)
4379 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4380 moof_offset, stream);
4381 gst_buffer_unref (buf);
4385 check_update_duration (qtdemux, time);
4392 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4397 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4402 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4408 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4410 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4411 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4412 GstBuffer *mfro = NULL, *mfra = NULL;
4414 gboolean ret = FALSE;
4415 GNode *mfra_node, *tfra_node;
4416 guint64 mfra_offset = 0;
4417 guint32 fourcc, mfra_size;
4420 /* query upstream size in bytes */
4421 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4422 goto size_query_failed;
4424 /* mfro box should be at the very end of the file */
4425 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4426 if (flow != GST_FLOW_OK)
4429 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4431 fourcc = QT_FOURCC (mfro_map.data + 4);
4432 if (fourcc != FOURCC_mfro)
4435 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4436 if (mfro_map.size < 16)
4437 goto invalid_mfro_size;
4439 mfra_size = QT_UINT32 (mfro_map.data + 12);
4440 if (mfra_size >= len)
4441 goto invalid_mfra_size;
4443 mfra_offset = len - mfra_size;
4445 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4446 mfra_offset, mfra_size);
4448 /* now get and parse mfra box */
4449 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4450 if (flow != GST_FLOW_OK)
4453 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4455 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4456 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4458 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4461 qtdemux_parse_tfra (qtdemux, tfra_node);
4462 /* iterate all siblings */
4463 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4465 g_node_destroy (mfra_node);
4467 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4473 if (mfro_map.memory != NULL)
4474 gst_buffer_unmap (mfro, &mfro_map);
4475 gst_buffer_unref (mfro);
4478 if (mfra_map.memory != NULL)
4479 gst_buffer_unmap (mfra, &mfra_map);
4480 gst_buffer_unref (mfra);
4487 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4492 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4497 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4502 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4508 add_offset (guint64 offset, guint64 advance)
4510 /* Avoid 64-bit overflow by clamping */
4511 if (offset > G_MAXUINT64 - advance)
4513 return offset + advance;
4516 static GstFlowReturn
4517 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4521 GstBuffer *buf = NULL;
4522 GstFlowReturn ret = GST_FLOW_OK;
4523 guint64 cur_offset = qtdemux->offset;
4526 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4527 if (G_UNLIKELY (ret != GST_FLOW_OK))
4529 gst_buffer_map (buf, &map, GST_MAP_READ);
4530 if (G_LIKELY (map.size >= 8))
4531 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4532 gst_buffer_unmap (buf, &map);
4533 gst_buffer_unref (buf);
4535 /* maybe we already got most we needed, so only consider this eof */
4536 if (G_UNLIKELY (length == 0)) {
4537 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4538 (_("Invalid atom size.")),
4539 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4540 GST_FOURCC_ARGS (fourcc)));
4547 /* record for later parsing when needed */
4548 if (!qtdemux->moof_offset) {
4549 qtdemux->moof_offset = qtdemux->offset;
4551 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4554 qtdemux->offset += length; /* skip moof and keep going */
4556 if (qtdemux->got_moov) {
4557 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4569 GST_LOG_OBJECT (qtdemux,
4570 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4571 GST_FOURCC_ARGS (fourcc), cur_offset);
4572 qtdemux->offset = add_offset (qtdemux->offset, length);
4577 GstBuffer *moov = NULL;
4579 if (qtdemux->got_moov) {
4580 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4581 qtdemux->offset = add_offset (qtdemux->offset, length);
4585 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4586 if (ret != GST_FLOW_OK)
4588 gst_buffer_map (moov, &map, GST_MAP_READ);
4590 if (length != map.size) {
4591 /* Some files have a 'moov' atom at the end of the file which contains
4592 * a terminal 'free' atom where the body of the atom is missing.
4593 * Check for, and permit, this special case.
4595 if (map.size >= 8) {
4596 guint8 *final_data = map.data + (map.size - 8);
4597 guint32 final_length = QT_UINT32 (final_data);
4598 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4600 if (final_fourcc == FOURCC_free
4601 && map.size + final_length - 8 == length) {
4602 /* Ok, we've found that special case. Allocate a new buffer with
4603 * that free atom actually present. */
4604 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4605 gst_buffer_fill (newmoov, 0, map.data, map.size);
4606 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4607 gst_buffer_unmap (moov, &map);
4608 gst_buffer_unref (moov);
4610 gst_buffer_map (moov, &map, GST_MAP_READ);
4615 if (length != map.size) {
4616 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4617 (_("This file is incomplete and cannot be played.")),
4618 ("We got less than expected (received %" G_GSIZE_FORMAT
4619 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4620 (guint) length, cur_offset));
4621 gst_buffer_unmap (moov, &map);
4622 gst_buffer_unref (moov);
4623 ret = GST_FLOW_ERROR;
4626 qtdemux->offset += length;
4628 qtdemux_parse_moov (qtdemux, map.data, length);
4629 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4631 qtdemux_parse_tree (qtdemux);
4632 if (qtdemux->moov_node_compressed) {
4633 g_node_destroy (qtdemux->moov_node_compressed);
4634 g_free (qtdemux->moov_node->data);
4636 qtdemux->moov_node_compressed = NULL;
4637 g_node_destroy (qtdemux->moov_node);
4638 qtdemux->moov_node = NULL;
4639 gst_buffer_unmap (moov, &map);
4640 gst_buffer_unref (moov);
4641 qtdemux->got_moov = TRUE;
4647 GstBuffer *ftyp = NULL;
4649 /* extract major brand; might come in handy for ISO vs QT issues */
4650 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4651 if (ret != GST_FLOW_OK)
4653 qtdemux->offset += length;
4654 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4655 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4656 gst_buffer_unmap (ftyp, &map);
4657 gst_buffer_unref (ftyp);
4662 GstBuffer *styp = NULL;
4664 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &styp);
4665 if (ret != GST_FLOW_OK)
4667 qtdemux->offset += length;
4668 gst_buffer_map (styp, &map, GST_MAP_READ);
4669 qtdemux_parse_styp (qtdemux, map.data, map.size);
4670 gst_buffer_unmap (styp, &map);
4671 gst_buffer_unref (styp);
4676 GstBuffer *uuid = NULL;
4678 /* uuid are extension atoms */
4679 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4680 if (ret != GST_FLOW_OK)
4682 qtdemux->offset += length;
4683 gst_buffer_map (uuid, &map, GST_MAP_READ);
4684 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4685 gst_buffer_unmap (uuid, &map);
4686 gst_buffer_unref (uuid);
4691 GstBuffer *sidx = NULL;
4692 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4693 if (ret != GST_FLOW_OK)
4695 qtdemux->offset += length;
4696 gst_buffer_map (sidx, &map, GST_MAP_READ);
4697 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4698 gst_buffer_unmap (sidx, &map);
4699 gst_buffer_unref (sidx);
4704 GstBuffer *unknown = NULL;
4706 GST_LOG_OBJECT (qtdemux,
4707 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4708 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4710 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4711 if (ret != GST_FLOW_OK)
4713 gst_buffer_map (unknown, &map, GST_MAP_READ);
4714 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4715 gst_buffer_unmap (unknown, &map);
4716 gst_buffer_unref (unknown);
4717 qtdemux->offset += length;
4723 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4724 /* digested all data, show what we have */
4725 qtdemux_prepare_streams (qtdemux);
4726 QTDEMUX_EXPOSE_LOCK (qtdemux);
4727 ret = qtdemux_expose_streams (qtdemux);
4728 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
4730 qtdemux->state = QTDEMUX_STATE_MOVIE;
4731 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4738 /* Seeks to the previous keyframe of the indexed stream and
4739 * aligns other streams with respect to the keyframe timestamp
4740 * of indexed stream. Only called in case of Reverse Playback
4742 static GstFlowReturn
4743 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4745 guint32 seg_idx = 0, k_index = 0;
4746 guint32 ref_seg_idx, ref_k_index;
4747 GstClockTime k_pos = 0, last_stop = 0;
4748 QtDemuxSegment *seg = NULL;
4749 QtDemuxStream *ref_str = NULL;
4750 guint64 seg_media_start_mov; /* segment media start time in mov format */
4754 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4755 * and finally align all the other streams on that timestamp with their
4756 * respective keyframes */
4757 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4758 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4760 /* No candidate yet, take the first stream */
4766 /* So that stream has a segment, we prefer video streams */
4767 if (str->subtype == FOURCC_vide) {
4773 if (G_UNLIKELY (!ref_str)) {
4774 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4778 if (G_UNLIKELY (!ref_str->from_sample)) {
4779 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4783 /* So that stream has been playing from from_sample to to_sample. We will
4784 * get the timestamp of the previous sample and search for a keyframe before
4785 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4786 if (ref_str->subtype == FOURCC_vide) {
4787 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4788 ref_str->from_sample - 1, FALSE);
4790 if (ref_str->from_sample >= 10)
4791 k_index = ref_str->from_sample - 10;
4797 ref_str->samples[k_index].timestamp +
4798 ref_str->samples[k_index].pts_offset;
4800 /* get current segment for that stream */
4801 seg = &ref_str->segments[ref_str->segment_index];
4802 /* Use segment start in original timescale for comparisons */
4803 seg_media_start_mov = seg->trak_media_start;
4805 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4806 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4807 k_index, target_ts, seg_media_start_mov,
4808 GST_TIME_ARGS (seg->media_start));
4810 /* Crawl back through segments to find the one containing this I frame */
4811 while (target_ts < seg_media_start_mov) {
4812 GST_DEBUG_OBJECT (qtdemux,
4813 "keyframe position (sample %u) is out of segment %u " " target %"
4814 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4815 ref_str->segment_index, target_ts, seg_media_start_mov);
4817 if (G_UNLIKELY (!ref_str->segment_index)) {
4818 /* Reached first segment, let's consider it's EOS */
4821 ref_str->segment_index--;
4822 seg = &ref_str->segments[ref_str->segment_index];
4823 /* Use segment start in original timescale for comparisons */
4824 seg_media_start_mov = seg->trak_media_start;
4826 /* Calculate time position of the keyframe and where we should stop */
4828 QTSTREAMTIME_TO_GSTTIME (ref_str,
4829 target_ts - seg->trak_media_start) + seg->time;
4831 QTSTREAMTIME_TO_GSTTIME (ref_str,
4832 ref_str->samples[ref_str->from_sample].timestamp -
4833 seg->trak_media_start) + seg->time;
4835 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4836 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4837 k_index, GST_TIME_ARGS (k_pos));
4839 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4840 qtdemux->segment.position = last_stop;
4841 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4842 GST_TIME_ARGS (last_stop));
4844 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4845 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4849 ref_seg_idx = ref_str->segment_index;
4850 ref_k_index = k_index;
4852 /* Align them all on this */
4853 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4855 GstClockTime seg_time = 0;
4856 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4858 /* aligning reference stream again might lead to backing up to yet another
4859 * keyframe (due to timestamp rounding issues),
4860 * potentially putting more load on downstream; so let's try to avoid */
4861 if (str == ref_str) {
4862 seg_idx = ref_seg_idx;
4863 seg = &str->segments[seg_idx];
4864 k_index = ref_k_index;
4865 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
4866 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
4868 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4869 GST_DEBUG_OBJECT (qtdemux,
4870 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
4871 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
4873 /* get segment and time in the segment */
4874 seg = &str->segments[seg_idx];
4875 seg_time = k_pos - seg->time;
4877 /* get the media time in the segment.
4878 * No adjustment for empty "filler" segments */
4879 if (seg->media_start != GST_CLOCK_TIME_NONE)
4880 seg_time += seg->media_start;
4882 /* get the index of the sample with media time */
4883 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4884 GST_DEBUG_OBJECT (qtdemux,
4885 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
4886 GST_TIME_ARGS (seg_time), index);
4888 /* find previous keyframe */
4889 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
4892 /* Remember until where we want to go */
4893 str->to_sample = str->from_sample - 1;
4894 /* Define our time position */
4896 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4897 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4898 if (seg->media_start != GST_CLOCK_TIME_NONE)
4899 str->time_position -= seg->media_start;
4901 /* Now seek back in time */
4902 gst_qtdemux_move_stream (qtdemux, str, k_index);
4903 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
4904 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
4905 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4911 return GST_FLOW_EOS;
4915 * Gets the current qt segment start, stop and position for the
4916 * given time offset. This is used in update_segment()
4919 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
4920 QtDemuxStream * stream, GstClockTime offset,
4921 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
4923 GstClockTime seg_time;
4924 GstClockTime start, stop, time;
4925 QtDemuxSegment *segment;
4927 segment = &stream->segments[stream->segment_index];
4929 /* get time in this segment */
4930 seg_time = (offset - segment->time) * segment->rate;
4932 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4933 GST_TIME_ARGS (seg_time));
4935 if (G_UNLIKELY (seg_time > segment->duration)) {
4936 GST_LOG_OBJECT (stream->pad,
4937 "seg_time > segment->duration %" GST_TIME_FORMAT,
4938 GST_TIME_ARGS (segment->duration));
4939 seg_time = segment->duration;
4942 /* qtdemux->segment.stop is in outside-time-realm, whereas
4943 * segment->media_stop is in track-time-realm.
4945 * In order to compare the two, we need to bring segment.stop
4946 * into the track-time-realm
4948 * FIXME - does this comment still hold? Don't see any conversion here */
4950 stop = qtdemux->segment.stop;
4951 if (stop == GST_CLOCK_TIME_NONE)
4952 stop = qtdemux->segment.duration;
4953 if (stop == GST_CLOCK_TIME_NONE)
4954 stop = segment->media_stop;
4957 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4959 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4960 start = segment->time + seg_time;
4962 stop = start - seg_time + segment->duration;
4963 } else if (qtdemux->segment.rate >= 0) {
4964 start = MIN (segment->media_start + seg_time, stop);
4967 if (segment->media_start >= qtdemux->segment.start) {
4968 time = segment->time;
4970 time = segment->time + (qtdemux->segment.start - segment->media_start);
4973 start = MAX (segment->media_start, qtdemux->segment.start);
4974 stop = MIN (segment->media_start + seg_time, stop);
4983 * Updates the qt segment used for the stream and pushes a new segment event
4984 * downstream on this stream's pad.
4987 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4988 gint seg_idx, GstClockTime offset, GstClockTime * _start,
4989 GstClockTime * _stop)
4991 QtDemuxSegment *segment;
4992 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
4996 /* update the current segment */
4997 stream->segment_index = seg_idx;
4999 /* get the segment */
5000 segment = &stream->segments[seg_idx];
5002 if (G_UNLIKELY (offset < segment->time)) {
5003 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5004 GST_TIME_ARGS (segment->time));
5008 /* segment lies beyond total indicated duration */
5009 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5010 segment->time > qtdemux->segment.duration)) {
5011 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5012 " < segment->time %" GST_TIME_FORMAT,
5013 GST_TIME_ARGS (qtdemux->segment.duration),
5014 GST_TIME_ARGS (segment->time));
5018 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5019 &start, &stop, &time);
5021 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5022 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5023 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5025 /* combine global rate with that of the segment */
5026 rate = segment->rate * qtdemux->segment.rate;
5028 /* Copy flags from main segment */
5029 stream->segment.flags = qtdemux->segment.flags;
5031 /* update the segment values used for clipping */
5032 stream->segment.offset = qtdemux->segment.offset;
5033 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5034 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5035 stream->segment.rate = rate;
5036 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5037 stream->cslg_shift);
5039 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5040 stream->cslg_shift);
5042 stream->segment.stop = stop;
5043 stream->segment.time = time;
5044 stream->segment.position = stream->segment.start;
5046 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5049 /* now prepare and send the segment */
5051 event = gst_event_new_segment (&stream->segment);
5052 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5053 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5055 gst_pad_push_event (stream->pad, event);
5056 /* assume we can send more data now */
5057 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5058 /* clear to send tags on this pad now */
5059 gst_qtdemux_push_tags (qtdemux, stream);
5070 /* activate the given segment number @seg_idx of @stream at time @offset.
5071 * @offset is an absolute global position over all the segments.
5073 * This will push out a NEWSEGMENT event with the right values and
5074 * position the stream index to the first decodable sample before
5078 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5079 guint32 seg_idx, GstClockTime offset)
5081 QtDemuxSegment *segment;
5082 guint32 index, kf_index;
5083 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5085 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5086 seg_idx, GST_TIME_ARGS (offset));
5088 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5092 segment = &stream->segments[stream->segment_index];
5094 /* in the fragmented case, we pick a fragment that starts before our
5095 * desired position and rely on downstream to wait for a keyframe
5096 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5097 * tfra entries tells us which trun/sample the key unit is in, but we don't
5098 * make use of this additional information at the moment) */
5099 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5100 stream->to_sample = G_MAXUINT32;
5103 /* well, it will be taken care of below */
5104 qtdemux->fragmented_seek_pending = FALSE;
5105 /* FIXME ideally the do_fragmented_seek can be done right here,
5106 * rather than at loop level
5107 * (which might even allow handling edit lists in a fragmented file) */
5110 /* We don't need to look for a sample in push-based */
5111 if (!qtdemux->pullbased)
5114 /* and move to the keyframe before the indicated media time of the
5116 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5117 if (qtdemux->segment.rate >= 0) {
5118 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5119 stream->to_sample = G_MAXUINT32;
5120 GST_DEBUG_OBJECT (stream->pad,
5121 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5122 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5123 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5125 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5126 stream->to_sample = index;
5127 GST_DEBUG_OBJECT (stream->pad,
5128 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5129 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5130 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5133 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5134 "this is an empty segment");
5138 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5139 * encountered an error and printed a message so we return appropriately */
5143 /* we're at the right spot */
5144 if (index == stream->sample_index) {
5145 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5149 /* find keyframe of the target index */
5150 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5152 /* go back two frames to provide lead-in for non-raw audio decoders */
5153 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
5154 guint32 lead_in = 2;
5155 guint32 old_index = kf_index;
5156 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
5158 if (gst_structure_has_name (s, "audio/mpeg")) {
5160 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5161 && mpegversion == 1) {
5162 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5167 kf_index = MAX (kf_index, lead_in) - lead_in;
5168 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
5169 GST_DEBUG_OBJECT (stream->pad,
5170 "Moving backwards %u frames to ensure sufficient sound lead-in",
5171 old_index - kf_index);
5173 kf_index = old_index;
5177 /* if we move forwards, we don't have to go back to the previous
5178 * keyframe since we already sent that. We can also just jump to
5179 * the keyframe right before the target index if there is one. */
5180 if (index > stream->sample_index) {
5181 /* moving forwards check if we move past a keyframe */
5182 if (kf_index > stream->sample_index) {
5183 GST_DEBUG_OBJECT (stream->pad,
5184 "moving forwards to keyframe at %u "
5185 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5187 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5188 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5189 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5191 GST_DEBUG_OBJECT (stream->pad,
5192 "moving forwards, keyframe at %u "
5193 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5195 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5196 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5199 GST_DEBUG_OBJECT (stream->pad,
5200 "moving backwards to %sframe at %u "
5201 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5202 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5203 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5204 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5205 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5211 /* prepare to get the current sample of @stream, getting essential values.
5213 * This function will also prepare and send the segment when needed.
5215 * Return FALSE if the stream is EOS.
5220 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5221 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5222 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5223 gboolean * keyframe)
5225 QtDemuxSample *sample;
5226 GstClockTime time_position;
5229 g_return_val_if_fail (stream != NULL, FALSE);
5231 time_position = stream->time_position;
5232 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5235 seg_idx = stream->segment_index;
5236 if (G_UNLIKELY (seg_idx == -1)) {
5237 /* find segment corresponding to time_position if we are looking
5239 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5242 /* different segment, activate it, sample_index will be set. */
5243 if (G_UNLIKELY (stream->segment_index != seg_idx))
5244 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5246 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5247 segments[stream->segment_index]))) {
5248 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5250 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5251 " prepare empty sample");
5254 *pts = *dts = time_position;
5255 *duration = seg->duration - (time_position - seg->time);
5262 if (stream->sample_index == -1)
5263 stream->sample_index = 0;
5265 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5266 stream->sample_index, stream->n_samples);
5268 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5269 if (!qtdemux->fragmented)
5272 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5276 GST_OBJECT_LOCK (qtdemux);
5277 flow = qtdemux_add_fragmented_samples (qtdemux);
5278 GST_OBJECT_UNLOCK (qtdemux);
5280 if (flow != GST_FLOW_OK)
5283 while (stream->sample_index >= stream->n_samples);
5286 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5287 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5288 stream->sample_index);
5292 /* now get the info for the sample we're at */
5293 sample = &stream->samples[stream->sample_index];
5295 *dts = QTSAMPLE_DTS (stream, sample);
5296 *pts = QTSAMPLE_PTS (stream, sample);
5297 *offset = sample->offset;
5298 *size = sample->size;
5299 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5300 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5307 stream->time_position = GST_CLOCK_TIME_NONE;
5312 /* move to the next sample in @stream.
5314 * Moves to the next segment when needed.
5317 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5319 QtDemuxSample *sample;
5320 QtDemuxSegment *segment;
5322 /* get current segment */
5323 segment = &stream->segments[stream->segment_index];
5325 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5326 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5330 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5331 /* Mark the stream as EOS */
5332 GST_DEBUG_OBJECT (qtdemux,
5333 "reached max allowed sample %u, mark EOS", stream->to_sample);
5334 stream->time_position = GST_CLOCK_TIME_NONE;
5338 /* move to next sample */
5339 stream->sample_index++;
5340 stream->offset_in_sample = 0;
5342 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
5345 /* reached the last sample, we need the next segment */
5346 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5349 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5350 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5351 stream->sample_index);
5355 /* get next sample */
5356 sample = &stream->samples[stream->sample_index];
5358 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
5359 GST_TIME_FORMAT, GST_TIME_ARGS (QTSAMPLE_DTS (stream, sample)),
5360 GST_TIME_ARGS (segment->media_stop));
5362 /* see if we are past the segment */
5363 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5366 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5367 /* inside the segment, update time_position, looks very familiar to
5368 * GStreamer segments, doesn't it? */
5369 stream->time_position =
5370 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5372 /* not yet in segment, time does not yet increment. This means
5373 * that we are still prerolling keyframes to the decoder so it can
5374 * decode the first sample of the segment. */
5375 stream->time_position = segment->time;
5379 /* move to the next segment */
5382 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5384 if (stream->segment_index == stream->n_segments - 1) {
5385 /* are we at the end of the last segment, we're EOS */
5386 stream->time_position = GST_CLOCK_TIME_NONE;
5388 /* else we're only at the end of the current segment */
5389 stream->time_position = segment->stop_time;
5391 /* make sure we select a new segment */
5393 /* accumulate previous segments */
5394 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5395 stream->accumulated_base +=
5396 (stream->segment.stop -
5397 stream->segment.start) / ABS (stream->segment.rate);
5399 stream->segment_index = -1;
5404 gst_qtdemux_sync_streams (GstQTDemux * demux)
5408 if (QTDEMUX_N_STREAMS (demux) <= 1)
5411 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5412 QtDemuxStream *stream;
5413 GstClockTime end_time;
5415 stream = QTDEMUX_NTH_STREAM (demux, i);
5420 /* TODO advance time on subtitle streams here, if any some day */
5422 /* some clips/trailers may have unbalanced streams at the end,
5423 * so send EOS on shorter stream to prevent stalling others */
5425 /* do not mess with EOS if SEGMENT seeking */
5426 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5429 if (demux->pullbased) {
5430 /* loop mode is sample time based */
5431 if (!STREAM_IS_EOS (stream))
5434 /* push mode is byte position based */
5435 if (stream->n_samples &&
5436 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5440 if (stream->sent_eos)
5443 /* only act if some gap */
5444 end_time = stream->segments[stream->n_segments - 1].stop_time;
5445 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5446 ", stream end: %" GST_TIME_FORMAT,
5447 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5448 if (GST_CLOCK_TIME_IS_VALID (end_time)
5449 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5452 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5453 GST_PAD_NAME (stream->pad));
5454 stream->sent_eos = TRUE;
5455 event = gst_event_new_eos ();
5456 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5457 gst_event_set_seqnum (event, demux->segment_seqnum);
5458 gst_pad_push_event (stream->pad, event);
5463 /* EOS and NOT_LINKED need to be combined. This means that we return:
5465 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5466 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5468 static GstFlowReturn
5469 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5472 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5475 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5478 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5480 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5484 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5485 * completely clipped
5487 * Should be used only with raw buffers */
5489 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5492 guint64 start, stop, cstart, cstop, diff;
5493 GstClockTime pts, duration;
5495 gint num_rate, denom_rate;
5500 osize = size = gst_buffer_get_size (buf);
5503 /* depending on the type, setup the clip parameters */
5504 if (stream->subtype == FOURCC_soun) {
5505 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5506 num_rate = GST_SECOND;
5507 denom_rate = (gint) CUR_STREAM (stream)->rate;
5509 } else if (stream->subtype == FOURCC_vide) {
5511 num_rate = CUR_STREAM (stream)->fps_n;
5512 denom_rate = CUR_STREAM (stream)->fps_d;
5517 if (frame_size <= 0)
5518 goto bad_frame_size;
5520 /* we can only clip if we have a valid pts */
5521 pts = GST_BUFFER_PTS (buf);
5522 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5525 duration = GST_BUFFER_DURATION (buf);
5527 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5529 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5533 stop = start + duration;
5535 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5536 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5539 /* see if some clipping happened */
5540 diff = cstart - start;
5546 /* bring clipped time to samples and to bytes */
5547 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5550 GST_DEBUG_OBJECT (qtdemux,
5551 "clipping start to %" GST_TIME_FORMAT " %"
5552 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5558 diff = stop - cstop;
5563 /* bring clipped time to samples and then to bytes */
5564 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5566 GST_DEBUG_OBJECT (qtdemux,
5567 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5568 " bytes", GST_TIME_ARGS (cstop), diff);
5573 if (offset != 0 || size != osize)
5574 gst_buffer_resize (buf, offset, size);
5576 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5577 GST_BUFFER_PTS (buf) = pts;
5578 GST_BUFFER_DURATION (buf) = duration;
5582 /* dropped buffer */
5585 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5590 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5595 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5600 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5601 gst_buffer_unref (buf);
5607 gst_qtdemux_align_buffer (GstQTDemux * demux,
5608 GstBuffer * buffer, gsize alignment)
5612 gst_buffer_map (buffer, &map, GST_MAP_READ);
5614 if (map.size < sizeof (guintptr)) {
5615 gst_buffer_unmap (buffer, &map);
5619 if (((guintptr) map.data) & (alignment - 1)) {
5620 GstBuffer *new_buffer;
5621 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5623 new_buffer = gst_buffer_new_allocate (NULL,
5624 gst_buffer_get_size (buffer), ¶ms);
5626 /* Copy data "by hand", so ensure alignment is kept: */
5627 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5629 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5630 GST_DEBUG_OBJECT (demux,
5631 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5634 gst_buffer_unmap (buffer, &map);
5635 gst_buffer_unref (buffer);
5640 gst_buffer_unmap (buffer, &map);
5645 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
5651 /* We are converting from pairs to triplets */
5652 *res = ccpair_size / 2 * 3;
5653 storage = g_malloc (*res);
5654 for (i = 0; i * 2 < ccpair_size; i += 1) {
5655 /* FIXME: Use line offset 0 as we simply can't know here */
5657 storage[i * 3] = 0x80 | 0x00;
5659 storage[i * 3] = 0x00 | 0x00;
5660 storage[i * 3 + 1] = ccpair[i * 2];
5661 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5668 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
5672 guint32 atom_length, fourcc;
5673 QtDemuxStreamStsdEntry *stsd_entry;
5675 GST_MEMDUMP ("caption atom", data, size);
5677 /* There might be multiple atoms */
5682 atom_length = QT_UINT32 (data);
5683 fourcc = QT_FOURCC (data + 4);
5684 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5687 GST_DEBUG_OBJECT (stream->pad, "here");
5689 /* Check if we have something compatible */
5690 stsd_entry = CUR_STREAM (stream);
5691 switch (stsd_entry->fourcc) {
5693 guint8 *cdat = NULL, *cdt2 = NULL;
5694 gsize cdat_size = 0, cdt2_size = 0;
5695 /* Should be cdat or cdt2 */
5696 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
5697 GST_WARNING_OBJECT (stream->pad,
5698 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5699 GST_FOURCC_ARGS (fourcc));
5703 /* Convert to S334-1 Annex A byte triplet */
5704 if (fourcc == FOURCC_cdat)
5705 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
5707 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
5708 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5711 /* Check for another atom ? */
5712 if (size > atom_length + 8) {
5713 guint32 new_atom_length = QT_UINT32 (data + atom_length);
5714 if (size >= atom_length + new_atom_length) {
5715 fourcc = QT_FOURCC (data + atom_length + 4);
5716 if (fourcc == FOURCC_cdat) {
5719 convert_to_s334_1a (data + atom_length + 8,
5720 new_atom_length - 8, 1, &cdat_size);
5722 GST_WARNING_OBJECT (stream->pad,
5723 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5727 convert_to_s334_1a (data + atom_length + 8,
5728 new_atom_length - 8, 2, &cdt2_size);
5730 GST_WARNING_OBJECT (stream->pad,
5731 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5736 *cclen = cdat_size + cdt2_size;
5737 res = g_malloc (*cclen);
5739 memcpy (res, cdat, cdat_size);
5741 memcpy (res + cdat_size, cdt2, cdt2_size);
5747 if (fourcc != FOURCC_ccdp) {
5748 GST_WARNING_OBJECT (stream->pad,
5749 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5750 GST_FOURCC_ARGS (fourcc));
5753 *cclen = atom_length - 8;
5754 res = g_memdup2 (data + 8, *cclen);
5757 /* Keep this here in case other closed caption formats are added */
5758 g_assert_not_reached ();
5762 GST_MEMDUMP ("Output", res, *cclen);
5767 GST_WARNING ("[cdat] atom is too small or invalid");
5771 /* Handle Closed Caption sample buffers.
5772 * The input buffer metadata must be writable,
5773 * but time/duration etc not yet set and need not be preserved */
5775 gst_qtdemux_process_buffer_clcp (GstQTDemux * qtdemux, QtDemuxStream * stream,
5778 GstBuffer *outbuf = NULL;
5783 gst_buffer_map (buf, &map, GST_MAP_READ);
5785 /* empty buffer is sent to terminate previous subtitle */
5786 if (map.size <= 2) {
5787 gst_buffer_unmap (buf, &map);
5788 gst_buffer_unref (buf);
5792 /* For closed caption, we need to extract the information from the
5793 * [cdat],[cdt2] or [ccdp] atom */
5794 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
5795 gst_buffer_unmap (buf, &map);
5797 outbuf = _gst_buffer_new_wrapped (cc, cclen, g_free);
5798 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5800 /* Conversion failed or there's nothing */
5802 gst_buffer_unref (buf);
5807 /* DVD subpicture specific sample handling.
5808 * the input buffer metadata must be writable,
5809 * but time/duration etc not yet set and need not be preserved */
5811 gst_qtdemux_process_buffer_dvd (GstQTDemux * qtdemux, QtDemuxStream * stream,
5814 /* send a one time dvd clut event */
5815 if (stream->pending_event && stream->pad)
5816 gst_pad_push_event (stream->pad, stream->pending_event);
5817 stream->pending_event = NULL;
5819 /* empty buffer is sent to terminate previous subtitle */
5820 if (gst_buffer_get_size (buf) <= 2) {
5821 gst_buffer_unref (buf);
5825 /* That's all the processing needed for subpictures */
5829 /* Timed text formats
5830 * the input buffer metadata must be writable,
5831 * but time/duration etc not yet set and need not be preserved */
5833 gst_qtdemux_process_buffer_text (GstQTDemux * qtdemux, QtDemuxStream * stream,
5836 GstBuffer *outbuf = NULL;
5841 /* not many cases for now */
5842 if (G_UNLIKELY (stream->subtype != FOURCC_text &&
5843 stream->subtype != FOURCC_sbtl)) {
5847 gst_buffer_map (buf, &map, GST_MAP_READ);
5849 /* empty buffer is sent to terminate previous subtitle */
5850 if (map.size <= 2) {
5851 gst_buffer_unmap (buf, &map);
5852 gst_buffer_unref (buf);
5856 nsize = GST_READ_UINT16_BE (map.data);
5857 nsize = MIN (nsize, map.size - 2);
5859 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5862 /* takes care of UTF-8 validation or UTF-16 recognition,
5863 * no other encoding expected */
5864 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5865 gst_buffer_unmap (buf, &map);
5868 outbuf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5869 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5871 /* this should not really happen unless the subtitle is corrupted */
5873 gst_buffer_unref (buf);
5875 /* FIXME ? convert optional subsequent style info to markup */
5880 /* WebVTT sample handling according to 14496-30 */
5882 gst_qtdemux_process_buffer_wvtt (GstQTDemux * qtdemux, QtDemuxStream * stream,
5885 GstBuffer *outbuf = NULL;
5888 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
5889 g_assert_not_reached (); /* The buffer must be mappable */
5892 if (qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
5893 GstEvent *gap = NULL;
5894 /* Push a gap event */
5895 stream->segment.position = GST_BUFFER_PTS (buf);
5897 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
5898 gst_pad_push_event (stream->pad, gap);
5900 if (GST_BUFFER_DURATION_IS_VALID (buf))
5901 stream->segment.position += GST_BUFFER_DURATION (buf);
5904 qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
5905 GST_BUFFER_DURATION (buf), map.data, map.size);
5906 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5909 gst_buffer_unmap (buf, &map);
5910 gst_buffer_unref (buf);
5915 static GstFlowReturn
5916 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5919 GstFlowReturn ret = GST_FLOW_OK;
5920 GstClockTime pts, duration;
5922 if (stream->need_clip)
5923 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5925 if (G_UNLIKELY (buf == NULL))
5928 if (G_UNLIKELY (stream->discont)) {
5929 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5930 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5931 stream->discont = FALSE;
5933 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5936 GST_LOG_OBJECT (qtdemux,
5937 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
5938 ", duration %" GST_TIME_FORMAT " on pad %s",
5939 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
5940 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
5941 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
5943 if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
5944 GstStructure *crypto_info;
5945 QtDemuxAavdEncryptionInfo *info =
5946 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
5948 crypto_info = gst_structure_copy (info->default_properties);
5949 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5950 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
5953 if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
5954 || stream->protection_scheme_type == FOURCC_cbcs)) {
5955 GstStructure *crypto_info;
5956 QtDemuxCencSampleSetInfo *info =
5957 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5961 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5962 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
5963 GST_PTR_FORMAT, event);
5964 gst_pad_push_event (stream->pad, event);
5967 if (info->crypto_info == NULL) {
5968 if (stream->protection_scheme_type == FOURCC_cbcs) {
5969 crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
5970 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) {
5971 GST_ERROR_OBJECT (qtdemux,
5972 "failed to attach cbcs metadata to buffer");
5973 qtdemux_gst_structure_free (crypto_info);
5975 GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
5978 GST_DEBUG_OBJECT (qtdemux,
5979 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
5982 /* The end of the crypto_info array matches our n_samples position,
5983 * so count backward from there */
5984 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
5985 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5986 /* steal structure from array */
5987 crypto_info = g_ptr_array_index (info->crypto_info, index);
5988 g_ptr_array_index (info->crypto_info, index) = NULL;
5989 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
5990 info->crypto_info->len);
5991 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5992 GST_ERROR_OBJECT (qtdemux,
5993 "failed to attach cenc metadata to buffer");
5995 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
5996 index, stream->sample_index);
6001 if (stream->alignment > 1)
6002 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6004 pts = GST_BUFFER_PTS (buf);
6005 duration = GST_BUFFER_DURATION (buf);
6007 ret = gst_pad_push (stream->pad, buf);
6009 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6010 /* mark position in stream, we'll need this to know when to send GAP event */
6011 stream->segment.position = pts + duration;
6019 static GstFlowReturn
6020 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6023 GstFlowReturn ret = GST_FLOW_OK;
6025 if (stream->subtype == FOURCC_clcp
6026 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6028 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6029 guint n_triplets, i;
6030 guint field1_off = 0, field2_off = 0;
6032 /* We have to split CEA608 buffers so that each outgoing buffer contains
6033 * one byte pair per field according to the framerate of the video track.
6035 * If there is only a single byte pair per field we don't have to do
6039 gst_buffer_map (buf, &map, GST_MAP_READ);
6041 n_triplets = map.size / 3;
6042 for (i = 0; i < n_triplets; i++) {
6043 if (map.data[3 * i] & 0x80)
6049 g_assert (n_field1 || n_field2);
6051 /* If there's more than 1 frame we have to split, otherwise we can just
6053 if (n_field1 > 1 || n_field2 > 1) {
6055 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6056 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6058 for (i = 0; i < n_output_buffers; i++) {
6060 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6064 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6065 outptr = outmap.data;
6068 gboolean found = FALSE;
6070 while (map.data + field1_off < map.data + map.size) {
6071 if (map.data[field1_off] & 0x80) {
6072 memcpy (outptr, &map.data[field1_off], 3);
6081 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6083 memcpy (outptr, empty, 3);
6090 gboolean found = FALSE;
6092 while (map.data + field2_off < map.data + map.size) {
6093 if ((map.data[field2_off] & 0x80) == 0) {
6094 memcpy (outptr, &map.data[field2_off], 3);
6103 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6105 memcpy (outptr, empty, 3);
6111 gst_buffer_unmap (outbuf, &outmap);
6113 GST_BUFFER_PTS (outbuf) =
6114 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6115 GST_SECOND * CUR_STREAM (stream)->fps_d,
6116 CUR_STREAM (stream)->fps_n);
6117 GST_BUFFER_DURATION (outbuf) =
6118 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6119 CUR_STREAM (stream)->fps_n);
6120 GST_BUFFER_OFFSET (outbuf) = -1;
6121 GST_BUFFER_OFFSET_END (outbuf) = -1;
6123 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6125 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6128 gst_buffer_unmap (buf, &map);
6129 gst_buffer_unref (buf);
6131 gst_buffer_unmap (buf, &map);
6132 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6135 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6141 /* Sets a buffer's attributes properly and pushes it downstream.
6142 * Also checks for additional actions and custom processing that may
6143 * need to be done first.
6145 static GstFlowReturn
6146 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6147 QtDemuxStream * stream, GstBuffer * buf,
6148 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6149 gboolean keyframe, GstClockTime position, guint64 byte_position)
6151 GstFlowReturn ret = GST_FLOW_OK;
6153 /* offset the timestamps according to the edit list */
6155 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6159 gst_buffer_map (buf, &map, GST_MAP_READ);
6160 url = g_strndup ((gchar *) map.data, map.size);
6161 gst_buffer_unmap (buf, &map);
6162 if (url != NULL && strlen (url) != 0) {
6163 /* we have RTSP redirect now */
6164 g_free (qtdemux->redirect_location);
6165 qtdemux->redirect_location = g_strdup (url);
6166 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6167 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6168 gst_structure_new ("redirect",
6169 "new-location", G_TYPE_STRING, url, NULL)));
6171 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6177 /* position reporting */
6178 if (qtdemux->segment.rate >= 0) {
6179 qtdemux->segment.position = position;
6180 gst_qtdemux_sync_streams (qtdemux);
6183 if (G_UNLIKELY (!stream->pad)) {
6184 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6185 gst_buffer_unref (buf);
6189 /* send out pending buffers */
6190 while (stream->buffers) {
6191 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6193 if (G_UNLIKELY (stream->discont)) {
6194 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6195 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6196 stream->discont = FALSE;
6198 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6201 if (stream->alignment > 1)
6202 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6203 gst_pad_push (stream->pad, buffer);
6205 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6208 /* we're going to modify the metadata */
6209 buf = gst_buffer_make_writable (buf);
6211 GST_BUFFER_DTS (buf) = dts;
6212 GST_BUFFER_PTS (buf) = pts;
6213 GST_BUFFER_DURATION (buf) = duration;
6214 GST_BUFFER_OFFSET (buf) = -1;
6215 GST_BUFFER_OFFSET_END (buf) = -1;
6217 if (G_UNLIKELY (stream->process_func))
6218 buf = stream->process_func (qtdemux, stream, buf);
6225 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6226 stream->on_keyframe = FALSE;
6228 stream->on_keyframe = TRUE;
6231 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6232 gst_buffer_append_memory (buf,
6233 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6235 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6236 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6239 if (G_UNLIKELY (qtdemux->element_index)) {
6240 GstClockTime stream_time;
6243 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6245 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6246 GST_LOG_OBJECT (qtdemux,
6247 "adding association %" GST_TIME_FORMAT "-> %"
6248 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6249 gst_index_add_association (qtdemux->element_index,
6251 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6252 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6253 GST_FORMAT_BYTES, byte_position, NULL);
6258 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6264 static const QtDemuxRandomAccessEntry *
6265 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6266 GstClockTime pos, gboolean after)
6268 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6269 guint n_entries = stream->n_ra_entries;
6272 /* we assume the table is sorted */
6273 for (i = 0; i < n_entries; ++i) {
6274 if (entries[i].ts > pos)
6278 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6279 * probably okay to assume that the index lists the very first fragment */
6286 return &entries[i - 1];
6290 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6292 const QtDemuxRandomAccessEntry *best_entry = NULL;
6295 GST_OBJECT_LOCK (qtdemux);
6297 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6299 /* first see if we can determine where to go to using mfra,
6300 * before we start clearing things */
6301 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6302 const QtDemuxRandomAccessEntry *entry;
6303 QtDemuxStream *stream;
6304 gboolean is_audio_or_video;
6306 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6308 if (stream->ra_entries == NULL)
6311 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6312 is_audio_or_video = TRUE;
6314 is_audio_or_video = FALSE;
6317 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6318 stream->time_position, !is_audio_or_video);
6320 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6321 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6323 stream->pending_seek = entry;
6325 /* decide position to jump to just based on audio/video tracks, not subs */
6326 if (!is_audio_or_video)
6329 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6333 /* no luck, will handle seek otherwise */
6334 if (best_entry == NULL) {
6335 GST_OBJECT_UNLOCK (qtdemux);
6339 /* ok, now we can prepare for processing as of located moof */
6340 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6341 QtDemuxStream *stream;
6343 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6345 g_free (stream->samples);
6346 stream->samples = NULL;
6347 stream->n_samples = 0;
6348 stream->stbl_index = -1; /* no samples have yet been parsed */
6349 stream->sample_index = -1;
6351 if (stream->protection_scheme_info) {
6352 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6353 if (stream->protection_scheme_type == FOURCC_cenc
6354 || stream->protection_scheme_type == FOURCC_cbcs) {
6355 QtDemuxCencSampleSetInfo *info =
6356 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6357 if (info->crypto_info) {
6358 g_ptr_array_free (info->crypto_info, TRUE);
6359 info->crypto_info = NULL;
6365 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6366 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6367 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6368 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6370 qtdemux->moof_offset = best_entry->moof_offset;
6372 qtdemux_add_fragmented_samples (qtdemux);
6374 GST_OBJECT_UNLOCK (qtdemux);
6378 static GstFlowReturn
6379 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6381 GstFlowReturn ret = GST_FLOW_OK;
6382 GstBuffer *buf = NULL;
6383 QtDemuxStream *stream, *target_stream = NULL;
6384 GstClockTime min_time;
6386 GstClockTime dts = GST_CLOCK_TIME_NONE;
6387 GstClockTime pts = GST_CLOCK_TIME_NONE;
6388 GstClockTime duration = 0;
6389 gboolean keyframe = FALSE;
6390 guint sample_size = 0;
6391 guint num_samples = 1;
6396 if (qtdemux->fragmented_seek_pending) {
6397 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6398 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6399 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6400 qtdemux->fragmented_seek_pending = FALSE;
6402 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6406 /* Figure out the next stream sample to output, min_time is expressed in
6407 * global time and runs over the edit list segments. */
6408 min_time = G_MAXUINT64;
6409 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6410 GstClockTime position;
6412 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6413 position = stream->time_position;
6415 if (!GST_CLOCK_TIME_IS_VALID (position))
6418 if (stream->segment_index != -1) {
6419 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6420 position += segment->media_start;
6423 /* position of -1 is EOS */
6424 if (position < min_time) {
6425 min_time = position;
6426 target_stream = stream;
6430 if (G_UNLIKELY (target_stream == NULL)) {
6431 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6435 /* check for segment end */
6436 if (G_UNLIKELY (qtdemux->segment.stop != -1
6437 && qtdemux->segment.rate >= 0
6438 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
6439 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6440 target_stream->time_position = GST_CLOCK_TIME_NONE;
6444 /* fetch info for the current sample of this stream */
6445 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, target_stream,
6446 &empty, &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6449 /* Send catche-up GAP event for each other stream if required.
6450 * This logic will be applied only for positive rate */
6451 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux) &&
6452 qtdemux->segment.rate >= 0; i++) {
6453 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6455 if (stream == target_stream ||
6456 !GST_CLOCK_TIME_IS_VALID (stream->segment.stop) ||
6457 !GST_CLOCK_TIME_IS_VALID (stream->segment.position))
6461 GstClockTime gap_threshold;
6462 /* kind of running time with offset segment.base and segment.start */
6463 GstClockTime pseudo_target_time = target_stream->segment.base;
6464 GstClockTime pseudo_cur_time = stream->segment.base;
6466 /* make sure positive offset, segment.position can be smallr than
6467 * segment.start for some reasons */
6468 if (target_stream->segment.position >= target_stream->segment.start) {
6469 pseudo_target_time +=
6470 (target_stream->segment.position - target_stream->segment.start);
6473 if (stream->segment.position >= stream->segment.start)
6474 pseudo_cur_time += (stream->segment.position - stream->segment.start);
6476 /* Only send gap events on non-subtitle streams if lagging way behind. */
6477 if (stream->subtype == FOURCC_subp
6478 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl ||
6479 stream->subtype == FOURCC_wvtt)
6480 gap_threshold = 1 * GST_SECOND;
6482 gap_threshold = 3 * GST_SECOND;
6484 /* send gap events until the stream catches up */
6485 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6486 while (GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6487 pseudo_cur_time + gap_threshold < pseudo_target_time) {
6489 gst_event_new_gap (stream->segment.position, gap_threshold);
6490 GST_LOG_OBJECT (stream->pad, "Sending %" GST_PTR_FORMAT, gap);
6492 gst_pad_push_event (stream->pad, gap);
6493 stream->segment.position += gap_threshold;
6494 pseudo_cur_time += gap_threshold;
6499 stream = target_stream;
6501 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6502 if (stream->new_caps) {
6503 gst_qtdemux_configure_stream (qtdemux, stream);
6504 qtdemux_do_allocation (stream, qtdemux);
6507 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6508 if (G_UNLIKELY (qtdemux->segment.
6509 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6510 if (stream->subtype == FOURCC_vide) {
6512 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6515 } else if (qtdemux->trickmode_interval > 0) {
6516 GstClockTimeDiff interval;
6518 if (qtdemux->segment.rate > 0)
6519 interval = stream->time_position - stream->last_keyframe_dts;
6521 interval = stream->last_keyframe_dts - stream->time_position;
6523 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
6524 && interval < qtdemux->trickmode_interval) {
6525 GST_LOG_OBJECT (qtdemux,
6526 "Skipping keyframe within interval on track-id %u",
6530 stream->last_keyframe_dts = stream->time_position;
6536 GST_DEBUG_OBJECT (qtdemux,
6537 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6538 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6539 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6540 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6541 GST_TIME_ARGS (duration));
6543 if (G_UNLIKELY (empty)) {
6544 /* empty segment, push a gap if there's a second or more
6545 * difference and move to the next one */
6546 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6547 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6548 stream->segment.position = pts + duration;
6552 /* hmm, empty sample, skip and move to next sample */
6553 if (G_UNLIKELY (sample_size <= 0))
6556 /* last pushed sample was out of boundary, goto next sample */
6557 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6560 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
6561 GST_DEBUG_OBJECT (qtdemux,
6562 "size %d larger than stream max_buffer_size %d, trimming",
6563 sample_size, stream->max_buffer_size);
6565 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6566 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
6567 && sample_size < stream->min_buffer_size) {
6568 guint start_sample_index = stream->sample_index;
6569 guint accumulated_size = sample_size;
6570 guint64 expected_next_offset = offset + sample_size;
6572 GST_DEBUG_OBJECT (qtdemux,
6573 "size %d smaller than stream min_buffer_size %d, combining with the next",
6574 sample_size, stream->min_buffer_size);
6576 while (stream->sample_index < stream->to_sample
6577 && stream->sample_index + 1 < stream->n_samples) {
6578 const QtDemuxSample *next_sample;
6580 /* Increment temporarily */
6581 stream->sample_index++;
6583 /* Failed to parse sample so let's go back to the previous one that was
6584 * still successful */
6585 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6586 stream->sample_index--;
6590 next_sample = &stream->samples[stream->sample_index];
6592 /* Not contiguous with the previous sample so let's go back to the
6593 * previous one that was still successful */
6594 if (next_sample->offset != expected_next_offset) {
6595 stream->sample_index--;
6599 accumulated_size += next_sample->size;
6600 expected_next_offset += next_sample->size;
6601 if (accumulated_size >= stream->min_buffer_size)
6605 num_samples = stream->sample_index + 1 - start_sample_index;
6606 stream->sample_index = start_sample_index;
6607 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
6608 num_samples, accumulated_size);
6609 size = accumulated_size;
6614 if (qtdemux->cenc_aux_info_offset > 0) {
6617 GstBuffer *aux_info = NULL;
6619 /* pull the data stored before the sample */
6621 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6622 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6623 if (G_UNLIKELY (ret != GST_FLOW_OK))
6625 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6626 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6627 gst_byte_reader_init (&br, map.data + 8, map.size);
6628 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6629 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6630 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6631 gst_buffer_unmap (aux_info, &map);
6632 gst_buffer_unref (aux_info);
6633 ret = GST_FLOW_ERROR;
6636 gst_buffer_unmap (aux_info, &map);
6637 gst_buffer_unref (aux_info);
6640 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6643 if (stream->use_allocator) {
6644 /* if we have a per-stream allocator, use it */
6645 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6648 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6650 if (G_UNLIKELY (ret != GST_FLOW_OK))
6653 /* Update for both splitting and combining of samples */
6654 if (size != sample_size) {
6655 pts += gst_util_uint64_scale_int (GST_SECOND,
6656 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6659 gst_util_uint64_scale_int (GST_SECOND,
6660 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6663 gst_util_uint64_scale_int (GST_SECOND,
6664 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6667 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6668 dts, pts, duration, keyframe, min_time, offset);
6670 if (size < sample_size) {
6671 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6672 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6674 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6676 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6677 if (time_position >= segment->media_start) {
6678 /* inside the segment, update time_position, looks very familiar to
6679 * GStreamer segments, doesn't it? */
6680 stream->time_position = (time_position - segment->media_start) +
6683 /* not yet in segment, time does not yet increment. This means
6684 * that we are still prerolling keyframes to the decoder so it can
6685 * decode the first sample of the segment. */
6686 stream->time_position = segment->time;
6688 } else if (size > sample_size) {
6689 /* Increase to the last sample we already pulled so that advancing
6690 * below brings us to the next sample we need to pull */
6691 stream->sample_index += num_samples - 1;
6695 GST_OBJECT_LOCK (qtdemux);
6696 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6697 GST_OBJECT_UNLOCK (qtdemux);
6698 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6699 * we have no more data for the pad to push */
6700 if (ret == GST_FLOW_EOS)
6703 stream->offset_in_sample += size;
6704 if (stream->offset_in_sample >= sample_size) {
6705 gst_qtdemux_advance_sample (qtdemux, stream);
6710 gst_qtdemux_advance_sample (qtdemux, stream);
6718 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6724 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6725 /* EOS will be raised if all are EOS */
6732 gst_qtdemux_loop (GstPad * pad)
6734 GstQTDemux *qtdemux;
6738 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6740 cur_offset = qtdemux->offset;
6741 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6742 cur_offset, qt_demux_state_string (qtdemux->state));
6744 switch (qtdemux->state) {
6745 case QTDEMUX_STATE_INITIAL:
6746 case QTDEMUX_STATE_HEADER:
6747 ret = gst_qtdemux_loop_state_header (qtdemux);
6749 case QTDEMUX_STATE_MOVIE:
6750 ret = gst_qtdemux_loop_state_movie (qtdemux);
6751 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6752 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6760 /* if something went wrong, pause */
6761 if (ret != GST_FLOW_OK)
6765 gst_object_unref (qtdemux);
6771 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6772 (NULL), ("streaming stopped, invalid state"));
6773 gst_pad_pause_task (pad);
6774 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6779 const gchar *reason = gst_flow_get_name (ret);
6781 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6783 gst_pad_pause_task (pad);
6785 /* fatal errors need special actions */
6787 if (ret == GST_FLOW_EOS) {
6788 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
6789 /* we have no streams, post an error */
6790 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6792 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6795 if ((stop = qtdemux->segment.stop) == -1)
6796 stop = qtdemux->segment.duration;
6798 if (qtdemux->segment.rate >= 0) {
6799 GstMessage *message;
6802 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6803 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6804 GST_FORMAT_TIME, stop);
6805 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6806 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6807 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6808 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6810 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6811 gst_qtdemux_push_event (qtdemux, event);
6813 GstMessage *message;
6816 /* For Reverse Playback */
6817 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6818 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6819 GST_FORMAT_TIME, qtdemux->segment.start);
6820 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6821 qtdemux->segment.start);
6822 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6823 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6824 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6826 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6827 gst_qtdemux_push_event (qtdemux, event);
6832 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6833 event = gst_event_new_eos ();
6834 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
6835 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6836 gst_qtdemux_push_event (qtdemux, event);
6838 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6839 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6840 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6849 * Returns if there are samples to be played.
6852 has_next_entry (GstQTDemux * demux)
6854 QtDemuxStream *stream;
6857 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6859 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6860 stream = QTDEMUX_NTH_STREAM (demux, i);
6862 if (stream->sample_index == -1) {
6863 stream->sample_index = 0;
6864 stream->offset_in_sample = 0;
6867 if (stream->sample_index >= stream->n_samples) {
6868 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6871 GST_DEBUG_OBJECT (demux, "Found a sample");
6875 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6882 * Returns the size of the first entry at the current offset.
6883 * If -1, there are none (which means EOS or empty file).
6886 next_entry_size (GstQTDemux * demux)
6888 QtDemuxStream *stream, *target_stream = NULL;
6889 guint64 smalloffs = (guint64) - 1;
6890 QtDemuxSample *sample;
6893 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6896 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6897 stream = QTDEMUX_NTH_STREAM (demux, i);
6899 if (stream->sample_index == -1) {
6900 stream->sample_index = 0;
6901 stream->offset_in_sample = 0;
6904 if (stream->sample_index >= stream->n_samples) {
6905 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6909 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6910 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6911 stream->sample_index);
6915 sample = &stream->samples[stream->sample_index];
6917 GST_LOG_OBJECT (demux,
6918 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
6919 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
6920 stream->sample_index, sample->offset, sample->size);
6922 if (((smalloffs == -1)
6923 || (sample->offset < smalloffs)) && (sample->size)) {
6924 smalloffs = sample->offset;
6925 target_stream = stream;
6932 GST_LOG_OBJECT (demux,
6933 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
6934 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
6936 stream = target_stream;
6937 sample = &stream->samples[stream->sample_index];
6939 if (sample->offset >= demux->offset) {
6940 demux->todrop = sample->offset - demux->offset;
6941 return sample->size + demux->todrop;
6944 GST_DEBUG_OBJECT (demux,
6945 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6950 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6952 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6954 gst_element_post_message (GST_ELEMENT_CAST (demux),
6955 gst_message_new_element (GST_OBJECT_CAST (demux),
6956 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6960 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6965 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6968 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6969 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6970 GST_SEEK_TYPE_NONE, -1);
6972 /* store seqnum to drop flush events, they don't need to reach downstream */
6973 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6974 res = gst_pad_push_event (demux->sinkpad, event);
6975 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
6980 /* check for seekable upstream, above and beyond a mere query */
6982 gst_qtdemux_check_seekability (GstQTDemux * demux)
6985 gboolean seekable = FALSE;
6986 gint64 start = -1, stop = -1;
6988 if (demux->upstream_size)
6991 if (demux->upstream_format_is_time)
6994 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6995 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6996 GST_DEBUG_OBJECT (demux, "seeking query failed");
7000 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7002 /* try harder to query upstream size if we didn't get it the first time */
7003 if (seekable && stop == -1) {
7004 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7005 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7008 /* if upstream doesn't know the size, it's likely that it's not seekable in
7009 * practice even if it technically may be seekable */
7010 if (seekable && (start != 0 || stop <= start)) {
7011 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7016 gst_query_unref (query);
7018 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7019 G_GUINT64_FORMAT ")", seekable, start, stop);
7020 demux->upstream_seekable = seekable;
7021 demux->upstream_size = seekable ? stop : -1;
7025 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7027 g_return_if_fail (bytes <= demux->todrop);
7029 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7030 gst_adapter_flush (demux->adapter, bytes);
7031 demux->neededbytes -= bytes;
7032 demux->offset += bytes;
7033 demux->todrop -= bytes;
7036 /* PUSH-MODE only: Send a segment, if not done already. */
7038 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7040 if (G_UNLIKELY (demux->need_segment)) {
7043 if (!demux->upstream_format_is_time) {
7044 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7046 GstEvent *segment_event;
7047 segment_event = gst_event_new_segment (&demux->segment);
7048 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7049 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7050 gst_qtdemux_push_event (demux, segment_event);
7053 demux->need_segment = FALSE;
7055 /* clear to send tags on all streams */
7056 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7057 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7058 gst_qtdemux_push_tags (demux, stream);
7059 if (CUR_STREAM (stream)->sparse) {
7060 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7061 gst_pad_push_event (stream->pad,
7062 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7068 /* Used for push mode only. */
7070 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7071 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7073 GstClockTime ts, dur;
7077 stream->segments[segment_index].duration - (pos -
7078 stream->segments[segment_index].time);
7079 stream->time_position += dur;
7081 /* Only gaps with a duration of at least one second are propagated.
7082 * Same workaround as in pull mode.
7083 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7084 if (dur >= GST_SECOND) {
7086 gap = gst_event_new_gap (ts, dur);
7088 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7089 "segment: %" GST_PTR_FORMAT, gap);
7090 gst_pad_push_event (stream->pad, gap);
7094 static GstFlowReturn
7095 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7099 demux = GST_QTDEMUX (parent);
7101 GST_DEBUG_OBJECT (demux,
7102 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7103 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7104 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7105 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7106 gst_buffer_get_size (inbuf), demux->offset);
7108 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7109 gboolean is_gap_input = FALSE;
7112 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7114 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7115 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7118 /* Check if we can land back on our feet in the case where upstream is
7119 * handling the seeking/pushing of samples with gaps in between (like
7120 * in the case of trick-mode DASH for example) */
7121 if (demux->upstream_format_is_time
7122 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7123 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7125 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7126 GST_LOG_OBJECT (demux,
7127 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7128 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7130 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7131 stream, GST_BUFFER_OFFSET (inbuf));
7133 QtDemuxSample *sample = &stream->samples[res];
7134 GST_LOG_OBJECT (demux,
7135 "Checking if sample %d from track-id %u is valid (offset:%"
7136 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7137 stream->track_id, sample->offset, sample->size);
7138 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7139 GST_LOG_OBJECT (demux,
7140 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7142 is_gap_input = TRUE;
7143 /* We can go back to standard playback mode */
7144 demux->state = QTDEMUX_STATE_MOVIE;
7145 /* Remember which sample this stream is at */
7146 stream->sample_index = res;
7147 /* Finally update all push-based values to the expected values */
7148 demux->neededbytes = stream->samples[res].size;
7149 demux->offset = GST_BUFFER_OFFSET (inbuf);
7151 demux->mdatsize - demux->offset + demux->mdatoffset;
7156 if (!is_gap_input) {
7157 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7158 /* Reset state if it's a real discont */
7159 demux->neededbytes = 16;
7160 demux->state = QTDEMUX_STATE_INITIAL;
7161 demux->offset = GST_BUFFER_OFFSET (inbuf);
7162 gst_adapter_clear (demux->adapter);
7165 /* Reverse fragmented playback, need to flush all we have before
7166 * consuming a new fragment.
7167 * The samples array have the timestamps calculated by accumulating the
7168 * durations but this won't work for reverse playback of fragments as
7169 * the timestamps of a subsequent fragment should be smaller than the
7170 * previously received one. */
7171 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7172 gst_qtdemux_process_adapter (demux, TRUE);
7173 g_ptr_array_foreach (demux->active_streams,
7174 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7178 gst_adapter_push (demux->adapter, inbuf);
7180 GST_DEBUG_OBJECT (demux,
7181 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7182 demux->neededbytes, gst_adapter_available (demux->adapter));
7184 return gst_qtdemux_process_adapter (demux, FALSE);
7187 static GstFlowReturn
7188 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7190 GstFlowReturn ret = GST_FLOW_OK;
7192 /* we never really mean to buffer that much */
7193 if (demux->neededbytes == -1) {
7197 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7198 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7200 #ifndef GST_DISABLE_GST_DEBUG
7202 guint64 discont_offset, distance_from_discont;
7204 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7205 distance_from_discont =
7206 gst_adapter_distance_from_discont (demux->adapter);
7208 GST_DEBUG_OBJECT (demux,
7209 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7210 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7211 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7212 demux->offset, discont_offset, distance_from_discont);
7216 switch (demux->state) {
7217 case QTDEMUX_STATE_INITIAL:{
7222 gst_qtdemux_check_seekability (demux);
7224 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7226 /* get fourcc/length, set neededbytes */
7227 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7229 gst_adapter_unmap (demux->adapter);
7231 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7232 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7234 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7235 (_("This file is invalid and cannot be played.")),
7236 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7237 GST_FOURCC_ARGS (fourcc)));
7238 ret = GST_FLOW_ERROR;
7241 if (fourcc == FOURCC_mdat) {
7242 gint next_entry = next_entry_size (demux);
7243 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7244 || !demux->fragmented)) {
7245 /* we have the headers, start playback */
7246 demux->state = QTDEMUX_STATE_MOVIE;
7247 demux->neededbytes = next_entry;
7248 demux->mdatleft = size;
7249 demux->mdatsize = demux->mdatleft;
7251 /* no headers yet, try to get them */
7254 guint64 old, target;
7257 old = demux->offset;
7258 target = old + size;
7260 /* try to jump over the atom with a seek */
7261 /* only bother if it seems worth doing so,
7262 * and avoids possible upstream/server problems */
7263 if (demux->upstream_seekable &&
7264 demux->upstream_size > 4 * (1 << 20)) {
7265 res = qtdemux_seek_offset (demux, target);
7267 GST_DEBUG_OBJECT (demux, "skipping seek");
7272 GST_DEBUG_OBJECT (demux, "seek success");
7273 /* remember the offset fo the first mdat so we can seek back to it
7274 * after we have the headers */
7275 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7276 demux->first_mdat = old;
7277 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7280 /* seek worked, continue reading */
7281 demux->offset = target;
7282 demux->neededbytes = 16;
7283 demux->state = QTDEMUX_STATE_INITIAL;
7285 /* seek failed, need to buffer */
7286 demux->offset = old;
7287 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7288 /* there may be multiple mdat (or alike) buffers */
7290 if (demux->mdatbuffer)
7291 bs = gst_buffer_get_size (demux->mdatbuffer);
7294 if (size + bs > 10 * (1 << 20))
7296 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7297 demux->neededbytes = size;
7298 if (!demux->mdatbuffer)
7299 demux->mdatoffset = demux->offset;
7302 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7303 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7304 (_("This file is invalid and cannot be played.")),
7305 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7306 GST_FOURCC_ARGS (fourcc), size));
7307 ret = GST_FLOW_ERROR;
7310 /* this means we already started buffering and still no moov header,
7311 * let's continue buffering everything till we get moov */
7312 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7313 || fourcc == FOURCC_moof))
7315 demux->neededbytes = size;
7316 demux->state = QTDEMUX_STATE_HEADER;
7320 case QTDEMUX_STATE_HEADER:{
7324 GST_DEBUG_OBJECT (demux, "In header");
7326 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7328 /* parse the header */
7329 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7331 if (fourcc == FOURCC_moov) {
7332 /* in usual fragmented setup we could try to scan for more
7333 * and end up at the the moov (after mdat) again */
7334 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7336 || demux->last_moov_offset == demux->offset)) {
7337 GST_DEBUG_OBJECT (demux,
7338 "Skipping moov atom as we have (this) one already");
7340 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7342 if (demux->got_moov && demux->fragmented) {
7343 GST_DEBUG_OBJECT (demux,
7344 "Got a second moov, clean up data from old one");
7345 if (demux->moov_node_compressed) {
7346 g_node_destroy (demux->moov_node_compressed);
7347 if (demux->moov_node)
7348 g_free (demux->moov_node->data);
7350 demux->moov_node_compressed = NULL;
7351 if (demux->moov_node)
7352 g_node_destroy (demux->moov_node);
7353 demux->moov_node = NULL;
7356 demux->last_moov_offset = demux->offset;
7358 /* Update streams with new moov */
7359 gst_qtdemux_stream_concat (demux,
7360 demux->old_streams, demux->active_streams);
7362 qtdemux_parse_moov (demux, data, demux->neededbytes);
7363 qtdemux_node_dump (demux, demux->moov_node);
7364 qtdemux_parse_tree (demux);
7365 qtdemux_prepare_streams (demux);
7366 QTDEMUX_EXPOSE_LOCK (demux);
7367 qtdemux_expose_streams (demux);
7368 QTDEMUX_EXPOSE_UNLOCK (demux);
7370 demux->got_moov = TRUE;
7372 gst_qtdemux_check_send_pending_segment (demux);
7374 if (demux->moov_node_compressed) {
7375 g_node_destroy (demux->moov_node_compressed);
7376 g_free (demux->moov_node->data);
7378 demux->moov_node_compressed = NULL;
7379 g_node_destroy (demux->moov_node);
7380 demux->moov_node = NULL;
7381 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7383 } else if (fourcc == FOURCC_moof) {
7384 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7386 GstClockTime prev_pts;
7387 guint64 prev_offset;
7388 guint64 adapter_discont_offset, adapter_discont_dist;
7390 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7393 * The timestamp of the moof buffer is relevant as some scenarios
7394 * won't have the initial timestamp in the atoms. Whenever a new
7395 * buffer has started, we get that buffer's PTS and use it as a base
7396 * timestamp for the trun entries.
7398 * To keep track of the current buffer timestamp and starting point
7399 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7400 * from the beginning of the buffer, with the distance and demux->offset
7401 * we know if it is still the same buffer or not.
7403 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7404 prev_offset = demux->offset - dist;
7405 if (demux->fragment_start_offset == -1
7406 || prev_offset > demux->fragment_start_offset) {
7407 demux->fragment_start_offset = prev_offset;
7408 demux->fragment_start = prev_pts;
7409 GST_DEBUG_OBJECT (demux,
7410 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7411 GST_TIME_FORMAT, demux->fragment_start_offset,
7412 GST_TIME_ARGS (demux->fragment_start));
7415 /* We can't use prev_offset() here because this would require
7416 * upstream to set consistent and correct offsets on all buffers
7417 * since the discont. Nothing ever did that in the past and we
7418 * would break backwards compatibility here then.
7419 * Instead take the offset we had at the last discont and count
7420 * the bytes from there. This works with old code as there would
7421 * be no discont between moov and moof, and also works with
7422 * adaptivedemux which correctly sets offset and will set the
7423 * DISCONT flag accordingly when needed.
7425 * We also only do this for upstream TIME segments as otherwise
7426 * there are potential backwards compatibility problems with
7427 * seeking in PUSH mode and upstream providing inconsistent
7429 adapter_discont_offset =
7430 gst_adapter_offset_at_discont (demux->adapter);
7431 adapter_discont_dist =
7432 gst_adapter_distance_from_discont (demux->adapter);
7434 GST_DEBUG_OBJECT (demux,
7435 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7436 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7437 demux->offset, adapter_discont_offset, adapter_discont_dist);
7439 if (demux->upstream_format_is_time) {
7440 demux->moof_offset = adapter_discont_offset;
7441 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7442 demux->moof_offset += adapter_discont_dist;
7443 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7444 demux->moof_offset = demux->offset;
7446 demux->moof_offset = demux->offset;
7449 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7450 demux->moof_offset, NULL)) {
7451 gst_adapter_unmap (demux->adapter);
7452 ret = GST_FLOW_ERROR;
7456 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7457 if (demux->mss_mode && !demux->exposed) {
7458 QTDEMUX_EXPOSE_LOCK (demux);
7459 qtdemux_expose_streams (demux);
7460 QTDEMUX_EXPOSE_UNLOCK (demux);
7463 gst_qtdemux_check_send_pending_segment (demux);
7465 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7467 } else if (fourcc == FOURCC_ftyp) {
7468 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7469 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7470 } else if (fourcc == FOURCC_uuid) {
7471 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7472 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7473 } else if (fourcc == FOURCC_sidx) {
7474 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7475 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7479 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7483 /* [free] and [skip] are padding atoms */
7484 GST_DEBUG_OBJECT (demux,
7485 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7486 GST_FOURCC_ARGS (fourcc));
7489 GST_WARNING_OBJECT (demux,
7490 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7491 GST_FOURCC_ARGS (fourcc));
7492 /* Let's jump that one and go back to initial state */
7496 gst_adapter_unmap (demux->adapter);
7499 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7500 gsize remaining_data_size = 0;
7502 /* the mdat was before the header */
7503 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7504 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7505 /* restore our adapter/offset view of things with upstream;
7506 * put preceding buffered data ahead of current moov data.
7507 * This should also handle evil mdat, moov, mdat cases and alike */
7508 gst_adapter_flush (demux->adapter, demux->neededbytes);
7510 /* Store any remaining data after the mdat for later usage */
7511 remaining_data_size = gst_adapter_available (demux->adapter);
7512 if (remaining_data_size > 0) {
7513 g_assert (demux->restoredata_buffer == NULL);
7514 demux->restoredata_buffer =
7515 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7516 demux->restoredata_offset = demux->offset + demux->neededbytes;
7517 GST_DEBUG_OBJECT (demux,
7518 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7519 G_GUINT64_FORMAT, remaining_data_size,
7520 demux->restoredata_offset);
7523 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7524 demux->mdatbuffer = NULL;
7525 demux->offset = demux->mdatoffset;
7526 demux->neededbytes = next_entry_size (demux);
7527 demux->state = QTDEMUX_STATE_MOVIE;
7528 demux->mdatleft = gst_adapter_available (demux->adapter);
7529 demux->mdatsize = demux->mdatleft;
7531 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7532 gst_adapter_flush (demux->adapter, demux->neededbytes);
7534 /* only go back to the mdat if there are samples to play */
7535 if (demux->got_moov && demux->first_mdat != -1
7536 && has_next_entry (demux)) {
7539 /* we need to seek back */
7540 res = qtdemux_seek_offset (demux, demux->first_mdat);
7542 demux->offset = demux->first_mdat;
7544 GST_DEBUG_OBJECT (demux, "Seek back failed");
7547 demux->offset += demux->neededbytes;
7549 demux->neededbytes = 16;
7550 demux->state = QTDEMUX_STATE_INITIAL;
7555 case QTDEMUX_STATE_BUFFER_MDAT:{
7559 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7561 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7562 gst_buffer_extract (buf, 0, fourcc, 4);
7563 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7564 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7565 if (demux->mdatbuffer)
7566 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7568 demux->mdatbuffer = buf;
7569 demux->offset += demux->neededbytes;
7570 demux->neededbytes = 16;
7571 demux->state = QTDEMUX_STATE_INITIAL;
7572 gst_qtdemux_post_progress (demux, 1, 1);
7576 case QTDEMUX_STATE_MOVIE:{
7577 QtDemuxStream *stream = NULL;
7578 QtDemuxSample *sample;
7579 GstClockTime dts, pts, duration;
7583 GST_DEBUG_OBJECT (demux,
7584 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7586 if (demux->fragmented) {
7587 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7589 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7590 /* if needed data starts within this atom,
7591 * then it should not exceed this atom */
7592 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7593 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7594 (_("This file is invalid and cannot be played.")),
7595 ("sample data crosses atom boundary"));
7596 ret = GST_FLOW_ERROR;
7599 demux->mdatleft -= demux->neededbytes;
7601 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7602 /* so we are dropping more than left in this atom */
7603 gst_qtdemux_drop_data (demux, demux->mdatleft);
7604 demux->mdatleft = 0;
7606 /* need to resume atom parsing so we do not miss any other pieces */
7607 demux->state = QTDEMUX_STATE_INITIAL;
7608 demux->neededbytes = 16;
7610 /* check if there was any stored post mdat data from previous buffers */
7611 if (demux->restoredata_buffer) {
7612 g_assert (gst_adapter_available (demux->adapter) == 0);
7614 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7615 demux->restoredata_buffer = NULL;
7616 demux->offset = demux->restoredata_offset;
7623 if (demux->todrop) {
7624 if (demux->cenc_aux_info_offset > 0) {
7628 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7629 data = gst_adapter_map (demux->adapter, demux->todrop);
7630 gst_byte_reader_init (&br, data + 8, demux->todrop);
7631 if (!qtdemux_parse_cenc_aux_info (demux,
7632 QTDEMUX_NTH_STREAM (demux, 0), &br,
7633 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7634 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7635 ret = GST_FLOW_ERROR;
7636 gst_adapter_unmap (demux->adapter);
7637 g_free (demux->cenc_aux_info_sizes);
7638 demux->cenc_aux_info_sizes = NULL;
7641 demux->cenc_aux_info_offset = 0;
7642 g_free (demux->cenc_aux_info_sizes);
7643 demux->cenc_aux_info_sizes = NULL;
7644 gst_adapter_unmap (demux->adapter);
7646 gst_qtdemux_drop_data (demux, demux->todrop);
7650 /* initial newsegment sent here after having added pads,
7651 * possible others in sink_event */
7652 gst_qtdemux_check_send_pending_segment (demux);
7654 /* Figure out which stream this packet belongs to */
7655 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7656 stream = QTDEMUX_NTH_STREAM (demux, i);
7657 if (stream->sample_index >= stream->n_samples) {
7658 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7662 GST_LOG_OBJECT (demux,
7663 "Checking track-id %u (sample_index:%d / offset:%"
7664 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7665 stream->sample_index,
7666 stream->samples[stream->sample_index].offset,
7667 stream->samples[stream->sample_index].size);
7669 if (stream->samples[stream->sample_index].offset == demux->offset)
7673 if (G_UNLIKELY (stream == NULL))
7674 goto unknown_stream;
7676 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7678 if (stream->new_caps) {
7679 gst_qtdemux_configure_stream (demux, stream);
7682 /* Put data in a buffer, set timestamps, caps, ... */
7683 sample = &stream->samples[stream->sample_index];
7685 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7686 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7687 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7689 dts = QTSAMPLE_DTS (stream, sample);
7690 pts = QTSAMPLE_PTS (stream, sample);
7691 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7692 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7694 /* check for segment end */
7695 if (G_UNLIKELY (demux->segment.stop != -1
7696 && demux->segment.stop <= pts && stream->on_keyframe)
7697 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7698 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7699 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7701 /* skip this data, stream is EOS */
7702 gst_adapter_flush (demux->adapter, demux->neededbytes);
7703 demux->offset += demux->neededbytes;
7705 /* check if all streams are eos */
7707 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7708 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
7717 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7719 /* FIXME: should either be an assert or a plain check */
7720 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7722 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7723 dts, pts, duration, keyframe, dts, demux->offset);
7727 GST_OBJECT_LOCK (demux);
7728 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7729 GST_OBJECT_UNLOCK (demux);
7731 /* skip this data, stream is EOS */
7732 gst_adapter_flush (demux->adapter, demux->neededbytes);
7735 stream->sample_index++;
7736 stream->offset_in_sample = 0;
7738 /* update current offset and figure out size of next buffer */
7739 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7740 demux->offset, demux->neededbytes);
7741 demux->offset += demux->neededbytes;
7742 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7746 if (ret == GST_FLOW_EOS) {
7747 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7748 demux->neededbytes = -1;
7752 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7753 if (demux->fragmented) {
7754 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7755 /* there may be more to follow, only finish this atom */
7756 demux->todrop = demux->mdatleft;
7757 demux->neededbytes = demux->todrop;
7762 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7763 goto non_ok_unlinked_flow;
7772 /* when buffering movie data, at least show user something is happening */
7773 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7774 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7775 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7776 demux->neededbytes);
7783 non_ok_unlinked_flow:
7785 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7786 gst_flow_get_name (ret));
7791 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7792 ret = GST_FLOW_ERROR;
7797 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7803 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7804 (NULL), ("qtdemuxer invalid state %d", demux->state));
7805 ret = GST_FLOW_ERROR;
7810 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7811 (NULL), ("no 'moov' atom within the first 10 MB"));
7812 ret = GST_FLOW_ERROR;
7818 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7823 query = gst_query_new_scheduling ();
7825 if (!gst_pad_peer_query (sinkpad, query)) {
7826 gst_query_unref (query);
7830 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7831 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7832 gst_query_unref (query);
7837 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7838 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7842 GST_DEBUG_OBJECT (sinkpad, "activating push");
7843 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7848 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7849 GstPadMode mode, gboolean active)
7852 GstQTDemux *demux = GST_QTDEMUX (parent);
7855 case GST_PAD_MODE_PUSH:
7856 demux->pullbased = FALSE;
7859 case GST_PAD_MODE_PULL:
7861 demux->pullbased = TRUE;
7862 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7865 res = gst_pad_stop_task (sinkpad);
7877 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7883 memset (&z, 0, sizeof (z));
7888 if ((ret = inflateInit (&z)) != Z_OK) {
7889 GST_ERROR ("inflateInit() returned %d", ret);
7893 z.next_in = z_buffer;
7894 z.avail_in = z_length;
7896 buffer = (guint8 *) g_malloc (*length);
7897 z.avail_out = *length;
7898 z.next_out = (Bytef *) buffer;
7900 ret = inflate (&z, Z_NO_FLUSH);
7901 if (ret == Z_STREAM_END) {
7903 } else if (ret != Z_OK) {
7904 GST_WARNING ("inflate() returned %d", ret);
7909 buffer = (guint8 *) g_realloc (buffer, *length);
7910 z.next_out = (Bytef *) (buffer + z.total_out);
7911 z.avail_out += 4096;
7912 } while (z.avail_in > 0);
7914 if (ret != Z_STREAM_END) {
7919 *length = z.total_out;
7926 #endif /* HAVE_ZLIB */
7929 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7933 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7935 /* counts as header data */
7936 qtdemux->header_size += length;
7938 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7939 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7941 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7948 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7949 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7950 if (dcom == NULL || cmvd == NULL)
7951 goto invalid_compression;
7953 dcom_len = QT_UINT32 (dcom->data);
7955 goto invalid_compression;
7957 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7961 guint uncompressed_length;
7962 guint compressed_length;
7966 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7968 goto invalid_compression;
7970 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7971 compressed_length = cmvd_len - 12;
7972 GST_LOG ("length = %u", uncompressed_length);
7975 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7976 compressed_length, &uncompressed_length);
7979 qtdemux->moov_node_compressed = qtdemux->moov_node;
7980 qtdemux->moov_node = g_node_new (buf);
7982 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7983 uncompressed_length);
7987 #endif /* HAVE_ZLIB */
7989 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7990 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7997 invalid_compression:
7999 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8005 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8008 while (G_UNLIKELY (buf < end)) {
8012 if (G_UNLIKELY (buf + 4 > end)) {
8013 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8016 len = QT_UINT32 (buf);
8017 if (G_UNLIKELY (len == 0)) {
8018 GST_LOG_OBJECT (qtdemux, "empty container");
8021 if (G_UNLIKELY (len < 8)) {
8022 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8025 if (G_UNLIKELY (len > (end - buf))) {
8026 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8027 (gint) (end - buf));
8031 child = g_node_new ((guint8 *) buf);
8032 g_node_append (node, child);
8033 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8034 qtdemux_parse_node (qtdemux, child, buf, len);
8042 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8045 int len = QT_UINT32 (xdxt->data);
8046 guint8 *buf = xdxt->data;
8047 guint8 *end = buf + len;
8050 /* skip size and type */
8058 size = QT_UINT32 (buf);
8059 type = QT_FOURCC (buf + 4);
8061 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8063 if (buf + size > end || size <= 0)
8069 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8070 GST_FOURCC_ARGS (type));
8074 buffer = gst_buffer_new_and_alloc (size);
8075 gst_buffer_fill (buffer, 0, buf, size);
8076 stream->buffers = g_slist_append (stream->buffers, buffer);
8077 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8080 buffer = gst_buffer_new_and_alloc (size);
8081 gst_buffer_fill (buffer, 0, buf, size);
8082 stream->buffers = g_slist_append (stream->buffers, buffer);
8083 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8086 buffer = gst_buffer_new_and_alloc (size);
8087 gst_buffer_fill (buffer, 0, buf, size);
8088 stream->buffers = g_slist_append (stream->buffers, buffer);
8089 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8092 GST_WARNING_OBJECT (qtdemux,
8093 "unknown theora cookie %" GST_FOURCC_FORMAT,
8094 GST_FOURCC_ARGS (type));
8103 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8107 guint32 node_length = 0;
8108 const QtNodeType *type;
8111 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8113 if (G_UNLIKELY (length < 8))
8114 goto not_enough_data;
8116 node_length = QT_UINT32 (buffer);
8117 fourcc = QT_FOURCC (buffer + 4);
8119 /* ignore empty nodes */
8120 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8123 type = qtdemux_type_get (fourcc);
8125 end = buffer + length;
8127 GST_LOG_OBJECT (qtdemux,
8128 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8129 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8131 if (node_length > length)
8132 goto broken_atom_size;
8134 if (type->flags & QT_FLAG_CONTAINER) {
8135 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8140 if (node_length < 20) {
8141 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8144 GST_DEBUG_OBJECT (qtdemux,
8145 "parsing stsd (sample table, sample description) atom");
8146 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8147 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8159 /* also read alac (or whatever) in stead of mp4a in the following,
8160 * since a similar layout is used in other cases as well */
8161 if (fourcc == FOURCC_mp4a)
8163 else if (fourcc == FOURCC_fLaC)
8168 /* There are two things we might encounter here: a true mp4a atom, and
8169 an mp4a entry in an stsd atom. The latter is what we're interested
8170 in, and it looks like an atom, but isn't really one. The true mp4a
8171 atom is short, so we detect it based on length here. */
8172 if (length < min_size) {
8173 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8174 GST_FOURCC_ARGS (fourcc));
8178 /* 'version' here is the sound sample description version. Types 0 and
8179 1 are documented in the QTFF reference, but type 2 is not: it's
8180 described in Apple header files instead (struct SoundDescriptionV2
8182 version = QT_UINT16 (buffer + 16);
8184 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8185 GST_FOURCC_ARGS (fourcc), version);
8187 /* parse any esds descriptors */
8199 GST_WARNING_OBJECT (qtdemux,
8200 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8201 GST_FOURCC_ARGS (fourcc), version);
8206 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8234 /* codec_data is contained inside these atoms, which all have
8235 * the same format. */
8236 /* video sample description size is 86 bytes without extension.
8237 * node_length have to be bigger than 86 bytes because video sample
8238 * description can include extensions such as esds, fiel, glbl, etc. */
8239 if (node_length < 86) {
8240 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8241 " sample description length too short (%u < 86)",
8242 GST_FOURCC_ARGS (fourcc), node_length);
8246 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8247 GST_FOURCC_ARGS (fourcc));
8249 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8251 * revision level (2 bytes) : must be set to 0. */
8252 version = QT_UINT32 (buffer + 16);
8253 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8255 /* compressor name : PASCAL string and informative purposes
8256 * first byte : the number of bytes to be displayed.
8257 * it has to be less than 32 because it is reserved
8258 * space of 32 bytes total including itself. */
8259 str_len = QT_UINT8 (buffer + 50);
8261 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8262 (char *) buffer + 51);
8264 GST_WARNING_OBJECT (qtdemux,
8265 "compressorname length too big (%u > 31)", str_len);
8267 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8269 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8274 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8276 /* You are reading this correctly. QTFF specifies that the
8277 * metadata atom is a short atom, whereas ISO BMFF specifies
8278 * it's a full atom. But since so many people are doing things
8279 * differently, we actually peek into the atom to see which
8282 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8283 GST_FOURCC_ARGS (fourcc));
8286 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8287 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8288 * starts with a 'hdlr' atom */
8289 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8290 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8291 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8292 * with version/flags both set to zero */
8293 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8295 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8300 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8301 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8302 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8311 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8312 GST_FOURCC_ARGS (fourcc));
8316 version = QT_UINT32 (buffer + 12);
8317 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8324 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8329 if (length < offset) {
8330 GST_WARNING_OBJECT (qtdemux,
8331 "skipping too small %" GST_FOURCC_FORMAT " box",
8332 GST_FOURCC_ARGS (fourcc));
8335 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8341 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8346 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8351 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8355 if (!strcmp (type->name, "unknown"))
8356 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8360 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8361 GST_FOURCC_ARGS (fourcc));
8367 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8368 (_("This file is corrupt and cannot be played.")),
8369 ("Not enough data for an atom header, got only %u bytes", length));
8374 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8375 (_("This file is corrupt and cannot be played.")),
8376 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8377 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8384 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8386 /* FIXME: This can only reliably work if demuxers have a
8387 * separate streaming thread per srcpad. This should be
8388 * done in a demuxer base class, which integrates parts
8391 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8396 query = gst_query_new_allocation (stream->caps, FALSE);
8398 if (!gst_pad_peer_query (stream->pad, query)) {
8399 /* not a problem, just debug a little */
8400 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8403 if (stream->allocator)
8404 gst_object_unref (stream->allocator);
8406 if (gst_query_get_n_allocation_params (query) > 0) {
8407 /* try the allocator */
8408 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8410 stream->use_allocator = TRUE;
8412 stream->allocator = NULL;
8413 gst_allocation_params_init (&stream->params);
8414 stream->use_allocator = FALSE;
8416 gst_query_unref (query);
8421 pad_query (const GValue * item, GValue * value, gpointer user_data)
8423 GstPad *pad = g_value_get_object (item);
8424 GstQuery *query = user_data;
8427 res = gst_pad_peer_query (pad, query);
8430 g_value_set_boolean (value, TRUE);
8434 GST_INFO_OBJECT (pad, "pad peer query failed");
8439 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8440 GstPadDirection direction)
8443 GstIteratorFoldFunction func = pad_query;
8444 GValue res = { 0, };
8446 g_value_init (&res, G_TYPE_BOOLEAN);
8447 g_value_set_boolean (&res, FALSE);
8450 if (direction == GST_PAD_SRC)
8451 it = gst_element_iterate_src_pads (element);
8453 it = gst_element_iterate_sink_pads (element);
8455 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8456 gst_iterator_resync (it);
8458 gst_iterator_free (it);
8460 return g_value_get_boolean (&res);
8464 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8465 QtDemuxStream * stream)
8469 GstElement *element = GST_ELEMENT (qtdemux);
8471 gchar **filtered_sys_ids;
8472 GValue event_list = G_VALUE_INIT;
8475 /* 1. Check if we already have the context. */
8476 if (qtdemux->preferred_protection_system_id != NULL) {
8477 GST_LOG_OBJECT (element,
8478 "already have the protection context, no need to request it again");
8482 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8483 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8484 (const gchar **) qtdemux->protection_system_ids->pdata);
8486 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8487 qtdemux->protection_system_ids->len - 1);
8488 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8489 "decryptors for %u of them, running context request",
8490 qtdemux->protection_system_ids->len,
8491 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8494 if (stream->protection_scheme_event_queue.length) {
8495 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8496 stream->protection_scheme_event_queue.length);
8497 walk = stream->protection_scheme_event_queue.tail;
8499 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8500 qtdemux->protection_event_queue.length);
8501 walk = qtdemux->protection_event_queue.tail;
8504 g_value_init (&event_list, GST_TYPE_LIST);
8505 for (; walk; walk = g_list_previous (walk)) {
8506 GValue *event_value = g_new0 (GValue, 1);
8507 g_value_init (event_value, GST_TYPE_EVENT);
8508 g_value_set_boxed (event_value, walk->data);
8509 gst_value_list_append_and_take_value (&event_list, event_value);
8512 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8513 * check if downstream already has a context of the specific type
8514 * 2b) Query upstream as above.
8516 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8517 st = gst_query_writable_structure (query);
8518 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8519 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8521 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8522 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8523 gst_query_parse_context (query, &ctxt);
8524 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8525 gst_element_set_context (element, ctxt);
8526 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8527 gst_query_parse_context (query, &ctxt);
8528 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8529 gst_element_set_context (element, ctxt);
8531 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8532 * the required context type and afterwards check if a
8533 * usable context was set now as in 1). The message could
8534 * be handled by the parent bins of the element and the
8539 GST_INFO_OBJECT (element, "posting need context message");
8540 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8541 "drm-preferred-decryption-system-id");
8542 st = (GstStructure *) gst_message_get_structure (msg);
8543 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8544 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8547 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8548 gst_element_post_message (element, msg);
8551 g_strfreev (filtered_sys_ids);
8552 g_value_unset (&event_list);
8553 gst_query_unref (query);
8557 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8558 QtDemuxStream * stream)
8561 const gchar *selected_system = NULL;
8563 g_return_val_if_fail (qtdemux != NULL, FALSE);
8564 g_return_val_if_fail (stream != NULL, FALSE);
8565 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8568 if (stream->protection_scheme_type == FOURCC_aavd) {
8569 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8570 if (!gst_structure_has_name (s, "application/x-aavd")) {
8571 gst_structure_set (s,
8572 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8574 gst_structure_set_name (s, "application/x-aavd");
8579 if (stream->protection_scheme_type != FOURCC_cenc
8580 && stream->protection_scheme_type != FOURCC_cbcs) {
8581 GST_ERROR_OBJECT (qtdemux,
8582 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8583 GST_FOURCC_ARGS (stream->protection_scheme_type));
8587 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8588 if (!gst_structure_has_name (s, "application/x-cenc")) {
8589 gst_structure_set (s,
8590 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
8591 gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
8592 (stream->protection_scheme_type == FOURCC_cbcs) ? "cbcs" : "cenc",
8594 gst_structure_set_name (s, "application/x-cenc");
8597 if (qtdemux->protection_system_ids == NULL) {
8598 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
8599 "cenc protection system information has been found, not setting a "
8600 "protection system UUID");
8604 gst_qtdemux_request_protection_context (qtdemux, stream);
8605 if (qtdemux->preferred_protection_system_id != NULL) {
8606 const gchar *preferred_system_array[] =
8607 { qtdemux->preferred_protection_system_id, NULL };
8609 selected_system = gst_protection_select_system (preferred_system_array);
8611 if (selected_system) {
8612 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8613 qtdemux->preferred_protection_system_id);
8615 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8616 "because there is no available decryptor",
8617 qtdemux->preferred_protection_system_id);
8621 if (!selected_system) {
8622 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8623 selected_system = gst_protection_select_system ((const gchar **)
8624 qtdemux->protection_system_ids->pdata);
8625 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8626 qtdemux->protection_system_ids->len - 1);
8629 if (!selected_system) {
8630 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8631 "suitable decryptor element has been found");
8635 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8638 gst_structure_set (s,
8639 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8646 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
8648 /* fps is calculated base on the duration of the average framerate since
8649 * qt does not have a fixed framerate. */
8650 gboolean fps_available = TRUE;
8651 guint32 first_duration = 0;
8653 if (stream->n_samples > 0)
8654 first_duration = stream->samples[0].duration;
8656 if ((stream->n_samples == 1 && first_duration == 0)
8657 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8659 CUR_STREAM (stream)->fps_n = 0;
8660 CUR_STREAM (stream)->fps_d = 1;
8662 if (stream->duration == 0 || stream->n_samples < 2) {
8663 CUR_STREAM (stream)->fps_n = stream->timescale;
8664 CUR_STREAM (stream)->fps_d = 1;
8665 fps_available = FALSE;
8667 GstClockTime avg_duration;
8671 /* duration and n_samples can be updated for fragmented format
8672 * so, framerate of fragmented format is calculated using data in a moof */
8673 if (qtdemux->fragmented && stream->n_samples_moof > 0
8674 && stream->duration_moof > 0) {
8675 n_samples = stream->n_samples_moof;
8676 duration = stream->duration_moof;
8678 n_samples = stream->n_samples;
8679 duration = stream->duration;
8682 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8683 /* stream->duration is guint64, timescale, n_samples are guint32 */
8685 gst_util_uint64_scale_round (duration -
8686 first_duration, GST_SECOND,
8687 (guint64) (stream->timescale) * (n_samples - 1));
8689 GST_LOG_OBJECT (qtdemux,
8690 "Calculating avg sample duration based on stream (or moof) duration %"
8692 " minus first sample %u, leaving %d samples gives %"
8693 GST_TIME_FORMAT, duration, first_duration,
8694 n_samples - 1, GST_TIME_ARGS (avg_duration));
8697 gst_video_guess_framerate (avg_duration,
8698 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
8700 GST_DEBUG_OBJECT (qtdemux,
8701 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8702 stream->timescale, CUR_STREAM (stream)->fps_n,
8703 CUR_STREAM (stream)->fps_d);
8707 return fps_available;
8711 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8713 if (stream->subtype == FOURCC_vide) {
8714 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8716 if (CUR_STREAM (stream)->caps) {
8717 CUR_STREAM (stream)->caps =
8718 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8720 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
8721 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8722 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8723 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8725 /* set framerate if calculated framerate is reliable */
8726 if (fps_available) {
8727 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8728 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8729 CUR_STREAM (stream)->fps_d, NULL);
8732 /* calculate pixel-aspect-ratio using display width and height */
8733 GST_DEBUG_OBJECT (qtdemux,
8734 "video size %dx%d, target display size %dx%d",
8735 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8736 stream->display_width, stream->display_height);
8737 /* qt file might have pasp atom */
8738 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8739 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8740 CUR_STREAM (stream)->par_h);
8741 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8742 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8743 CUR_STREAM (stream)->par_h, NULL);
8744 } else if (stream->display_width > 0 && stream->display_height > 0
8745 && CUR_STREAM (stream)->width > 0
8746 && CUR_STREAM (stream)->height > 0) {
8749 /* calculate the pixel aspect ratio using the display and pixel w/h */
8750 n = stream->display_width * CUR_STREAM (stream)->height;
8751 d = stream->display_height * CUR_STREAM (stream)->width;
8754 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8755 CUR_STREAM (stream)->par_w = n;
8756 CUR_STREAM (stream)->par_h = d;
8757 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8758 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8759 CUR_STREAM (stream)->par_h, NULL);
8762 if (CUR_STREAM (stream)->interlace_mode > 0) {
8763 if (CUR_STREAM (stream)->interlace_mode == 1) {
8764 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8765 G_TYPE_STRING, "progressive", NULL);
8766 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8767 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8768 G_TYPE_STRING, "interleaved", NULL);
8769 if (CUR_STREAM (stream)->field_order == 9) {
8770 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8771 G_TYPE_STRING, "top-field-first", NULL);
8772 } else if (CUR_STREAM (stream)->field_order == 14) {
8773 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8774 G_TYPE_STRING, "bottom-field-first", NULL);
8779 /* Create incomplete colorimetry here if needed */
8780 if (CUR_STREAM (stream)->colorimetry.range ||
8781 CUR_STREAM (stream)->colorimetry.matrix ||
8782 CUR_STREAM (stream)->colorimetry.transfer
8783 || CUR_STREAM (stream)->colorimetry.primaries) {
8784 gchar *colorimetry =
8785 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8786 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8787 G_TYPE_STRING, colorimetry, NULL);
8788 g_free (colorimetry);
8791 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8792 guint par_w = 1, par_h = 1;
8794 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8795 par_w = CUR_STREAM (stream)->par_w;
8796 par_h = CUR_STREAM (stream)->par_h;
8799 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8800 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8802 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8805 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8806 "multiview-mode", G_TYPE_STRING,
8807 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8808 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8809 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8814 else if (stream->subtype == FOURCC_soun) {
8815 if (CUR_STREAM (stream)->caps) {
8816 CUR_STREAM (stream)->caps =
8817 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8818 if (CUR_STREAM (stream)->rate > 0)
8819 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8820 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8821 if (CUR_STREAM (stream)->n_channels > 0)
8822 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8823 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8824 if (CUR_STREAM (stream)->n_channels > 2) {
8825 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8826 * correctly; this is just the minimum we can do - assume
8827 * we don't actually have any channel positions. */
8828 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8829 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8834 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
8835 const GstStructure *s;
8836 QtDemuxStream *fps_stream = NULL;
8837 gboolean fps_available = FALSE;
8839 /* CEA608 closed caption tracks are a bit special in that each sample
8840 * can contain CCs for multiple frames, and CCs can be omitted and have to
8841 * be inferred from the duration of the sample then.
8843 * As such we take the framerate from the (first) video track here for
8844 * CEA608 as there must be one CC byte pair for every video frame
8845 * according to the spec.
8847 * For CEA708 all is fine and there is one sample per frame.
8850 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8851 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
8854 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
8855 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
8857 if (tmp->subtype == FOURCC_vide) {
8864 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
8865 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
8866 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
8869 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8870 fps_stream = stream;
8873 CUR_STREAM (stream)->caps =
8874 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8876 /* set framerate if calculated framerate is reliable */
8877 if (fps_available) {
8878 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8879 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8880 CUR_STREAM (stream)->fps_d, NULL);
8885 gboolean forward_collection = FALSE;
8886 GstCaps *prev_caps = NULL;
8888 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8889 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8890 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8891 gst_pad_set_active (stream->pad, TRUE);
8893 gst_pad_use_fixed_caps (stream->pad);
8895 if (stream->protected) {
8896 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8897 GST_ERROR_OBJECT (qtdemux,
8898 "Failed to configure protected stream caps.");
8903 if (stream->new_stream) {
8905 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8908 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8911 gst_event_parse_stream_flags (event, &stream_flags);
8912 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8913 qtdemux->have_group_id = TRUE;
8915 qtdemux->have_group_id = FALSE;
8916 gst_event_unref (event);
8917 } else if (!qtdemux->have_group_id) {
8918 qtdemux->have_group_id = TRUE;
8919 qtdemux->group_id = gst_util_group_id_next ();
8922 stream->new_stream = FALSE;
8923 event = gst_event_new_stream_start (stream->stream_id);
8924 if (qtdemux->have_group_id)
8925 gst_event_set_group_id (event, qtdemux->group_id);
8926 if (stream->disabled)
8927 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8928 if (CUR_STREAM (stream)->sparse) {
8929 stream_flags |= GST_STREAM_FLAG_SPARSE;
8931 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8933 gst_event_set_stream_flags (event, stream_flags);
8934 gst_pad_push_event (stream->pad, event);
8936 forward_collection = TRUE;
8939 prev_caps = gst_pad_get_current_caps (stream->pad);
8941 if (CUR_STREAM (stream)->caps) {
8943 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8944 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8945 CUR_STREAM (stream)->caps);
8946 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8948 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8951 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8955 gst_caps_unref (prev_caps);
8956 stream->new_caps = FALSE;
8958 if (forward_collection) {
8959 /* Forward upstream collection and selection if any */
8960 GstEvent *upstream_event = gst_pad_get_sticky_event (qtdemux->sinkpad,
8961 GST_EVENT_STREAM_COLLECTION, 0);
8963 gst_pad_push_event (stream->pad, upstream_event);
8970 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8971 QtDemuxStream * stream)
8973 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8976 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8977 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8978 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8979 stream->stsd_entries_length)) {
8980 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8981 (_("This file is invalid and cannot be played.")),
8982 ("New sample description id is out of bounds (%d >= %d)",
8983 stream->stsd_sample_description_id, stream->stsd_entries_length));
8985 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8986 stream->new_caps = TRUE;
8991 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8992 QtDemuxStream * stream, GstTagList * list)
8994 gboolean ret = TRUE;
8996 if (stream->subtype == FOURCC_vide) {
8997 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9000 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9003 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9004 gst_object_unref (stream->pad);
9010 qtdemux->n_video_streams++;
9011 } else if (stream->subtype == FOURCC_soun) {
9012 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9015 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9017 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9018 gst_object_unref (stream->pad);
9023 qtdemux->n_audio_streams++;
9024 } else if (stream->subtype == FOURCC_strm) {
9025 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9026 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9027 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9028 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
9029 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9032 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9034 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9035 gst_object_unref (stream->pad);
9040 qtdemux->n_sub_streams++;
9041 } else if (stream->subtype == FOURCC_meta) {
9042 gchar *name = g_strdup_printf ("meta_%u", qtdemux->n_meta_streams);
9045 gst_pad_new_from_static_template (&gst_qtdemux_metasrc_template, name);
9047 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9048 gst_object_unref (stream->pad);
9053 qtdemux->n_meta_streams++;
9054 } else if (CUR_STREAM (stream)->caps) {
9055 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9058 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9060 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9061 gst_object_unref (stream->pad);
9066 qtdemux->n_video_streams++;
9068 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9075 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9076 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9077 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9078 GST_OBJECT_LOCK (qtdemux);
9079 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9080 GST_OBJECT_UNLOCK (qtdemux);
9082 if (stream->stream_tags)
9083 gst_tag_list_unref (stream->stream_tags);
9084 stream->stream_tags = list;
9086 /* global tags go on each pad anyway */
9087 stream->send_global_tags = TRUE;
9088 /* send upstream GST_EVENT_PROTECTION events that were received before
9089 this source pad was created */
9090 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9091 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9095 gst_tag_list_unref (list);
9099 /* find next atom with @fourcc starting at @offset */
9100 static GstFlowReturn
9101 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9102 guint64 * length, guint32 fourcc)
9108 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9109 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9115 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9116 if (G_UNLIKELY (ret != GST_FLOW_OK))
9118 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9121 gst_buffer_unref (buf);
9124 gst_buffer_map (buf, &map, GST_MAP_READ);
9125 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9126 gst_buffer_unmap (buf, &map);
9127 gst_buffer_unref (buf);
9129 if (G_UNLIKELY (*length == 0)) {
9130 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9131 ret = GST_FLOW_ERROR;
9135 if (lfourcc == fourcc) {
9136 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9137 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9140 GST_LOG_OBJECT (qtdemux,
9141 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9142 GST_FOURCC_ARGS (lfourcc), *offset);
9143 if (*offset == G_MAXUINT64)
9153 /* might simply have had last one */
9154 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9159 /* should only do something in pull mode */
9160 /* call with OBJECT lock */
9161 static GstFlowReturn
9162 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9164 guint64 length, offset;
9165 GstBuffer *buf = NULL;
9166 GstFlowReturn ret = GST_FLOW_OK;
9167 GstFlowReturn res = GST_FLOW_OK;
9170 offset = qtdemux->moof_offset;
9171 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9174 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9175 return GST_FLOW_EOS;
9178 /* best not do pull etc with lock held */
9179 GST_OBJECT_UNLOCK (qtdemux);
9181 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9182 if (ret != GST_FLOW_OK)
9185 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9186 if (G_UNLIKELY (ret != GST_FLOW_OK))
9188 gst_buffer_map (buf, &map, GST_MAP_READ);
9189 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9190 gst_buffer_unmap (buf, &map);
9191 gst_buffer_unref (buf);
9196 gst_buffer_unmap (buf, &map);
9197 gst_buffer_unref (buf);
9201 /* look for next moof */
9202 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9203 if (G_UNLIKELY (ret != GST_FLOW_OK))
9207 GST_OBJECT_LOCK (qtdemux);
9209 qtdemux->moof_offset = offset;
9215 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9217 res = GST_FLOW_ERROR;
9222 /* maybe upstream temporarily flushing */
9223 if (ret != GST_FLOW_FLUSHING) {
9224 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9227 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9228 /* resume at current position next time */
9236 qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
9240 gint32 stts_duration;
9241 GstByteWriter stsc, stts, stsz;
9243 /* Each sample has a different size, which we don't support for merging */
9244 if (stream->sample_size == 0) {
9245 GST_DEBUG_OBJECT (qtdemux,
9246 "Not all samples have the same size, not merging");
9250 /* The stream has a ctts table, we don't support that */
9251 if (stream->ctts_present) {
9252 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
9256 /* If there's a sync sample table also ignore this stream */
9257 if (stream->stps_present || stream->stss_present) {
9258 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
9262 /* If chunks are considered samples already ignore this stream */
9263 if (stream->chunks_are_samples) {
9264 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
9268 /* Require that all samples have the same duration */
9269 if (stream->n_sample_times > 1) {
9270 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
9274 /* Parse the stts to get the sample duration and number of samples */
9275 gst_byte_reader_skip_unchecked (&stream->stts, 4);
9276 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9278 /* Parse the number of chunks from the stco manually because the
9279 * reader is already behind that */
9280 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
9282 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
9285 /* Now parse stsc, convert chunks into single samples and generate a
9286 * new stsc, stts and stsz from this information */
9287 gst_byte_writer_init (&stsc);
9288 gst_byte_writer_init (&stts);
9289 gst_byte_writer_init (&stsz);
9291 /* Note: we skip fourccs, size, version, flags and other fields of the new
9292 * atoms as the byte readers with them are already behind that position
9293 * anyway and only update the values of those inside the stream directly.
9295 stream->n_sample_times = 0;
9296 stream->n_samples = 0;
9297 for (i = 0; i < stream->n_samples_per_chunk; i++) {
9299 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
9301 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9302 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9303 sample_description_id =
9304 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9306 if (i == stream->n_samples_per_chunk - 1) {
9307 /* +1 because first_chunk is 1-based */
9308 last_chunk = num_chunks + 1;
9310 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9313 GST_DEBUG_OBJECT (qtdemux,
9314 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
9315 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
9317 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
9318 /* One sample in this chunk */
9319 gst_byte_writer_put_uint32_be (&stsc, 1);
9320 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
9322 /* For each chunk write a stts and stsz entry now */
9323 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
9324 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
9325 for (j = first_chunk; j < last_chunk; j++) {
9326 gst_byte_writer_put_uint32_be (&stsz,
9327 stream->sample_size * samples_per_chunk);
9330 stream->n_sample_times += 1;
9331 stream->n_samples += last_chunk - first_chunk;
9334 g_assert_cmpint (stream->n_samples, ==, num_chunks);
9336 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
9337 stream->n_samples, stream->n_sample_times);
9339 /* We don't have a fixed sample size anymore */
9340 stream->sample_size = 0;
9342 /* Free old data for the atoms */
9343 g_free ((gpointer) stream->stsz.data);
9344 stream->stsz.data = NULL;
9345 g_free ((gpointer) stream->stsc.data);
9346 stream->stsc.data = NULL;
9347 g_free ((gpointer) stream->stts.data);
9348 stream->stts.data = NULL;
9350 /* Store new data and replace byte readers */
9351 stream->stsz.size = gst_byte_writer_get_size (&stsz);
9352 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
9353 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
9354 stream->stts.size = gst_byte_writer_get_size (&stts);
9355 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
9356 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
9357 stream->stsc.size = gst_byte_writer_get_size (&stsc);
9358 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
9359 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
9362 /* initialise bytereaders for stbl sub-atoms */
9364 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9366 stream->stbl_index = -1; /* no samples have yet been parsed */
9367 stream->sample_index = -1;
9369 /* time-to-sample atom */
9370 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9373 /* copy atom data into a new buffer for later use */
9374 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
9376 /* skip version + flags */
9377 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9378 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9380 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9382 /* make sure there's enough data */
9383 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9384 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9385 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9386 stream->n_sample_times);
9387 if (!stream->n_sample_times)
9391 /* sync sample atom */
9392 stream->stps_present = FALSE;
9393 if ((stream->stss_present =
9394 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9395 &stream->stss) ? TRUE : FALSE) == TRUE) {
9396 /* copy atom data into a new buffer for later use */
9397 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
9399 /* skip version + flags */
9400 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9401 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9404 if (stream->n_sample_syncs) {
9405 /* make sure there's enough data */
9406 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9410 /* partial sync sample atom */
9411 if ((stream->stps_present =
9412 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9413 &stream->stps) ? TRUE : FALSE) == TRUE) {
9414 /* copy atom data into a new buffer for later use */
9415 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
9417 /* skip version + flags */
9418 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9419 !gst_byte_reader_get_uint32_be (&stream->stps,
9420 &stream->n_sample_partial_syncs))
9423 /* if there are no entries, the stss table contains the real
9425 if (stream->n_sample_partial_syncs) {
9426 /* make sure there's enough data */
9427 if (!qt_atom_parser_has_chunks (&stream->stps,
9428 stream->n_sample_partial_syncs, 4))
9435 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9438 /* copy atom data into a new buffer for later use */
9439 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
9441 /* skip version + flags */
9442 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9443 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9446 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9449 if (!stream->n_samples)
9452 /* sample-to-chunk atom */
9453 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9456 /* copy atom data into a new buffer for later use */
9457 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
9459 /* skip version + flags */
9460 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9461 !gst_byte_reader_get_uint32_be (&stream->stsc,
9462 &stream->n_samples_per_chunk))
9465 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9466 stream->n_samples_per_chunk);
9468 /* make sure there's enough data */
9469 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9475 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9476 stream->co_size = sizeof (guint32);
9477 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9479 stream->co_size = sizeof (guint64);
9483 /* copy atom data into a new buffer for later use */
9484 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
9486 /* skip version + flags */
9487 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9490 /* chunks_are_samples == TRUE means treat chunks as samples */
9491 stream->chunks_are_samples = stream->sample_size
9492 && !CUR_STREAM (stream)->sampled;
9493 if (stream->chunks_are_samples) {
9494 /* treat chunks as samples */
9495 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9498 /* skip number of entries */
9499 if (!gst_byte_reader_skip (&stream->stco, 4))
9502 /* make sure there are enough data in the stsz atom */
9503 if (!stream->sample_size) {
9504 /* different sizes for each sample */
9505 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9510 /* composition time-to-sample */
9511 if ((stream->ctts_present =
9512 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9513 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9514 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9515 guint8 ctts_version;
9516 gboolean checked_ctts = FALSE;
9518 /* copy atom data into a new buffer for later use */
9519 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
9521 /* version 1 has signed offsets */
9522 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
9526 if (!gst_byte_reader_skip (&stream->ctts, 3)
9527 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9528 &stream->n_composition_times))
9531 /* make sure there's enough data */
9532 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9536 /* This is optional, if missing we iterate the ctts */
9537 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9538 guint8 cslg_version;
9540 /* cslg version 1 has 64 bit fields */
9541 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
9545 if (!gst_byte_reader_skip (&cslg, 3))
9548 if (cslg_version == 0) {
9549 gint32 composition_to_dts_shift;
9551 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
9554 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9556 gint64 composition_to_dts_shift;
9558 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
9561 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9564 gint32 cslg_least = 0;
9565 guint num_entries, pos;
9568 pos = gst_byte_reader_get_pos (&stream->ctts);
9569 num_entries = stream->n_composition_times;
9571 checked_ctts = TRUE;
9573 stream->cslg_shift = 0;
9575 for (i = 0; i < num_entries; i++) {
9578 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9579 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9580 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9581 * slightly inaccurate PTS could be more usable than corrupted one */
9582 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
9583 && ABS (offset) / 2 > stream->duration)) {
9584 GST_WARNING_OBJECT (qtdemux,
9585 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9586 " larger than duration %" G_GUINT64_FORMAT, offset,
9589 stream->cslg_shift = 0;
9590 stream->ctts_present = FALSE;
9594 /* Don't consider "no decode samples" with offset G_MININT32
9595 * for the DTS/PTS shift */
9596 if (offset != G_MININT32 && offset < cslg_least)
9597 cslg_least = offset;
9601 stream->cslg_shift = -cslg_least;
9603 stream->cslg_shift = 0;
9605 /* reset the reader so we can generate sample table */
9606 gst_byte_reader_set_pos (&stream->ctts, pos);
9609 /* Check if ctts values are looking reasonable if that didn't happen above */
9610 if (!checked_ctts) {
9611 guint num_entries, pos;
9614 pos = gst_byte_reader_get_pos (&stream->ctts);
9615 num_entries = stream->n_composition_times;
9617 for (i = 0; i < num_entries; i++) {
9620 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9621 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9622 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9623 * slightly inaccurate PTS could be more usable than corrupted one */
9624 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
9625 && ABS (offset) / 2 > stream->duration)) {
9626 GST_WARNING_OBJECT (qtdemux,
9627 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9628 " larger than duration %" G_GUINT64_FORMAT, offset,
9631 stream->cslg_shift = 0;
9632 stream->ctts_present = FALSE;
9637 /* reset the reader so we can generate sample table */
9638 gst_byte_reader_set_pos (&stream->ctts, pos);
9641 /* Ensure the cslg_shift value is consistent so we can use it
9642 * unconditionally to produce TS and Segment */
9643 stream->cslg_shift = 0;
9646 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
9647 stream->cslg_shift);
9649 /* For raw audio streams especially we might want to merge the samples
9650 * to not output one audio sample per buffer. We're doing this here
9651 * before allocating the sample tables so that from this point onwards
9652 * the number of container samples are static */
9653 if (stream->min_buffer_size > 0) {
9654 qtdemux_merge_sample_table (qtdemux, stream);
9658 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9659 stream->n_samples, (guint) sizeof (QtDemuxSample),
9660 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9662 if (stream->n_samples >=
9663 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9664 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9665 "be larger than %uMB (broken file?)", stream->n_samples,
9666 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9670 g_assert (stream->samples == NULL);
9671 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9672 if (!stream->samples) {
9673 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9682 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9683 (_("This file is corrupt and cannot be played.")), (NULL));
9688 gst_qtdemux_stbl_free (stream);
9689 if (!qtdemux->fragmented) {
9690 /* not quite good */
9691 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9694 /* may pick up samples elsewhere */
9700 /* collect samples from the next sample to be parsed up to sample @n for @stream
9701 * by reading the info from @stbl
9703 * This code can be executed from both the streaming thread and the seeking
9704 * thread so it takes the object lock to protect itself
9707 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9710 QtDemuxSample *samples, *first, *cur, *last;
9711 guint32 n_samples_per_chunk;
9714 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9715 GST_FOURCC_FORMAT ", pad %s",
9716 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9717 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9719 n_samples = stream->n_samples;
9722 goto out_of_samples;
9724 GST_OBJECT_LOCK (qtdemux);
9725 if (n <= stream->stbl_index)
9726 goto already_parsed;
9728 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9730 if (!stream->stsz.data) {
9731 /* so we already parsed and passed all the moov samples;
9732 * onto fragmented ones */
9733 g_assert (qtdemux->fragmented);
9737 /* pointer to the sample table */
9738 samples = stream->samples;
9740 /* starts from -1, moves to the next sample index to parse */
9741 stream->stbl_index++;
9743 /* keep track of the first and last sample to fill */
9744 first = &samples[stream->stbl_index];
9747 if (!stream->chunks_are_samples) {
9748 /* set the sample sizes */
9749 if (stream->sample_size == 0) {
9750 /* different sizes for each sample */
9751 for (cur = first; cur <= last; cur++) {
9752 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9753 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9754 (guint) (cur - samples), cur->size);
9757 /* samples have the same size */
9758 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9759 for (cur = first; cur <= last; cur++)
9760 cur->size = stream->sample_size;
9764 n_samples_per_chunk = stream->n_samples_per_chunk;
9767 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9770 if (stream->stsc_chunk_index >= stream->last_chunk
9771 || stream->stsc_chunk_index < stream->first_chunk) {
9772 stream->first_chunk =
9773 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9774 stream->samples_per_chunk =
9775 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9777 stream->stsd_sample_description_id =
9778 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9780 /* chunk numbers are counted from 1 it seems */
9781 if (G_UNLIKELY (stream->first_chunk == 0))
9784 --stream->first_chunk;
9786 /* the last chunk of each entry is calculated by taking the first chunk
9787 * of the next entry; except if there is no next, where we fake it with
9789 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9790 stream->last_chunk = G_MAXUINT32;
9792 stream->last_chunk =
9793 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9794 if (G_UNLIKELY (stream->last_chunk == 0))
9797 --stream->last_chunk;
9800 GST_LOG_OBJECT (qtdemux,
9801 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9802 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9803 stream->samples_per_chunk, stream->stsd_sample_description_id);
9805 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9808 if (stream->last_chunk != G_MAXUINT32) {
9809 if (!qt_atom_parser_peek_sub (&stream->stco,
9810 stream->first_chunk * stream->co_size,
9811 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9816 stream->co_chunk = stream->stco;
9817 if (!gst_byte_reader_skip (&stream->co_chunk,
9818 stream->first_chunk * stream->co_size))
9822 stream->stsc_chunk_index = stream->first_chunk;
9825 last_chunk = stream->last_chunk;
9827 if (stream->chunks_are_samples) {
9828 cur = &samples[stream->stsc_chunk_index];
9830 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9833 stream->stsc_chunk_index = j;
9838 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9841 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9842 "%" G_GUINT64_FORMAT, j, cur->offset);
9844 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9845 CUR_STREAM (stream)->bytes_per_frame > 0) {
9847 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9848 CUR_STREAM (stream)->samples_per_frame *
9849 CUR_STREAM (stream)->bytes_per_frame;
9851 cur->size = stream->samples_per_chunk;
9854 GST_DEBUG_OBJECT (qtdemux,
9855 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9856 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9857 stream->stco_sample_index)), cur->size);
9859 cur->timestamp = stream->stco_sample_index;
9860 cur->duration = stream->samples_per_chunk;
9861 cur->keyframe = TRUE;
9864 stream->stco_sample_index += stream->samples_per_chunk;
9866 stream->stsc_chunk_index = j;
9868 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9869 guint32 samples_per_chunk;
9870 guint64 chunk_offset;
9872 if (!stream->stsc_sample_index
9873 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9874 &stream->chunk_offset))
9877 samples_per_chunk = stream->samples_per_chunk;
9878 chunk_offset = stream->chunk_offset;
9880 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9881 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9882 G_GUINT64_FORMAT " and size %d",
9883 (guint) (cur - samples), chunk_offset, cur->size);
9885 cur->offset = chunk_offset;
9886 chunk_offset += cur->size;
9889 if (G_UNLIKELY (cur > last)) {
9891 stream->stsc_sample_index = k + 1;
9892 stream->chunk_offset = chunk_offset;
9893 stream->stsc_chunk_index = j;
9897 stream->stsc_sample_index = 0;
9899 stream->stsc_chunk_index = j;
9901 stream->stsc_index++;
9904 if (stream->chunks_are_samples)
9908 guint32 n_sample_times;
9910 n_sample_times = stream->n_sample_times;
9913 for (i = stream->stts_index; i < n_sample_times; i++) {
9914 guint32 stts_samples;
9915 gint32 stts_duration;
9918 if (stream->stts_sample_index >= stream->stts_samples
9919 || !stream->stts_sample_index) {
9921 stream->stts_samples =
9922 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9923 stream->stts_duration =
9924 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9926 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9927 i, stream->stts_samples, stream->stts_duration);
9929 stream->stts_sample_index = 0;
9932 stts_samples = stream->stts_samples;
9933 stts_duration = stream->stts_duration;
9934 stts_time = stream->stts_time;
9936 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9937 GST_DEBUG_OBJECT (qtdemux,
9938 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9939 (guint) (cur - samples), j,
9940 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9942 cur->timestamp = stts_time;
9943 cur->duration = stts_duration;
9945 /* avoid 32-bit wrap-around,
9946 * but still mind possible 'negative' duration */
9947 stts_time += (gint64) stts_duration;
9950 if (G_UNLIKELY (cur > last)) {
9952 stream->stts_time = stts_time;
9953 stream->stts_sample_index = j + 1;
9954 if (stream->stts_sample_index >= stream->stts_samples)
9955 stream->stts_index++;
9959 stream->stts_sample_index = 0;
9960 stream->stts_time = stts_time;
9961 stream->stts_index++;
9963 /* fill up empty timestamps with the last timestamp, this can happen when
9964 * the last samples do not decode and so we don't have timestamps for them.
9965 * We however look at the last timestamp to estimate the track length so we
9966 * need something in here. */
9967 for (; cur < last; cur++) {
9968 GST_DEBUG_OBJECT (qtdemux,
9969 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9970 (guint) (cur - samples),
9971 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9972 cur->timestamp = stream->stts_time;
9978 /* sample sync, can be NULL */
9979 if (stream->stss_present == TRUE) {
9980 guint32 n_sample_syncs;
9982 n_sample_syncs = stream->n_sample_syncs;
9984 if (!n_sample_syncs) {
9985 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9986 stream->all_keyframe = TRUE;
9988 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9989 /* note that the first sample is index 1, not 0 */
9992 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9994 if (G_LIKELY (index > 0 && index <= n_samples)) {
9996 samples[index].keyframe = TRUE;
9997 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9998 /* and exit if we have enough samples */
9999 if (G_UNLIKELY (index >= n)) {
10006 stream->stss_index = i;
10009 /* stps marks partial sync frames like open GOP I-Frames */
10010 if (stream->stps_present == TRUE) {
10011 guint32 n_sample_partial_syncs;
10013 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10015 /* if there are no entries, the stss table contains the real
10017 if (n_sample_partial_syncs) {
10018 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10019 /* note that the first sample is index 1, not 0 */
10022 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10024 if (G_LIKELY (index > 0 && index <= n_samples)) {
10026 samples[index].keyframe = TRUE;
10027 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10028 /* and exit if we have enough samples */
10029 if (G_UNLIKELY (index >= n)) {
10036 stream->stps_index = i;
10040 /* no stss, all samples are keyframes */
10041 stream->all_keyframe = TRUE;
10042 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10047 /* composition time to sample */
10048 if (stream->ctts_present == TRUE) {
10049 guint32 n_composition_times;
10050 guint32 ctts_count;
10051 gint32 ctts_soffset;
10053 /* Fill in the pts_offsets */
10055 n_composition_times = stream->n_composition_times;
10057 for (i = stream->ctts_index; i < n_composition_times; i++) {
10058 if (stream->ctts_sample_index >= stream->ctts_count
10059 || !stream->ctts_sample_index) {
10060 stream->ctts_count =
10061 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10062 stream->ctts_soffset =
10063 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10064 stream->ctts_sample_index = 0;
10067 ctts_count = stream->ctts_count;
10068 ctts_soffset = stream->ctts_soffset;
10070 /* FIXME: Set offset to 0 for "no decode samples". This needs
10071 * to be handled in a codec specific manner ideally. */
10072 if (ctts_soffset == G_MININT32)
10075 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10076 cur->pts_offset = ctts_soffset;
10079 if (G_UNLIKELY (cur > last)) {
10081 stream->ctts_sample_index = j + 1;
10085 stream->ctts_sample_index = 0;
10086 stream->ctts_index++;
10090 stream->stbl_index = n;
10091 /* if index has been completely parsed, free data that is no-longer needed */
10092 if (n + 1 == stream->n_samples) {
10093 gst_qtdemux_stbl_free (stream);
10094 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10095 if (qtdemux->pullbased) {
10096 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10097 while (n + 1 == stream->n_samples)
10098 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10102 GST_OBJECT_UNLOCK (qtdemux);
10109 GST_LOG_OBJECT (qtdemux,
10110 "Tried to parse up to sample %u but this sample has already been parsed",
10112 /* if fragmented, there may be more */
10113 if (qtdemux->fragmented && n == stream->stbl_index)
10115 GST_OBJECT_UNLOCK (qtdemux);
10121 GST_LOG_OBJECT (qtdemux,
10122 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10123 stream->n_samples);
10124 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10125 (_("This file is corrupt and cannot be played.")), (NULL));
10130 GST_OBJECT_UNLOCK (qtdemux);
10131 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10132 (_("This file is corrupt and cannot be played.")), (NULL));
10137 /* collect all segment info for @stream.
10140 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10144 /* accept edts if they contain gaps at start and there is only
10145 * one media segment */
10146 gboolean allow_pushbased_edts = TRUE;
10147 gint media_segments_count = 0;
10149 /* parse and prepare segment info from the edit list */
10150 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10151 stream->n_segments = 0;
10152 stream->segments = NULL;
10153 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10156 gint segment_number, entry_size;
10158 GstClockTime stime;
10159 const guint8 *buffer;
10163 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10164 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10167 buffer = elst->data;
10169 size = QT_UINT32 (buffer);
10170 /* version, flags, n_segments */
10172 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10175 version = QT_UINT8 (buffer + 8);
10176 entry_size = (version == 1) ? 20 : 12;
10178 n_segments = QT_UINT32 (buffer + 12);
10180 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10181 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10185 /* we might allocate a bit too much, at least allocate 1 segment */
10186 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10188 /* segments always start from 0 */
10192 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10194 guint64 media_time;
10195 gboolean empty_edit = FALSE;
10196 QtDemuxSegment *segment;
10198 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10200 if (version == 1) {
10201 media_time = QT_UINT64 (buffer + 8);
10202 duration = QT_UINT64 (buffer);
10203 if (media_time == G_MAXUINT64)
10206 media_time = QT_UINT32 (buffer + 4);
10207 duration = QT_UINT32 (buffer);
10208 if (media_time == G_MAXUINT32)
10213 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
10215 segment = &stream->segments[segment_number];
10217 /* time and duration expressed in global timescale */
10218 segment->time = stime;
10219 if (duration != 0 || empty_edit) {
10220 /* edge case: empty edits with duration=zero are treated here.
10221 * (files should not have these anyway). */
10223 /* add non scaled values so we don't cause roundoff errors */
10225 stime = QTTIME_TO_GSTTIME (qtdemux, time);
10226 segment->duration = stime - segment->time;
10228 /* zero duration does not imply media_start == media_stop
10229 * but, only specify media_start. The edit ends with the track. */
10230 stime = segment->duration = GST_CLOCK_TIME_NONE;
10231 /* Don't allow more edits after this one. */
10232 n_segments = segment_number + 1;
10234 segment->stop_time = stime;
10236 segment->trak_media_start = media_time;
10237 /* media_time expressed in stream timescale */
10239 segment->media_start = media_start;
10240 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10241 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10242 media_segments_count++;
10244 segment->media_start = GST_CLOCK_TIME_NONE;
10245 segment->media_stop = GST_CLOCK_TIME_NONE;
10247 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
10249 if (rate_int <= 1) {
10250 /* 0 is not allowed, some programs write 1 instead of the floating point
10252 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10256 segment->rate = rate_int / 65536.0;
10259 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10260 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10261 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10262 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10263 segment_number, GST_TIME_ARGS (segment->time),
10264 GST_TIME_ARGS (segment->duration),
10265 GST_TIME_ARGS (segment->media_start), media_time,
10266 GST_TIME_ARGS (segment->media_stop),
10267 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10268 stream->timescale);
10269 if (segment->stop_time > qtdemux->segment.stop &&
10270 !qtdemux->upstream_format_is_time) {
10271 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10272 " extends to %" GST_TIME_FORMAT
10273 " past the end of the declared movie duration %" GST_TIME_FORMAT
10274 " movie segment will be extended", segment_number,
10275 GST_TIME_ARGS (segment->stop_time),
10276 GST_TIME_ARGS (qtdemux->segment.stop));
10277 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10280 buffer += entry_size;
10282 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10283 stream->n_segments = n_segments;
10284 if (media_segments_count != 1)
10285 allow_pushbased_edts = FALSE;
10289 /* push based does not handle segments, so act accordingly here,
10290 * and warn if applicable */
10291 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10292 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10293 /* remove and use default one below, we stream like it anyway */
10294 g_free (stream->segments);
10295 stream->segments = NULL;
10296 stream->n_segments = 0;
10299 /* no segments, create one to play the complete trak */
10300 if (stream->n_segments == 0) {
10301 GstClockTime stream_duration =
10302 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
10304 if (stream->segments == NULL)
10305 stream->segments = g_new (QtDemuxSegment, 1);
10307 /* represent unknown our way */
10308 if (stream_duration == 0)
10309 stream_duration = GST_CLOCK_TIME_NONE;
10311 stream->segments[0].time = 0;
10312 stream->segments[0].stop_time = stream_duration;
10313 stream->segments[0].duration = stream_duration;
10314 stream->segments[0].media_start = 0;
10315 stream->segments[0].media_stop = stream_duration;
10316 stream->segments[0].rate = 1.0;
10317 stream->segments[0].trak_media_start = 0;
10319 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10320 GST_TIME_ARGS (stream_duration));
10321 stream->n_segments = 1;
10322 stream->dummy_segment = TRUE;
10324 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10330 * Parses the stsd atom of a svq3 trak looking for
10331 * the SMI and gama atoms.
10334 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
10335 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10337 const guint8 *_gamma = NULL;
10338 GstBuffer *_seqh = NULL;
10339 const guint8 *stsd_data = stsd_entry_data;
10340 guint32 length = QT_UINT32 (stsd_data);
10344 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10350 version = QT_UINT16 (stsd_data);
10351 if (version == 3) {
10352 if (length >= 70) {
10355 while (length > 8) {
10356 guint32 fourcc, size;
10357 const guint8 *data;
10358 size = QT_UINT32 (stsd_data);
10359 fourcc = QT_FOURCC (stsd_data + 4);
10360 data = stsd_data + 8;
10363 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10364 "svq3 atom parsing");
10373 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10374 " for gama atom, expected 12", size);
10379 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
10381 if (_seqh != NULL) {
10382 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10383 " found, ignoring");
10385 seqh_size = QT_UINT32 (data + 4);
10386 if (seqh_size > 0) {
10387 _seqh = gst_buffer_new_and_alloc (seqh_size);
10388 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10395 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10396 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10400 if (size <= length) {
10406 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10409 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10410 G_GUINT16_FORMAT, version);
10420 } else if (_seqh) {
10421 gst_buffer_unref (_seqh);
10426 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10429 GstByteReader dref;
10433 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10434 * atom that might contain a 'data' atom with the rtsp uri.
10435 * This case was reported in bug #597497, some info about
10436 * the hndl atom can be found in TN1195
10438 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10439 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10442 guint32 dref_num_entries = 0;
10443 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10444 gst_byte_reader_skip (&dref, 4) &&
10445 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10448 /* search dref entries for hndl atom */
10449 for (i = 0; i < dref_num_entries; i++) {
10450 guint32 size = 0, type;
10451 guint8 string_len = 0;
10452 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10453 qt_atom_parser_get_fourcc (&dref, &type)) {
10454 if (type == FOURCC_hndl) {
10455 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10457 /* skip data reference handle bytes and the
10458 * following pascal string and some extra 4
10459 * bytes I have no idea what are */
10460 if (!gst_byte_reader_skip (&dref, 4) ||
10461 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10462 !gst_byte_reader_skip (&dref, string_len + 4)) {
10463 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10467 /* iterate over the atoms to find the data atom */
10468 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10472 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10473 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10474 if (atom_type == FOURCC_data) {
10475 const guint8 *uri_aux = NULL;
10477 /* found the data atom that might contain the rtsp uri */
10478 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10479 "hndl atom, interpreting it as an URI");
10480 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10482 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10483 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10485 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10486 "didn't contain a rtsp address");
10488 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10493 /* skipping to the next entry */
10494 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10497 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10504 /* skip to the next entry */
10505 if (!gst_byte_reader_skip (&dref, size - 8))
10508 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10511 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10517 #define AMR_NB_ALL_MODES 0x81ff
10518 #define AMR_WB_ALL_MODES 0x83ff
10520 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10522 /* The 'damr' atom is of the form:
10524 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10525 * 32 b 8 b 16 b 8 b 8 b
10527 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10528 * represents the highest mode used in the stream (and thus the maximum
10529 * bitrate), with a couple of special cases as seen below.
10532 /* Map of frame type ID -> bitrate */
10533 static const guint nb_bitrates[] = {
10534 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10536 static const guint wb_bitrates[] = {
10537 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10543 gst_buffer_map (buf, &map, GST_MAP_READ);
10545 if (map.size != 0x11) {
10546 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10550 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10551 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10552 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10556 mode_set = QT_UINT16 (map.data + 13);
10558 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10559 max_mode = 7 + (wb ? 1 : 0);
10561 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10562 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10564 if (max_mode == -1) {
10565 GST_DEBUG ("No mode indication was found (mode set) = %x",
10570 gst_buffer_unmap (buf, &map);
10571 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10574 gst_buffer_unmap (buf, &map);
10579 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10580 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10583 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10589 if (gst_byte_reader_get_remaining (reader) < 36)
10592 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10593 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10594 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10595 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10596 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10597 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10598 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10599 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10600 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10602 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10603 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10604 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10606 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10607 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10609 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10610 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10617 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10618 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10625 * This macro will only compare value abdegh, it expects cfi to have already
10628 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10629 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10631 /* only handle the cases where the last column has standard values */
10632 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10633 const gchar *rotation_tag = NULL;
10635 /* no rotation needed */
10636 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10638 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10639 rotation_tag = "rotate-90";
10640 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10641 rotation_tag = "rotate-180";
10642 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10643 rotation_tag = "rotate-270";
10645 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10648 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10649 GST_STR_NULL (rotation_tag));
10650 if (rotation_tag != NULL) {
10651 if (*taglist == NULL)
10652 *taglist = gst_tag_list_new_empty ();
10653 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10654 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10657 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10662 qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
10663 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10667 GstBuffer *adrm_buf = NULL;
10668 QtDemuxAavdEncryptionInfo *info;
10670 adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
10671 if (G_UNLIKELY (!adrm)) {
10672 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
10675 adrm_size = QT_UINT32 (adrm->data);
10676 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
10678 stream->protection_scheme_type = FOURCC_aavd;
10680 if (!stream->protection_scheme_info)
10681 stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
10683 info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
10685 if (info->default_properties)
10686 gst_structure_free (info->default_properties);
10687 info->default_properties = gst_structure_new ("application/x-aavd",
10688 "encrypted", G_TYPE_BOOLEAN, TRUE,
10689 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
10690 gst_buffer_unref (adrm_buf);
10692 *original_fmt = FOURCC_mp4a;
10696 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10697 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10698 * Common Encryption (cenc), the function will also parse the tenc box (defined
10699 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10700 * (typically an enc[v|a|t|s] sample entry); the function will set
10701 * @original_fmt to the fourcc of the original unencrypted stream format.
10702 * Returns TRUE if successful; FALSE otherwise. */
10704 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10705 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10711 QtDemuxCencSampleSetInfo *info;
10713 const guint8 *tenc_data;
10715 g_return_val_if_fail (qtdemux != NULL, FALSE);
10716 g_return_val_if_fail (stream != NULL, FALSE);
10717 g_return_val_if_fail (container != NULL, FALSE);
10718 g_return_val_if_fail (original_fmt != NULL, FALSE);
10720 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10721 if (G_UNLIKELY (!sinf)) {
10722 if (stream->protection_scheme_type == FOURCC_cenc
10723 || stream->protection_scheme_type == FOURCC_cbcs) {
10724 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10725 "mandatory for Common Encryption");
10731 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10732 if (G_UNLIKELY (!frma)) {
10733 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10737 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10738 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10739 GST_FOURCC_ARGS (*original_fmt));
10741 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10743 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10746 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10747 stream->protection_scheme_version =
10748 QT_UINT32 ((const guint8 *) schm->data + 16);
10750 GST_DEBUG_OBJECT (qtdemux,
10751 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10752 "protection_scheme_version: %#010x",
10753 GST_FOURCC_ARGS (stream->protection_scheme_type),
10754 stream->protection_scheme_version);
10756 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10758 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10761 if (stream->protection_scheme_type != FOURCC_cenc &&
10762 stream->protection_scheme_type != FOURCC_piff &&
10763 stream->protection_scheme_type != FOURCC_cbcs) {
10764 GST_ERROR_OBJECT (qtdemux,
10765 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10766 GST_FOURCC_ARGS (stream->protection_scheme_type));
10770 if (G_UNLIKELY (!stream->protection_scheme_info))
10771 stream->protection_scheme_info =
10772 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10774 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10776 if (stream->protection_scheme_type == FOURCC_cenc
10777 || stream->protection_scheme_type == FOURCC_cbcs) {
10778 guint8 is_encrypted;
10780 guint8 constant_iv_size = 0;
10781 const guint8 *default_kid;
10782 guint8 crypt_byte_block = 0;
10783 guint8 skip_byte_block = 0;
10784 const guint8 *constant_iv = NULL;
10786 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10788 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10789 "which is mandatory for Common Encryption");
10792 tenc_data = (const guint8 *) tenc->data + 12;
10793 is_encrypted = QT_UINT8 (tenc_data + 2);
10794 iv_size = QT_UINT8 (tenc_data + 3);
10795 default_kid = (tenc_data + 4);
10796 if (stream->protection_scheme_type == FOURCC_cbcs) {
10797 guint8 possible_pattern_info;
10798 if (iv_size == 0) {
10799 constant_iv_size = QT_UINT8 (tenc_data + 20);
10800 if (constant_iv_size != 8 && constant_iv_size != 16) {
10801 GST_ERROR_OBJECT (qtdemux,
10802 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
10805 constant_iv = (tenc_data + 21);
10807 possible_pattern_info = QT_UINT8 (tenc_data + 1);
10808 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
10809 skip_byte_block = possible_pattern_info & 0x0f;
10811 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
10812 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
10813 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
10814 } else if (stream->protection_scheme_type == FOURCC_piff) {
10816 static const guint8 piff_track_encryption_uuid[] = {
10817 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10818 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10821 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10823 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10824 "which is mandatory for Common Encryption");
10828 tenc_data = (const guint8 *) tenc->data + 8;
10829 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10830 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10831 GST_ERROR_OBJECT (qtdemux,
10832 "Unsupported track encryption box with uuid: %s", box_uuid);
10836 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10837 gst_byte_reader_init (&br, tenc_data, 20);
10838 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10839 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10842 stream->protection_scheme_type = FOURCC_cenc;
10849 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10850 QtDemuxStream ** stream2)
10852 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10856 qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
10861 /*parse svmi header if existing */
10862 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10864 guint len = QT_UINT32 ((guint8 *) svmi->data);
10865 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10867 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10868 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10869 guint8 frame_type, frame_layout;
10870 guint32 stereo_mono_change_count;
10875 /* MPEG-A stereo video */
10876 if (qtdemux->major_brand == FOURCC_ss02)
10877 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10879 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10880 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10881 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
10883 switch (frame_type) {
10885 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10888 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10891 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10894 /* mode 3 is primary/secondary view sequence, ie
10895 * left/right views in separate tracks. See section 7.2
10896 * of ISO/IEC 23000-11:2009 */
10897 /* In the future this might be supported using related
10898 * streams, like an enhancement track - if files like this
10900 GST_FIXME_OBJECT (qtdemux,
10901 "Implement stereo video in separate streams");
10904 if ((frame_layout & 0x1) == 0)
10905 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10907 GST_LOG_OBJECT (qtdemux,
10908 "StereoVideo: composition type: %u, is_left_first: %u",
10909 frame_type, frame_layout);
10911 if (stereo_mono_change_count > 1) {
10912 GST_FIXME_OBJECT (qtdemux,
10913 "Mixed-mono flags are not yet supported in qtdemux.");
10916 stream->multiview_mode = mode;
10917 stream->multiview_flags = flags;
10924 /* parse the traks.
10925 * With each track we associate a new QtDemuxStream that contains all the info
10927 * traks that do not decode to something (like strm traks) will not have a pad.
10930 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10932 GstByteReader tkhd;
10946 QtDemuxStream *stream = NULL;
10947 const guint8 *stsd_data;
10948 const guint8 *stsd_entry_data;
10949 guint remaining_stsd_len;
10950 guint stsd_entry_count;
10952 guint16 lang_code; /* quicktime lang code or packed iso code */
10954 guint32 tkhd_flags = 0;
10955 guint8 tkhd_version = 0;
10956 guint32 w = 0, h = 0;
10957 guint value_size, stsd_len, len;
10961 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10963 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10964 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10965 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10968 /* pick between 64 or 32 bits */
10969 value_size = tkhd_version == 1 ? 8 : 4;
10970 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10971 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10974 /* Check if current moov has duplicated track_id */
10975 if (qtdemux_find_stream (qtdemux, track_id))
10976 goto existing_stream;
10978 stream = _create_stream (qtdemux, track_id);
10979 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10981 /* need defaults for fragments */
10982 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10984 if ((tkhd_flags & 1) == 0)
10985 stream->disabled = TRUE;
10987 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10988 tkhd_version, tkhd_flags, stream->track_id);
10990 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10993 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10994 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10995 if (qtdemux->major_brand != FOURCC_mjp2 ||
10996 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11000 len = QT_UINT32 ((guint8 *) mdhd->data);
11001 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11002 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11003 if (version == 0x01000000) {
11006 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11007 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11008 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11012 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11013 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11014 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11017 if (lang_code < 0x400) {
11018 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11019 } else if (lang_code == 0x7fff) {
11020 stream->lang_id[0] = 0; /* unspecified */
11022 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11023 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11024 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11025 stream->lang_id[3] = 0;
11028 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11029 stream->timescale);
11030 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11032 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11033 lang_code, stream->lang_id);
11035 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11038 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11039 /* chapters track reference */
11040 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11042 gsize length = GST_READ_UINT32_BE (chap->data);
11043 if (qtdemux->chapters_track_id)
11044 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11046 if (length >= 12) {
11047 qtdemux->chapters_track_id =
11048 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11053 /* fragmented files may have bogus duration in moov */
11054 if (!qtdemux->fragmented &&
11055 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11056 guint64 tdur1, tdur2;
11058 /* don't overflow */
11059 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11060 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11063 * some of those trailers, nowadays, have prologue images that are
11064 * themselves video tracks as well. I haven't really found a way to
11065 * identify those yet, except for just looking at their duration. */
11066 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11067 GST_WARNING_OBJECT (qtdemux,
11068 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11069 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11070 "found, assuming preview image or something; skipping track",
11071 stream->duration, stream->timescale, qtdemux->duration,
11072 qtdemux->timescale);
11073 gst_qtdemux_stream_unref (stream);
11078 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11081 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11082 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11084 len = QT_UINT32 ((guint8 *) hdlr->data);
11086 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11087 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11088 GST_FOURCC_ARGS (stream->subtype));
11090 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11093 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11096 /* Parse out svmi (and later st3d/sv3d) atoms */
11097 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
11100 /* parse rest of tkhd */
11101 if (stream->subtype == FOURCC_vide) {
11104 /* version 1 uses some 64-bit ints */
11105 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11108 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11111 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11112 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11115 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11116 &stream->stream_tags);
11120 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11122 stsd_data = (const guint8 *) stsd->data;
11124 /* stsd should at least have one entry */
11125 stsd_len = QT_UINT32 (stsd_data);
11126 if (stsd_len < 24) {
11127 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11128 if (stream->subtype == FOURCC_vivo) {
11129 gst_qtdemux_stream_unref (stream);
11136 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11137 /* each stsd entry must contain at least 8 bytes */
11138 if (stream->stsd_entries_length == 0
11139 || stream->stsd_entries_length > stsd_len / 8) {
11140 stream->stsd_entries_length = 0;
11143 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11144 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11145 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11147 stsd_entry_data = stsd_data + 16;
11148 remaining_stsd_len = stsd_len - 16;
11149 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11151 gchar *codec = NULL;
11152 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11154 /* and that entry should fit within stsd */
11155 len = QT_UINT32 (stsd_entry_data);
11156 if (len > remaining_stsd_len)
11159 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11160 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11161 GST_FOURCC_ARGS (entry->fourcc));
11162 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11164 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11165 goto error_encrypted;
11167 if (fourcc == FOURCC_aavd) {
11168 if (stream->subtype != FOURCC_soun) {
11169 GST_ERROR_OBJECT (qtdemux,
11170 "Unexpeced stsd type 'aavd' outside 'soun' track");
11172 /* encrypted audio with sound sample description v0 */
11173 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11174 stream->protected = TRUE;
11175 if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
11176 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11180 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11181 /* FIXME this looks wrong, there might be multiple children
11182 * with the same type */
11183 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11184 stream->protected = TRUE;
11185 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11186 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11189 if (stream->subtype == FOURCC_vide) {
11194 gint depth, palette_size, palette_count;
11195 guint32 *palette_data = NULL;
11197 entry->sampled = TRUE;
11199 stream->display_width = w >> 16;
11200 stream->display_height = h >> 16;
11203 if (len < 86) /* TODO verify */
11206 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
11207 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
11208 entry->fps_n = 0; /* this is filled in later */
11209 entry->fps_d = 0; /* this is filled in later */
11210 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
11211 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
11213 /* if color_table_id is 0, ctab atom must follow; however some files
11214 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11215 * if color table is not present we'll correct the value */
11216 if (entry->color_table_id == 0 &&
11218 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
11219 entry->color_table_id = -1;
11222 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11223 entry->width, entry->height, entry->bits_per_sample,
11224 entry->color_table_id);
11226 depth = entry->bits_per_sample;
11228 /* more than 32 bits means grayscale */
11229 gray = (depth > 32);
11230 /* low 32 bits specify the depth */
11233 /* different number of palette entries is determined by depth. */
11235 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11236 palette_count = (1 << depth);
11237 palette_size = palette_count * 4;
11239 if (entry->color_table_id) {
11240 switch (palette_count) {
11244 palette_data = g_memdup2 (ff_qt_default_palette_2, palette_size);
11247 palette_data = g_memdup2 (ff_qt_default_palette_4, palette_size);
11252 g_memdup2 (ff_qt_grayscale_palette_16, palette_size);
11254 palette_data = g_memdup2 (ff_qt_default_palette_16, palette_size);
11259 g_memdup2 (ff_qt_grayscale_palette_256, palette_size);
11262 g_memdup2 (ff_qt_default_palette_256, palette_size);
11265 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11266 (_("The video in this file might not play correctly.")),
11267 ("unsupported palette depth %d", depth));
11271 gint i, j, start, end;
11277 start = QT_UINT32 (stsd_entry_data + offset + 70);
11278 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
11279 end = QT_UINT16 (stsd_entry_data + offset + 76);
11281 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11282 start, end, palette_count);
11289 if (len < 94 + (end - start) * 8)
11292 /* palette is always the same size */
11293 palette_data = g_malloc0 (256 * 4);
11294 palette_size = 256 * 4;
11296 for (j = 0, i = start; i <= end; j++, i++) {
11297 guint32 a, r, g, b;
11299 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11300 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11301 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11302 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
11304 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11305 (g & 0xff00) | (b >> 8);
11310 gst_caps_unref (entry->caps);
11313 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
11315 if (G_UNLIKELY (!entry->caps)) {
11316 g_free (palette_data);
11317 goto unknown_stream;
11321 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11322 GST_TAG_VIDEO_CODEC, codec, NULL);
11327 if (palette_data) {
11330 if (entry->rgb8_palette)
11331 gst_memory_unref (entry->rgb8_palette);
11332 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
11333 palette_data, palette_size, 0, palette_size, palette_data, g_free);
11335 s = gst_caps_get_structure (entry->caps, 0);
11337 /* non-raw video has a palette_data property. raw video has the palette as
11338 * an extra plane that we append to the output buffers before we push
11340 if (!gst_structure_has_name (s, "video/x-raw")) {
11341 GstBuffer *palette;
11343 palette = gst_buffer_new ();
11344 gst_buffer_append_memory (palette, entry->rgb8_palette);
11345 entry->rgb8_palette = NULL;
11347 gst_caps_set_simple (entry->caps, "palette_data",
11348 GST_TYPE_BUFFER, palette, NULL);
11349 gst_buffer_unref (palette);
11351 } else if (palette_count != 0) {
11352 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
11353 (NULL), ("Unsupported palette depth %d", depth));
11356 GST_LOG_OBJECT (qtdemux, "frame count: %u",
11357 QT_UINT16 (stsd_entry_data + offset + 32));
11363 /* pick 'the' stsd child */
11364 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11365 // We should skip parsing the stsd for non-protected streams if
11366 // the entry doesn't match the fourcc, since they don't change
11367 // format. However, for protected streams we can have partial
11368 // encryption, where parts of the stream are encrypted and parts
11369 // not. For both parts of such streams, we should ensure the
11370 // esds overrides are parsed for both from the stsd.
11371 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
11372 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
11374 else if (!stream->protected)
11379 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
11380 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
11381 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
11382 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
11386 const guint8 *pasp_data = (const guint8 *) pasp->data;
11387 gint len = QT_UINT32 (pasp_data);
11390 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
11391 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
11393 CUR_STREAM (stream)->par_w = 0;
11394 CUR_STREAM (stream)->par_h = 0;
11397 CUR_STREAM (stream)->par_w = 0;
11398 CUR_STREAM (stream)->par_h = 0;
11402 const guint8 *fiel_data = (const guint8 *) fiel->data;
11403 gint len = QT_UINT32 (fiel_data);
11406 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
11407 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
11412 const guint8 *colr_data = (const guint8 *) colr->data;
11413 gint len = QT_UINT32 (colr_data);
11415 if (len == 19 || len == 18) {
11416 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
11418 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
11419 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
11420 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
11421 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
11422 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
11424 CUR_STREAM (stream)->colorimetry.primaries =
11425 gst_video_color_primaries_from_iso (primaries);
11426 CUR_STREAM (stream)->colorimetry.transfer =
11427 gst_video_transfer_function_from_iso (transfer_function);
11428 CUR_STREAM (stream)->colorimetry.matrix =
11429 gst_video_color_matrix_from_iso (matrix);
11430 CUR_STREAM (stream)->colorimetry.range =
11431 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
11432 GST_VIDEO_COLOR_RANGE_16_235;
11434 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
11437 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
11442 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11443 stream->stream_tags);
11450 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11451 const guint8 *avc_data = stsd_entry_data + 0x56;
11454 while (len >= 0x8) {
11457 if (QT_UINT32 (avc_data) <= len)
11458 size = QT_UINT32 (avc_data) - 0x8;
11463 /* No real data, so break out */
11466 switch (QT_FOURCC (avc_data + 0x4)) {
11469 /* parse, if found */
11472 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11474 /* First 4 bytes are the length of the atom, the next 4 bytes
11475 * are the fourcc, the next 1 byte is the version, and the
11476 * subsequent bytes are profile_tier_level structure like data. */
11477 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11478 avc_data + 8 + 1, size - 1);
11479 buf = gst_buffer_new_and_alloc (size);
11480 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11481 gst_caps_set_simple (entry->caps,
11482 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11483 gst_buffer_unref (buf);
11491 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11493 /* First 4 bytes are the length of the atom, the next 4 bytes
11494 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11495 * next 1 byte is the version, and the
11496 * subsequent bytes are sequence parameter set like data. */
11498 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11500 gst_codec_utils_h264_caps_set_level_and_profile
11501 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11503 buf = gst_buffer_new_and_alloc (size);
11504 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11505 gst_caps_set_simple (entry->caps,
11506 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11507 gst_buffer_unref (buf);
11513 guint avg_bitrate, max_bitrate;
11515 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11519 max_bitrate = QT_UINT32 (avc_data + 0xc);
11520 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11522 if (!max_bitrate && !avg_bitrate)
11525 /* Some muxers seem to swap the average and maximum bitrates
11526 * (I'm looking at you, YouTube), so we swap for sanity. */
11527 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11528 guint temp = avg_bitrate;
11530 avg_bitrate = max_bitrate;
11531 max_bitrate = temp;
11534 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11535 gst_tag_list_add (stream->stream_tags,
11536 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11537 max_bitrate, NULL);
11539 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11540 gst_tag_list_add (stream->stream_tags,
11541 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11553 avc_data += size + 8;
11564 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11565 const guint8 *hevc_data = stsd_entry_data + 0x56;
11568 while (len >= 0x8) {
11571 if (QT_UINT32 (hevc_data) <= len)
11572 size = QT_UINT32 (hevc_data) - 0x8;
11577 /* No real data, so break out */
11580 switch (QT_FOURCC (hevc_data + 0x4)) {
11583 /* parse, if found */
11586 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11588 /* First 4 bytes are the length of the atom, the next 4 bytes
11589 * are the fourcc, the next 1 byte is the version, and the
11590 * subsequent bytes are sequence parameter set like data. */
11591 gst_codec_utils_h265_caps_set_level_tier_and_profile
11592 (entry->caps, hevc_data + 8 + 1, size - 1);
11594 buf = gst_buffer_new_and_alloc (size);
11595 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11596 gst_caps_set_simple (entry->caps,
11597 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11598 gst_buffer_unref (buf);
11605 hevc_data += size + 8;
11618 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11619 GST_FOURCC_ARGS (fourcc));
11621 /* codec data might be in glbl extension atom */
11623 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11629 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11631 len = QT_UINT32 (data);
11634 buf = gst_buffer_new_and_alloc (len);
11635 gst_buffer_fill (buf, 0, data + 8, len);
11636 gst_caps_set_simple (entry->caps,
11637 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11638 gst_buffer_unref (buf);
11645 /* see annex I of the jpeg2000 spec */
11646 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11647 const guint8 *data;
11648 const gchar *colorspace = NULL;
11650 guint32 ncomp_map = 0;
11651 gint32 *comp_map = NULL;
11652 guint32 nchan_def = 0;
11653 gint32 *chan_def = NULL;
11655 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
11656 /* some required atoms */
11657 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11660 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
11664 /* number of components; redundant with info in codestream, but useful
11666 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
11667 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
11669 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
11671 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
11674 GST_DEBUG_OBJECT (qtdemux, "found colr");
11675 /* extract colour space info */
11676 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
11677 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
11679 colorspace = "sRGB";
11682 colorspace = "GRAY";
11685 colorspace = "sYUV";
11693 /* colr is required, and only values 16, 17, and 18 are specified,
11694 so error if we have no colorspace */
11697 /* extract component mapping */
11698 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
11700 guint32 cmap_len = 0;
11702 cmap_len = QT_UINT32 (cmap->data);
11703 if (cmap_len >= 8) {
11704 /* normal box, subtract off header */
11706 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
11707 if (cmap_len % 4 == 0) {
11708 ncomp_map = (cmap_len / 4);
11709 comp_map = g_new0 (gint32, ncomp_map);
11710 for (i = 0; i < ncomp_map; i++) {
11713 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11714 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11715 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11716 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11721 /* extract channel definitions */
11722 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11724 guint32 cdef_len = 0;
11726 cdef_len = QT_UINT32 (cdef->data);
11727 if (cdef_len >= 10) {
11728 /* normal box, subtract off header and len */
11730 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11731 if (cdef_len % 6 == 0) {
11732 nchan_def = (cdef_len / 6);
11733 chan_def = g_new0 (gint32, nchan_def);
11734 for (i = 0; i < nchan_def; i++)
11736 for (i = 0; i < nchan_def; i++) {
11737 guint16 cn, typ, asoc;
11738 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11739 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11740 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11741 if (cn < nchan_def) {
11744 chan_def[cn] = asoc;
11747 chan_def[cn] = 0; /* alpha */
11750 chan_def[cn] = -typ;
11758 gst_caps_set_simple (entry->caps,
11759 "num-components", G_TYPE_INT, ncomp, NULL);
11760 gst_caps_set_simple (entry->caps,
11761 "colorspace", G_TYPE_STRING, colorspace, NULL);
11764 GValue arr = { 0, };
11765 GValue elt = { 0, };
11767 g_value_init (&arr, GST_TYPE_ARRAY);
11768 g_value_init (&elt, G_TYPE_INT);
11769 for (i = 0; i < ncomp_map; i++) {
11770 g_value_set_int (&elt, comp_map[i]);
11771 gst_value_array_append_value (&arr, &elt);
11773 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11774 "component-map", &arr);
11775 g_value_unset (&elt);
11776 g_value_unset (&arr);
11781 GValue arr = { 0, };
11782 GValue elt = { 0, };
11784 g_value_init (&arr, GST_TYPE_ARRAY);
11785 g_value_init (&elt, G_TYPE_INT);
11786 for (i = 0; i < nchan_def; i++) {
11787 g_value_set_int (&elt, chan_def[i]);
11788 gst_value_array_append_value (&arr, &elt);
11790 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11791 "channel-definitions", &arr);
11792 g_value_unset (&elt);
11793 g_value_unset (&arr);
11797 /* some optional atoms */
11798 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11799 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11801 /* indicate possible fields in caps */
11803 data = (guint8 *) field->data + 8;
11805 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11806 (gint) * data, NULL);
11808 /* add codec_data if provided */
11813 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11814 data = prefix->data;
11815 len = QT_UINT32 (data);
11818 buf = gst_buffer_new_and_alloc (len);
11819 gst_buffer_fill (buf, 0, data + 8, len);
11820 gst_caps_set_simple (entry->caps,
11821 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11822 gst_buffer_unref (buf);
11831 GstBuffer *seqh = NULL;
11832 const guint8 *gamma_data = NULL;
11833 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11835 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11838 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11839 QT_FP32 (gamma_data), NULL);
11842 /* sorry for the bad name, but we don't know what this is, other
11843 * than its own fourcc */
11844 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11846 gst_buffer_unref (seqh);
11849 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11850 buf = gst_buffer_new_and_alloc (len);
11851 gst_buffer_fill (buf, 0, stsd_data, len);
11852 gst_caps_set_simple (entry->caps,
11853 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11854 gst_buffer_unref (buf);
11859 /* https://developer.apple.com/standards/qtff-2001.pdf,
11860 * page 92, "Video Sample Description", under table 3.1 */
11863 const gint compressor_offset =
11864 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11865 const gint min_size = compressor_offset + 32 + 2 + 2;
11868 guint16 color_table_id = 0;
11871 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11873 /* recover information on interlaced/progressive */
11874 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11878 len = QT_UINT32 (jpeg->data);
11879 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11881 if (len >= min_size) {
11882 gst_byte_reader_init (&br, jpeg->data, len);
11884 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11885 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11886 if (color_table_id != 0) {
11887 /* the spec says there can be concatenated chunks in the data, and we want
11888 * to find one called field. Walk through them. */
11889 gint offset = min_size;
11890 while (offset + 8 < len) {
11891 guint32 size = 0, tag;
11892 ok = gst_byte_reader_get_uint32_le (&br, &size);
11893 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11894 if (!ok || size < 8) {
11895 GST_WARNING_OBJECT (qtdemux,
11896 "Failed to walk optional chunk list");
11899 GST_DEBUG_OBJECT (qtdemux,
11900 "Found optional %4.4s chunk, size %u",
11901 (const char *) &tag, size);
11902 if (tag == FOURCC_fiel) {
11903 guint8 n_fields = 0, ordering = 0;
11904 gst_byte_reader_get_uint8 (&br, &n_fields);
11905 gst_byte_reader_get_uint8 (&br, &ordering);
11906 if (n_fields == 1 || n_fields == 2) {
11907 GST_DEBUG_OBJECT (qtdemux,
11908 "Found fiel tag with %u fields, ordering %u",
11909 n_fields, ordering);
11911 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11912 "interlace-mode", G_TYPE_STRING, "interleaved",
11915 GST_WARNING_OBJECT (qtdemux,
11916 "Found fiel tag with invalid fields (%u)", n_fields);
11922 GST_DEBUG_OBJECT (qtdemux,
11923 "Color table ID is 0, not trying to get interlacedness");
11926 GST_WARNING_OBJECT (qtdemux,
11927 "Length of jpeg chunk is too small, not trying to get interlacedness");
11935 gst_caps_set_simple (entry->caps,
11936 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11942 GNode *xith, *xdxt;
11944 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11945 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11949 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11953 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11954 /* collect the headers and store them in a stream list so that we can
11955 * send them out first */
11956 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11966 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11967 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11970 ovc1_data = ovc1->data;
11971 ovc1_len = QT_UINT32 (ovc1_data);
11972 if (ovc1_len <= 198) {
11973 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11976 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11977 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11978 gst_caps_set_simple (entry->caps,
11979 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11980 gst_buffer_unref (buf);
11985 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11986 const guint8 *vc1_data = stsd_entry_data + 0x56;
11992 if (QT_UINT32 (vc1_data) <= len)
11993 size = QT_UINT32 (vc1_data) - 8;
11998 /* No real data, so break out */
12001 switch (QT_FOURCC (vc1_data + 0x4)) {
12002 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12006 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12007 buf = gst_buffer_new_and_alloc (size);
12008 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12009 gst_caps_set_simple (entry->caps,
12010 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12011 gst_buffer_unref (buf);
12018 vc1_data += size + 8;
12024 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12025 const guint8 *av1_data = stsd_entry_data + 0x56;
12028 while (len >= 0x8) {
12031 if (QT_UINT32 (av1_data) <= len)
12032 size = QT_UINT32 (av1_data) - 0x8;
12037 /* No real data, so break out */
12040 switch (QT_FOURCC (av1_data + 0x4)) {
12043 /* parse, if found */
12045 guint8 pres_delay_field;
12047 GST_DEBUG_OBJECT (qtdemux,
12048 "found av1C codec_data in stsd of size %d", size);
12050 /* not enough data, just ignore and hope for the best */
12055 * 4 bytes: atom length
12060 * 1 bits: initial_presentation_delay_present
12061 * 4 bits: initial_presentation_delay (if present else reserved
12065 if (av1_data[9] != 0) {
12066 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
12070 /* We skip initial_presentation_delay* for now */
12071 pres_delay_field = *(av1_data + 12);
12072 if (pres_delay_field & (1 << 5)) {
12073 gst_caps_set_simple (entry->caps,
12074 "presentation-delay", G_TYPE_INT,
12075 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12078 buf = gst_buffer_new_and_alloc (size - 5);
12079 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12080 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
12081 gst_caps_set_simple (entry->caps,
12082 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12083 gst_buffer_unref (buf);
12092 av1_data += size + 8;
12098 /* TODO: Need to parse vpcC for VP8 codec too.
12099 * Note that VPCodecConfigurationBox (vpcC) is defined for
12100 * vp08, vp09, and vp10 fourcc. */
12103 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12104 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12107 while (len >= 0x8) {
12110 if (QT_UINT32 (vpcc_data) <= len)
12111 size = QT_UINT32 (vpcc_data) - 0x8;
12116 /* No real data, so break out */
12119 switch (QT_FOURCC (vpcc_data + 0x4)) {
12122 const gchar *profile_str = NULL;
12123 const gchar *chroma_format_str = NULL;
12126 guint8 chroma_format;
12127 GstVideoColorimetry cinfo;
12129 /* parse, if found */
12130 GST_DEBUG_OBJECT (qtdemux,
12131 "found vp codec_data in stsd of size %d", size);
12133 /* the meaning of "size" is length of the atom body, excluding
12134 * atom length and fourcc fields */
12139 * 4 bytes: atom length
12146 * 3 bits: chromaSubsampling
12147 * 1 bit: videoFullRangeFlag
12148 * 1 byte: colourPrimaries
12149 * 1 byte: transferCharacteristics
12150 * 1 byte: matrixCoefficients
12151 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
12152 * rest: codecIntializationData (not used for vp8 and vp9)
12155 if (vpcc_data[8] != 1) {
12156 GST_WARNING_OBJECT (qtdemux,
12157 "unknown vpcC version %d", vpcc_data[8]);
12161 profile = vpcc_data[12];
12180 gst_caps_set_simple (entry->caps,
12181 "profile", G_TYPE_STRING, profile_str, NULL);
12184 /* skip level, the VP9 spec v0.6 defines only one level atm,
12185 * but webm spec define various ones. Add level to caps
12186 * if we really need it then */
12188 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
12189 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
12190 gst_caps_set_simple (entry->caps,
12191 "bit-depth-luma", G_TYPE_UINT, bitdepth,
12192 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
12195 chroma_format = (vpcc_data[14] & 0xe) >> 1;
12196 switch (chroma_format) {
12199 chroma_format_str = "4:2:0";
12202 chroma_format_str = "4:2:2";
12205 chroma_format_str = "4:4:4";
12211 if (chroma_format_str) {
12212 gst_caps_set_simple (entry->caps,
12213 "chroma-format", G_TYPE_STRING, chroma_format_str,
12217 if ((vpcc_data[14] & 0x1) != 0)
12218 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
12220 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
12222 gst_video_color_primaries_from_iso (vpcc_data[15]);
12224 gst_video_transfer_function_from_iso (vpcc_data[16]);
12226 gst_video_color_matrix_from_iso (vpcc_data[17]);
12228 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
12229 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
12230 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
12231 /* set this only if all values are known, otherwise this
12232 * might overwrite valid ones parsed from other color box */
12233 CUR_STREAM (stream)->colorimetry = cinfo;
12242 vpcc_data += size + 8;
12252 GST_INFO_OBJECT (qtdemux,
12253 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12254 GST_FOURCC_ARGS (fourcc), entry->caps);
12256 } else if (stream->subtype == FOURCC_soun) {
12258 int version, samplesize;
12259 guint16 compression_id;
12260 gboolean amrwb = FALSE;
12263 /* sample description entry (16) + sound sample description v0 (20) */
12267 version = QT_UINT32 (stsd_entry_data + offset);
12268 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
12269 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
12270 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
12271 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
12273 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
12274 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
12275 QT_UINT32 (stsd_entry_data + offset + 4));
12276 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12277 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
12278 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
12279 GST_LOG_OBJECT (qtdemux, "packet size: %d",
12280 QT_UINT16 (stsd_entry_data + offset + 14));
12281 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12283 if (compression_id == 0xfffe)
12284 entry->sampled = TRUE;
12286 /* first assume uncompressed audio */
12287 entry->bytes_per_sample = samplesize / 8;
12288 entry->samples_per_frame = entry->n_channels;
12289 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12290 entry->samples_per_packet = entry->samples_per_frame;
12291 entry->bytes_per_packet = entry->bytes_per_sample;
12295 if (version == 0x00010000) {
12296 /* sample description entry (16) + sound sample description v1 (20+16) */
12300 /* take information from here over the normal sample description */
12301 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
12302 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
12303 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
12304 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
12306 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
12307 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
12308 entry->samples_per_packet);
12309 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12310 entry->bytes_per_packet);
12311 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
12312 entry->bytes_per_frame);
12313 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
12314 entry->bytes_per_sample);
12316 if (!entry->sampled && entry->bytes_per_packet) {
12317 entry->samples_per_frame = (entry->bytes_per_frame /
12318 entry->bytes_per_packet) * entry->samples_per_packet;
12319 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
12320 entry->samples_per_frame);
12322 } else if (version == 0x00020000) {
12323 /* sample description entry (16) + sound sample description v2 (56) */
12327 /* take information from here over the normal sample description */
12328 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
12329 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
12330 entry->samples_per_frame = entry->n_channels;
12331 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
12332 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
12333 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
12334 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
12336 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
12337 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12338 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12339 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
12340 entry->bytes_per_sample * 8);
12341 GST_LOG_OBJECT (qtdemux, "format flags: %X",
12342 QT_UINT32 (stsd_entry_data + offset + 24));
12343 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12344 entry->bytes_per_packet);
12345 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
12346 entry->samples_per_packet);
12347 } else if (version != 0x00000) {
12348 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
12353 /* Yes, these have to be hard-coded */
12356 entry->samples_per_packet = 6;
12357 entry->bytes_per_packet = 1;
12358 entry->bytes_per_frame = 1 * entry->n_channels;
12359 entry->bytes_per_sample = 1;
12360 entry->samples_per_frame = 6 * entry->n_channels;
12365 entry->samples_per_packet = 3;
12366 entry->bytes_per_packet = 1;
12367 entry->bytes_per_frame = 1 * entry->n_channels;
12368 entry->bytes_per_sample = 1;
12369 entry->samples_per_frame = 3 * entry->n_channels;
12374 entry->samples_per_packet = 64;
12375 entry->bytes_per_packet = 34;
12376 entry->bytes_per_frame = 34 * entry->n_channels;
12377 entry->bytes_per_sample = 2;
12378 entry->samples_per_frame = 64 * entry->n_channels;
12384 entry->samples_per_packet = 1;
12385 entry->bytes_per_packet = 1;
12386 entry->bytes_per_frame = 1 * entry->n_channels;
12387 entry->bytes_per_sample = 1;
12388 entry->samples_per_frame = 1 * entry->n_channels;
12393 entry->samples_per_packet = 160;
12394 entry->bytes_per_packet = 33;
12395 entry->bytes_per_frame = 33 * entry->n_channels;
12396 entry->bytes_per_sample = 2;
12397 entry->samples_per_frame = 160 * entry->n_channels;
12400 /* fix up any invalid header information from above */
12405 /* Sometimes these are set to 0 in the sound sample descriptions so
12406 * let's try to infer useful values from the other information we
12407 * have available */
12408 if (entry->bytes_per_sample == 0)
12409 entry->bytes_per_sample =
12410 entry->bytes_per_frame / entry->n_channels;
12411 if (entry->bytes_per_sample == 0)
12412 entry->bytes_per_sample = samplesize / 8;
12414 if (entry->bytes_per_frame == 0)
12415 entry->bytes_per_frame =
12416 entry->bytes_per_sample * entry->n_channels;
12418 if (entry->bytes_per_packet == 0)
12419 entry->bytes_per_packet = entry->bytes_per_sample;
12421 if (entry->samples_per_frame == 0)
12422 entry->samples_per_frame = entry->n_channels;
12424 if (entry->samples_per_packet == 0)
12425 entry->samples_per_packet = entry->samples_per_frame;
12435 entry->bytes_per_sample = 3;
12439 entry->bytes_per_sample = 4;
12442 entry->bytes_per_sample = 8;
12445 entry->bytes_per_sample = 2;
12448 g_assert_not_reached ();
12451 entry->samples_per_frame = entry->n_channels;
12452 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12453 entry->samples_per_packet = entry->samples_per_frame;
12454 entry->bytes_per_packet = entry->bytes_per_sample;
12462 gst_caps_unref (entry->caps);
12464 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
12465 stsd_entry_data + 32, len - 16, &codec);
12476 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
12478 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
12480 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
12482 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
12485 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
12486 const gchar *format_str;
12490 format_str = (enda_value) ? "S24LE" : "S24BE";
12493 format_str = (enda_value) ? "S32LE" : "S32BE";
12496 format_str = (enda_value) ? "F32LE" : "F32BE";
12499 format_str = (enda_value) ? "F64LE" : "F64BE";
12502 g_assert_not_reached ();
12505 gst_caps_set_simple (entry->caps,
12506 "format", G_TYPE_STRING, format_str, NULL);
12512 const guint8 *owma_data;
12513 const gchar *codec_name = NULL;
12517 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12518 /* FIXME this should also be gst_riff_strf_auds,
12519 * but the latter one is actually missing bits-per-sample :( */
12524 gint32 nSamplesPerSec;
12525 gint32 nAvgBytesPerSec;
12526 gint16 nBlockAlign;
12527 gint16 wBitsPerSample;
12530 WAVEFORMATEX *wfex;
12532 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12533 owma_data = stsd_entry_data;
12534 owma_len = QT_UINT32 (owma_data);
12535 if (owma_len <= 54) {
12536 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12539 wfex = (WAVEFORMATEX *) (owma_data + 36);
12540 buf = gst_buffer_new_and_alloc (owma_len - 54);
12541 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12542 if (wfex->wFormatTag == 0x0161) {
12543 codec_name = "Windows Media Audio";
12545 } else if (wfex->wFormatTag == 0x0162) {
12546 codec_name = "Windows Media Audio 9 Pro";
12548 } else if (wfex->wFormatTag == 0x0163) {
12549 codec_name = "Windows Media Audio 9 Lossless";
12550 /* is that correct? gstffmpegcodecmap.c is missing it, but
12551 * fluendo codec seems to support it */
12555 gst_caps_set_simple (entry->caps,
12556 "codec_data", GST_TYPE_BUFFER, buf,
12557 "wmaversion", G_TYPE_INT, version,
12558 "block_align", G_TYPE_INT,
12559 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12560 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
12561 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
12562 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
12563 gst_buffer_unref (buf);
12567 codec = g_strdup (codec_name);
12573 gint len = QT_UINT32 (stsd_entry_data) - offset;
12574 const guint8 *wfex_data = stsd_entry_data + offset;
12575 const gchar *codec_name = NULL;
12577 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12578 /* FIXME this should also be gst_riff_strf_auds,
12579 * but the latter one is actually missing bits-per-sample :( */
12584 gint32 nSamplesPerSec;
12585 gint32 nAvgBytesPerSec;
12586 gint16 nBlockAlign;
12587 gint16 wBitsPerSample;
12592 /* FIXME: unify with similar wavformatex parsing code above */
12593 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
12599 if (QT_UINT32 (wfex_data) <= len)
12600 size = QT_UINT32 (wfex_data) - 8;
12605 /* No real data, so break out */
12608 switch (QT_FOURCC (wfex_data + 4)) {
12609 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
12611 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
12616 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
12617 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
12618 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
12619 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
12620 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
12621 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
12622 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
12624 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
12625 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
12626 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
12627 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
12628 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
12629 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
12631 if (wfex.wFormatTag == 0x0161) {
12632 codec_name = "Windows Media Audio";
12634 } else if (wfex.wFormatTag == 0x0162) {
12635 codec_name = "Windows Media Audio 9 Pro";
12637 } else if (wfex.wFormatTag == 0x0163) {
12638 codec_name = "Windows Media Audio 9 Lossless";
12639 /* is that correct? gstffmpegcodecmap.c is missing it, but
12640 * fluendo codec seems to support it */
12644 gst_caps_set_simple (entry->caps,
12645 "wmaversion", G_TYPE_INT, version,
12646 "block_align", G_TYPE_INT, wfex.nBlockAlign,
12647 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
12648 "width", G_TYPE_INT, wfex.wBitsPerSample,
12649 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
12651 if (size > wfex.cbSize) {
12654 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
12655 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
12656 size - wfex.cbSize);
12657 gst_caps_set_simple (entry->caps,
12658 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12659 gst_buffer_unref (buf);
12661 GST_WARNING_OBJECT (qtdemux, "no codec data");
12666 codec = g_strdup (codec_name);
12674 wfex_data += size + 8;
12680 const guint8 *dops_data;
12681 guint8 *channel_mapping = NULL;
12684 guint8 channel_mapping_family;
12685 guint8 stream_count;
12686 guint8 coupled_count;
12689 version = GST_READ_UINT16_BE (stsd_entry_data + 16);
12691 dops_data = stsd_entry_data + 51;
12693 dops_data = stsd_entry_data + 35;
12695 channels = GST_READ_UINT8 (dops_data + 10);
12696 rate = GST_READ_UINT32_LE (dops_data + 13);
12697 channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
12698 stream_count = GST_READ_UINT8 (dops_data + 20);
12699 coupled_count = GST_READ_UINT8 (dops_data + 21);
12701 if (channels > 0) {
12702 channel_mapping = g_malloc (channels * sizeof (guint8));
12703 for (i = 0; i < channels; i++)
12704 channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
12707 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
12708 channel_mapping_family, stream_count, coupled_count,
12710 g_free (channel_mapping);
12721 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12722 GST_TAG_AUDIO_CODEC, codec, NULL);
12726 /* some bitrate info may have ended up in caps */
12727 s = gst_caps_get_structure (entry->caps, 0);
12728 gst_structure_get_int (s, "bitrate", &bitrate);
12730 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12731 GST_TAG_BITRATE, bitrate, NULL);
12735 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12736 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
12737 if (stream->protected) {
12738 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
12739 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12741 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
12751 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
12753 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
12755 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
12759 /* If the fourcc's bottom 16 bits gives 'sm', then the top
12760 16 bits is a byte-swapped wave-style codec identifier,
12761 and we can find a WAVE header internally to a 'wave' atom here.
12762 This can more clearly be thought of as 'ms' as the top 16 bits, and a
12763 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
12766 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
12767 if (len < offset + 20) {
12768 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
12770 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
12771 const guint8 *data = stsd_entry_data + offset + 16;
12773 GNode *waveheadernode;
12775 wavenode = g_node_new ((guint8 *) data);
12776 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
12777 const guint8 *waveheader;
12780 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
12781 if (waveheadernode) {
12782 waveheader = (const guint8 *) waveheadernode->data;
12783 headerlen = QT_UINT32 (waveheader);
12785 if (headerlen > 8) {
12786 gst_riff_strf_auds *header = NULL;
12787 GstBuffer *headerbuf;
12793 headerbuf = gst_buffer_new_and_alloc (headerlen);
12794 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
12796 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
12797 headerbuf, &header, &extra)) {
12798 gst_caps_unref (entry->caps);
12799 /* FIXME: Need to do something with the channel reorder map */
12801 gst_riff_create_audio_caps (header->format, NULL, header,
12802 extra, NULL, NULL, NULL);
12805 gst_buffer_unref (extra);
12810 GST_DEBUG ("Didn't find waveheadernode for this codec");
12812 g_node_destroy (wavenode);
12815 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12816 stream->stream_tags);
12820 /* FIXME: what is in the chunk? */
12823 gint len = QT_UINT32 (stsd_data);
12825 /* seems to be always = 116 = 0x74 */
12831 gint len = QT_UINT32 (stsd_entry_data);
12834 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
12836 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
12837 gst_caps_set_simple (entry->caps,
12838 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12839 gst_buffer_unref (buf);
12841 gst_caps_set_simple (entry->caps,
12842 "samplesize", G_TYPE_INT, samplesize, NULL);
12847 GNode *alac, *wave = NULL;
12849 /* apparently, m4a has this atom appended directly in the stsd entry,
12850 * while mov has it in a wave atom */
12851 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
12853 /* alac now refers to stsd entry atom */
12854 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
12856 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
12858 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
12861 const guint8 *alac_data = alac->data;
12862 gint len = QT_UINT32 (alac->data);
12866 GST_DEBUG_OBJECT (qtdemux,
12867 "discarding alac atom with unexpected len %d", len);
12869 /* codec-data contains alac atom size and prefix,
12870 * ffmpeg likes it that way, not quite gst-ish though ...*/
12871 buf = gst_buffer_new_and_alloc (len);
12872 gst_buffer_fill (buf, 0, alac->data, len);
12873 gst_caps_set_simple (entry->caps,
12874 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12875 gst_buffer_unref (buf);
12877 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
12878 entry->n_channels = QT_UINT8 (alac_data + 21);
12879 entry->rate = QT_UINT32 (alac_data + 32);
12880 samplesize = QT_UINT8 (alac_data + 16 + 1);
12883 gst_caps_set_simple (entry->caps,
12884 "samplesize", G_TYPE_INT, samplesize, NULL);
12889 /* The codingname of the sample entry is 'fLaC' */
12890 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
12893 /* The 'dfLa' box is added to the sample entry to convey
12894 initializing information for the decoder. */
12895 const GNode *dfla =
12896 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
12899 const guint32 len = QT_UINT32 (dfla->data);
12901 /* Must contain at least dfLa box header (12),
12902 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
12904 GST_DEBUG_OBJECT (qtdemux,
12905 "discarding dfla atom with unexpected len %d", len);
12907 /* skip dfLa header to get the METADATA_BLOCKs */
12908 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
12909 const guint32 metadata_blocks_len = len - 12;
12911 gchar *stream_marker = g_strdup ("fLaC");
12912 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
12913 strlen (stream_marker));
12916 guint32 remainder = 0;
12917 guint32 block_size = 0;
12918 gboolean is_last = FALSE;
12920 GValue array = G_VALUE_INIT;
12921 GValue value = G_VALUE_INIT;
12923 g_value_init (&array, GST_TYPE_ARRAY);
12924 g_value_init (&value, GST_TYPE_BUFFER);
12926 gst_value_set_buffer (&value, block);
12927 gst_value_array_append_value (&array, &value);
12928 g_value_reset (&value);
12930 gst_buffer_unref (block);
12932 /* check there's at least one METADATA_BLOCK_HEADER's worth
12933 * of data, and we haven't already finished parsing */
12934 while (!is_last && ((index + 3) < metadata_blocks_len)) {
12935 remainder = metadata_blocks_len - index;
12937 /* add the METADATA_BLOCK_HEADER size to the signalled size */
12939 (metadata_blocks[index + 1] << 16) +
12940 (metadata_blocks[index + 2] << 8) +
12941 metadata_blocks[index + 3];
12943 /* be careful not to read off end of box */
12944 if (block_size > remainder) {
12948 is_last = metadata_blocks[index] >> 7;
12950 block = gst_buffer_new_and_alloc (block_size);
12952 gst_buffer_fill (block, 0, &metadata_blocks[index],
12955 gst_value_set_buffer (&value, block);
12956 gst_value_array_append_value (&array, &value);
12957 g_value_reset (&value);
12959 gst_buffer_unref (block);
12961 index += block_size;
12964 /* only append the metadata if we successfully read all of it */
12966 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
12967 (stream)->caps, 0), "streamheader", &array);
12969 GST_WARNING_OBJECT (qtdemux,
12970 "discarding all METADATA_BLOCKs due to invalid "
12971 "block_size %d at idx %d, rem %d", block_size, index,
12975 g_value_unset (&value);
12976 g_value_unset (&array);
12978 /* The sample rate obtained from the stsd may not be accurate
12979 * since it cannot represent rates greater than 65535Hz, so
12980 * override that value with the sample rate from the
12981 * METADATA_BLOCK_STREAMINFO block */
12982 CUR_STREAM (stream)->rate =
12983 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
12994 gint len = QT_UINT32 (stsd_entry_data);
12997 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13000 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13002 /* If we have enough data, let's try to get the 'damr' atom. See
13003 * the 3GPP container spec (26.244) for more details. */
13004 if ((len - 0x34) > 8 &&
13005 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
13006 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13007 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13010 gst_caps_set_simple (entry->caps,
13011 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13012 gst_buffer_unref (buf);
13018 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
13019 gint len = QT_UINT32 (stsd_entry_data);
13020 guint16 sound_version = 0;
13021 /* FIXME: Can this be determined somehow? There doesn't seem to be
13022 * anything in mp4a atom that specifis compression */
13024 guint16 channels = entry->n_channels;
13025 guint32 time_scale = (guint32) entry->rate;
13026 gint sample_rate_index = -1;
13029 sound_version = QT_UINT16 (stsd_entry_data + 16);
13031 if (sound_version == 1) {
13032 channels = QT_UINT16 (stsd_entry_data + 24);
13033 time_scale = QT_UINT32 (stsd_entry_data + 30);
13035 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13039 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13043 sample_rate_index =
13044 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13045 if (sample_rate_index >= 0 && channels > 0) {
13046 guint8 codec_data[2];
13049 /* build AAC codec data */
13050 codec_data[0] = profile << 3;
13051 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13052 codec_data[1] = (sample_rate_index & 0x01) << 7;
13053 codec_data[1] |= (channels & 0xF) << 3;
13055 buf = gst_buffer_new_and_alloc (2);
13056 gst_buffer_fill (buf, 0, codec_data, 2);
13057 gst_caps_set_simple (entry->caps,
13058 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13059 gst_buffer_unref (buf);
13069 /* Fully handled elsewhere */
13072 GST_INFO_OBJECT (qtdemux,
13073 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13077 GST_INFO_OBJECT (qtdemux,
13078 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13079 GST_FOURCC_ARGS (fourcc), entry->caps);
13081 } else if (stream->subtype == FOURCC_strm) {
13082 if (fourcc == FOURCC_rtsp) {
13083 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
13085 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
13086 GST_FOURCC_ARGS (fourcc));
13087 goto unknown_stream;
13089 entry->sampled = TRUE;
13090 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
13091 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
13092 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
13094 entry->sampled = TRUE;
13095 entry->sparse = TRUE;
13098 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13101 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13102 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13107 /* hunt for sort-of codec data */
13111 GNode *mp4s = NULL;
13112 GNode *esds = NULL;
13114 /* look for palette in a stsd->mp4s->esds sub-atom */
13115 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
13117 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
13118 if (esds == NULL) {
13120 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13124 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13125 stream->stream_tags);
13129 GST_INFO_OBJECT (qtdemux,
13130 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13133 GST_INFO_OBJECT (qtdemux,
13134 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13135 GST_FOURCC_ARGS (fourcc), entry->caps);
13136 } else if (stream->subtype == FOURCC_meta) {
13137 entry->sampled = TRUE;
13138 entry->sparse = TRUE;
13141 qtdemux_meta_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13144 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13145 GST_TAG_CODEC, codec, NULL);
13150 GST_INFO_OBJECT (qtdemux,
13151 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13152 GST_FOURCC_ARGS (fourcc), entry->caps);
13154 /* everything in 1 sample */
13155 entry->sampled = TRUE;
13158 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13161 if (entry->caps == NULL)
13162 goto unknown_stream;
13165 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13166 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13172 /* promote to sampled format */
13173 if (entry->fourcc == FOURCC_samr) {
13174 /* force mono 8000 Hz for AMR */
13175 entry->sampled = TRUE;
13176 entry->n_channels = 1;
13177 entry->rate = 8000;
13178 } else if (entry->fourcc == FOURCC_sawb) {
13179 /* force mono 16000 Hz for AMR-WB */
13180 entry->sampled = TRUE;
13181 entry->n_channels = 1;
13182 entry->rate = 16000;
13183 } else if (entry->fourcc == FOURCC_mp4a) {
13184 entry->sampled = TRUE;
13188 stsd_entry_data += len;
13189 remaining_stsd_len -= len;
13193 /* collect sample information */
13194 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
13195 goto samples_failed;
13197 if (qtdemux->fragmented) {
13200 /* need all moov samples as basis; probably not many if any at all */
13201 /* prevent moof parsing taking of at this time */
13202 offset = qtdemux->moof_offset;
13203 qtdemux->moof_offset = 0;
13204 if (stream->n_samples &&
13205 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
13206 qtdemux->moof_offset = offset;
13207 goto samples_failed;
13209 qtdemux->moof_offset = offset;
13210 /* movie duration more reliable in this case (e.g. mehd) */
13211 if (qtdemux->segment.duration &&
13212 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13214 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
13217 /* configure segments */
13218 if (!qtdemux_parse_segments (qtdemux, stream, trak))
13219 goto segments_failed;
13221 /* add some language tag, if useful */
13222 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13223 strcmp (stream->lang_id, "und")) {
13224 const gchar *lang_code;
13226 /* convert ISO 639-2 code to ISO 639-1 */
13227 lang_code = gst_tag_get_language_code (stream->lang_id);
13228 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13229 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13232 /* Check for UDTA tags */
13233 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
13234 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
13237 /* Insert and sort new stream in track-id order.
13238 * This will help in comparing old/new streams during stream update check */
13239 g_ptr_array_add (qtdemux->active_streams, stream);
13240 g_ptr_array_sort (qtdemux->active_streams,
13241 (GCompareFunc) qtdemux_track_id_compare_func);
13242 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
13243 QTDEMUX_N_STREAMS (qtdemux));
13250 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
13251 (_("This file is corrupt and cannot be played.")), (NULL));
13253 gst_qtdemux_stream_unref (stream);
13258 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
13259 gst_qtdemux_stream_unref (stream);
13265 /* we posted an error already */
13266 /* free stbl sub-atoms */
13267 gst_qtdemux_stbl_free (stream);
13268 gst_qtdemux_stream_unref (stream);
13273 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
13279 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
13280 GST_FOURCC_ARGS (stream->subtype));
13281 gst_qtdemux_stream_unref (stream);
13286 /* If we can estimate the overall bitrate, and don't have information about the
13287 * stream bitrate for exactly one stream, this guesses the stream bitrate as
13288 * the overall bitrate minus the sum of the bitrates of all other streams. This
13289 * should be useful for the common case where we have one audio and one video
13290 * stream and can estimate the bitrate of one, but not the other. */
13292 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
13294 QtDemuxStream *stream = NULL;
13295 gint64 size, sys_bitrate, sum_bitrate = 0;
13296 GstClockTime duration;
13300 if (qtdemux->fragmented)
13303 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
13305 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
13307 GST_DEBUG_OBJECT (qtdemux,
13308 "Size in bytes of the stream not known - bailing");
13312 /* Subtract the header size */
13313 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
13314 size, qtdemux->header_size);
13316 if (size < qtdemux->header_size)
13319 size = size - qtdemux->header_size;
13321 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
13322 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
13326 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13327 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
13328 switch (str->subtype) {
13331 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
13332 CUR_STREAM (str)->caps);
13333 /* retrieve bitrate, prefer avg then max */
13335 if (str->stream_tags) {
13336 if (gst_tag_list_get_uint (str->stream_tags,
13337 GST_TAG_MAXIMUM_BITRATE, &bitrate))
13338 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
13339 if (gst_tag_list_get_uint (str->stream_tags,
13340 GST_TAG_NOMINAL_BITRATE, &bitrate))
13341 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
13342 if (gst_tag_list_get_uint (str->stream_tags,
13343 GST_TAG_BITRATE, &bitrate))
13344 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
13347 sum_bitrate += bitrate;
13350 GST_DEBUG_OBJECT (qtdemux,
13351 ">1 stream with unknown bitrate - bailing");
13358 /* For other subtypes, we assume no significant impact on bitrate */
13364 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
13368 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
13370 if (sys_bitrate < sum_bitrate) {
13371 /* This can happen, since sum_bitrate might be derived from maximum
13372 * bitrates and not average bitrates */
13373 GST_DEBUG_OBJECT (qtdemux,
13374 "System bitrate less than sum bitrate - bailing");
13378 bitrate = sys_bitrate - sum_bitrate;
13379 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
13380 ", Stream bitrate = %u", sys_bitrate, bitrate);
13382 if (!stream->stream_tags)
13383 stream->stream_tags = gst_tag_list_new_empty ();
13385 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
13387 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13388 GST_TAG_BITRATE, bitrate, NULL);
13391 static GstFlowReturn
13392 qtdemux_prepare_streams (GstQTDemux * qtdemux)
13394 GstFlowReturn ret = GST_FLOW_OK;
13397 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
13399 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13400 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13401 guint32 sample_num = 0;
13403 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13404 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13406 if (qtdemux->fragmented && qtdemux->pullbased) {
13407 /* need all moov samples first */
13408 GST_OBJECT_LOCK (qtdemux);
13409 while (stream->n_samples == 0)
13410 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
13412 GST_OBJECT_UNLOCK (qtdemux);
13414 /* discard any stray moof */
13415 qtdemux->moof_offset = 0;
13418 /* prepare braking */
13419 if (ret != GST_FLOW_ERROR)
13422 /* in pull mode, we should have parsed some sample info by now;
13423 * and quite some code will not handle no samples.
13424 * in push mode, we'll just have to deal with it */
13425 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
13426 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
13427 g_ptr_array_remove_index (qtdemux->active_streams, i);
13430 } else if (stream->track_id == qtdemux->chapters_track_id &&
13431 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
13432 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
13433 so that it doesn't look like a subtitle track */
13434 g_ptr_array_remove_index (qtdemux->active_streams, i);
13439 /* parse the initial sample for use in setting the frame rate cap */
13440 while (sample_num == 0 && sample_num < stream->n_samples) {
13441 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
13451 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
13453 return g_strcmp0 (stream->stream_id, stream_id) == 0;
13457 qtdemux_is_streams_update (GstQTDemux * qtdemux)
13461 /* Different length, updated */
13462 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
13465 /* streams in list are sorted in track-id order */
13466 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13467 /* Different stream-id, updated */
13468 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
13469 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
13477 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
13478 QtDemuxStream * oldstream, QtDemuxStream * newstream)
13480 /* Connect old stream's srcpad to new stream */
13481 newstream->pad = oldstream->pad;
13482 oldstream->pad = NULL;
13484 /* unset new_stream to prevent stream-start event, unless we are EOS in which
13485 * case we need to force one through */
13486 newstream->new_stream = GST_PAD_IS_EOS (newstream->pad);
13488 return gst_qtdemux_configure_stream (qtdemux, newstream);
13492 qtdemux_update_streams (GstQTDemux * qtdemux)
13495 g_assert (qtdemux->streams_aware);
13497 /* At below, figure out which stream in active_streams has identical stream-id
13498 * with that of in old_streams. If there is matching stream-id,
13499 * corresponding newstream will not be exposed again,
13500 * but demux will reuse srcpad of matched old stream
13502 * active_streams : newly created streams from the latest moov
13503 * old_streams : existing streams (belong to previous moov)
13506 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13507 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13508 QtDemuxStream *oldstream = NULL;
13511 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13512 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13514 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
13515 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
13516 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
13518 /* null pad stream cannot be reused */
13519 if (oldstream->pad == NULL)
13524 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13526 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13529 /* we don't need to preserve order of old streams */
13530 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13534 /* now we have all info and can expose */
13535 list = stream->stream_tags;
13536 stream->stream_tags = NULL;
13537 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13545 /* Must be called with expose lock */
13546 static GstFlowReturn
13547 qtdemux_expose_streams (GstQTDemux * qtdemux)
13551 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13553 if (!qtdemux_is_streams_update (qtdemux)) {
13554 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13555 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13556 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13557 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13558 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13559 return GST_FLOW_ERROR;
13562 g_ptr_array_set_size (qtdemux->old_streams, 0);
13563 qtdemux->need_segment = TRUE;
13565 return GST_FLOW_OK;
13568 if (qtdemux->streams_aware) {
13569 if (!qtdemux_update_streams (qtdemux))
13570 return GST_FLOW_ERROR;
13572 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13573 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13576 /* now we have all info and can expose */
13577 list = stream->stream_tags;
13578 stream->stream_tags = NULL;
13579 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13580 return GST_FLOW_ERROR;
13585 gst_qtdemux_guess_bitrate (qtdemux);
13587 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
13589 /* If we have still old_streams, it's no more used stream */
13590 for (i = 0; i < qtdemux->old_streams->len; i++) {
13591 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13596 event = gst_event_new_eos ();
13597 if (qtdemux->segment_seqnum)
13598 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
13600 gst_pad_push_event (stream->pad, event);
13604 g_ptr_array_set_size (qtdemux->old_streams, 0);
13606 /* check if we should post a redirect in case there is a single trak
13607 * and it is a redirecting trak */
13608 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
13609 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
13612 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
13613 "an external content");
13614 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
13615 gst_structure_new ("redirect",
13616 "new-location", G_TYPE_STRING,
13617 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
13618 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
13619 g_free (qtdemux->redirect_location);
13620 qtdemux->redirect_location =
13621 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
13624 g_ptr_array_foreach (qtdemux->active_streams,
13625 (GFunc) qtdemux_do_allocation, qtdemux);
13627 qtdemux->need_segment = TRUE;
13629 qtdemux->exposed = TRUE;
13630 return GST_FLOW_OK;
13635 GstStructure *structure; /* helper for sort function */
13637 guint min_req_bitrate;
13638 guint min_req_qt_version;
13642 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13644 GstQtReference *ref_a = (GstQtReference *) a;
13645 GstQtReference *ref_b = (GstQtReference *) b;
13647 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13648 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13650 /* known bitrates go before unknown; higher bitrates go first */
13651 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13654 /* sort the redirects and post a message for the application.
13657 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13659 GstQtReference *best;
13662 GValue list_val = { 0, };
13665 g_assert (references != NULL);
13667 references = g_list_sort (references, qtdemux_redirects_sort_func);
13669 best = (GstQtReference *) references->data;
13671 g_value_init (&list_val, GST_TYPE_LIST);
13673 for (l = references; l != NULL; l = l->next) {
13674 GstQtReference *ref = (GstQtReference *) l->data;
13675 GValue struct_val = { 0, };
13677 ref->structure = gst_structure_new ("redirect",
13678 "new-location", G_TYPE_STRING, ref->location, NULL);
13680 if (ref->min_req_bitrate > 0) {
13681 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13682 ref->min_req_bitrate, NULL);
13685 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13686 g_value_set_boxed (&struct_val, ref->structure);
13687 gst_value_list_append_value (&list_val, &struct_val);
13688 g_value_unset (&struct_val);
13689 /* don't free anything here yet, since we need best->structure below */
13692 g_assert (best != NULL);
13693 s = gst_structure_copy (best->structure);
13695 if (g_list_length (references) > 1) {
13696 gst_structure_set_value (s, "locations", &list_val);
13699 g_value_unset (&list_val);
13701 for (l = references; l != NULL; l = l->next) {
13702 GstQtReference *ref = (GstQtReference *) l->data;
13704 gst_structure_free (ref->structure);
13705 g_free (ref->location);
13708 g_list_free (references);
13710 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13711 g_free (qtdemux->redirect_location);
13712 qtdemux->redirect_location =
13713 g_strdup (gst_structure_get_string (s, "new-location"));
13714 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13715 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13718 /* look for redirect nodes, collect all redirect information and
13722 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13724 GNode *rmra, *rmda, *rdrf;
13726 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13728 GList *redirects = NULL;
13730 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13732 GstQtReference ref = { NULL, NULL, 0, 0 };
13733 GNode *rmdr, *rmvc;
13735 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13736 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13737 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13738 ref.min_req_bitrate);
13741 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13742 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13743 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
13745 #ifndef GST_DISABLE_GST_DEBUG
13746 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
13748 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
13750 GST_LOG_OBJECT (qtdemux,
13751 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
13752 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
13753 bitmask, check_type);
13754 if (package == FOURCC_qtim && check_type == 0) {
13755 ref.min_req_qt_version = version;
13759 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
13765 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
13766 if (ref_len > 20) {
13767 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
13768 ref_data = (guint8 *) rdrf->data + 20;
13769 if (ref_type == FOURCC_alis) {
13770 guint record_len, record_version, fn_len;
13772 if (ref_len > 70) {
13773 /* MacOSX alias record, google for alias-layout.txt */
13774 record_len = QT_UINT16 (ref_data + 4);
13775 record_version = QT_UINT16 (ref_data + 4 + 2);
13776 fn_len = QT_UINT8 (ref_data + 50);
13777 if (record_len > 50 && record_version == 2 && fn_len > 0) {
13778 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
13781 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
13784 } else if (ref_type == FOURCC_url_) {
13785 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
13787 GST_DEBUG_OBJECT (qtdemux,
13788 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
13789 GST_FOURCC_ARGS (ref_type));
13791 if (ref.location != NULL) {
13792 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
13794 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
13796 GST_WARNING_OBJECT (qtdemux,
13797 "Failed to extract redirect location from rdrf atom");
13800 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
13804 /* look for others */
13805 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
13808 if (redirects != NULL) {
13809 qtdemux_process_redirects (qtdemux, redirects);
13815 static GstTagList *
13816 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
13820 if (tags == NULL) {
13821 tags = gst_tag_list_new_empty ();
13822 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
13825 if (qtdemux->major_brand == FOURCC_mjp2)
13826 fmt = "Motion JPEG 2000";
13827 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
13829 else if (qtdemux->major_brand == FOURCC_qt__)
13831 else if (qtdemux->fragmented)
13834 fmt = "ISO MP4/M4A";
13836 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
13837 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
13839 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
13845 /* we have read the complete moov node now.
13846 * This function parses all of the relevant info, creates the traks and
13847 * prepares all data structures for playback
13850 qtdemux_parse_tree (GstQTDemux * qtdemux)
13857 guint64 creation_time;
13858 GstDateTime *datetime = NULL;
13861 /* make sure we have a usable taglist */
13862 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13864 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
13865 if (mvhd == NULL) {
13866 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
13867 return qtdemux_parse_redirects (qtdemux);
13870 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
13871 if (version == 1) {
13872 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
13873 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
13874 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
13875 } else if (version == 0) {
13876 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
13877 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
13878 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
13880 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
13884 /* Moving qt creation time (secs since 1904) to unix time */
13885 if (creation_time != 0) {
13886 /* Try to use epoch first as it should be faster and more commonly found */
13887 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
13890 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
13891 /* some data cleansing sanity */
13892 now_s = g_get_real_time () / G_USEC_PER_SEC;
13893 if (now_s + 24 * 3600 < creation_time) {
13894 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
13896 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
13899 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
13900 GDateTime *dt, *dt_local;
13902 dt = g_date_time_add_seconds (base_dt, creation_time);
13903 dt_local = g_date_time_to_local (dt);
13904 datetime = gst_date_time_new_from_g_date_time (dt_local);
13906 g_date_time_unref (base_dt);
13907 g_date_time_unref (dt);
13911 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
13912 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
13914 gst_date_time_unref (datetime);
13917 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
13918 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
13920 /* check for fragmented file and get some (default) data */
13921 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
13924 GstByteReader mehd_data;
13926 /* let track parsing or anyone know weird stuff might happen ... */
13927 qtdemux->fragmented = TRUE;
13929 /* compensate for total duration */
13930 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
13932 qtdemux_parse_mehd (qtdemux, &mehd_data);
13935 /* Update the movie segment duration, unless it was directly given to us
13936 * by upstream. Otherwise let it as is, as we don't want to mangle the
13937 * duration provided by upstream that may come e.g. from a MPD file. */
13938 if (!qtdemux->upstream_format_is_time) {
13939 GstClockTime duration;
13940 /* set duration in the segment info */
13941 gst_qtdemux_get_duration (qtdemux, &duration);
13942 qtdemux->segment.duration = duration;
13943 /* also do not exceed duration; stop is set that way post seek anyway,
13944 * and segment activation falls back to duration,
13945 * whereas loop only checks stop, so let's align this here as well */
13946 qtdemux->segment.stop = duration;
13949 /* parse all traks */
13950 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
13952 qtdemux_parse_trak (qtdemux, trak);
13953 /* iterate all siblings */
13954 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
13957 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13960 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
13962 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13964 GST_LOG_OBJECT (qtdemux, "No udta node found.");
13967 /* maybe also some tags in meta box */
13968 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
13970 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
13971 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13973 GST_LOG_OBJECT (qtdemux, "No meta node found.");
13976 /* parse any protection system info */
13977 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
13979 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
13980 qtdemux_parse_pssh (qtdemux, pssh);
13981 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
13984 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
13989 /* taken from ffmpeg */
13991 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14003 len = (len << 7) | (c & 0x7f);
14012 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14013 gsize codec_data_size)
14015 GList *list = NULL;
14016 guint8 *p = codec_data;
14017 gint i, offset, num_packets;
14018 guint *length, last;
14020 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14022 if (codec_data == NULL || codec_data_size == 0)
14025 /* start of the stream and vorbis audio or theora video, need to
14026 * send the codec_priv data as first three packets */
14027 num_packets = p[0] + 1;
14028 GST_DEBUG_OBJECT (qtdemux,
14029 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14030 (guint) num_packets, codec_data_size);
14032 /* Let's put some limits, Don't think there even is a xiph codec
14033 * with more than 3-4 headers */
14034 if (G_UNLIKELY (num_packets > 16)) {
14035 GST_WARNING_OBJECT (qtdemux,
14036 "Unlikely number of xiph headers, most likely not valid");
14040 length = g_alloca (num_packets * sizeof (guint));
14044 /* first packets, read length values */
14045 for (i = 0; i < num_packets - 1; i++) {
14047 while (offset < codec_data_size) {
14048 length[i] += p[offset];
14049 if (p[offset++] != 0xff)
14054 if (offset + last > codec_data_size)
14057 /* last packet is the remaining size */
14058 length[i] = codec_data_size - offset - last;
14060 for (i = 0; i < num_packets; i++) {
14063 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14065 if (offset + length[i] > codec_data_size)
14068 hdr = gst_buffer_new_memdup (p + offset, length[i]);
14069 list = g_list_append (list, hdr);
14071 offset += length[i];
14080 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14086 /* this can change the codec originally present in @list */
14088 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14089 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14091 int len = QT_UINT32 (esds->data);
14092 guint8 *ptr = esds->data;
14093 guint8 *end = ptr + len;
14095 guint8 *data_ptr = NULL;
14097 guint8 object_type_id = 0;
14098 guint8 stream_type = 0;
14099 const char *codec_name = NULL;
14100 GstCaps *caps = NULL;
14102 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14104 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14106 while (ptr + 1 < end) {
14107 tag = QT_UINT8 (ptr);
14108 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14110 len = read_descr_size (ptr, end, &ptr);
14111 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14113 /* Check the stated amount of data is available for reading */
14114 if (len < 0 || ptr + len > end)
14118 case ES_DESCRIPTOR_TAG:
14119 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14120 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14123 case DECODER_CONFIG_DESC_TAG:{
14124 guint max_bitrate, avg_bitrate;
14126 object_type_id = QT_UINT8 (ptr);
14127 stream_type = QT_UINT8 (ptr + 1) >> 2;
14128 max_bitrate = QT_UINT32 (ptr + 5);
14129 avg_bitrate = QT_UINT32 (ptr + 9);
14130 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14131 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14132 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14133 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14134 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14135 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14136 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14137 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14139 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14140 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14141 avg_bitrate, NULL);
14146 case DECODER_SPECIFIC_INFO_TAG:
14147 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14148 if (object_type_id == 0xe0 && len == 0x40) {
14154 GST_DEBUG_OBJECT (qtdemux,
14155 "Have VOBSUB palette. Creating palette event");
14156 /* move to decConfigDescr data and read palette */
14158 for (i = 0; i < 16; i++) {
14159 clut[i] = QT_UINT32 (data);
14163 s = gst_structure_new ("application/x-gst-dvd", "event",
14164 G_TYPE_STRING, "dvd-spu-clut-change",
14165 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14166 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14167 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14168 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14169 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14170 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14171 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14172 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14175 /* store event and trigger custom processing */
14176 stream->pending_event =
14177 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14179 /* Generic codec_data handler puts it on the caps */
14186 case SL_CONFIG_DESC_TAG:
14187 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
14191 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14193 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14199 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14200 * in use, and should also be used to override some other parameters for some
14202 switch (object_type_id) {
14203 case 0x20: /* MPEG-4 */
14204 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14205 * profile_and_level_indication */
14206 if (data_ptr != NULL && data_len >= 5 &&
14207 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14208 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14209 data_ptr + 4, data_len - 4);
14211 break; /* Nothing special needed here */
14212 case 0x21: /* H.264 */
14213 codec_name = "H.264 / AVC";
14214 caps = gst_caps_new_simple ("video/x-h264",
14215 "stream-format", G_TYPE_STRING, "avc",
14216 "alignment", G_TYPE_STRING, "au", NULL);
14218 case 0x40: /* AAC (any) */
14219 case 0x66: /* AAC Main */
14220 case 0x67: /* AAC LC */
14221 case 0x68: /* AAC SSR */
14222 /* Override channels and rate based on the codec_data, as it's often
14224 /* Only do so for basic setup without HE-AAC extension */
14225 if (data_ptr && data_len == 2) {
14226 guint channels, rate;
14228 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14230 entry->n_channels = channels;
14232 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
14234 entry->rate = rate;
14237 /* Set level and profile if possible */
14238 if (data_ptr != NULL && data_len >= 2) {
14239 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
14240 data_ptr, data_len);
14242 const gchar *profile_str = NULL;
14245 guint8 *codec_data;
14246 gint rate_idx, profile;
14248 /* No codec_data, let's invent something.
14249 * FIXME: This is wrong for SBR! */
14251 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
14253 buffer = gst_buffer_new_and_alloc (2);
14254 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
14255 codec_data = map.data;
14258 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
14261 switch (object_type_id) {
14263 profile_str = "main";
14267 profile_str = "lc";
14271 profile_str = "ssr";
14279 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14281 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14283 gst_buffer_unmap (buffer, &map);
14284 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14285 GST_TYPE_BUFFER, buffer, NULL);
14286 gst_buffer_unref (buffer);
14289 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14290 G_TYPE_STRING, profile_str, NULL);
14294 case 0x60: /* MPEG-2, various profiles */
14300 codec_name = "MPEG-2 video";
14301 caps = gst_caps_new_simple ("video/mpeg",
14302 "mpegversion", G_TYPE_INT, 2,
14303 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14305 case 0x69: /* MPEG-2 BC audio */
14306 case 0x6B: /* MPEG-1 audio */
14307 caps = gst_caps_new_simple ("audio/mpeg",
14308 "mpegversion", G_TYPE_INT, 1, NULL);
14309 codec_name = "MPEG-1 audio";
14311 case 0x6A: /* MPEG-1 */
14312 codec_name = "MPEG-1 video";
14313 caps = gst_caps_new_simple ("video/mpeg",
14314 "mpegversion", G_TYPE_INT, 1,
14315 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14317 case 0x6C: /* MJPEG */
14319 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14321 codec_name = "Motion-JPEG";
14323 case 0x6D: /* PNG */
14325 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14327 codec_name = "PNG still images";
14329 case 0x6E: /* JPEG2000 */
14330 codec_name = "JPEG-2000";
14331 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14333 case 0xA4: /* Dirac */
14334 codec_name = "Dirac";
14335 caps = gst_caps_new_empty_simple ("video/x-dirac");
14337 case 0xA5: /* AC3 */
14338 codec_name = "AC-3 audio";
14339 caps = gst_caps_new_simple ("audio/x-ac3",
14340 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14342 case 0xA9: /* AC3 */
14343 codec_name = "DTS audio";
14344 caps = gst_caps_new_simple ("audio/x-dts",
14345 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14348 if (stream_type == 0x05 && data_ptr) {
14350 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
14353 GValue arr_val = G_VALUE_INIT;
14354 GValue buf_val = G_VALUE_INIT;
14357 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
14358 codec_name = "Vorbis";
14359 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
14360 g_value_init (&arr_val, GST_TYPE_ARRAY);
14361 g_value_init (&buf_val, GST_TYPE_BUFFER);
14362 for (tmp = headers; tmp; tmp = tmp->next) {
14363 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
14364 gst_value_array_append_value (&arr_val, &buf_val);
14366 s = gst_caps_get_structure (caps, 0);
14367 gst_structure_take_value (s, "streamheader", &arr_val);
14368 g_value_unset (&buf_val);
14369 g_list_free (headers);
14376 case 0xE1: /* QCELP */
14377 /* QCELP, the codec_data is a riff tag (little endian) with
14378 * 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). */
14379 caps = gst_caps_new_empty_simple ("audio/qcelp");
14380 codec_name = "QCELP";
14386 /* If we have a replacement caps, then change our caps for this stream */
14388 gst_caps_unref (entry->caps);
14389 entry->caps = caps;
14392 if (codec_name && list)
14393 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14394 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14396 /* Add the codec_data attribute to caps, if we have it */
14400 buffer = gst_buffer_new_and_alloc (data_len);
14401 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14403 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14404 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14406 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14408 gst_buffer_unref (buffer);
14413 static inline GstCaps *
14414 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14418 char *s, fourstr[5];
14420 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14421 for (i = 0; i < 4; i++) {
14422 if (!g_ascii_isalnum (fourstr[i]))
14425 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14426 caps = gst_caps_new_empty_simple (s);
14431 #define _codec(name) \
14433 if (codec_name) { \
14434 *codec_name = g_strdup (name); \
14439 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14440 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14441 const guint8 * stsd_entry_data, gchar ** codec_name)
14443 GstCaps *caps = NULL;
14444 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14448 _codec ("PNG still images");
14449 caps = gst_caps_new_empty_simple ("image/png");
14452 _codec ("JPEG still images");
14454 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14457 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14458 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14459 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14460 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14461 _codec ("Motion-JPEG");
14463 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14466 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14467 _codec ("Motion-JPEG format B");
14468 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14471 _codec ("JPEG-2000");
14472 /* override to what it should be according to spec, avoid palette_data */
14473 entry->bits_per_sample = 24;
14474 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14477 _codec ("Sorensen video v.3");
14478 caps = gst_caps_new_simple ("video/x-svq",
14479 "svqversion", G_TYPE_INT, 3, NULL);
14481 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14482 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14483 _codec ("Sorensen video v.1");
14484 caps = gst_caps_new_simple ("video/x-svq",
14485 "svqversion", G_TYPE_INT, 1, NULL);
14487 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14488 caps = gst_caps_new_empty_simple ("video/x-raw");
14489 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14490 _codec ("Windows Raw RGB");
14491 stream->alignment = 32;
14497 bps = QT_UINT16 (stsd_entry_data + 82);
14500 format = GST_VIDEO_FORMAT_RGB15;
14503 format = GST_VIDEO_FORMAT_RGB16;
14506 format = GST_VIDEO_FORMAT_RGB;
14509 format = GST_VIDEO_FORMAT_ARGB;
14517 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14518 format = GST_VIDEO_FORMAT_I420;
14520 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14521 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14522 format = GST_VIDEO_FORMAT_I420;
14525 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14526 format = GST_VIDEO_FORMAT_UYVY;
14528 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14529 format = GST_VIDEO_FORMAT_v308;
14531 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14532 format = GST_VIDEO_FORMAT_v216;
14535 format = GST_VIDEO_FORMAT_v210;
14537 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14538 format = GST_VIDEO_FORMAT_r210;
14540 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14541 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14542 format = GST_VIDEO_FORMAT_v410;
14545 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14546 * but different order than AYUV
14547 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14548 format = GST_VIDEO_FORMAT_v408;
14551 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14552 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14553 _codec ("MPEG-1 video");
14554 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14555 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14557 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14558 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14559 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14560 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14561 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14562 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14563 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14564 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14565 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14566 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14567 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14568 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14569 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14570 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14571 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14572 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14573 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14574 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14575 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14576 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14577 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14578 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14579 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14580 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14581 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14582 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14583 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14584 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14585 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14586 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14587 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14588 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14589 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14590 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14591 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14592 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14593 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14594 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14595 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14596 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14597 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14598 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14599 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14600 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14601 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14602 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14603 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14604 _codec ("MPEG-2 video");
14605 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14606 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14608 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14609 _codec ("GIF still images");
14610 caps = gst_caps_new_empty_simple ("image/gif");
14613 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14615 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14617 /* ffmpeg uses the height/width props, don't know why */
14618 caps = gst_caps_new_simple ("video/x-h263",
14619 "variant", G_TYPE_STRING, "itu", NULL);
14623 _codec ("MPEG-4 video");
14624 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14625 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14627 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14628 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14629 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14630 caps = gst_caps_new_simple ("video/x-msmpeg",
14631 "msmpegversion", G_TYPE_INT, 43, NULL);
14633 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14635 caps = gst_caps_new_simple ("video/x-divx",
14636 "divxversion", G_TYPE_INT, 3, NULL);
14638 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14639 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14641 caps = gst_caps_new_simple ("video/x-divx",
14642 "divxversion", G_TYPE_INT, 4, NULL);
14644 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14646 caps = gst_caps_new_simple ("video/x-divx",
14647 "divxversion", G_TYPE_INT, 5, NULL);
14650 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14652 caps = gst_caps_new_simple ("video/x-ffv",
14653 "ffvversion", G_TYPE_INT, 1, NULL);
14656 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14657 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14662 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14663 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14664 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14668 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14669 _codec ("Cinepak");
14670 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14672 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14673 _codec ("Apple QuickDraw");
14674 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14676 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14677 _codec ("Apple video");
14678 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14683 _codec ("H.264 / AVC");
14684 caps = gst_caps_new_simple ("video/x-h264",
14685 "stream-format", G_TYPE_STRING, "avc",
14686 "alignment", G_TYPE_STRING, "au", NULL);
14690 _codec ("H.264 / AVC");
14691 caps = gst_caps_new_simple ("video/x-h264",
14692 "stream-format", G_TYPE_STRING, "avc3",
14693 "alignment", G_TYPE_STRING, "au", NULL);
14698 _codec ("H.265 / HEVC");
14699 caps = gst_caps_new_simple ("video/x-h265",
14700 "stream-format", G_TYPE_STRING, "hvc1",
14701 "alignment", G_TYPE_STRING, "au", NULL);
14705 _codec ("H.265 / HEVC");
14706 caps = gst_caps_new_simple ("video/x-h265",
14707 "stream-format", G_TYPE_STRING, "hev1",
14708 "alignment", G_TYPE_STRING, "au", NULL);
14711 _codec ("Run-length encoding");
14712 caps = gst_caps_new_simple ("video/x-rle",
14713 "layout", G_TYPE_STRING, "quicktime", NULL);
14716 _codec ("Run-length encoding");
14717 caps = gst_caps_new_simple ("video/x-rle",
14718 "layout", G_TYPE_STRING, "microsoft", NULL);
14720 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14721 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14722 _codec ("Indeo Video 3");
14723 caps = gst_caps_new_simple ("video/x-indeo",
14724 "indeoversion", G_TYPE_INT, 3, NULL);
14726 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14727 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14728 _codec ("Intel Video 4");
14729 caps = gst_caps_new_simple ("video/x-indeo",
14730 "indeoversion", G_TYPE_INT, 4, NULL);
14734 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14735 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14736 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14737 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14738 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14739 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14740 _codec ("DV Video");
14741 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14742 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14744 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14745 case FOURCC_dv5p: /* DVCPRO50 PAL */
14746 _codec ("DVCPro50 Video");
14747 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14748 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14750 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
14751 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
14752 _codec ("DVCProHD Video");
14753 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
14754 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14756 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
14757 _codec ("Apple Graphics (SMC)");
14758 caps = gst_caps_new_empty_simple ("video/x-smc");
14760 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
14762 caps = gst_caps_new_empty_simple ("video/x-vp3");
14764 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
14765 _codec ("VP6 Flash");
14766 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
14770 caps = gst_caps_new_empty_simple ("video/x-theora");
14771 /* theora uses one byte of padding in the data stream because it does not
14772 * allow 0 sized packets while theora does */
14773 entry->padding = 1;
14777 caps = gst_caps_new_empty_simple ("video/x-dirac");
14779 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
14780 _codec ("TIFF still images");
14781 caps = gst_caps_new_empty_simple ("image/tiff");
14783 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
14784 _codec ("Apple Intermediate Codec");
14785 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
14787 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
14788 _codec ("AVID DNxHD");
14789 caps = gst_caps_from_string ("video/x-dnxhd");
14793 _codec ("On2 VP8");
14794 caps = gst_caps_from_string ("video/x-vp8");
14797 _codec ("Google VP9");
14798 caps = gst_caps_from_string ("video/x-vp9");
14801 _codec ("Apple ProRes LT");
14803 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
14807 _codec ("Apple ProRes HQ");
14809 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
14813 _codec ("Apple ProRes");
14815 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14819 _codec ("Apple ProRes Proxy");
14821 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14825 _codec ("Apple ProRes 4444");
14827 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14830 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
14831 if (entry->bits_per_sample > 0) {
14832 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
14837 _codec ("Apple ProRes 4444 XQ");
14839 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14842 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
14843 if (entry->bits_per_sample > 0) {
14844 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
14849 _codec ("GoPro CineForm");
14850 caps = gst_caps_from_string ("video/x-cineform");
14855 caps = gst_caps_new_simple ("video/x-wmv",
14856 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
14860 caps = gst_caps_new_simple ("video/x-av1",
14861 "alignment", G_TYPE_STRING, "tu", NULL);
14863 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
14866 caps = _get_unknown_codec_name ("video", fourcc);
14871 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
14874 gst_video_info_init (&info);
14875 gst_video_info_set_format (&info, format, entry->width, entry->height);
14877 caps = gst_video_info_to_caps (&info);
14878 *codec_name = gst_pb_utils_get_codec_description (caps);
14880 /* enable clipping for raw video streams */
14881 stream->need_clip = TRUE;
14882 stream->alignment = 32;
14889 round_up_pow2 (guint n)
14901 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14902 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
14903 int len, gchar ** codec_name)
14906 const GstStructure *s;
14909 GstAudioFormat format = 0;
14912 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14914 depth = entry->bytes_per_packet * 8;
14917 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
14919 /* 8-bit audio is unsigned */
14921 format = GST_AUDIO_FORMAT_U8;
14922 /* otherwise it's signed and big-endian just like 'twos' */
14924 endian = G_BIG_ENDIAN;
14931 endian = G_LITTLE_ENDIAN;
14934 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
14936 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
14940 caps = gst_caps_new_simple ("audio/x-raw",
14941 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14942 "layout", G_TYPE_STRING, "interleaved", NULL);
14943 stream->alignment = GST_ROUND_UP_8 (depth);
14944 stream->alignment = round_up_pow2 (stream->alignment);
14948 _codec ("Raw 64-bit floating-point audio");
14949 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14951 caps = gst_caps_new_simple ("audio/x-raw",
14952 "format", G_TYPE_STRING, "F64BE",
14953 "layout", G_TYPE_STRING, "interleaved", NULL);
14954 stream->alignment = 8;
14957 _codec ("Raw 32-bit floating-point audio");
14958 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14960 caps = gst_caps_new_simple ("audio/x-raw",
14961 "format", G_TYPE_STRING, "F32BE",
14962 "layout", G_TYPE_STRING, "interleaved", NULL);
14963 stream->alignment = 4;
14966 _codec ("Raw 24-bit PCM audio");
14967 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14969 caps = gst_caps_new_simple ("audio/x-raw",
14970 "format", G_TYPE_STRING, "S24BE",
14971 "layout", G_TYPE_STRING, "interleaved", NULL);
14972 stream->alignment = 4;
14975 _codec ("Raw 32-bit PCM audio");
14976 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14978 caps = gst_caps_new_simple ("audio/x-raw",
14979 "format", G_TYPE_STRING, "S32BE",
14980 "layout", G_TYPE_STRING, "interleaved", NULL);
14981 stream->alignment = 4;
14984 _codec ("Raw 16-bit PCM audio");
14985 caps = gst_caps_new_simple ("audio/x-raw",
14986 "format", G_TYPE_STRING, "S16LE",
14987 "layout", G_TYPE_STRING, "interleaved", NULL);
14988 stream->alignment = 2;
14991 _codec ("Mu-law audio");
14992 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
14995 _codec ("A-law audio");
14996 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15000 _codec ("Microsoft ADPCM");
15001 /* Microsoft ADPCM-ACM code 2 */
15002 caps = gst_caps_new_simple ("audio/x-adpcm",
15003 "layout", G_TYPE_STRING, "microsoft", NULL);
15007 _codec ("DVI/IMA ADPCM");
15008 caps = gst_caps_new_simple ("audio/x-adpcm",
15009 "layout", G_TYPE_STRING, "dvi", NULL);
15013 _codec ("DVI/Intel IMA ADPCM");
15014 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15015 caps = gst_caps_new_simple ("audio/x-adpcm",
15016 "layout", G_TYPE_STRING, "quicktime", NULL);
15020 /* MPEG layer 3, CBR only (pre QT4.1) */
15023 _codec ("MPEG-1 layer 3");
15024 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15025 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15026 "mpegversion", G_TYPE_INT, 1, NULL);
15028 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15029 _codec ("MPEG-1 layer 2");
15031 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15032 "mpegversion", G_TYPE_INT, 1, NULL);
15035 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15036 _codec ("EAC-3 audio");
15037 caps = gst_caps_new_simple ("audio/x-eac3",
15038 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15039 entry->sampled = TRUE;
15041 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15043 _codec ("AC-3 audio");
15044 caps = gst_caps_new_simple ("audio/x-ac3",
15045 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15046 entry->sampled = TRUE;
15048 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15049 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15050 _codec ("DTS audio");
15051 caps = gst_caps_new_simple ("audio/x-dts",
15052 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15053 entry->sampled = TRUE;
15055 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15056 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15057 _codec ("DTS-HD audio");
15058 caps = gst_caps_new_simple ("audio/x-dts",
15059 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15060 entry->sampled = TRUE;
15064 caps = gst_caps_new_simple ("audio/x-mace",
15065 "maceversion", G_TYPE_INT, 3, NULL);
15069 caps = gst_caps_new_simple ("audio/x-mace",
15070 "maceversion", G_TYPE_INT, 6, NULL);
15072 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15074 caps = gst_caps_new_empty_simple ("application/ogg");
15076 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15077 _codec ("DV audio");
15078 caps = gst_caps_new_empty_simple ("audio/x-dv");
15081 _codec ("MPEG-4 AAC audio");
15082 caps = gst_caps_new_simple ("audio/mpeg",
15083 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15084 "stream-format", G_TYPE_STRING, "raw", NULL);
15086 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15087 _codec ("QDesign Music");
15088 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15091 _codec ("QDesign Music v.2");
15092 /* FIXME: QDesign music version 2 (no constant) */
15093 if (FALSE && data) {
15094 caps = gst_caps_new_simple ("audio/x-qdm2",
15095 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15096 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15097 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15099 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15103 _codec ("GSM audio");
15104 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15107 _codec ("AMR audio");
15108 caps = gst_caps_new_empty_simple ("audio/AMR");
15111 _codec ("AMR-WB audio");
15112 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15115 _codec ("Quicktime IMA ADPCM");
15116 caps = gst_caps_new_simple ("audio/x-adpcm",
15117 "layout", G_TYPE_STRING, "quicktime", NULL);
15120 _codec ("Apple lossless audio");
15121 caps = gst_caps_new_empty_simple ("audio/x-alac");
15124 _codec ("Free Lossless Audio Codec");
15125 caps = gst_caps_new_simple ("audio/x-flac",
15126 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15128 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15129 _codec ("QualComm PureVoice");
15130 caps = gst_caps_from_string ("audio/qcelp");
15135 caps = gst_caps_new_empty_simple ("audio/x-wma");
15139 caps = gst_caps_new_empty_simple ("audio/x-opus");
15146 GstAudioFormat format;
15149 FLAG_IS_FLOAT = 0x1,
15150 FLAG_IS_BIG_ENDIAN = 0x2,
15151 FLAG_IS_SIGNED = 0x4,
15152 FLAG_IS_PACKED = 0x8,
15153 FLAG_IS_ALIGNED_HIGH = 0x10,
15154 FLAG_IS_NON_INTERLEAVED = 0x20
15156 _codec ("Raw LPCM audio");
15158 if (data && len >= 36) {
15159 depth = QT_UINT32 (data + 24);
15160 flags = QT_UINT32 (data + 28);
15161 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15163 if ((flags & FLAG_IS_FLOAT) == 0) {
15168 if ((flags & FLAG_IS_ALIGNED_HIGH))
15171 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15172 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15173 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15174 caps = gst_caps_new_simple ("audio/x-raw",
15175 "format", G_TYPE_STRING,
15177 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15178 "UNKNOWN", "layout", G_TYPE_STRING,
15179 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15180 "interleaved", NULL);
15181 stream->alignment = GST_ROUND_UP_8 (depth);
15182 stream->alignment = round_up_pow2 (stream->alignment);
15187 if (flags & FLAG_IS_BIG_ENDIAN)
15188 format = GST_AUDIO_FORMAT_F64BE;
15190 format = GST_AUDIO_FORMAT_F64LE;
15192 if (flags & FLAG_IS_BIG_ENDIAN)
15193 format = GST_AUDIO_FORMAT_F32BE;
15195 format = GST_AUDIO_FORMAT_F32LE;
15197 caps = gst_caps_new_simple ("audio/x-raw",
15198 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15199 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15200 "non-interleaved" : "interleaved", NULL);
15201 stream->alignment = width / 8;
15205 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
15208 caps = gst_caps_new_empty_simple ("audio/x-ac4");
15211 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15215 caps = _get_unknown_codec_name ("audio", fourcc);
15221 GstCaps *templ_caps =
15222 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
15223 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
15224 gst_caps_unref (caps);
15225 gst_caps_unref (templ_caps);
15226 caps = intersection;
15229 /* enable clipping for raw audio streams */
15230 s = gst_caps_get_structure (caps, 0);
15231 name = gst_structure_get_name (s);
15232 if (g_str_has_prefix (name, "audio/x-raw")) {
15233 stream->need_clip = TRUE;
15234 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
15235 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
15236 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
15237 stream->max_buffer_size);
15243 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15244 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15245 const guint8 * stsd_entry_data, gchar ** codec_name)
15249 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15253 _codec ("DVD subtitle");
15254 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
15255 stream->process_func = gst_qtdemux_process_buffer_dvd;
15258 _codec ("Quicktime timed text");
15261 _codec ("3GPP timed text");
15263 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
15265 /* actual text piece needs to be extracted */
15266 stream->process_func = gst_qtdemux_process_buffer_text;
15269 _codec ("XML subtitles");
15270 caps = gst_caps_new_empty_simple ("application/ttml+xml");
15275 const gchar *buf = "WEBVTT\n\n";
15277 _codec ("WebVTT subtitles");
15278 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
15279 stream->process_func = gst_qtdemux_process_buffer_wvtt;
15281 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
15282 buffer = gst_buffer_new_and_alloc (8);
15283 gst_buffer_fill (buffer, 0, buf, 8);
15284 stream->buffers = g_slist_append (stream->buffers, buffer);
15289 _codec ("CEA 608 Closed Caption");
15291 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
15292 G_TYPE_STRING, "s334-1a", NULL);
15293 stream->process_func = gst_qtdemux_process_buffer_clcp;
15294 stream->need_split = TRUE;
15297 _codec ("CEA 708 Closed Caption");
15299 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
15300 G_TYPE_STRING, "cdp", NULL);
15301 stream->process_func = gst_qtdemux_process_buffer_clcp;
15306 caps = _get_unknown_codec_name ("text", fourcc);
15314 qtdemux_meta_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15315 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15316 const guint8 * stsd_entry_data, gchar ** codec_name)
15318 GstCaps *caps = NULL;
15320 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15324 gsize size = QT_UINT32 (stsd_entry_data);
15325 GstByteReader reader = GST_BYTE_READER_INIT (stsd_entry_data, size);
15326 const gchar *content_encoding;
15327 const gchar *namespaces;
15328 const gchar *schema_locations;
15330 if (!gst_byte_reader_skip (&reader, 8 + 6 + 2)) {
15331 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
15335 if (!gst_byte_reader_get_string (&reader, &content_encoding) ||
15336 !gst_byte_reader_get_string (&reader, &namespaces) ||
15337 !gst_byte_reader_get_string (&reader, &schema_locations)) {
15338 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
15342 if (strstr (namespaces, "http://www.onvif.org/ver10/schema") != 0) {
15343 if (content_encoding == NULL || *content_encoding == '\0'
15344 || g_ascii_strcasecmp (content_encoding, "xml") == 0) {
15345 _codec ("ONVIF Timed XML MetaData");
15347 gst_caps_new_simple ("application/x-onvif-metadata", "encoding",
15348 G_TYPE_STRING, "utf8", NULL);
15350 GST_DEBUG_OBJECT (qtdemux, "Unknown content encoding: %s",
15354 GST_DEBUG_OBJECT (qtdemux, "Unknown metadata namespaces: %s",
15365 caps = _get_unknown_codec_name ("meta", fourcc);
15371 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15372 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15373 const guint8 * stsd_entry_data, gchar ** codec_name)
15379 _codec ("MPEG 1 video");
15380 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15381 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15391 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
15392 const gchar * system_id)
15396 if (!qtdemux->protection_system_ids)
15397 qtdemux->protection_system_ids =
15398 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
15399 /* Check whether we already have an entry for this system ID. */
15400 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
15401 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
15402 if (g_ascii_strcasecmp (system_id, id) == 0) {
15406 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
15407 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,