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 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
119 typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
121 struct _QtDemuxSphericalMetadata
123 gboolean is_spherical;
124 gboolean is_stitched;
125 char *stitching_software;
126 char *projection_type;
129 int init_view_heading;
133 int full_pano_width_pixels;
134 int full_pano_height_pixels;
135 int cropped_area_image_width;
136 int cropped_area_image_height;
137 int cropped_area_left;
138 int cropped_area_top;
139 QTDEMUX_AMBISONIC_TYPE ambisonic_type;
140 QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
141 QTDEMUX_AMBISONIC_ORDER ambisonic_order;
144 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
146 /* Macros for converting to/from timescale */
147 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
148 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
150 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
151 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
153 /* timestamp is the DTS */
154 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
155 /* timestamp + offset + cslg_shift is the outgoing PTS */
156 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
157 /* timestamp + offset is the PTS used for internal seek calculations */
158 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
159 /* timestamp + duration - dts is the duration */
160 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
162 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
164 #define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
165 #define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
166 GST_TRACE("Locking from thread %p", g_thread_self()); \
167 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
168 GST_TRACE("Locked from thread %p", g_thread_self()); \
171 #define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
172 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
173 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
177 * Quicktime has tracks and segments. A track is a continuous piece of
178 * multimedia content. The track is not always played from start to finish but
179 * instead, pieces of the track are 'cut out' and played in sequence. This is
180 * what the segments do.
182 * Inside the track we have keyframes (K) and delta frames. The track has its
183 * own timing, which starts from 0 and extends to end. The position in the track
184 * is called the media_time.
186 * The segments now describe the pieces that should be played from this track
187 * and are basically tuples of media_time/duration/rate entries. We can have
188 * multiple segments and they are all played after one another. An example:
190 * segment 1: media_time: 1 second, duration: 1 second, rate 1
191 * segment 2: media_time: 3 second, duration: 2 second, rate 2
193 * To correctly play back this track, one must play: 1 second of media starting
194 * from media_time 1 followed by 2 seconds of media starting from media_time 3
197 * Each of the segments will be played at a specific time, the first segment at
198 * time 0, the second one after the duration of the first one, etc.. Note that
199 * the time in resulting playback is not identical to the media_time of the
202 * Visually, assuming the track has 4 second of media_time:
205 * .-----------------------------------------------------------.
206 * track: | K.....K.........K........K.......K.......K...........K... |
207 * '-----------------------------------------------------------'
209 * .------------^ ^ .----------^ ^
210 * / .-------------' / .------------------'
212 * .--------------. .--------------.
213 * | segment 1 | | segment 2 |
214 * '--------------' '--------------'
216 * The challenge here is to cut out the right pieces of the track for each of
217 * the playback segments. This fortunately can easily be done with the SEGMENT
218 * events of GStreamer.
220 * For playback of segment 1, we need to provide the decoder with the keyframe
221 * (a), in the above figure, but we must instruct it only to output the decoded
222 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
223 * position set to the time of the segment: 0.
225 * We then proceed to push data from keyframe (a) to frame (b). The decoder
226 * decodes but clips all before media_time 1.
228 * After finishing a segment, we push out a new SEGMENT event with the clipping
229 * boundaries of the new data.
231 * This is a good usecase for the GStreamer accumulated SEGMENT events.
234 struct _QtDemuxSegment
236 /* global time and duration, all gst time */
238 GstClockTime stop_time;
239 GstClockTime duration;
240 /* media time of trak, all gst time */
241 GstClockTime media_start;
242 GstClockTime media_stop;
244 /* Media start time in trak timescale units */
245 guint32 trak_media_start;
248 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
250 /* Used with fragmented MP4 files (mfra atom) */
251 struct _QtDemuxRandomAccessEntry
258 /* Contains properties and cryptographic info for a set of samples from a
259 * track protected using Common Encryption (cenc) */
260 struct _QtDemuxCencSampleSetInfo
262 GstStructure *default_properties;
264 /* @crypto_info holds one GstStructure per sample */
265 GPtrArray *crypto_info;
268 struct _QtDemuxAavdEncryptionInfo
270 GstStructure *default_properties;
274 qt_demux_state_string (enum QtDemuxState state)
277 case QTDEMUX_STATE_INITIAL:
279 case QTDEMUX_STATE_HEADER:
281 case QTDEMUX_STATE_MOVIE:
283 case QTDEMUX_STATE_BUFFER_MDAT:
284 return "<BUFFER_MDAT>";
290 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
292 static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
294 static GstStaticPadTemplate gst_qtdemux_sink_template =
295 GST_STATIC_PAD_TEMPLATE ("sink",
298 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
302 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
303 GST_STATIC_PAD_TEMPLATE ("video_%u",
306 GST_STATIC_CAPS_ANY);
308 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
309 GST_STATIC_PAD_TEMPLATE ("audio_%u",
312 GST_STATIC_CAPS_ANY);
314 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
315 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
318 GST_STATIC_CAPS_ANY);
320 static GstStaticPadTemplate gst_qtdemux_metasrc_template =
321 GST_STATIC_PAD_TEMPLATE ("meta_%u",
324 GST_STATIC_CAPS_ANY);
326 #define gst_qtdemux_parent_class parent_class
327 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
328 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qtdemux, "qtdemux",
329 GST_RANK_PRIMARY, GST_TYPE_QTDEMUX, isomp4_element_init (plugin));
331 static void gst_qtdemux_dispose (GObject * object);
332 static void gst_qtdemux_finalize (GObject * object);
335 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
336 GstClockTime media_time);
338 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
339 QtDemuxStream * str, gint64 media_offset);
342 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
343 static GstIndex *gst_qtdemux_get_index (GstElement * element);
345 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
346 GstStateChange transition);
347 static void gst_qtdemux_set_context (GstElement * element,
348 GstContext * context);
349 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
350 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
351 GstObject * parent, GstPadMode mode, gboolean active);
353 static void gst_qtdemux_loop (GstPad * pad);
354 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
356 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
358 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
360 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
361 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
362 QtDemuxStream * stream);
363 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
364 QtDemuxStream * stream);
365 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
368 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
370 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
371 const guint8 * buffer, guint length);
372 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
373 const guint8 * buffer, guint length);
374 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
376 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
377 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
379 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
380 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
381 const guint8 * stsd_entry_data, gchar ** codec_name);
382 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
383 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
384 const guint8 * data, int len, gchar ** codec_name);
385 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
386 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
387 gchar ** codec_name);
388 static GstCaps *qtdemux_meta_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
389 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
390 gchar ** codec_name);
391 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
392 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
393 const guint8 * stsd_entry_data, gchar ** codec_name);
395 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
396 QtDemuxStream * stream, guint32 n);
397 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
398 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
399 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
400 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
401 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
402 static void qtdemux_do_allocation (QtDemuxStream * stream,
403 GstQTDemux * qtdemux);
404 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
405 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
406 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
407 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
408 GstClockTime * _start, GstClockTime * _stop);
409 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
410 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
412 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
413 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
415 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
417 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
418 QtDemuxStream * stream, guint sample_index);
419 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
421 static void qtdemux_gst_structure_free (GstStructure * gststructure);
422 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
423 static void qtdemux_clear_protection_events_on_all_streams (GstQTDemux *
426 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
427 static void gst_tag_register_spherical_tags (void);
428 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
431 gst_qtdemux_class_init (GstQTDemuxClass * klass)
433 GObjectClass *gobject_class;
434 GstElementClass *gstelement_class;
436 gobject_class = (GObjectClass *) klass;
437 gstelement_class = (GstElementClass *) klass;
439 parent_class = g_type_class_peek_parent (klass);
441 gobject_class->dispose = gst_qtdemux_dispose;
442 gobject_class->finalize = gst_qtdemux_finalize;
444 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
446 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
447 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
449 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
451 gst_tag_register_musicbrainz_tags ();
453 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
454 gst_tag_register_spherical_tags ();
455 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
457 gst_element_class_add_static_pad_template (gstelement_class,
458 &gst_qtdemux_sink_template);
459 gst_element_class_add_static_pad_template (gstelement_class,
460 &gst_qtdemux_videosrc_template);
461 gst_element_class_add_static_pad_template (gstelement_class,
462 &gst_qtdemux_audiosrc_template);
463 gst_element_class_add_static_pad_template (gstelement_class,
464 &gst_qtdemux_subsrc_template);
465 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
467 "Demultiplex a QuickTime file into audio and video streams",
468 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
470 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
475 gst_qtdemux_init (GstQTDemux * qtdemux)
478 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
479 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
480 gst_pad_set_activatemode_function (qtdemux->sinkpad,
481 qtdemux_sink_activate_mode);
482 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
483 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
484 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
485 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
487 qtdemux->adapter = gst_adapter_new ();
488 g_queue_init (&qtdemux->protection_event_queue);
489 qtdemux->flowcombiner = gst_flow_combiner_new ();
490 g_mutex_init (&qtdemux->expose_lock);
492 qtdemux->active_streams = g_ptr_array_new_with_free_func
493 ((GDestroyNotify) gst_qtdemux_stream_unref);
494 qtdemux->old_streams = g_ptr_array_new_with_free_func
495 ((GDestroyNotify) gst_qtdemux_stream_unref);
497 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
498 qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
499 malloc (sizeof (QtDemuxSphericalMetadata));
501 if (qtdemux->spherical_metadata) {
502 qtdemux->spherical_metadata->is_spherical = FALSE;
503 qtdemux->spherical_metadata->is_stitched = FALSE;
504 qtdemux->spherical_metadata->stitching_software = NULL;
505 qtdemux->spherical_metadata->projection_type = NULL;
506 qtdemux->spherical_metadata->stereo_mode = NULL;
507 qtdemux->spherical_metadata->source_count = 0;
508 qtdemux->spherical_metadata->init_view_heading = 0;
509 qtdemux->spherical_metadata->init_view_pitch = 0;
510 qtdemux->spherical_metadata->init_view_roll = 0;
511 qtdemux->spherical_metadata->timestamp = 0;
512 qtdemux->spherical_metadata->full_pano_width_pixels = 0;
513 qtdemux->spherical_metadata->full_pano_height_pixels = 0;
514 qtdemux->spherical_metadata->cropped_area_image_width = 0;
515 qtdemux->spherical_metadata->cropped_area_image_height = 0;
516 qtdemux->spherical_metadata->cropped_area_left = 0;
517 qtdemux->spherical_metadata->cropped_area_top = 0;
518 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
519 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
520 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
522 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
524 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
526 gst_qtdemux_reset (qtdemux, TRUE);
530 gst_qtdemux_finalize (GObject * object)
532 GstQTDemux *qtdemux = GST_QTDEMUX (object);
534 g_free (qtdemux->redirect_location);
536 G_OBJECT_CLASS (parent_class)->finalize (object);
540 gst_qtdemux_dispose (GObject * object)
542 GstQTDemux *qtdemux = GST_QTDEMUX (object);
544 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
545 if (qtdemux->spherical_metadata) {
546 if (qtdemux->spherical_metadata->stitching_software)
547 free(qtdemux->spherical_metadata->stitching_software);
548 if (qtdemux->spherical_metadata->projection_type)
549 free(qtdemux->spherical_metadata->projection_type);
550 if (qtdemux->spherical_metadata->stereo_mode)
551 free(qtdemux->spherical_metadata->stereo_mode);
553 free(qtdemux->spherical_metadata);
554 qtdemux->spherical_metadata = NULL;
556 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
558 if (qtdemux->adapter) {
559 g_object_unref (G_OBJECT (qtdemux->adapter));
560 qtdemux->adapter = NULL;
562 gst_tag_list_unref (qtdemux->tag_list);
563 gst_flow_combiner_free (qtdemux->flowcombiner);
564 g_queue_clear_full (&qtdemux->protection_event_queue,
565 (GDestroyNotify) gst_event_unref);
567 g_free (qtdemux->cenc_aux_info_sizes);
568 qtdemux->cenc_aux_info_sizes = NULL;
569 g_mutex_clear (&qtdemux->expose_lock);
571 g_ptr_array_free (qtdemux->active_streams, TRUE);
572 g_ptr_array_free (qtdemux->old_streams, TRUE);
574 G_OBJECT_CLASS (parent_class)->dispose (object);
578 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
580 if (qtdemux->redirect_location) {
581 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
582 (_("This file contains no playable streams.")),
583 ("no known streams found, a redirect message has been posted"),
584 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
586 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
587 (_("This file contains no playable streams.")),
588 ("no known streams found"));
593 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
595 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
596 mem, size, 0, size, mem, free_func);
600 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
607 if (G_UNLIKELY (size == 0)) {
609 GstBuffer *tmp = NULL;
611 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
612 if (ret != GST_FLOW_OK)
615 gst_buffer_map (tmp, &map, GST_MAP_READ);
616 size = QT_UINT32 (map.data);
617 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
619 gst_buffer_unmap (tmp, &map);
620 gst_buffer_unref (tmp);
623 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
624 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
625 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
626 /* we're pulling header but already got most interesting bits,
627 * so never mind the rest (e.g. tags) (that much) */
628 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
632 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
633 (_("This file is invalid and cannot be played.")),
634 ("atom has bogus size %" G_GUINT64_FORMAT, size));
635 return GST_FLOW_ERROR;
639 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
641 if (G_UNLIKELY (flow != GST_FLOW_OK))
644 bsize = gst_buffer_get_size (*buf);
645 /* Catch short reads - we don't want any partial atoms */
646 if (G_UNLIKELY (bsize < size)) {
647 GST_WARNING_OBJECT (qtdemux,
648 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
649 gst_buffer_unref (*buf);
659 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
660 GstFormat src_format, gint64 src_value, GstFormat dest_format,
664 QtDemuxStream *stream = gst_pad_get_element_private (pad);
667 if (stream->subtype != FOURCC_vide) {
672 switch (src_format) {
673 case GST_FORMAT_TIME:
674 switch (dest_format) {
675 case GST_FORMAT_BYTES:{
676 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
682 *dest_value = stream->samples[index].offset;
684 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
685 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
686 GST_TIME_ARGS (src_value), *dest_value);
694 case GST_FORMAT_BYTES:
695 switch (dest_format) {
696 case GST_FORMAT_TIME:{
698 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
707 QTSTREAMTIME_TO_GSTTIME (stream,
708 stream->samples[index].timestamp);
709 GST_DEBUG_OBJECT (qtdemux,
710 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
711 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
730 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
732 gboolean res = FALSE;
734 *duration = GST_CLOCK_TIME_NONE;
736 if (qtdemux->duration != 0 &&
737 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
738 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
741 *duration = GST_CLOCK_TIME_NONE;
748 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
751 gboolean res = FALSE;
752 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
754 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
756 switch (GST_QUERY_TYPE (query)) {
757 case GST_QUERY_POSITION:{
760 gst_query_parse_position (query, &fmt, NULL);
761 if (fmt == GST_FORMAT_TIME
762 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
763 gst_query_set_position (query, GST_FORMAT_TIME,
764 qtdemux->segment.position);
769 case GST_QUERY_DURATION:{
772 gst_query_parse_duration (query, &fmt, NULL);
773 if (fmt == GST_FORMAT_TIME) {
774 /* First try to query upstream */
775 res = gst_pad_query_default (pad, parent, query);
777 GstClockTime duration;
778 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
779 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
786 case GST_QUERY_CONVERT:{
787 GstFormat src_fmt, dest_fmt;
788 gint64 src_value, dest_value = 0;
790 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
792 res = gst_qtdemux_src_convert (qtdemux, pad,
793 src_fmt, src_value, dest_fmt, &dest_value);
795 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
799 case GST_QUERY_FORMATS:
800 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
803 case GST_QUERY_SEEKING:{
807 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
809 if (fmt == GST_FORMAT_BYTES) {
810 /* We always refuse BYTES seeks from downstream */
814 /* try upstream first */
815 res = gst_pad_query_default (pad, parent, query);
818 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
819 if (fmt == GST_FORMAT_TIME) {
820 GstClockTime duration;
822 gst_qtdemux_get_duration (qtdemux, &duration);
824 if (!qtdemux->pullbased) {
827 /* we might be able with help from upstream */
829 q = gst_query_new_seeking (GST_FORMAT_BYTES);
830 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
831 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
832 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
836 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
842 case GST_QUERY_SEGMENT:
847 format = qtdemux->segment.format;
850 gst_segment_to_stream_time (&qtdemux->segment, format,
851 qtdemux->segment.start);
852 if ((stop = qtdemux->segment.stop) == -1)
853 stop = qtdemux->segment.duration;
855 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
857 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
862 res = gst_pad_query_default (pad, parent, query);
870 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
872 if (G_LIKELY (stream->pad)) {
873 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
874 GST_DEBUG_PAD_NAME (stream->pad));
876 if (!gst_tag_list_is_empty (stream->stream_tags)) {
877 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
878 stream->stream_tags);
879 gst_pad_push_event (stream->pad,
880 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
881 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
882 /* post message qtdemux tag (for early recive application) */
883 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
884 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
885 gst_tag_list_copy (stream->stream_tags)));
889 if (G_UNLIKELY (stream->send_global_tags)) {
890 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
892 gst_pad_push_event (stream->pad,
893 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
894 stream->send_global_tags = FALSE;
899 /* push event on all source pads; takes ownership of the event */
901 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
903 gboolean has_valid_stream = FALSE;
904 GstEventType etype = GST_EVENT_TYPE (event);
907 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
908 GST_EVENT_TYPE_NAME (event));
910 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
912 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
913 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
915 if ((pad = stream->pad)) {
916 has_valid_stream = TRUE;
918 if (etype == GST_EVENT_EOS) {
919 /* let's not send twice */
920 if (stream->sent_eos)
922 stream->sent_eos = TRUE;
925 gst_pad_push_event (pad, gst_event_ref (event));
929 gst_event_unref (event);
931 /* if it is EOS and there are no pads, post an error */
932 if (!has_valid_stream && etype == GST_EVENT_EOS) {
933 gst_qtdemux_post_no_playable_stream_error (qtdemux);
943 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
945 if ((gint64) s1->timestamp > *media_time)
947 if ((gint64) s1->timestamp == *media_time)
953 /* find the index of the sample that includes the data for @media_time using a
954 * binary search. Only to be called in optimized cases of linear search below.
956 * Returns the index of the sample with the corresponding *DTS*.
959 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
962 QtDemuxSample *result;
965 /* convert media_time to mov format */
967 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
969 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
970 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
971 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
973 if (G_LIKELY (result))
974 index = result - str->samples;
983 /* find the index of the sample that includes the data for @media_offset using a
986 * Returns the index of the sample.
989 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
990 QtDemuxStream * str, gint64 media_offset)
992 QtDemuxSample *result = str->samples;
995 if (result == NULL || str->n_samples == 0)
998 if (media_offset == result->offset)
1002 while (index < str->n_samples - 1) {
1003 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1006 if (media_offset < result->offset)
1017 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1022 /* find the index of the sample that includes the data for @media_time using a
1023 * linear search, and keeping in mind that not all samples may have been parsed
1024 * yet. If possible, it will delegate to binary search.
1026 * Returns the index of the sample.
1029 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1030 GstClockTime media_time)
1034 QtDemuxSample *sample;
1036 /* convert media_time to mov format */
1038 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1040 sample = str->samples;
1041 if (mov_time == sample->timestamp + sample->pts_offset)
1044 /* use faster search if requested time in already parsed range */
1045 sample = str->samples + str->stbl_index;
1046 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
1047 index = gst_qtdemux_find_index (qtdemux, str, media_time);
1048 sample = str->samples + index;
1050 while (index < str->n_samples - 1) {
1051 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1054 sample = str->samples + index + 1;
1055 if (mov_time < sample->timestamp) {
1056 sample = str->samples + index;
1064 /* sample->timestamp is now <= media_time, need to find the corresponding
1065 * PTS now by looking backwards */
1066 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
1068 sample = str->samples + index;
1076 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1081 /* find the index of the keyframe needed to decode the sample at @index
1082 * of stream @str, or of a subsequent keyframe (depending on @next)
1084 * Returns the index of the keyframe.
1087 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1088 guint32 index, gboolean next)
1090 guint32 new_index = index;
1092 if (index >= str->n_samples) {
1093 new_index = str->n_samples;
1097 /* all keyframes, return index */
1098 if (str->all_keyframe) {
1103 /* else search until we have a keyframe */
1104 while (new_index < str->n_samples) {
1105 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1108 if (str->samples[new_index].keyframe)
1120 if (new_index == str->n_samples) {
1121 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1126 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1127 "gave %u", next ? "after" : "before", index, new_index);
1134 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1139 /* find the segment for @time_position for @stream
1141 * Returns the index of the segment containing @time_position.
1142 * Returns the last segment and sets the @eos variable to TRUE
1143 * if the time is beyond the end. @eos may be NULL
1146 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1147 GstClockTime time_position)
1152 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1153 GST_TIME_ARGS (time_position));
1156 for (i = 0; i < stream->n_segments; i++) {
1157 QtDemuxSegment *segment = &stream->segments[i];
1159 GST_LOG_OBJECT (stream->pad,
1160 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1161 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1163 /* For the last segment we include stop_time in the last segment */
1164 if (i < stream->n_segments - 1) {
1165 if (segment->time <= time_position && time_position < segment->stop_time) {
1166 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1171 /* Last segment always matches */
1179 /* move the stream @str to the sample position @index.
1181 * Updates @str->sample_index and marks discontinuity if needed.
1184 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1187 /* no change needed */
1188 if (index == str->sample_index)
1191 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1194 /* position changed, we have a discont */
1195 str->sample_index = index;
1196 str->offset_in_sample = 0;
1197 /* Each time we move in the stream we store the position where we are
1199 str->from_sample = index;
1200 str->discont = TRUE;
1204 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1205 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1208 gint64 min_byte_offset = -1;
1211 min_offset = next ? G_MAXUINT64 : desired_time;
1213 /* for each stream, find the index of the sample in the segment
1214 * and move back to the previous keyframe. */
1215 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1217 guint32 index, kindex;
1219 GstClockTime media_start;
1220 GstClockTime media_time;
1221 GstClockTime seg_time;
1222 QtDemuxSegment *seg;
1223 gboolean empty_segment = FALSE;
1225 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1227 if (CUR_STREAM (str)->sparse && !use_sparse)
1230 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1231 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1233 /* get segment and time in the segment */
1234 seg = &str->segments[seg_idx];
1235 seg_time = (desired_time - seg->time) * seg->rate;
1237 while (QTSEGMENT_IS_EMPTY (seg)) {
1239 empty_segment = TRUE;
1240 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1243 if (seg_idx == str->n_segments)
1245 seg = &str->segments[seg_idx];
1248 if (seg_idx == str->n_segments) {
1249 /* FIXME track shouldn't have the last segment as empty, but if it
1250 * happens we better handle it */
1254 /* get the media time in the segment */
1255 media_start = seg->media_start + seg_time;
1257 /* get the index of the sample with media time */
1258 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1259 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1260 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1261 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1264 /* shift to next frame if we are looking for next keyframe */
1265 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1266 && index < str->stbl_index)
1269 if (!empty_segment) {
1270 /* find previous or next keyframe */
1271 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1273 /* if looking for next one, we will settle for one before if none found after */
1274 if (next && kindex == -1)
1275 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1277 /* Update the requested time whenever a keyframe was found, to make it
1278 * accurate and avoid having the first buffer fall outside of the segment
1283 /* get timestamp of keyframe */
1284 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1285 GST_DEBUG_OBJECT (qtdemux,
1286 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1287 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1288 str->samples[kindex].offset);
1290 /* keyframes in the segment get a chance to change the
1291 * desired_offset. keyframes out of the segment are
1293 if (media_time >= seg->media_start) {
1294 GstClockTime seg_time;
1296 /* this keyframe is inside the segment, convert back to
1298 seg_time = (media_time - seg->media_start) + seg->time;
1300 /* Adjust the offset based on the earliest suitable keyframe found,
1301 * based on which GST_SEEK_FLAG_SNAP_* is present (indicated by 'next').
1302 * For SNAP_BEFORE we look for the earliest keyframe before desired_time,
1303 * and in case of SNAP_AFTER - for the closest one after it. */
1304 if (seg_time < min_offset)
1305 min_offset = seg_time;
1310 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1311 min_byte_offset = str->samples[index].offset;
1315 *key_time = min_offset;
1317 *key_offset = min_byte_offset;
1321 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1322 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1326 g_return_val_if_fail (format != NULL, FALSE);
1327 g_return_val_if_fail (cur != NULL, FALSE);
1328 g_return_val_if_fail (stop != NULL, FALSE);
1330 if (*format == GST_FORMAT_TIME)
1334 if (cur_type != GST_SEEK_TYPE_NONE)
1335 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1336 if (res && stop_type != GST_SEEK_TYPE_NONE)
1337 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1340 *format = GST_FORMAT_TIME;
1345 /* perform seek in push based mode:
1346 find BYTE position to move to based on time and delegate to upstream
1349 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1354 GstSeekType cur_type, stop_type;
1355 gint64 cur, stop, key_cur;
1358 gint64 original_stop;
1361 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1363 gst_event_parse_seek (event, &rate, &format, &flags,
1364 &cur_type, &cur, &stop_type, &stop);
1365 seqnum = gst_event_get_seqnum (event);
1367 /* Directly send the instant-rate-change event here before taking the
1368 * stream-lock so that it can be applied as soon as possible */
1369 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1372 /* instant rate change only supported if direction does not change. All
1373 * other requirements are already checked before creating the seek event
1374 * but let's double-check here to be sure */
1375 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1376 (qtdemux->segment.rate < 0 && rate > 0) ||
1377 cur_type != GST_SEEK_TYPE_NONE ||
1378 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1379 GST_ERROR_OBJECT (qtdemux,
1380 "Instant rate change seeks only supported in the "
1381 "same direction, without flushing and position change");
1385 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1386 (GstSegmentFlags) flags);
1387 gst_event_set_seqnum (ev, seqnum);
1388 gst_qtdemux_push_event (qtdemux, ev);
1392 /* only forward streaming and seeking is possible */
1394 goto unsupported_seek;
1396 /* convert to TIME if needed and possible */
1397 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1401 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1402 * the original stop position to use when upstream pushes the new segment
1404 original_stop = stop;
1407 /* find reasonable corresponding BYTE position,
1408 * also try to mind about keyframes, since we can not go back a bit for them
1410 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1411 * mostly just work, but let's not yet boldly go there ... */
1412 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1417 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1418 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1421 GST_OBJECT_LOCK (qtdemux);
1422 qtdemux->seek_offset = byte_cur;
1423 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1424 qtdemux->push_seek_start = cur;
1426 qtdemux->push_seek_start = key_cur;
1429 if (stop_type == GST_SEEK_TYPE_NONE) {
1430 qtdemux->push_seek_stop = qtdemux->segment.stop;
1432 qtdemux->push_seek_stop = original_stop;
1434 GST_OBJECT_UNLOCK (qtdemux);
1436 qtdemux->segment_seqnum = seqnum;
1437 /* BYTE seek event */
1438 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1440 gst_event_set_seqnum (event, seqnum);
1441 res = gst_pad_push_event (qtdemux->sinkpad, event);
1448 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1454 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1459 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1464 /* perform the seek.
1466 * We set all segment_indexes in the streams to unknown and
1467 * adjust the time_position to the desired position. this is enough
1468 * to trigger a segment switch in the streaming thread to start
1469 * streaming from the desired position.
1471 * Keyframe seeking is a little more complicated when dealing with
1472 * segments. Ideally we want to move to the previous keyframe in
1473 * the segment but there might not be a keyframe in the segment. In
1474 * fact, none of the segments could contain a keyframe. We take a
1475 * practical approach: seek to the previous keyframe in the segment,
1476 * if there is none, seek to the beginning of the segment.
1478 * Called with STREAM_LOCK
1481 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1482 guint32 seqnum, GstSeekFlags flags)
1484 gint64 desired_offset;
1487 desired_offset = segment->position;
1489 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1490 GST_TIME_ARGS (desired_offset));
1492 /* may not have enough fragmented info to do this adjustment,
1493 * and we can't scan (and probably should not) at this time with
1494 * possibly flushing upstream */
1495 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1497 gboolean next, before, after;
1499 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1500 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1501 next = after && !before;
1502 if (segment->rate < 0)
1505 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1507 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1508 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1509 desired_offset = min_offset;
1512 /* and set all streams to the final position */
1513 GST_OBJECT_LOCK (qtdemux);
1514 gst_flow_combiner_reset (qtdemux->flowcombiner);
1515 GST_OBJECT_UNLOCK (qtdemux);
1516 qtdemux->segment_seqnum = seqnum;
1517 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1518 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1520 stream->time_position = desired_offset;
1521 stream->accumulated_base = 0;
1522 stream->sample_index = -1;
1523 stream->offset_in_sample = 0;
1524 stream->segment_index = -1;
1525 stream->sent_eos = FALSE;
1526 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1528 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1529 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1531 segment->position = desired_offset;
1532 if (segment->rate >= 0) {
1533 segment->start = desired_offset;
1534 /* We need to update time as we update start in that direction */
1535 segment->time = desired_offset;
1537 /* we stop at the end */
1538 if (segment->stop == -1)
1539 segment->stop = segment->duration;
1541 segment->stop = desired_offset;
1544 if (qtdemux->fragmented)
1545 qtdemux->fragmented_seek_pending = TRUE;
1550 /* do a seek in pull based mode */
1552 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1557 GstSeekType cur_type, stop_type;
1559 gboolean flush, instant_rate_change;
1561 GstSegment seeksegment;
1562 guint32 seqnum = GST_SEQNUM_INVALID;
1563 GstEvent *flush_event;
1566 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1568 gst_event_parse_seek (event, &rate, &format, &flags,
1569 &cur_type, &cur, &stop_type, &stop);
1570 seqnum = gst_event_get_seqnum (event);
1572 /* we have to have a format as the segment format. Try to convert
1574 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1578 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1580 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1581 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1583 /* Directly send the instant-rate-change event here before taking the
1584 * stream-lock so that it can be applied as soon as possible */
1585 if (instant_rate_change) {
1588 /* instant rate change only supported if direction does not change. All
1589 * other requirements are already checked before creating the seek event
1590 * but let's double-check here to be sure */
1591 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1592 (qtdemux->segment.rate < 0 && rate > 0) ||
1593 cur_type != GST_SEEK_TYPE_NONE ||
1594 stop_type != GST_SEEK_TYPE_NONE || flush) {
1595 GST_ERROR_OBJECT (qtdemux,
1596 "Instant rate change seeks only supported in the "
1597 "same direction, without flushing and position change");
1601 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1602 (GstSegmentFlags) flags);
1603 gst_event_set_seqnum (ev, seqnum);
1604 gst_qtdemux_push_event (qtdemux, ev);
1608 /* stop streaming, either by flushing or by pausing the task */
1610 flush_event = gst_event_new_flush_start ();
1611 if (seqnum != GST_SEQNUM_INVALID)
1612 gst_event_set_seqnum (flush_event, seqnum);
1613 /* unlock upstream pull_range */
1614 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1615 /* make sure out loop function exits */
1616 gst_qtdemux_push_event (qtdemux, flush_event);
1618 /* non flushing seek, pause the task */
1619 gst_pad_pause_task (qtdemux->sinkpad);
1622 /* wait for streaming to finish */
1623 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1625 /* copy segment, we need this because we still need the old
1626 * segment when we close the current segment. */
1627 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1629 /* configure the segment with the seek variables */
1630 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1631 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1632 cur_type, cur, stop_type, stop, &update)) {
1634 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1636 /* now do the seek */
1637 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1640 /* prepare for streaming again */
1642 flush_event = gst_event_new_flush_stop (TRUE);
1643 if (seqnum != GST_SEQNUM_INVALID)
1644 gst_event_set_seqnum (flush_event, seqnum);
1646 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1647 gst_qtdemux_push_event (qtdemux, flush_event);
1650 /* commit the new segment */
1651 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1653 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1654 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1655 qtdemux->segment.format, qtdemux->segment.position);
1656 if (seqnum != GST_SEQNUM_INVALID)
1657 gst_message_set_seqnum (msg, seqnum);
1658 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1661 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1662 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1663 qtdemux->sinkpad, NULL);
1665 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1672 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1678 qtdemux_ensure_index (GstQTDemux * qtdemux)
1682 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1684 /* Build complete index */
1685 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1686 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1688 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1689 GST_LOG_OBJECT (qtdemux,
1690 "Building complete index of track-id %u for seeking failed!",
1700 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1703 gboolean res = TRUE;
1704 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1706 switch (GST_EVENT_TYPE (event)) {
1707 case GST_EVENT_RECONFIGURE:
1708 GST_OBJECT_LOCK (qtdemux);
1709 gst_flow_combiner_reset (qtdemux->flowcombiner);
1710 GST_OBJECT_UNLOCK (qtdemux);
1711 res = gst_pad_event_default (pad, parent, event);
1713 case GST_EVENT_SEEK:
1715 GstSeekFlags flags = 0;
1716 GstFormat seek_format;
1717 gboolean instant_rate_change;
1719 #ifndef GST_DISABLE_GST_DEBUG
1720 GstClockTime ts = gst_util_get_timestamp ();
1722 guint32 seqnum = gst_event_get_seqnum (event);
1724 qtdemux->received_seek = TRUE;
1726 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1728 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1730 if (seqnum == qtdemux->segment_seqnum) {
1731 GST_LOG_OBJECT (pad,
1732 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1733 gst_event_unref (event);
1737 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1738 /* seek should be handled by upstream, we might need to re-download fragments */
1739 GST_DEBUG_OBJECT (qtdemux,
1740 "let upstream handle seek for fragmented playback");
1744 if (seek_format == GST_FORMAT_BYTES) {
1745 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1746 gst_event_unref (event);
1750 gst_event_parse_seek_trickmode_interval (event,
1751 &qtdemux->trickmode_interval);
1753 /* Build complete index for seeking;
1754 * if not a fragmented file at least and we're really doing a seek,
1755 * not just an instant-rate-change */
1756 if (!qtdemux->fragmented && !instant_rate_change) {
1757 if (!qtdemux_ensure_index (qtdemux))
1760 #ifndef GST_DISABLE_GST_DEBUG
1761 ts = gst_util_get_timestamp () - ts;
1762 GST_INFO_OBJECT (qtdemux,
1763 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1765 if (qtdemux->pullbased) {
1766 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1767 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1768 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1770 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1771 && QTDEMUX_N_STREAMS (qtdemux)
1772 && !qtdemux->fragmented) {
1773 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1775 GST_DEBUG_OBJECT (qtdemux,
1776 "ignoring seek in push mode in current state");
1779 gst_event_unref (event);
1784 res = gst_pad_event_default (pad, parent, event);
1794 GST_ERROR_OBJECT (qtdemux, "Index failed");
1795 gst_event_unref (event);
1801 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1803 * If @fw is false, the coding order is explored backwards.
1805 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1806 * sample is found for that track.
1808 * The stream and sample index of the sample with the minimum offset in the direction explored
1809 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1811 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1812 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1813 * @_stream and @_index. */
1815 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1816 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1819 gint64 time, min_time;
1820 QtDemuxStream *stream;
1827 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1830 gboolean set_sample;
1832 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1839 i = str->n_samples - 1;
1843 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1844 if (str->samples[i].size == 0)
1847 if (fw && (str->samples[i].offset < byte_pos))
1850 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1853 /* move stream to first available sample */
1855 gst_qtdemux_move_stream (qtdemux, str, i);
1859 /* avoid index from sparse streams since they might be far away */
1860 if (!CUR_STREAM (str)->sparse) {
1861 /* determine min/max time */
1862 time = QTSAMPLE_PTS (str, &str->samples[i]);
1863 if (min_time == -1 || (!fw && time > min_time) ||
1864 (fw && time < min_time)) {
1868 /* determine stream with leading sample, to get its position */
1870 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1871 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1879 /* no sample for this stream, mark eos */
1881 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1892 /* Copied from mpegtsbase code */
1893 /* FIXME: replace this function when we add new util function for stream-id creation */
1895 _get_upstream_id (GstQTDemux * demux)
1897 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1900 /* Try to create one from the upstream URI, else use a randome number */
1904 /* Try to generate one from the URI query and
1905 * if it fails take a random number instead */
1906 query = gst_query_new_uri ();
1907 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1908 gst_query_parse_uri (query, &uri);
1914 /* And then generate an SHA256 sum of the URI */
1915 cs = g_checksum_new (G_CHECKSUM_SHA256);
1916 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1918 upstream_id = g_strdup (g_checksum_get_string (cs));
1919 g_checksum_free (cs);
1921 /* Just get some random number if the URI query fails */
1922 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1923 "implementing a deterministic way of creating a stream-id");
1925 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1926 g_random_int (), g_random_int ());
1929 gst_query_unref (query);
1934 static QtDemuxStream *
1935 _create_stream (GstQTDemux * demux, guint32 track_id)
1937 QtDemuxStream *stream;
1940 stream = g_new0 (QtDemuxStream, 1);
1941 stream->demux = demux;
1942 stream->track_id = track_id;
1943 upstream_id = _get_upstream_id (demux);
1944 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1945 g_free (upstream_id);
1946 /* new streams always need a discont */
1947 stream->discont = TRUE;
1948 /* we enable clipping for raw audio/video streams */
1949 stream->need_clip = FALSE;
1950 stream->process_func = NULL;
1951 stream->segment_index = -1;
1952 stream->time_position = 0;
1953 stream->sample_index = -1;
1954 stream->offset_in_sample = 0;
1955 stream->new_stream = TRUE;
1956 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1957 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1958 stream->protected = FALSE;
1959 stream->protection_scheme_type = 0;
1960 stream->protection_scheme_version = 0;
1961 stream->protection_scheme_info = NULL;
1962 stream->n_samples_moof = 0;
1963 stream->duration_moof = 0;
1964 stream->duration_last_moof = 0;
1965 stream->alignment = 1;
1966 stream->stream_tags = gst_tag_list_new_empty ();
1967 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1968 g_queue_init (&stream->protection_scheme_event_queue);
1969 stream->ref_count = 1;
1970 /* consistent default for push based mode */
1971 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1976 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1978 GstStructure *structure;
1979 const gchar *variant;
1980 const GstCaps *mediacaps = NULL;
1982 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1984 structure = gst_caps_get_structure (caps, 0);
1985 variant = gst_structure_get_string (structure, "variant");
1987 if (variant && strcmp (variant, "mse-bytestream") == 0) {
1988 demux->variant = VARIANT_MSE_BYTESTREAM;
1991 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1992 QtDemuxStream *stream;
1993 const GValue *value;
1995 demux->fragmented = TRUE;
1996 demux->variant = VARIANT_MSS_FRAGMENTED;
1998 if (QTDEMUX_N_STREAMS (demux) > 1) {
1999 /* can't do this, we can only renegotiate for another mss format */
2003 value = gst_structure_get_value (structure, "media-caps");
2006 const GValue *timescale_v;
2008 /* TODO update when stream changes during playback */
2010 if (QTDEMUX_N_STREAMS (demux) == 0) {
2011 stream = _create_stream (demux, 1);
2012 g_ptr_array_add (demux->active_streams, stream);
2013 /* mss has no stsd/stsd entry, use id 0 as default */
2014 stream->stsd_entries_length = 1;
2015 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2016 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2018 stream = QTDEMUX_NTH_STREAM (demux, 0);
2021 timescale_v = gst_structure_get_value (structure, "timescale");
2023 stream->timescale = g_value_get_uint64 (timescale_v);
2025 /* default mss timescale */
2026 stream->timescale = 10000000;
2028 demux->timescale = stream->timescale;
2030 mediacaps = gst_value_get_caps (value);
2031 if (!CUR_STREAM (stream)->caps
2032 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2033 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2035 stream->new_caps = TRUE;
2037 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2038 structure = gst_caps_get_structure (mediacaps, 0);
2039 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2040 stream->subtype = FOURCC_vide;
2042 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2043 gst_structure_get_int (structure, "height",
2044 &CUR_STREAM (stream)->height);
2045 gst_structure_get_fraction (structure, "framerate",
2046 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2047 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2049 stream->subtype = FOURCC_soun;
2050 gst_structure_get_int (structure, "channels",
2051 &CUR_STREAM (stream)->n_channels);
2052 gst_structure_get_int (structure, "rate", &rate);
2053 CUR_STREAM (stream)->rate = rate;
2054 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
2055 if (gst_structure_has_field (structure, "original-media-type")) {
2056 const gchar *media_type =
2057 gst_structure_get_string (structure, "original-media-type");
2058 if (g_str_has_prefix (media_type, "video")) {
2059 stream->subtype = FOURCC_vide;
2060 } else if (g_str_has_prefix (media_type, "audio")) {
2061 stream->subtype = FOURCC_soun;
2066 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2073 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2077 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2079 if (hard || qtdemux->upstream_format_is_time) {
2080 qtdemux->state = QTDEMUX_STATE_INITIAL;
2081 qtdemux->neededbytes = 16;
2082 qtdemux->todrop = 0;
2083 qtdemux->pullbased = FALSE;
2084 g_clear_pointer (&qtdemux->redirect_location, g_free);
2085 qtdemux->first_mdat = -1;
2086 qtdemux->header_size = 0;
2087 qtdemux->mdatoffset = -1;
2088 qtdemux->restoredata_offset = -1;
2089 if (qtdemux->mdatbuffer)
2090 gst_buffer_unref (qtdemux->mdatbuffer);
2091 if (qtdemux->restoredata_buffer)
2092 gst_buffer_unref (qtdemux->restoredata_buffer);
2093 qtdemux->mdatbuffer = NULL;
2094 qtdemux->restoredata_buffer = NULL;
2095 qtdemux->mdatleft = 0;
2096 qtdemux->mdatsize = 0;
2097 if (qtdemux->comp_brands)
2098 gst_buffer_unref (qtdemux->comp_brands);
2099 qtdemux->comp_brands = NULL;
2100 qtdemux->last_moov_offset = -1;
2101 if (qtdemux->moov_node_compressed) {
2102 g_node_destroy (qtdemux->moov_node_compressed);
2103 if (qtdemux->moov_node)
2104 g_free (qtdemux->moov_node->data);
2106 qtdemux->moov_node_compressed = NULL;
2107 if (qtdemux->moov_node)
2108 g_node_destroy (qtdemux->moov_node);
2109 qtdemux->moov_node = NULL;
2110 if (qtdemux->tag_list)
2111 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2112 qtdemux->tag_list = gst_tag_list_new_empty ();
2113 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2115 if (qtdemux->element_index)
2116 gst_object_unref (qtdemux->element_index);
2117 qtdemux->element_index = NULL;
2119 qtdemux->major_brand = 0;
2120 qtdemux->upstream_format_is_time = FALSE;
2121 qtdemux->upstream_seekable = FALSE;
2122 qtdemux->upstream_size = 0;
2124 qtdemux->fragment_start = -1;
2125 qtdemux->fragment_start_offset = -1;
2126 qtdemux->duration = 0;
2127 qtdemux->moof_offset = 0;
2128 qtdemux->chapters_track_id = 0;
2129 qtdemux->have_group_id = FALSE;
2130 qtdemux->group_id = G_MAXUINT;
2132 g_queue_clear_full (&qtdemux->protection_event_queue,
2133 (GDestroyNotify) gst_event_unref);
2135 qtdemux->received_seek = FALSE;
2136 qtdemux->first_moof_already_parsed = FALSE;
2138 qtdemux->offset = 0;
2139 gst_adapter_clear (qtdemux->adapter);
2140 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2141 qtdemux->need_segment = TRUE;
2144 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2145 qtdemux->trickmode_interval = 0;
2146 g_ptr_array_set_size (qtdemux->active_streams, 0);
2147 g_ptr_array_set_size (qtdemux->old_streams, 0);
2148 qtdemux->n_video_streams = 0;
2149 qtdemux->n_audio_streams = 0;
2150 qtdemux->n_sub_streams = 0;
2151 qtdemux->n_meta_streams = 0;
2152 qtdemux->exposed = FALSE;
2153 qtdemux->fragmented = FALSE;
2154 qtdemux->variant = VARIANT_NONE;
2155 gst_caps_replace (&qtdemux->media_caps, NULL);
2156 qtdemux->timescale = 0;
2157 qtdemux->got_moov = FALSE;
2158 qtdemux->start_utc_time = GST_CLOCK_TIME_NONE;
2159 qtdemux->cenc_aux_info_offset = 0;
2160 g_free (qtdemux->cenc_aux_info_sizes);
2161 qtdemux->cenc_aux_info_sizes = NULL;
2162 qtdemux->cenc_aux_sample_count = 0;
2163 if (qtdemux->protection_system_ids) {
2164 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2165 qtdemux->protection_system_ids = NULL;
2167 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2168 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2169 GST_BIN_FLAG_STREAMS_AWARE);
2171 if (qtdemux->preferred_protection_system_id) {
2172 g_free (qtdemux->preferred_protection_system_id);
2173 qtdemux->preferred_protection_system_id = NULL;
2175 } else if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
2176 gst_flow_combiner_reset (qtdemux->flowcombiner);
2177 g_ptr_array_foreach (qtdemux->active_streams,
2178 (GFunc) gst_qtdemux_stream_clear, NULL);
2180 gst_flow_combiner_reset (qtdemux->flowcombiner);
2181 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2182 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2183 stream->sent_eos = FALSE;
2184 stream->time_position = 0;
2185 stream->accumulated_base = 0;
2186 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2192 qtdemux_clear_protection_events_on_all_streams (GstQTDemux * qtdemux)
2194 for (unsigned i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2195 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2196 g_queue_clear_full (&stream->protection_scheme_event_queue,
2197 (GDestroyNotify) gst_event_unref);
2201 /* Maps the @segment to the qt edts internal segments and pushes
2202 * the corresponding segment event.
2204 * If it ends up being at a empty segment, a gap will be pushed and the next
2205 * edts segment will be activated in sequence.
2207 * To be used in push-mode only */
2209 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2213 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2214 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2216 stream->time_position = segment->start;
2218 /* in push mode we should be guaranteed that we will have empty segments
2219 * at the beginning and then one segment after, other scenarios are not
2220 * supported and are discarded when parsing the edts */
2221 for (i = 0; i < stream->n_segments; i++) {
2222 if (stream->segments[i].stop_time > segment->start) {
2223 /* push the empty segment and move to the next one */
2224 gst_qtdemux_activate_segment (qtdemux, stream, i,
2225 stream->time_position);
2226 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2227 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2228 stream->time_position);
2230 /* accumulate previous segments */
2231 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2232 stream->accumulated_base +=
2233 (stream->segment.stop -
2234 stream->segment.start) / ABS (stream->segment.rate);
2238 g_assert (i == stream->n_segments - 1);
2245 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2256 for (i = 0; i < len; i++) {
2257 QtDemuxStream *stream = g_ptr_array_index (src, i);
2259 #ifndef GST_DISABLE_GST_DEBUG
2260 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2261 stream, GST_STR_NULL (stream->stream_id), dest);
2263 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2266 g_ptr_array_set_size (src, 0);
2270 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2273 GstQTDemux *demux = GST_QTDEMUX (parent);
2274 gboolean res = TRUE;
2276 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2278 switch (GST_EVENT_TYPE (event)) {
2279 case GST_EVENT_SEGMENT:
2282 QtDemuxStream *stream;
2286 /* some debug output */
2287 gst_event_copy_segment (event, &segment);
2288 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2291 if (segment.format == GST_FORMAT_TIME) {
2292 demux->upstream_format_is_time = TRUE;
2293 demux->segment_seqnum = gst_event_get_seqnum (event);
2295 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2296 "not in time format");
2298 /* chain will send initial newsegment after pads have been added */
2299 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2300 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2305 /* check if this matches a time seek we received previously
2306 * FIXME for backwards compatibility reasons we use the
2307 * seek_offset here to compare. In the future we might want to
2308 * change this to use the seqnum as it uniquely should identify
2309 * the segment that corresponds to the seek. */
2310 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2311 ", received segment offset %" G_GINT64_FORMAT,
2312 demux->seek_offset, segment.start);
2313 if (segment.format == GST_FORMAT_BYTES
2314 && demux->seek_offset == segment.start) {
2315 GST_OBJECT_LOCK (demux);
2316 offset = segment.start;
2318 segment.format = GST_FORMAT_TIME;
2319 segment.start = demux->push_seek_start;
2320 segment.stop = demux->push_seek_stop;
2321 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2322 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2323 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2324 GST_OBJECT_UNLOCK (demux);
2327 /* we only expect a BYTE segment, e.g. following a seek */
2328 if (segment.format == GST_FORMAT_BYTES) {
2329 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2330 offset = segment.start;
2332 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2333 NULL, (gint64 *) & segment.start);
2334 if ((gint64) segment.start < 0)
2337 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2338 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2339 NULL, (gint64 *) & segment.stop);
2340 /* keyframe seeking should already arrange for start >= stop,
2341 * but make sure in other rare cases */
2342 segment.stop = MAX (segment.stop, segment.start);
2344 } else if (segment.format == GST_FORMAT_TIME) {
2345 /* push all data on the adapter before starting this
2347 gst_qtdemux_process_adapter (demux, TRUE);
2349 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2353 /* We shouldn't modify upstream driven TIME FORMAT segment */
2354 if (!demux->upstream_format_is_time) {
2355 /* accept upstream's notion of segment and distribute along */
2356 segment.format = GST_FORMAT_TIME;
2357 segment.position = segment.time = segment.start;
2358 segment.duration = demux->segment.duration;
2359 segment.base = gst_segment_to_running_time (&demux->segment,
2360 GST_FORMAT_TIME, demux->segment.position);
2363 gst_segment_copy_into (&segment, &demux->segment);
2364 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2366 /* map segment to internal qt segments and push on each stream */
2367 if (QTDEMUX_N_STREAMS (demux)) {
2368 demux->need_segment = TRUE;
2369 gst_qtdemux_check_send_pending_segment (demux);
2372 /* clear leftover in current segment, if any */
2373 gst_adapter_clear (demux->adapter);
2375 /* set up streaming thread */
2376 demux->offset = offset;
2377 if (demux->upstream_format_is_time) {
2378 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2379 "set values to restart reading from a new atom");
2380 demux->neededbytes = 16;
2383 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2386 demux->todrop = stream->samples[idx].offset - offset;
2387 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2389 /* set up for EOS */
2390 demux->neededbytes = -1;
2395 gst_event_unref (event);
2399 case GST_EVENT_FLUSH_START:
2401 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2402 gst_event_unref (event);
2405 QTDEMUX_EXPOSE_LOCK (demux);
2406 res = gst_pad_event_default (demux->sinkpad, parent, event);
2407 QTDEMUX_EXPOSE_UNLOCK (demux);
2410 case GST_EVENT_FLUSH_STOP:
2414 dur = demux->segment.duration;
2415 gst_qtdemux_reset (demux, FALSE);
2416 demux->segment.duration = dur;
2418 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2419 gst_event_unref (event);
2425 /* If we are in push mode, and get an EOS before we've seen any streams,
2426 * then error out - we have nowhere to send the EOS */
2427 if (!demux->pullbased) {
2429 gboolean has_valid_stream = FALSE;
2430 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2431 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2432 has_valid_stream = TRUE;
2436 if (!has_valid_stream)
2437 gst_qtdemux_post_no_playable_stream_error (demux);
2439 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2440 (guint) gst_adapter_available (demux->adapter));
2441 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2447 case GST_EVENT_CAPS:{
2448 GstCaps *caps = NULL;
2450 gst_event_parse_caps (event, &caps);
2451 gst_qtdemux_setcaps (demux, caps);
2453 gst_event_unref (event);
2456 case GST_EVENT_PROTECTION:
2458 const gchar *system_id = NULL;
2460 gst_event_parse_protection (event, &system_id, NULL, NULL);
2461 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2463 gst_qtdemux_append_protection_system_id (demux, system_id);
2464 /* save the event for later, for source pads that have not been created */
2465 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2466 /* send it to all pads that already exist */
2467 gst_qtdemux_push_event (demux, event);
2471 case GST_EVENT_STREAM_START:
2474 gst_event_unref (event);
2476 /* Drain all the buffers */
2477 gst_qtdemux_process_adapter (demux, TRUE);
2478 gst_qtdemux_reset (demux, FALSE);
2479 /* We expect new moov box after new stream-start event */
2480 if (demux->exposed) {
2481 gst_qtdemux_stream_concat (demux,
2482 demux->old_streams, demux->active_streams);
2491 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2498 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2501 GstQTDemux *demux = GST_QTDEMUX (parent);
2502 gboolean res = FALSE;
2504 switch (GST_QUERY_TYPE (query)) {
2505 case GST_QUERY_BITRATE:
2507 GstClockTime duration;
2509 /* populate demux->upstream_size if not done yet */
2510 gst_qtdemux_check_seekability (demux);
2512 if (demux->upstream_size != -1
2513 && gst_qtdemux_get_duration (demux, &duration)) {
2515 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2518 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2519 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2520 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2522 /* TODO: better results based on ranges/index tables */
2523 gst_query_set_bitrate (query, bitrate);
2529 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2539 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2541 GstQTDemux *demux = GST_QTDEMUX (element);
2543 GST_OBJECT_LOCK (demux);
2544 if (demux->element_index)
2545 gst_object_unref (demux->element_index);
2547 demux->element_index = gst_object_ref (index);
2549 demux->element_index = NULL;
2551 GST_OBJECT_UNLOCK (demux);
2552 /* object lock might be taken again */
2554 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2555 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2556 demux->element_index, demux->index_id);
2560 gst_qtdemux_get_index (GstElement * element)
2562 GstIndex *result = NULL;
2563 GstQTDemux *demux = GST_QTDEMUX (element);
2565 GST_OBJECT_LOCK (demux);
2566 if (demux->element_index)
2567 result = gst_object_ref (demux->element_index);
2568 GST_OBJECT_UNLOCK (demux);
2570 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2577 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2579 g_free ((gpointer) stream->stco.data);
2580 stream->stco.data = NULL;
2581 g_free ((gpointer) stream->stsz.data);
2582 stream->stsz.data = NULL;
2583 g_free ((gpointer) stream->stsc.data);
2584 stream->stsc.data = NULL;
2585 g_free ((gpointer) stream->stts.data);
2586 stream->stts.data = NULL;
2587 g_free ((gpointer) stream->stss.data);
2588 stream->stss.data = NULL;
2589 g_free ((gpointer) stream->stps.data);
2590 stream->stps.data = NULL;
2591 g_free ((gpointer) stream->ctts.data);
2592 stream->ctts.data = NULL;
2596 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2598 g_free (stream->segments);
2599 stream->segments = NULL;
2600 stream->segment_index = -1;
2601 stream->accumulated_base = 0;
2605 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2607 g_free (stream->samples);
2608 stream->samples = NULL;
2609 gst_qtdemux_stbl_free (stream);
2612 g_free (stream->ra_entries);
2613 stream->ra_entries = NULL;
2614 stream->n_ra_entries = 0;
2616 stream->sample_index = -1;
2617 stream->stbl_index = -1;
2618 stream->n_samples = 0;
2619 stream->time_position = 0;
2621 stream->n_samples_moof = 0;
2622 stream->duration_moof = 0;
2623 stream->duration_last_moof = 0;
2627 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2630 if (stream->allocator)
2631 gst_object_unref (stream->allocator);
2632 while (stream->buffers) {
2633 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2634 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2636 for (i = 0; i < stream->stsd_entries_length; i++) {
2637 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2638 if (entry->rgb8_palette) {
2639 gst_memory_unref (entry->rgb8_palette);
2640 entry->rgb8_palette = NULL;
2642 entry->sparse = FALSE;
2645 if (stream->stream_tags)
2646 gst_tag_list_unref (stream->stream_tags);
2648 stream->stream_tags = gst_tag_list_new_empty ();
2649 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2650 g_free (stream->redirect_uri);
2651 stream->redirect_uri = NULL;
2652 stream->sent_eos = FALSE;
2653 stream->protected = FALSE;
2654 if (stream->protection_scheme_info) {
2655 if (stream->protection_scheme_type == FOURCC_cenc
2656 || stream->protection_scheme_type == FOURCC_cbcs) {
2657 QtDemuxCencSampleSetInfo *info =
2658 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2659 if (info->default_properties)
2660 gst_structure_free (info->default_properties);
2661 if (info->crypto_info)
2662 g_ptr_array_free (info->crypto_info, TRUE);
2664 if (stream->protection_scheme_type == FOURCC_aavd) {
2665 QtDemuxAavdEncryptionInfo *info =
2666 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
2667 if (info->default_properties)
2668 gst_structure_free (info->default_properties);
2670 g_free (stream->protection_scheme_info);
2671 stream->protection_scheme_info = NULL;
2673 stream->protection_scheme_type = 0;
2674 stream->protection_scheme_version = 0;
2675 g_queue_clear_full (&stream->protection_scheme_event_queue,
2676 (GDestroyNotify) gst_event_unref);
2677 gst_qtdemux_stream_flush_segments_data (stream);
2678 gst_qtdemux_stream_flush_samples_data (stream);
2682 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2685 gst_qtdemux_stream_clear (stream);
2686 for (i = 0; i < stream->stsd_entries_length; i++) {
2687 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2689 gst_caps_unref (entry->caps);
2693 g_free (stream->stsd_entries);
2694 stream->stsd_entries = NULL;
2695 stream->stsd_entries_length = 0;
2698 static QtDemuxStream *
2699 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2701 g_atomic_int_add (&stream->ref_count, 1);
2707 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2709 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2710 gst_qtdemux_stream_reset (stream);
2711 gst_tag_list_unref (stream->stream_tags);
2713 GstQTDemux *demux = stream->demux;
2714 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2715 GST_OBJECT_LOCK (demux);
2716 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2717 GST_OBJECT_UNLOCK (demux);
2719 g_free (stream->stream_id);
2724 static GstStateChangeReturn
2725 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2727 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2728 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2730 switch (transition) {
2731 case GST_STATE_CHANGE_READY_TO_PAUSED:
2732 gst_qtdemux_reset (qtdemux, TRUE);
2738 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2740 switch (transition) {
2741 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2742 gst_qtdemux_reset (qtdemux, TRUE);
2753 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2755 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2757 g_return_if_fail (GST_IS_CONTEXT (context));
2759 if (gst_context_has_context_type (context,
2760 "drm-preferred-decryption-system-id")) {
2761 const GstStructure *s;
2763 s = gst_context_get_structure (context);
2764 g_free (qtdemux->preferred_protection_system_id);
2765 qtdemux->preferred_protection_system_id =
2766 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2767 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2768 qtdemux->preferred_protection_system_id);
2771 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2775 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2777 /* counts as header data */
2778 qtdemux->header_size += length;
2780 /* only consider at least a sufficiently complete ftyp atom */
2783 guint32 minor_version;
2786 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2787 GST_DEBUG_OBJECT (qtdemux, "ftyp major brand: %" GST_FOURCC_FORMAT,
2788 GST_FOURCC_ARGS (qtdemux->major_brand));
2789 minor_version = QT_UINT32 (buffer + 12);
2790 GST_DEBUG_OBJECT (qtdemux, "ftyp minor version: %u", minor_version);
2791 if (qtdemux->comp_brands)
2792 gst_buffer_unref (qtdemux->comp_brands);
2793 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2794 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2797 length = length - 16;
2798 while (length > 0) {
2799 GST_DEBUG_OBJECT (qtdemux, "ftyp compatible brand: %" GST_FOURCC_FORMAT,
2800 GST_FOURCC_ARGS (QT_FOURCC (p)));
2808 qtdemux_parse_styp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2810 /* only consider at least a sufficiently complete styp atom */
2813 guint32 major_brand;
2814 guint32 minor_version;
2817 major_brand = QT_FOURCC (buffer + 8);
2818 GST_DEBUG_OBJECT (qtdemux, "styp major brand: %" GST_FOURCC_FORMAT,
2819 GST_FOURCC_ARGS (major_brand));
2820 minor_version = QT_UINT32 (buffer + 12);
2821 GST_DEBUG_OBJECT (qtdemux, "styp minor version: %u", minor_version);
2822 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2823 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2826 length = length - 16;
2827 while (length > 0) {
2828 GST_DEBUG_OBJECT (qtdemux, "styp compatible brand: %" GST_FOURCC_FORMAT,
2829 GST_FOURCC_ARGS (QT_FOURCC (p)));
2836 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2838 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2839 const char *xml_str, const char *param_name, int *value)
2841 char *value_start, *value_end, *endptr;
2842 const short value_length_max = 12;
2843 char init_view_ret[12];
2844 int value_length = 0;
2847 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2850 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2855 value_start += strlen (param_name);
2856 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2859 value_end = strchr (value_start, '<');
2861 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2865 value_length = value_end - value_start;
2866 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2867 || (value_start[value_length - 1] == '\t')))
2870 if (value_start[i] == '+' || value_start[i] == '-')
2872 while (i < value_length) {
2873 if (value_start[i] < '0' || value_start[i] > '9') {
2874 GST_ERROR_OBJECT (qtdemux,
2875 "error: incorrect value, integer was expected\n");
2881 if (value_length >= value_length_max || value_length < 1) {
2882 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2886 strncpy (init_view_ret, value_start, value_length_max);
2887 init_view_ret[value_length] = '\0';
2889 *value = strtol (init_view_ret, &endptr, 10);
2890 if (endptr == init_view_ret) {
2891 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2899 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2900 const char *xml_str, const char *param_name, char **value)
2902 char *value_start, *value_end;
2903 const short value_length_max = 256;
2904 int value_length = 0;
2906 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2909 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2914 value_start += strlen (param_name);
2915 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2918 value_end = strchr (value_start, '<');
2920 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2924 value_length = value_end - value_start;
2925 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2926 || (value_start[value_length - 1] == '\t')))
2929 if (value_length >= value_length_max || value_length < 1) {
2930 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2934 *value = strndup(value_start, value_length);
2940 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2941 const char *xml_str, const char *param_name, gboolean * value)
2943 char *value_start, *value_end;
2944 int value_length = 0;
2946 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2949 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2954 value_start += strlen (param_name);
2955 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2958 value_end = strchr (value_start, '<');
2960 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2964 value_length = value_end - value_start;
2965 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2966 || (value_start[value_length - 1] == '\t')))
2969 if (value_length < 1) {
2970 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2974 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
2980 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
2982 const char is_spherical_str[] = "<GSpherical:Spherical>";
2983 const char is_stitched_str[] = "<GSpherical:Stitched>";
2984 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2985 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2986 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2987 const char source_count_str[] = "<GSpherical:SourceCount>";
2988 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2989 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2990 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2991 const char timestamp_str[] = "<GSpherical:Timestamp>";
2992 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2993 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2994 const char cropped_area_image_width_str[] =
2995 "<GSpherical:CroppedAreaImageWidthPixels>";
2996 const char cropped_area_image_height_str[] =
2997 "<GSpherical:CroppedAreaImageHeightPixels>";
2998 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
2999 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
3001 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
3003 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
3004 (gboolean *) & spherical_metadata->is_spherical);
3005 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
3006 (gboolean *) & spherical_metadata->is_stitched);
3008 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
3009 _get_string_value_from_xml_string (qtdemux, xmlStr,
3010 stitching_software_str, &spherical_metadata->stitching_software);
3011 _get_string_value_from_xml_string (qtdemux, xmlStr,
3012 projection_type_str, &spherical_metadata->projection_type);
3013 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
3014 &spherical_metadata->stereo_mode);
3015 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
3016 &spherical_metadata->source_count);
3017 _get_int_value_from_xml_string (qtdemux, xmlStr,
3018 init_view_heading_str, &spherical_metadata->init_view_heading);
3019 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
3020 &spherical_metadata->init_view_pitch);
3021 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
3022 &spherical_metadata->init_view_roll);
3023 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
3024 &spherical_metadata->timestamp);
3025 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
3026 &spherical_metadata->full_pano_width_pixels);
3027 _get_int_value_from_xml_string (qtdemux, xmlStr,
3028 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
3029 _get_int_value_from_xml_string (qtdemux, xmlStr,
3030 cropped_area_image_width_str,
3031 &spherical_metadata->cropped_area_image_width);
3032 _get_int_value_from_xml_string (qtdemux, xmlStr,
3033 cropped_area_image_height_str,
3034 &spherical_metadata->cropped_area_image_height);
3035 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
3036 &spherical_metadata->cropped_area_left);
3037 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
3038 &spherical_metadata->cropped_area_top);
3045 gst_tag_register_spherical_tags (void) {
3046 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
3049 _("Flag indicating if the video is a spherical video"),
3051 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
3054 _("Flag indicating if the video is stitched"),
3056 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
3058 _("tag-stitching-software"),
3059 _("Software used to stitch the spherical video"),
3061 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
3063 _("tag-projection-type"),
3064 _("Projection type used in the video frames"),
3066 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
3068 _("tag-stereo-mode"),
3069 _("Description of stereoscopic 3D layout"),
3071 gst_tag_register ("source_count", GST_TAG_FLAG_META,
3073 _("tag-source-count"),
3074 _("Number of cameras used to create the spherical video"),
3076 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
3078 _("tag-init-view-heading"),
3079 _("The heading angle of the initial view in degrees"),
3081 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
3083 _("tag-init-view-pitch"),
3084 _("The pitch angle of the initial view in degrees"),
3086 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
3088 _("tag-init-view-roll"),
3089 _("The roll angle of the initial view in degrees"),
3091 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
3094 _("Epoch timestamp of when the first frame in the video was recorded"),
3096 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
3098 _("tag-full-pano-width"),
3099 _("Width of the encoded video frame in pixels"),
3101 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
3103 _("tag-full-pano-height"),
3104 _("Height of the encoded video frame in pixels"),
3106 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
3108 _("tag-cropped-area-image-width"),
3109 _("Width of the video frame to display (e.g. cropping)"),
3111 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
3113 _("tag-cropped-area-image-height"),
3114 _("Height of the video frame to display (e.g. cropping)"),
3116 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3118 _("tag-cropped-area-left"),
3119 _("Column where the left edge of the image was cropped from the"
3120 " full sized panorama"),
3122 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3124 _("tag-cropped-area-top"),
3125 _("Row where the top edge of the image was cropped from the"
3126 " full sized panorama"),
3128 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3130 _("tag-ambisonic-type"),
3131 _("Specifies the type of ambisonic audio represented"),
3133 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3135 _("tag-ambisonic-format"),
3136 _("Specifies the ambisonic audio format"),
3138 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3140 _("tag-ambisonic-order"),
3141 _("Specifies the ambisonic audio channel order"),
3148 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3150 GstTagList *taglist;
3151 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3153 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3154 spherical_metadata->is_spherical);
3155 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3156 spherical_metadata->is_stitched);
3157 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3158 spherical_metadata->stitching_software);
3159 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3160 spherical_metadata->projection_type);
3161 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3162 spherical_metadata->stereo_mode);
3163 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3164 spherical_metadata->source_count);
3165 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3166 spherical_metadata->init_view_heading);
3167 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3168 spherical_metadata->init_view_pitch);
3169 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3170 spherical_metadata->init_view_roll);
3171 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3172 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3173 spherical_metadata->full_pano_width_pixels);
3174 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3175 spherical_metadata->full_pano_height_pixels);
3176 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3177 spherical_metadata->cropped_area_image_width);
3178 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3179 spherical_metadata->cropped_area_image_height);
3180 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3181 spherical_metadata->cropped_area_left);
3182 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3183 spherical_metadata->cropped_area_top);
3184 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3185 spherical_metadata->ambisonic_type);
3186 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3187 spherical_metadata->ambisonic_order);
3188 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3189 spherical_metadata->ambisonic_format);
3191 taglist = gst_tag_list_new_empty ();
3192 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3193 "is_spherical", spherical_metadata->is_spherical,
3194 "is_stitched", spherical_metadata->is_stitched,
3195 "source_count", spherical_metadata->source_count,
3196 "init_view_heading", spherical_metadata->init_view_heading,
3197 "init_view_pitch", spherical_metadata->init_view_pitch,
3198 "init_view_roll", spherical_metadata->init_view_roll,
3199 "timestamp", spherical_metadata->timestamp,
3200 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3201 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3202 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3203 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3204 "cropped_area_left", spherical_metadata->cropped_area_left,
3205 "cropped_area_top", spherical_metadata->cropped_area_top,
3206 "ambisonic_type", spherical_metadata->ambisonic_type,
3207 "ambisonic_format", spherical_metadata->ambisonic_format,
3208 "ambisonic_order", spherical_metadata->ambisonic_order,
3211 if (spherical_metadata->stitching_software)
3212 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3213 "stitching_software", spherical_metadata->stitching_software,
3215 if (spherical_metadata->projection_type)
3216 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3217 "projection_type", spherical_metadata->projection_type,
3219 if (spherical_metadata->stereo_mode)
3220 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3221 "stereo_mode", spherical_metadata->stereo_mode,
3224 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3225 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3226 gst_tag_list_copy (taglist)));
3228 gst_tag_list_unref(taglist);
3234 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3239 guint8 ambisonic_type = 0;
3240 guint32 ambisonic_order = 0;
3241 guint8 ambisonic_channel_ordering = 0;
3242 guint8 ambisonic_normalization = 0;
3243 guint32 num_channels = 0;
3244 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3248 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3250 qtdemux->header_size += length;
3251 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3253 if (length <= offset + 16) {
3254 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3258 version = QT_UINT8 (buffer + offset);
3259 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3260 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3261 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3262 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3263 num_channels = QT_UINT32 (buffer + offset + 8);
3264 for (i = 0; i < num_channels; ++i)
3265 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3267 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3268 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3269 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3270 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3271 ambisonic_channel_ordering);
3272 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3273 ambisonic_normalization);
3274 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3275 for (i = 0; i < num_channels; ++i)
3276 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3278 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3279 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3280 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3282 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3283 if (num_channels == 4) {
3284 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3286 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3287 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3288 && (channel_map[0] == 0) && (channel_map[1] == 1)
3289 && (channel_map[2] == 2) && (channel_map[3] == 3))
3290 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3292 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3293 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3294 && (channel_map[0] == 0) && (channel_map[1] == 3)
3295 && (channel_map[2] == 1) && (channel_map[3] == 2))
3296 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3303 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3306 qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
3307 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
3308 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
3309 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
3310 const guint8 * constant_iv)
3312 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3313 gst_buffer_fill (kid_buf, 0, kid, 16);
3314 if (info->default_properties)
3315 gst_structure_free (info->default_properties);
3316 info->default_properties =
3317 gst_structure_new ("application/x-cenc",
3318 "iv_size", G_TYPE_UINT, iv_size,
3319 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
3320 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3321 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3322 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3323 gst_buffer_unref (kid_buf);
3324 if (protection_scheme_type == FOURCC_cbcs) {
3325 if (crypt_byte_block != 0 || skip_byte_block != 0) {
3326 gst_structure_set (info->default_properties, "crypt_byte_block",
3327 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
3328 skip_byte_block, NULL);
3330 if (constant_iv != NULL) {
3331 GstBuffer *constant_iv_buf =
3332 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
3333 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
3334 gst_structure_set (info->default_properties, "constant_iv_size",
3335 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
3337 gst_buffer_unref (constant_iv_buf);
3339 gst_structure_set (info->default_properties, "cipher-mode",
3340 G_TYPE_STRING, "cbcs", NULL);
3342 gst_structure_set (info->default_properties, "cipher-mode",
3343 G_TYPE_STRING, "cenc", NULL);
3348 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
3349 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
3351 guint32 algorithm_id = 0;
3353 gboolean is_encrypted = TRUE;
3356 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
3357 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3362 if (algorithm_id == 0) {
3363 is_encrypted = FALSE;
3364 } else if (algorithm_id == 1) {
3365 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3366 } else if (algorithm_id == 2) {
3367 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3370 if (!gst_byte_reader_get_uint8 (br, &iv_size))
3373 if (!gst_byte_reader_get_data (br, 16, &kid))
3376 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
3377 is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
3378 gst_structure_set (info->default_properties, "piff_algorithm_id",
3379 G_TYPE_UINT, algorithm_id, NULL);
3385 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3393 QtDemuxStream *stream;
3394 GstStructure *structure;
3395 QtDemuxCencSampleSetInfo *ss_info = NULL;
3396 const gchar *system_id;
3397 gboolean uses_sub_sample_encryption = FALSE;
3398 guint32 sample_count;
3400 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
3403 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
3405 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3406 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3407 GST_WARNING_OBJECT (qtdemux,
3408 "Attempting PIFF box parsing on an unencrypted stream.");
3412 if (!gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3413 G_TYPE_STRING, &system_id, NULL)) {
3414 GST_WARNING_OBJECT (qtdemux, "%s field not present in caps",
3415 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD);
3419 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3421 stream->protected = TRUE;
3422 stream->protection_scheme_type = FOURCC_cenc;
3424 if (!stream->protection_scheme_info)
3425 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3427 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3428 if (!ss_info->default_properties) {
3429 ss_info->default_properties =
3430 gst_structure_new ("application/x-cenc",
3431 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
3436 if (ss_info->crypto_info) {
3437 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3438 g_ptr_array_free (ss_info->crypto_info, TRUE);
3439 ss_info->crypto_info = NULL;
3443 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3445 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3446 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3450 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3451 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3455 if ((flags & 0x000001)) {
3456 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
3459 } else if ((flags & 0x000002)) {
3460 uses_sub_sample_encryption = TRUE;
3463 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
3465 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
3469 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
3470 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3474 ss_info->crypto_info =
3475 g_ptr_array_new_full (sample_count,
3476 (GDestroyNotify) qtdemux_gst_structure_free);
3478 for (i = 0; i < sample_count; ++i) {
3479 GstStructure *properties;
3483 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3484 if (properties == NULL) {
3485 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3486 qtdemux->cenc_aux_sample_count = i;
3490 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3491 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3492 gst_structure_free (properties);
3493 qtdemux->cenc_aux_sample_count = i;
3496 buf = gst_buffer_new_wrapped (data, iv_size);
3497 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3498 gst_buffer_unref (buf);
3500 if (uses_sub_sample_encryption) {
3501 guint16 n_subsamples;
3502 const GValue *kid_buf_value;
3504 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3505 || n_subsamples == 0) {
3506 GST_ERROR_OBJECT (qtdemux,
3507 "failed to get subsample count for sample %u", i);
3508 gst_structure_free (properties);
3509 qtdemux->cenc_aux_sample_count = i;
3512 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3513 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3514 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3516 gst_structure_free (properties);
3517 qtdemux->cenc_aux_sample_count = i;
3520 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3523 gst_structure_get_value (ss_info->default_properties, "kid");
3525 gst_structure_set (properties,
3526 "subsample_count", G_TYPE_UINT, n_subsamples,
3527 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3528 gst_structure_set_value (properties, "kid", kid_buf_value);
3529 gst_buffer_unref (buf);
3531 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3534 g_ptr_array_add (ss_info->crypto_info, properties);
3537 qtdemux->cenc_aux_sample_count = sample_count;
3541 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3543 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3544 0x97, 0xA9, 0x42, 0xE8,
3545 0x9C, 0x71, 0x99, 0x94,
3546 0x91, 0xE3, 0xAF, 0xAC
3548 static const guint8 playready_uuid[] = {
3549 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3550 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3553 static const guint8 piff_sample_encryption_uuid[] = {
3554 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3555 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3558 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3559 static const guint8 spherical_uuid[] = {
3560 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3561 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3563 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3567 /* counts as header data */
3568 qtdemux->header_size += length;
3570 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3572 if (length <= offset + 16) {
3573 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3577 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3578 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3579 const char *contents;
3581 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3582 contents = (char *) (buffer + offset + 16);
3583 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3585 if (qtdemux->spherical_metadata)
3586 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3590 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3592 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3594 GstTagList *taglist;
3596 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3597 length - offset - 16, NULL);
3598 taglist = gst_tag_list_from_xmp_buffer (buf);
3599 gst_buffer_unref (buf);
3601 /* make sure we have a usable taglist */
3602 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3604 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3606 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3608 const gunichar2 *s_utf16;
3611 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3612 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3613 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3614 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3618 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3619 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3621 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3622 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3624 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3625 GST_READ_UINT32_LE (buffer + offset),
3626 GST_READ_UINT32_LE (buffer + offset + 4),
3627 GST_READ_UINT32_LE (buffer + offset + 8),
3628 GST_READ_UINT32_LE (buffer + offset + 12));
3633 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3635 GstSidxParser sidx_parser;
3636 GstIsoffParserResult res;
3639 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3642 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3644 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3645 if (res == GST_ISOFF_QT_PARSER_DONE) {
3646 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3648 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3652 qtdemux_parse_cstb (GstQTDemux * qtdemux, GstByteReader * data)
3655 guint32 entry_count;
3657 GST_DEBUG_OBJECT (qtdemux, "Parsing CorrectStartTime box");
3659 qtdemux->start_utc_time = GST_CLOCK_TIME_NONE;
3661 if (gst_byte_reader_get_remaining (data) < 4) {
3662 GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box");
3666 entry_count = gst_byte_reader_get_uint32_be_unchecked (data);
3667 if (entry_count == 0)
3670 /* XXX: We assume that all start times are the same as different start times
3671 * would violate the MP4 synchronization model, so we just take the first
3672 * one here and apply it to all tracks.
3675 if (gst_byte_reader_get_remaining (data) < entry_count * 12) {
3676 GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box");
3681 gst_byte_reader_skip_unchecked (data, 4);
3683 /* In 100ns intervals */
3684 start_time = gst_byte_reader_get_uint64_be_unchecked (data);
3686 /* Convert from Jan 1 1601 to Jan 1 1970 */
3687 if (start_time < 11644473600 * G_GUINT64_CONSTANT (10000000)) {
3688 GST_WARNING_OBJECT (qtdemux, "Start UTC time before UNIX epoch");
3691 start_time -= 11644473600 * G_GUINT64_CONSTANT (10000000);
3693 /* Convert to GstClockTime */
3696 GST_DEBUG_OBJECT (qtdemux, "Start UTC time: %" GST_TIME_FORMAT,
3697 GST_TIME_ARGS (start_time));
3699 qtdemux->start_utc_time = start_time;
3702 /* caller verifies at least 8 bytes in buf */
3704 extract_initial_length_and_fourcc (const guint8 * data, gsize size,
3705 guint64 * plength, guint32 * pfourcc)
3710 length = QT_UINT32 (data);
3711 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3712 fourcc = QT_FOURCC (data + 4);
3713 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3716 length = G_MAXUINT64;
3717 } else if (length == 1 && size >= 16) {
3718 /* this means we have an extended size, which is the 64 bit value of
3719 * the next 8 bytes */
3720 length = QT_UINT64 (data + 8);
3721 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3731 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3733 guint32 version = 0;
3734 GstClockTime duration = 0;
3736 if (!gst_byte_reader_get_uint32_be (br, &version))
3741 if (!gst_byte_reader_get_uint64_be (br, &duration))
3746 if (!gst_byte_reader_get_uint32_be (br, &dur))
3751 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3752 qtdemux->duration = duration;
3758 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3764 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3765 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3767 if (!stream->parsed_trex && qtdemux->moov_node) {
3769 GstByteReader trex_data;
3771 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3773 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3776 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3778 /* skip version/flags */
3779 if (!gst_byte_reader_skip (&trex_data, 4))
3781 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3783 if (id != stream->track_id)
3785 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3787 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3789 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3791 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3794 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3795 "duration %d, size %d, flags 0x%x", stream->track_id,
3798 stream->parsed_trex = TRUE;
3799 stream->def_sample_description_index = sdi;
3800 stream->def_sample_duration = dur;
3801 stream->def_sample_size = size;
3802 stream->def_sample_flags = flags;
3805 /* iterate all siblings */
3806 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3812 *ds_duration = stream->def_sample_duration;
3813 *ds_size = stream->def_sample_size;
3814 *ds_flags = stream->def_sample_flags;
3816 /* even then, above values are better than random ... */
3817 if (G_UNLIKELY (!stream->parsed_trex)) {
3818 GST_WARNING_OBJECT (qtdemux,
3819 "failed to find fragment defaults for stream %d", stream->track_id);
3826 /* This method should be called whenever a more accurate duration might
3827 * have been found. It will update all relevant variables if/where needed
3830 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3834 GstClockTime prevdur;
3836 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3838 if (movdur > qtdemux->duration) {
3839 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3840 GST_DEBUG_OBJECT (qtdemux,
3841 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3842 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3843 qtdemux->duration = movdur;
3844 GST_DEBUG_OBJECT (qtdemux,
3845 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3846 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3847 GST_TIME_ARGS (qtdemux->segment.stop));
3848 if (qtdemux->segment.duration == prevdur) {
3849 /* If the current segment has duration/stop identical to previous duration
3850 * update them also (because they were set at that point in time with
3851 * the wrong duration */
3852 /* We convert the value *from* the timescale version to avoid rounding errors */
3853 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3854 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3855 qtdemux->segment.duration = fixeddur;
3856 qtdemux->segment.stop = fixeddur;
3860 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3861 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3863 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3864 if (movdur > stream->duration) {
3865 GST_DEBUG_OBJECT (qtdemux,
3866 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3867 GST_TIME_ARGS (duration));
3868 stream->duration = movdur;
3869 /* internal duration tracking state has been updated above, so */
3870 /* preserve an open-ended dummy segment rather than repeatedly updating
3871 * it and spamming downstream accordingly with segment events */
3872 /* also mangle the edit list end time when fragmented with a single edit
3873 * list that may only cover any non-fragmented data */
3874 if ((stream->dummy_segment ||
3875 (qtdemux->fragmented && stream->n_segments == 1)) &&
3876 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3877 /* Update all dummy values to new duration */
3878 stream->segments[0].stop_time = duration;
3879 stream->segments[0].duration = duration;
3880 stream->segments[0].media_stop = duration;
3882 /* let downstream know we possibly have a new stop time */
3883 if (stream->segment_index != -1) {
3886 if (qtdemux->segment.rate >= 0) {
3887 pos = stream->segment.start;
3889 pos = stream->segment.stop;
3892 gst_qtdemux_stream_update_segment (qtdemux, stream,
3893 stream->segment_index, pos, NULL, NULL);
3901 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3902 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3903 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3904 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3907 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3909 gint32 data_offset = 0;
3911 guint32 flags = 0, first_flags = 0, samples_count = 0;
3914 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3915 QtDemuxSample *sample;
3916 gboolean ismv = FALSE;
3917 gint64 initial_offset;
3920 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3921 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3922 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3923 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3925 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3926 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3930 /* presence of stss or not can't really tell us much,
3931 * and flags and so on tend to be marginally reliable in these files */
3932 if (stream->subtype == FOURCC_soun) {
3933 GST_DEBUG_OBJECT (qtdemux,
3934 "sound track in fragmented file; marking all keyframes");
3935 stream->all_keyframe = TRUE;
3938 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3939 !gst_byte_reader_get_uint24_be (trun, &flags))
3942 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3945 if (flags & TR_DATA_OFFSET) {
3946 /* note this is really signed */
3947 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3949 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3950 /* default base offset = first byte of moof */
3951 if (*base_offset == -1) {
3952 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3953 *base_offset = moof_offset;
3955 *running_offset = *base_offset + data_offset;
3957 /* if no offset at all, that would mean data starts at moof start,
3958 * which is a bit wrong and is ismv crappy way, so compensate
3959 * assuming data is in mdat following moof */
3960 if (*base_offset == -1) {
3961 *base_offset = moof_offset + moof_length + 8;
3962 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3965 if (*running_offset == -1)
3966 *running_offset = *base_offset;
3969 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3971 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3972 data_offset, flags, samples_count);
3974 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3975 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3976 GST_DEBUG_OBJECT (qtdemux,
3977 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3978 flags ^= TR_FIRST_SAMPLE_FLAGS;
3980 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3982 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3986 /* FIXME ? spec says other bits should also be checked to determine
3987 * entry size (and prefix size for that matter) */
3989 dur_offset = size_offset = 0;
3990 if (flags & TR_SAMPLE_DURATION) {
3991 GST_LOG_OBJECT (qtdemux, "entry duration present");
3992 dur_offset = entry_size;
3995 if (flags & TR_SAMPLE_SIZE) {
3996 GST_LOG_OBJECT (qtdemux, "entry size present");
3997 size_offset = entry_size;
4000 if (flags & TR_SAMPLE_FLAGS) {
4001 GST_LOG_OBJECT (qtdemux, "entry flags present");
4002 flags_offset = entry_size;
4005 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4006 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
4007 ct_offset = entry_size;
4011 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
4013 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
4015 if (stream->n_samples + samples_count >=
4016 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
4019 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
4020 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
4021 (stream->n_samples + samples_count) *
4022 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
4024 /* create a new array of samples if it's the first sample parsed */
4025 if (stream->n_samples == 0) {
4026 g_assert (stream->samples == NULL);
4027 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
4028 /* or try to reallocate it with space enough to insert the new samples */
4030 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
4031 stream->n_samples + samples_count);
4032 if (stream->samples == NULL)
4035 if (qtdemux->fragment_start != -1) {
4036 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
4037 qtdemux->fragment_start = -1;
4039 if (stream->n_samples == 0) {
4040 if (decode_ts > 0) {
4041 timestamp = decode_ts;
4042 } else if (stream->pending_seek != NULL) {
4043 /* if we don't have a timestamp from a tfdt box, we'll use the one
4044 * from the mfra seek table */
4045 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
4046 GST_TIME_ARGS (stream->pending_seek->ts));
4048 /* FIXME: this is not fully correct, the timestamp refers to the random
4049 * access sample refered to in the tfra entry, which may not necessarily
4050 * be the first sample in the tfrag/trun (but hopefully/usually is) */
4051 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
4056 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4057 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
4058 GST_TIME_ARGS (gst_ts));
4060 /* If this is a GST_FORMAT_BYTES stream and we have a tfdt then use it
4061 * instead of the sum of sample durations */
4062 if (has_tfdt && !qtdemux->upstream_format_is_time) {
4063 timestamp = decode_ts;
4064 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4065 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4066 " (using tfdt)", GST_TIME_ARGS (gst_ts));
4068 /* subsequent fragments extend stream */
4070 stream->samples[stream->n_samples - 1].timestamp +
4071 stream->samples[stream->n_samples - 1].duration;
4072 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4073 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4074 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
4079 initial_offset = *running_offset;
4081 sample = stream->samples + stream->n_samples;
4082 for (i = 0; i < samples_count; i++) {
4083 guint32 dur, size, sflags;
4086 /* first read sample data */
4087 if (flags & TR_SAMPLE_DURATION) {
4088 dur = QT_UINT32 (data + dur_offset);
4090 dur = d_sample_duration;
4092 if (flags & TR_SAMPLE_SIZE) {
4093 size = QT_UINT32 (data + size_offset);
4095 size = d_sample_size;
4097 if (flags & TR_FIRST_SAMPLE_FLAGS) {
4099 sflags = first_flags;
4101 sflags = d_sample_flags;
4103 } else if (flags & TR_SAMPLE_FLAGS) {
4104 sflags = QT_UINT32 (data + flags_offset);
4106 sflags = d_sample_flags;
4109 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4110 /* Read offsets as signed numbers regardless of trun version as very
4111 * high offsets are unlikely and there are files out there that use
4112 * version=0 truns with negative offsets */
4113 ct = QT_UINT32 (data + ct_offset);
4115 /* FIXME: Set offset to 0 for "no decode samples". This needs
4116 * to be handled in a codec specific manner ideally. */
4117 if (ct == G_MININT32)
4124 /* fill the sample information */
4125 sample->offset = *running_offset;
4126 sample->pts_offset = ct;
4127 sample->size = size;
4128 sample->timestamp = timestamp;
4129 sample->duration = dur;
4130 /* sample-is-difference-sample */
4131 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
4132 * now idea how it relates to bitfield other than massive LE/BE confusion */
4133 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
4134 *running_offset += size;
4136 stream->duration_moof += dur;
4143 /* Shift PTS/DTS to allow for negative composition offsets while keeping
4144 * A/V sync in place. This is similar to the code handling ctts/cslg in the
4145 * non-fragmented case.
4148 stream->cslg_shift = -min_ct;
4150 stream->cslg_shift = 0;
4152 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
4153 stream->cslg_shift);
4155 /* Update total duration if needed */
4156 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
4158 /* Pre-emptively figure out size of mdat based on trun information.
4159 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
4160 * size, else we will still be able to use this when dealing with gap'ed
4162 qtdemux->mdatleft = *running_offset - initial_offset;
4163 qtdemux->mdatoffset = initial_offset;
4164 qtdemux->mdatsize = qtdemux->mdatleft;
4166 stream->n_samples += samples_count;
4167 stream->n_samples_moof += samples_count;
4169 if (stream->pending_seek != NULL)
4170 stream->pending_seek = NULL;
4176 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
4181 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
4187 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
4188 "be larger than %uMB (broken file?)", stream->n_samples,
4189 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
4194 /* find stream with @id */
4195 static inline QtDemuxStream *
4196 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
4198 QtDemuxStream *stream;
4202 if (G_UNLIKELY (!id)) {
4203 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
4207 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4208 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4209 if (stream->track_id == id)
4212 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
4213 /* mss should have only 1 stream anyway */
4214 return QTDEMUX_NTH_STREAM (qtdemux, 0);
4221 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
4222 guint32 * fragment_number)
4224 if (!gst_byte_reader_skip (mfhd, 4))
4226 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
4231 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
4237 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
4238 QtDemuxStream ** stream, guint32 * default_sample_duration,
4239 guint32 * default_sample_size, guint32 * default_sample_flags,
4240 gint64 * base_offset)
4243 guint32 track_id = 0;
4245 if (!gst_byte_reader_skip (tfhd, 1) ||
4246 !gst_byte_reader_get_uint24_be (tfhd, &flags))
4249 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4252 *stream = qtdemux_find_stream (qtdemux, track_id);
4253 if (G_UNLIKELY (!*stream))
4254 goto unknown_stream;
4256 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4257 *base_offset = qtdemux->moof_offset;
4259 if (flags & TF_BASE_DATA_OFFSET)
4260 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4263 /* obtain stream defaults */
4264 if (qtdemux_parse_trex (qtdemux, *stream,
4265 default_sample_duration, default_sample_size, default_sample_flags)) {
4267 /* Default sample description index is only valid if trex parsing succeeded */
4268 (*stream)->stsd_sample_description_id =
4269 (*stream)->def_sample_description_index - 1;
4272 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4273 guint32 sample_description_index;
4274 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4276 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4279 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
4280 /* mss has no stsd entry */
4281 (*stream)->stsd_sample_description_id = 0;
4284 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4285 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4288 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4289 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4292 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4293 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4300 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4305 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
4311 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4312 guint64 * decode_time)
4314 guint32 version = 0;
4316 if (!gst_byte_reader_get_uint32_be (br, &version))
4321 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4324 guint32 dec_time = 0;
4325 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4327 *decode_time = dec_time;
4330 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4337 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4342 /* Returns a pointer to a GstStructure containing the properties of
4343 * the stream sample identified by @sample_index. The caller must unref
4344 * the returned object after use. Returns NULL if unsuccessful. */
4345 static GstStructure *
4346 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4347 QtDemuxStream * stream, guint sample_index)
4349 QtDemuxCencSampleSetInfo *info = NULL;
4351 g_return_val_if_fail (stream != NULL, NULL);
4352 g_return_val_if_fail (stream->protected, NULL);
4353 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4355 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4357 /* Currently, cenc properties for groups of samples are not supported, so
4358 * simply return a copy of the default sample properties */
4359 return gst_structure_copy (info->default_properties);
4362 /* Parses the sizes of sample auxiliary information contained within a stream,
4363 * as given in a saiz box. Returns array of sample_count guint8 size values,
4364 * or NULL on failure */
4366 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4367 GstByteReader * br, guint32 * sample_count)
4371 guint8 default_info_size;
4373 g_return_val_if_fail (qtdemux != NULL, NULL);
4374 g_return_val_if_fail (stream != NULL, NULL);
4375 g_return_val_if_fail (br != NULL, NULL);
4376 g_return_val_if_fail (sample_count != NULL, NULL);
4378 if (!gst_byte_reader_get_uint32_be (br, &flags))
4382 /* aux_info_type and aux_info_type_parameter are ignored */
4383 if (!gst_byte_reader_skip (br, 8))
4387 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4389 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4391 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4393 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4396 if (default_info_size == 0) {
4397 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4401 info_sizes = g_new (guint8, *sample_count);
4402 memset (info_sizes, default_info_size, *sample_count);
4408 /* Parses the offset of sample auxiliary information contained within a stream,
4409 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4411 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4412 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4417 guint32 aux_info_type = 0;
4418 guint32 aux_info_type_parameter = 0;
4419 guint32 entry_count;
4422 const guint8 *aux_info_type_data = NULL;
4424 g_return_val_if_fail (qtdemux != NULL, FALSE);
4425 g_return_val_if_fail (stream != NULL, FALSE);
4426 g_return_val_if_fail (br != NULL, FALSE);
4427 g_return_val_if_fail (offset != NULL, FALSE);
4429 if (!gst_byte_reader_get_uint8 (br, &version))
4432 if (!gst_byte_reader_get_uint24_be (br, &flags))
4437 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4439 aux_info_type = QT_FOURCC (aux_info_type_data);
4441 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4443 } else if (stream->protected) {
4444 aux_info_type = stream->protection_scheme_type;
4446 aux_info_type = CUR_STREAM (stream)->fourcc;
4450 *info_type = aux_info_type;
4451 if (info_type_parameter)
4452 *info_type_parameter = aux_info_type_parameter;
4454 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4455 "aux_info_type_parameter: %#06x",
4456 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4458 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4461 if (entry_count != 1) {
4462 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4467 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4469 *offset = (guint64) off_32;
4471 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4476 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4481 qtdemux_gst_structure_free (GstStructure * gststructure)
4484 gst_structure_free (gststructure);
4488 /* Parses auxiliary information relating to samples protected using
4489 * Common Encryption (cenc); the format of this information
4490 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
4493 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4494 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4496 QtDemuxCencSampleSetInfo *ss_info = NULL;
4499 GPtrArray *old_crypto_info = NULL;
4500 guint old_entries = 0;
4502 g_return_val_if_fail (qtdemux != NULL, FALSE);
4503 g_return_val_if_fail (stream != NULL, FALSE);
4504 g_return_val_if_fail (br != NULL, FALSE);
4505 g_return_val_if_fail (stream->protected, FALSE);
4506 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4508 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4510 if (ss_info->crypto_info) {
4511 old_crypto_info = ss_info->crypto_info;
4512 /* Count number of non-null entries remaining at the tail end */
4513 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4514 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4520 ss_info->crypto_info =
4521 g_ptr_array_new_full (sample_count + old_entries,
4522 (GDestroyNotify) qtdemux_gst_structure_free);
4524 /* We preserve old entries because we parse the next moof in advance
4525 * of consuming all samples from the previous moof, and otherwise
4526 * we'd discard the corresponding crypto info for the samples
4527 * from the previous fragment. */
4529 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4531 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4532 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4534 g_ptr_array_index (old_crypto_info, i) = NULL;
4538 if (old_crypto_info) {
4539 /* Everything now belongs to the new array */
4540 g_ptr_array_free (old_crypto_info, TRUE);
4543 for (i = 0; i < sample_count; ++i) {
4544 GstStructure *properties;
4545 guint16 n_subsamples = 0;
4549 gboolean could_read_iv;
4551 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4552 if (properties == NULL) {
4553 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4556 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4557 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4558 gst_structure_free (properties);
4562 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
4563 if (could_read_iv) {
4564 buf = gst_buffer_new_wrapped (data, iv_size);
4565 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4566 gst_buffer_unref (buf);
4567 } else if (stream->protection_scheme_type == FOURCC_cbcs) {
4568 const GValue *constant_iv_size_value =
4569 gst_structure_get_value (properties, "constant_iv_size");
4570 const GValue *constant_iv_value =
4571 gst_structure_get_value (properties, "iv");
4572 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
4573 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
4574 gst_structure_free (properties);
4577 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
4578 gst_structure_remove_field (properties, "constant_iv_size");
4579 } else if (stream->protection_scheme_type == FOURCC_cenc) {
4580 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4581 gst_structure_free (properties);
4584 size = info_sizes[i];
4585 if (size > iv_size) {
4586 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4587 || !(n_subsamples > 0)) {
4588 gst_structure_free (properties);
4589 GST_ERROR_OBJECT (qtdemux,
4590 "failed to get subsample count for sample %u", i);
4593 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4594 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4595 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4597 gst_structure_free (properties);
4600 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4602 gst_structure_free (properties);
4605 gst_structure_set (properties,
4606 "subsample_count", G_TYPE_UINT, n_subsamples,
4607 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4608 gst_buffer_unref (buf);
4610 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4612 g_ptr_array_add (ss_info->crypto_info, properties);
4617 /* Converts a UUID in raw byte form to a string representation, as defined in
4618 * RFC 4122. The caller takes ownership of the returned string and is
4619 * responsible for freeing it after use. */
4621 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4623 const guint8 *uuid = (const guint8 *) uuid_bytes;
4625 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4626 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4627 uuid[0], uuid[1], uuid[2], uuid[3],
4628 uuid[4], uuid[5], uuid[6], uuid[7],
4629 uuid[8], uuid[9], uuid[10], uuid[11],
4630 uuid[12], uuid[13], uuid[14], uuid[15]);
4633 /* Parses a Protection System Specific Header box (pssh), as defined in the
4634 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4635 * information needed by a specific content protection system in order to
4636 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4639 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4641 gchar *sysid_string;
4642 guint32 pssh_size = QT_UINT32 (node->data);
4643 GstBuffer *pssh = NULL;
4644 GstEvent *event = NULL;
4645 guint32 parent_box_type;
4648 if (G_UNLIKELY (pssh_size < 32U)) {
4649 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4654 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4656 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4658 pssh = gst_buffer_new_memdup (node->data, pssh_size);
4659 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4660 gst_buffer_get_size (pssh));
4662 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4664 /* Push an event containing the pssh box onto the queues of all streams. */
4665 event = gst_event_new_protection (sysid_string, pssh,
4666 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4667 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4668 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4669 GST_TRACE_OBJECT (qtdemux,
4670 "adding protection event for stream %s and system %s",
4671 stream->stream_id, sysid_string);
4672 g_queue_push_tail (&stream->protection_scheme_event_queue,
4673 gst_event_ref (event));
4675 g_free (sysid_string);
4676 gst_event_unref (event);
4677 gst_buffer_unref (pssh);
4682 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4683 guint64 moof_offset, QtDemuxStream * stream)
4685 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4687 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4688 GNode *saiz_node, *saio_node, *pssh_node;
4689 GstByteReader saiz_data, saio_data;
4690 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4691 gint64 base_offset, running_offset;
4693 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4695 /* NOTE @stream ignored */
4697 moof_node = g_node_new ((guint8 *) buffer);
4698 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4699 qtdemux_node_dump (qtdemux, moof_node);
4701 /* Get fragment number from mfhd and check it's valid */
4703 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4704 if (mfhd_node == NULL)
4706 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4708 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4710 /* unknown base_offset to start with */
4711 base_offset = running_offset = -1;
4712 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4714 guint64 decode_time = 0;
4716 /* Fragment Header node */
4718 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4722 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4723 &ds_size, &ds_flags, &base_offset))
4726 /* The following code assumes at most a single set of sample auxiliary
4727 * data in the fragment (consisting of a saiz box and a corresponding saio
4728 * box); in theory, however, there could be multiple sets of sample
4729 * auxiliary data in a fragment. */
4731 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4734 guint32 info_type = 0;
4736 guint32 info_type_parameter = 0;
4738 g_free (qtdemux->cenc_aux_info_sizes);
4740 qtdemux->cenc_aux_info_sizes =
4741 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4742 &qtdemux->cenc_aux_sample_count);
4743 if (qtdemux->cenc_aux_info_sizes == NULL) {
4744 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4748 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4751 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4752 g_free (qtdemux->cenc_aux_info_sizes);
4753 qtdemux->cenc_aux_info_sizes = NULL;
4757 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4758 &info_type, &info_type_parameter, &offset))) {
4759 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4760 g_free (qtdemux->cenc_aux_info_sizes);
4761 qtdemux->cenc_aux_info_sizes = NULL;
4764 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4765 offset += (guint64) (base_offset - qtdemux->moof_offset);
4766 if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs)
4767 && info_type_parameter == 0U) {
4769 if (offset > length) {
4770 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4771 qtdemux->cenc_aux_info_offset = offset;
4773 gst_byte_reader_init (&br, buffer + offset, length - offset);
4774 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4775 qtdemux->cenc_aux_info_sizes,
4776 qtdemux->cenc_aux_sample_count)) {
4777 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4778 g_free (qtdemux->cenc_aux_info_sizes);
4779 qtdemux->cenc_aux_info_sizes = NULL;
4787 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4790 /* We'll use decode_time to interpolate timestamps
4791 * in case the input timestamps are missing */
4792 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4794 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4795 " (%" GST_TIME_FORMAT ")", decode_time,
4796 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4797 decode_time) : GST_CLOCK_TIME_NONE));
4799 /* Discard the fragment buffer timestamp info to avoid using it.
4800 * Rely on tfdt instead as it is more accurate than the timestamp
4801 * that is fetched from a manifest/playlist and is usually
4803 qtdemux->fragment_start = -1;
4806 if (G_UNLIKELY (!stream)) {
4807 /* we lost track of offset, we'll need to regain it,
4808 * but can delay complaining until later or avoid doing so altogether */
4812 if (G_UNLIKELY (base_offset < -1))
4815 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4817 if (!qtdemux->pullbased) {
4818 /* Sample tables can grow enough to be problematic if the system memory
4819 * is very low (e.g. embedded devices) and the videos very long
4820 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4821 * Fortunately, we can easily discard them for each new fragment when
4822 * we know qtdemux will not receive seeks outside of the current fragment.
4823 * adaptivedemux honors this assumption.
4824 * This optimization is also useful for applications that use qtdemux as
4825 * a push-based simple demuxer, like Media Source Extensions. */
4826 gst_qtdemux_stream_flush_samples_data (stream);
4829 /* initialise moof sample data */
4830 stream->n_samples_moof = 0;
4831 stream->duration_last_moof = stream->duration_moof;
4832 stream->duration_moof = 0;
4834 /* Track Run node */
4836 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4839 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4840 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4841 &running_offset, decode_time, (tfdt_node != NULL));
4842 /* iterate all siblings */
4843 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4845 /* don't use tfdt for subsequent trun as it only refers to the first */
4849 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4851 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4852 guint32 box_length = QT_UINT32 (uuid_buffer);
4854 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4857 /* if no new base_offset provided for next traf,
4858 * base is end of current traf */
4859 base_offset = running_offset;
4860 running_offset = -1;
4862 if (stream->n_samples_moof && stream->duration_moof)
4863 stream->new_caps = TRUE;
4866 /* iterate all siblings */
4867 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4870 /* parse any protection system info */
4871 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4873 /* Unref old protection events if we are going to receive new ones. */
4874 qtdemux_clear_protection_events_on_all_streams (qtdemux);
4877 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4878 qtdemux_parse_pssh (qtdemux, pssh_node);
4879 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4882 if (!qtdemux->upstream_format_is_time
4883 && qtdemux->variant != VARIANT_MSE_BYTESTREAM
4884 && !qtdemux->first_moof_already_parsed
4885 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4887 /* Unless the user has explicitly requested another seek, perform an
4888 * internal seek to the time specified in the tfdt.
4890 * This way if the user opens a file where the first tfdt is 1 hour
4891 * into the presentation, they will not have to wait 1 hour for run
4892 * time to catch up and actual playback to start. */
4895 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4896 "performing an internal seek to %" GST_TIME_FORMAT,
4897 GST_TIME_ARGS (min_dts));
4899 qtdemux->segment.start = min_dts;
4900 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4902 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4903 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4904 stream->time_position = min_dts;
4907 /* Before this code was run a segment was already sent when the moov was
4908 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4909 * be emitted after a moov, and we can emit a second segment anyway for
4910 * special cases like this. */
4911 qtdemux->need_segment = TRUE;
4914 qtdemux->first_moof_already_parsed = TRUE;
4916 g_node_destroy (moof_node);
4921 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4926 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4931 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4936 g_node_destroy (moof_node);
4937 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4938 (_("This file is corrupt and cannot be played.")), (NULL));
4944 /* might be used if some day we actually use mfra & co
4945 * for random access to fragments,
4946 * but that will require quite some modifications and much less relying
4947 * on a sample array */
4951 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4953 QtDemuxStream *stream;
4954 guint32 ver_flags, track_id, len, num_entries, i;
4955 guint value_size, traf_size, trun_size, sample_size;
4956 guint64 time = 0, moof_offset = 0;
4958 GstBuffer *buf = NULL;
4963 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4965 if (!gst_byte_reader_skip (&tfra, 8))
4968 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4971 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4972 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4973 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4976 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4978 stream = qtdemux_find_stream (qtdemux, track_id);
4980 goto unknown_trackid;
4982 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4983 sample_size = (len & 3) + 1;
4984 trun_size = ((len & 12) >> 2) + 1;
4985 traf_size = ((len & 48) >> 4) + 1;
4987 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4988 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4990 if (num_entries == 0)
4993 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4994 value_size + value_size + traf_size + trun_size + sample_size))
4997 g_free (stream->ra_entries);
4998 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4999 stream->n_ra_entries = num_entries;
5001 for (i = 0; i < num_entries; i++) {
5002 qt_atom_parser_get_offset (&tfra, value_size, &time);
5003 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
5004 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
5005 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
5006 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
5008 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
5010 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
5011 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
5013 stream->ra_entries[i].ts = time;
5014 stream->ra_entries[i].moof_offset = moof_offset;
5016 /* don't want to go through the entire file and read all moofs at startup */
5018 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
5019 if (ret != GST_FLOW_OK)
5021 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
5022 moof_offset, stream);
5023 gst_buffer_unref (buf);
5027 check_update_duration (qtdemux, time);
5034 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
5039 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
5044 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
5050 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
5052 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
5053 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
5054 GstBuffer *mfro = NULL, *mfra = NULL;
5056 gboolean ret = FALSE;
5057 GNode *mfra_node, *tfra_node;
5058 guint64 mfra_offset = 0;
5059 guint32 fourcc, mfra_size;
5062 /* query upstream size in bytes */
5063 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
5064 goto size_query_failed;
5066 /* mfro box should be at the very end of the file */
5067 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
5068 if (flow != GST_FLOW_OK)
5071 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
5073 fourcc = QT_FOURCC (mfro_map.data + 4);
5074 if (fourcc != FOURCC_mfro)
5077 GST_INFO_OBJECT (qtdemux, "Found mfro box");
5078 if (mfro_map.size < 16)
5079 goto invalid_mfro_size;
5081 mfra_size = QT_UINT32 (mfro_map.data + 12);
5082 if (mfra_size >= len)
5083 goto invalid_mfra_size;
5085 mfra_offset = len - mfra_size;
5087 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
5088 mfra_offset, mfra_size);
5090 /* now get and parse mfra box */
5091 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
5092 if (flow != GST_FLOW_OK)
5095 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
5097 mfra_node = g_node_new ((guint8 *) mfra_map.data);
5098 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
5100 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
5103 qtdemux_parse_tfra (qtdemux, tfra_node);
5104 /* iterate all siblings */
5105 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
5107 g_node_destroy (mfra_node);
5109 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
5115 if (mfro_map.memory != NULL)
5116 gst_buffer_unmap (mfro, &mfro_map);
5117 gst_buffer_unref (mfro);
5120 if (mfra_map.memory != NULL)
5121 gst_buffer_unmap (mfra, &mfra_map);
5122 gst_buffer_unref (mfra);
5129 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
5134 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
5139 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
5144 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
5150 add_offset (guint64 offset, guint64 advance)
5152 /* Avoid 64-bit overflow by clamping */
5153 if (offset > G_MAXUINT64 - advance)
5155 return offset + advance;
5158 static GstFlowReturn
5159 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
5163 GstBuffer *buf = NULL;
5164 GstFlowReturn ret = GST_FLOW_OK;
5165 guint64 cur_offset = qtdemux->offset;
5168 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
5169 if (G_UNLIKELY (ret != GST_FLOW_OK))
5171 gst_buffer_map (buf, &map, GST_MAP_READ);
5172 if (G_LIKELY (map.size >= 8))
5173 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
5174 gst_buffer_unmap (buf, &map);
5175 gst_buffer_unref (buf);
5177 /* maybe we already got most we needed, so only consider this eof */
5178 if (G_UNLIKELY (length == 0)) {
5179 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5180 (_("Invalid atom size.")),
5181 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
5182 GST_FOURCC_ARGS (fourcc)));
5189 /* record for later parsing when needed */
5190 if (!qtdemux->moof_offset) {
5191 qtdemux->moof_offset = qtdemux->offset;
5193 if (qtdemux_pull_mfro_mfra (qtdemux)) {
5196 qtdemux->offset += length; /* skip moof and keep going */
5198 if (qtdemux->got_moov) {
5199 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
5211 GST_LOG_OBJECT (qtdemux,
5212 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5213 GST_FOURCC_ARGS (fourcc), cur_offset);
5214 qtdemux->offset = add_offset (qtdemux->offset, length);
5219 GstBuffer *moov = NULL;
5221 if (qtdemux->got_moov) {
5222 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
5223 qtdemux->offset = add_offset (qtdemux->offset, length);
5227 if (length == G_MAXUINT64) {
5228 /* Read until the end */
5230 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES,
5232 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5233 (_("Cannot query file size")),
5234 ("Duration query on sink pad failed"));
5235 ret = GST_FLOW_ERROR;
5238 if (G_UNLIKELY (cur_offset > duration)) {
5239 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5240 (_("Cannot query file size")),
5241 ("Duration %" G_GINT64_FORMAT " < current offset %"
5242 G_GUINT64_FORMAT, duration, cur_offset));
5243 ret = GST_FLOW_ERROR;
5246 length = duration - cur_offset;
5247 if (length > QTDEMUX_MAX_ATOM_SIZE) {
5248 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5249 (_("Cannot demux file")),
5250 ("Moov atom size %" G_GINT64_FORMAT " > maximum %d", length,
5251 QTDEMUX_MAX_ATOM_SIZE));
5252 ret = GST_FLOW_ERROR;
5257 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
5258 if (ret != GST_FLOW_OK)
5260 gst_buffer_map (moov, &map, GST_MAP_READ);
5262 if (length != map.size) {
5263 /* Some files have a 'moov' atom at the end of the file which contains
5264 * a terminal 'free' atom where the body of the atom is missing.
5265 * Check for, and permit, this special case.
5267 if (map.size >= 8) {
5268 guint8 *final_data = map.data + (map.size - 8);
5269 guint32 final_length = QT_UINT32 (final_data);
5270 guint32 final_fourcc = QT_FOURCC (final_data + 4);
5272 if (final_fourcc == FOURCC_free
5273 && map.size + final_length - 8 == length) {
5274 /* Ok, we've found that special case. Allocate a new buffer with
5275 * that free atom actually present. */
5276 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
5277 gst_buffer_fill (newmoov, 0, map.data, map.size);
5278 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
5279 gst_buffer_unmap (moov, &map);
5280 gst_buffer_unref (moov);
5282 gst_buffer_map (moov, &map, GST_MAP_READ);
5287 if (length != map.size) {
5288 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5289 (_("This file is incomplete and cannot be played.")),
5290 ("We got less than expected (received %" G_GSIZE_FORMAT
5291 ", wanted %" G_GUINT64_FORMAT ", offset %" G_GUINT64_FORMAT ")",
5292 map.size, length, cur_offset));
5293 gst_buffer_unmap (moov, &map);
5294 gst_buffer_unref (moov);
5295 ret = GST_FLOW_ERROR;
5298 qtdemux->offset += length;
5300 qtdemux_parse_moov (qtdemux, map.data, length);
5301 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
5303 qtdemux_parse_tree (qtdemux);
5304 if (qtdemux->moov_node_compressed) {
5305 g_node_destroy (qtdemux->moov_node_compressed);
5306 g_free (qtdemux->moov_node->data);
5308 qtdemux->moov_node_compressed = NULL;
5309 g_node_destroy (qtdemux->moov_node);
5310 qtdemux->moov_node = NULL;
5311 gst_buffer_unmap (moov, &map);
5312 gst_buffer_unref (moov);
5313 qtdemux->got_moov = TRUE;
5319 GstBuffer *ftyp = NULL;
5321 /* extract major brand; might come in handy for ISO vs QT issues */
5322 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
5323 if (ret != GST_FLOW_OK)
5325 qtdemux->offset += length;
5326 gst_buffer_map (ftyp, &map, GST_MAP_READ);
5327 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
5328 gst_buffer_unmap (ftyp, &map);
5329 gst_buffer_unref (ftyp);
5334 GstBuffer *styp = NULL;
5336 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &styp);
5337 if (ret != GST_FLOW_OK)
5339 qtdemux->offset += length;
5340 gst_buffer_map (styp, &map, GST_MAP_READ);
5341 qtdemux_parse_styp (qtdemux, map.data, map.size);
5342 gst_buffer_unmap (styp, &map);
5343 gst_buffer_unref (styp);
5348 GstBuffer *uuid = NULL;
5350 /* uuid are extension atoms */
5351 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
5352 if (ret != GST_FLOW_OK)
5354 qtdemux->offset += length;
5355 gst_buffer_map (uuid, &map, GST_MAP_READ);
5356 qtdemux_parse_uuid (qtdemux, map.data, map.size);
5357 gst_buffer_unmap (uuid, &map);
5358 gst_buffer_unref (uuid);
5363 GstBuffer *sidx = NULL;
5364 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
5365 if (ret != GST_FLOW_OK)
5367 qtdemux->offset += length;
5368 gst_buffer_map (sidx, &map, GST_MAP_READ);
5369 qtdemux_parse_sidx (qtdemux, map.data, map.size);
5370 gst_buffer_unmap (sidx, &map);
5371 gst_buffer_unref (sidx);
5376 GstBuffer *meta = NULL;
5377 GNode *node, *child;
5378 GstByteReader child_data;
5379 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &meta);
5380 if (ret != GST_FLOW_OK)
5382 qtdemux->offset += length;
5383 gst_buffer_map (meta, &map, GST_MAP_READ);
5385 node = g_node_new (map.data);
5387 qtdemux_parse_node (qtdemux, node, map.data, map.size);
5389 /* Parse ONVIF Export File Format CorrectStartTime box if available */
5391 qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb,
5393 qtdemux_parse_cstb (qtdemux, &child_data);
5396 g_node_destroy (node);
5398 gst_buffer_unmap (meta, &map);
5399 gst_buffer_unref (meta);
5404 GstBuffer *unknown = NULL;
5406 GST_LOG_OBJECT (qtdemux,
5407 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5408 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5410 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5411 if (ret != GST_FLOW_OK)
5413 gst_buffer_map (unknown, &map, GST_MAP_READ);
5414 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5415 gst_buffer_unmap (unknown, &map);
5416 gst_buffer_unref (unknown);
5417 qtdemux->offset += length;
5423 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5424 /* digested all data, show what we have */
5425 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5426 if (qtdemux->spherical_metadata)
5427 _send_spherical_metadata_msg_to_bus (qtdemux);
5428 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5429 qtdemux_prepare_streams (qtdemux);
5430 QTDEMUX_EXPOSE_LOCK (qtdemux);
5431 ret = qtdemux_expose_streams (qtdemux);
5432 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
5434 qtdemux->state = QTDEMUX_STATE_MOVIE;
5435 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5442 /* Seeks to the previous keyframe of the indexed stream and
5443 * aligns other streams with respect to the keyframe timestamp
5444 * of indexed stream. Only called in case of Reverse Playback
5446 static GstFlowReturn
5447 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5449 guint32 seg_idx = 0, k_index = 0;
5450 guint32 ref_seg_idx, ref_k_index;
5451 GstClockTime k_pos = 0, last_stop = 0;
5452 QtDemuxSegment *seg = NULL;
5453 QtDemuxStream *ref_str = NULL;
5454 guint64 seg_media_start_mov; /* segment media start time in mov format */
5458 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5459 * and finally align all the other streams on that timestamp with their
5460 * respective keyframes */
5461 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5462 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5464 /* No candidate yet, take the first stream */
5470 /* So that stream has a segment, we prefer video streams */
5471 if (str->subtype == FOURCC_vide) {
5477 if (G_UNLIKELY (!ref_str)) {
5478 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5482 if (G_UNLIKELY (!ref_str->from_sample)) {
5483 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5487 /* So that stream has been playing from from_sample to to_sample. We will
5488 * get the timestamp of the previous sample and search for a keyframe before
5489 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5490 if (ref_str->subtype == FOURCC_vide) {
5491 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5492 ref_str->from_sample - 1, FALSE);
5494 if (ref_str->from_sample >= 10)
5495 k_index = ref_str->from_sample - 10;
5501 ref_str->samples[k_index].timestamp +
5502 ref_str->samples[k_index].pts_offset;
5504 /* get current segment for that stream */
5505 seg = &ref_str->segments[ref_str->segment_index];
5506 /* Use segment start in original timescale for comparisons */
5507 seg_media_start_mov = seg->trak_media_start;
5509 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5510 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
5511 k_index, target_ts, seg_media_start_mov,
5512 GST_TIME_ARGS (seg->media_start));
5514 /* Crawl back through segments to find the one containing this I frame */
5515 while (target_ts < seg_media_start_mov) {
5516 GST_DEBUG_OBJECT (qtdemux,
5517 "keyframe position (sample %u) is out of segment %u " " target %"
5518 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5519 ref_str->segment_index, target_ts, seg_media_start_mov);
5521 if (G_UNLIKELY (!ref_str->segment_index)) {
5522 /* Reached first segment, let's consider it's EOS */
5525 ref_str->segment_index--;
5526 seg = &ref_str->segments[ref_str->segment_index];
5527 /* Use segment start in original timescale for comparisons */
5528 seg_media_start_mov = seg->trak_media_start;
5530 /* Calculate time position of the keyframe and where we should stop */
5532 QTSTREAMTIME_TO_GSTTIME (ref_str,
5533 target_ts - seg->trak_media_start) + seg->time;
5535 QTSTREAMTIME_TO_GSTTIME (ref_str,
5536 ref_str->samples[ref_str->from_sample].timestamp -
5537 seg->trak_media_start) + seg->time;
5539 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5540 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5541 k_index, GST_TIME_ARGS (k_pos));
5543 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5544 qtdemux->segment.position = last_stop;
5545 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5546 GST_TIME_ARGS (last_stop));
5548 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5549 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5553 ref_seg_idx = ref_str->segment_index;
5554 ref_k_index = k_index;
5556 /* Align them all on this */
5557 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5559 GstClockTime seg_time = 0;
5560 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5562 /* aligning reference stream again might lead to backing up to yet another
5563 * keyframe (due to timestamp rounding issues),
5564 * potentially putting more load on downstream; so let's try to avoid */
5565 if (str == ref_str) {
5566 seg_idx = ref_seg_idx;
5567 seg = &str->segments[seg_idx];
5568 k_index = ref_k_index;
5569 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5570 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5572 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5573 GST_DEBUG_OBJECT (qtdemux,
5574 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5575 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5577 /* get segment and time in the segment */
5578 seg = &str->segments[seg_idx];
5579 seg_time = k_pos - seg->time;
5581 /* get the media time in the segment.
5582 * No adjustment for empty "filler" segments */
5583 if (seg->media_start != GST_CLOCK_TIME_NONE)
5584 seg_time += seg->media_start;
5586 /* get the index of the sample with media time */
5587 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5588 GST_DEBUG_OBJECT (qtdemux,
5589 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5590 GST_TIME_ARGS (seg_time), index);
5592 /* find previous keyframe */
5593 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5596 /* Remember until where we want to go */
5597 str->to_sample = str->from_sample - 1;
5598 /* Define our time position */
5600 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5601 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5602 if (seg->media_start != GST_CLOCK_TIME_NONE)
5603 str->time_position -= seg->media_start;
5605 /* Now seek back in time */
5606 gst_qtdemux_move_stream (qtdemux, str, k_index);
5607 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5608 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5609 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5615 return GST_FLOW_EOS;
5619 * Gets the current qt segment start, stop and position for the
5620 * given time offset. This is used in update_segment()
5623 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5624 QtDemuxStream * stream, GstClockTime offset,
5625 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5627 GstClockTime seg_time;
5628 GstClockTime start, stop, time;
5629 QtDemuxSegment *segment;
5631 segment = &stream->segments[stream->segment_index];
5633 /* get time in this segment */
5634 seg_time = (offset - segment->time) * segment->rate;
5636 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5637 GST_TIME_ARGS (seg_time));
5639 if (G_UNLIKELY (seg_time > segment->duration)) {
5640 GST_LOG_OBJECT (stream->pad,
5641 "seg_time > segment->duration %" GST_TIME_FORMAT,
5642 GST_TIME_ARGS (segment->duration));
5643 seg_time = segment->duration;
5646 /* qtdemux->segment.stop is in outside-time-realm, whereas
5647 * segment->media_stop is in track-time-realm.
5649 * In order to compare the two, we need to bring segment.stop
5650 * into the track-time-realm
5652 * FIXME - does this comment still hold? Don't see any conversion here */
5654 stop = qtdemux->segment.stop;
5655 if (stop == GST_CLOCK_TIME_NONE)
5656 stop = qtdemux->segment.duration;
5657 if (stop == GST_CLOCK_TIME_NONE)
5658 stop = segment->media_stop;
5661 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5663 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5664 start = segment->time + seg_time;
5666 stop = start - seg_time + segment->duration;
5667 } else if (qtdemux->segment.rate >= 0) {
5668 start = MIN (segment->media_start + seg_time, stop);
5671 if (segment->media_start >= qtdemux->segment.start) {
5672 time = segment->time;
5674 time = segment->time + (qtdemux->segment.start - segment->media_start);
5677 start = MAX (segment->media_start, qtdemux->segment.start);
5678 stop = MIN (segment->media_start + seg_time, stop);
5687 * Updates the qt segment used for the stream and pushes a new segment event
5688 * downstream on this stream's pad.
5691 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5692 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5693 GstClockTime * _stop)
5695 QtDemuxSegment *segment;
5696 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5700 /* update the current segment */
5701 stream->segment_index = seg_idx;
5703 /* get the segment */
5704 segment = &stream->segments[seg_idx];
5706 if (G_UNLIKELY (offset < segment->time)) {
5707 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5708 GST_TIME_ARGS (segment->time));
5712 /* segment lies beyond total indicated duration */
5713 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5714 segment->time > qtdemux->segment.duration)) {
5715 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5716 " < segment->time %" GST_TIME_FORMAT,
5717 GST_TIME_ARGS (qtdemux->segment.duration),
5718 GST_TIME_ARGS (segment->time));
5722 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5723 &start, &stop, &time);
5725 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5726 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5727 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5729 /* combine global rate with that of the segment */
5730 rate = segment->rate * qtdemux->segment.rate;
5732 /* Copy flags from main segment */
5733 stream->segment.flags = qtdemux->segment.flags;
5735 /* update the segment values used for clipping */
5736 stream->segment.offset = qtdemux->segment.offset;
5737 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5738 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5739 stream->segment.rate = rate;
5740 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5741 stream->cslg_shift);
5743 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5744 stream->cslg_shift);
5746 stream->segment.stop = stop;
5747 stream->segment.time = time;
5748 stream->segment.position = stream->segment.start;
5750 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5753 /* now prepare and send the segment */
5755 event = gst_event_new_segment (&stream->segment);
5756 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5757 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5759 gst_pad_push_event (stream->pad, event);
5760 /* assume we can send more data now */
5761 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5762 /* clear to send tags on this pad now */
5763 gst_qtdemux_push_tags (qtdemux, stream);
5774 /* activate the given segment number @seg_idx of @stream at time @offset.
5775 * @offset is an absolute global position over all the segments.
5777 * This will push out a NEWSEGMENT event with the right values and
5778 * position the stream index to the first decodable sample before
5782 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5783 guint32 seg_idx, GstClockTime offset)
5785 QtDemuxSegment *segment;
5786 guint32 index, kf_index;
5787 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5789 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5790 seg_idx, GST_TIME_ARGS (offset));
5792 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5796 segment = &stream->segments[stream->segment_index];
5798 /* in the fragmented case, we pick a fragment that starts before our
5799 * desired position and rely on downstream to wait for a keyframe
5800 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5801 * tfra entries tells us which trun/sample the key unit is in, but we don't
5802 * make use of this additional information at the moment) */
5803 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5804 stream->to_sample = G_MAXUINT32;
5807 /* well, it will be taken care of below */
5808 qtdemux->fragmented_seek_pending = FALSE;
5809 /* FIXME ideally the do_fragmented_seek can be done right here,
5810 * rather than at loop level
5811 * (which might even allow handling edit lists in a fragmented file) */
5814 /* We don't need to look for a sample in push-based */
5815 if (!qtdemux->pullbased)
5818 /* and move to the keyframe before the indicated media time of the
5820 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5821 if (qtdemux->segment.rate >= 0) {
5822 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5823 stream->to_sample = G_MAXUINT32;
5824 GST_DEBUG_OBJECT (stream->pad,
5825 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5826 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5827 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5829 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5830 stream->to_sample = index;
5831 GST_DEBUG_OBJECT (stream->pad,
5832 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5833 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5834 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5837 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5838 "this is an empty segment");
5842 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5843 * encountered an error and printed a message so we return appropriately */
5847 /* we're at the right spot */
5848 if (index == stream->sample_index) {
5849 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5853 /* find keyframe of the target index */
5854 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5856 /* go back two frames to provide lead-in for non-raw audio decoders */
5857 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
5858 guint32 lead_in = 2;
5859 guint32 old_index = kf_index;
5860 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
5862 if (gst_structure_has_name (s, "audio/mpeg")) {
5864 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5865 && mpegversion == 1) {
5866 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5871 kf_index = MAX (kf_index, lead_in) - lead_in;
5872 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
5873 GST_DEBUG_OBJECT (stream->pad,
5874 "Moving backwards %u frames to ensure sufficient sound lead-in",
5875 old_index - kf_index);
5877 kf_index = old_index;
5881 /* if we move forwards, we don't have to go back to the previous
5882 * keyframe since we already sent that. We can also just jump to
5883 * the keyframe right before the target index if there is one. */
5884 if (index > stream->sample_index) {
5885 /* moving forwards check if we move past a keyframe */
5886 if (kf_index > stream->sample_index) {
5887 GST_DEBUG_OBJECT (stream->pad,
5888 "moving forwards to keyframe at %u "
5889 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5891 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5892 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5893 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5895 GST_DEBUG_OBJECT (stream->pad,
5896 "moving forwards, keyframe at %u "
5897 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5899 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5900 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5903 GST_DEBUG_OBJECT (stream->pad,
5904 "moving backwards to %sframe at %u "
5905 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5906 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5907 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5908 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5909 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5915 /* prepare to get the current sample of @stream, getting essential values.
5917 * This function will also prepare and send the segment when needed.
5919 * Return FALSE if the stream is EOS.
5924 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5925 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5926 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5927 gboolean * keyframe)
5929 QtDemuxSample *sample;
5930 GstClockTime time_position;
5933 g_return_val_if_fail (stream != NULL, FALSE);
5935 time_position = stream->time_position;
5936 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5939 seg_idx = stream->segment_index;
5940 if (G_UNLIKELY (seg_idx == -1)) {
5941 /* find segment corresponding to time_position if we are looking
5943 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5946 /* different segment, activate it, sample_index will be set. */
5947 if (G_UNLIKELY (stream->segment_index != seg_idx))
5948 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5950 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5951 segments[stream->segment_index]))) {
5952 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5954 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5955 " prepare empty sample");
5958 *pts = *dts = time_position;
5959 *duration = seg->duration - (time_position - seg->time);
5966 if (stream->sample_index == -1)
5967 stream->sample_index = 0;
5969 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5970 stream->sample_index, stream->n_samples);
5972 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5973 if (!qtdemux->fragmented)
5976 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5980 GST_OBJECT_LOCK (qtdemux);
5981 flow = qtdemux_add_fragmented_samples (qtdemux);
5982 GST_OBJECT_UNLOCK (qtdemux);
5984 if (flow != GST_FLOW_OK)
5987 while (stream->sample_index >= stream->n_samples);
5990 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5991 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5992 stream->sample_index);
5996 /* now get the info for the sample we're at */
5997 sample = &stream->samples[stream->sample_index];
5999 *dts = QTSAMPLE_DTS (stream, sample);
6000 *pts = QTSAMPLE_PTS (stream, sample);
6001 *offset = sample->offset;
6002 *size = sample->size;
6003 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
6004 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
6011 stream->time_position = GST_CLOCK_TIME_NONE;
6016 /* move to the next sample in @stream.
6018 * Moves to the next segment when needed.
6021 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
6023 QtDemuxSample *sample;
6024 QtDemuxSegment *segment;
6026 /* get current segment */
6027 segment = &stream->segments[stream->segment_index];
6029 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
6030 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
6034 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
6035 /* Mark the stream as EOS */
6036 GST_DEBUG_OBJECT (qtdemux,
6037 "reached max allowed sample %u, mark EOS", stream->to_sample);
6038 stream->time_position = GST_CLOCK_TIME_NONE;
6042 /* move to next sample */
6043 stream->sample_index++;
6044 stream->offset_in_sample = 0;
6046 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
6049 /* reached the last sample, we need the next segment */
6050 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
6053 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6054 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
6055 stream->sample_index);
6059 /* get next sample */
6060 sample = &stream->samples[stream->sample_index];
6062 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
6063 GST_TIME_FORMAT, GST_TIME_ARGS (QTSAMPLE_DTS (stream, sample)),
6064 GST_TIME_ARGS (segment->media_stop));
6066 /* see if we are past the segment */
6067 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
6070 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
6071 /* inside the segment, update time_position, looks very familiar to
6072 * GStreamer segments, doesn't it? */
6073 stream->time_position =
6074 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
6076 /* not yet in segment, time does not yet increment. This means
6077 * that we are still prerolling keyframes to the decoder so it can
6078 * decode the first sample of the segment. */
6079 stream->time_position = segment->time;
6083 /* move to the next segment */
6086 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
6088 if (stream->segment_index == stream->n_segments - 1) {
6089 /* are we at the end of the last segment, we're EOS */
6090 stream->time_position = GST_CLOCK_TIME_NONE;
6092 /* else we're only at the end of the current segment */
6093 stream->time_position = segment->stop_time;
6095 /* make sure we select a new segment */
6097 /* accumulate previous segments */
6098 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
6099 stream->accumulated_base +=
6100 (stream->segment.stop -
6101 stream->segment.start) / ABS (stream->segment.rate);
6103 stream->segment_index = -1;
6108 gst_qtdemux_sync_streams (GstQTDemux * demux)
6112 if (QTDEMUX_N_STREAMS (demux) <= 1)
6115 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6116 QtDemuxStream *stream;
6117 GstClockTime end_time;
6119 stream = QTDEMUX_NTH_STREAM (demux, i);
6124 /* TODO advance time on subtitle streams here, if any some day */
6126 /* some clips/trailers may have unbalanced streams at the end,
6127 * so send EOS on shorter stream to prevent stalling others */
6129 /* do not mess with EOS if SEGMENT seeking */
6130 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
6133 if (demux->pullbased) {
6134 /* loop mode is sample time based */
6135 if (!STREAM_IS_EOS (stream))
6138 /* push mode is byte position based */
6139 if (stream->n_samples &&
6140 stream->samples[stream->n_samples - 1].offset >= demux->offset)
6144 if (stream->sent_eos)
6147 /* only act if some gap */
6148 end_time = stream->segments[stream->n_segments - 1].stop_time;
6149 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
6150 ", stream end: %" GST_TIME_FORMAT,
6151 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
6152 if (GST_CLOCK_TIME_IS_VALID (end_time)
6153 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
6156 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
6157 GST_PAD_NAME (stream->pad));
6158 stream->sent_eos = TRUE;
6159 event = gst_event_new_eos ();
6160 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6161 gst_event_set_seqnum (event, demux->segment_seqnum);
6162 gst_pad_push_event (stream->pad, event);
6167 /* EOS and NOT_LINKED need to be combined. This means that we return:
6169 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
6170 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
6172 static GstFlowReturn
6173 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
6176 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
6179 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
6182 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
6184 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
6188 /* the input buffer metadata must be writable. Returns NULL when the buffer is
6189 * completely clipped
6191 * Should be used only with raw buffers */
6193 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6196 guint64 start, stop, cstart, cstop, diff;
6197 GstClockTime pts, duration;
6199 gint num_rate, denom_rate;
6204 osize = size = gst_buffer_get_size (buf);
6207 /* depending on the type, setup the clip parameters */
6208 if (stream->subtype == FOURCC_soun) {
6209 frame_size = CUR_STREAM (stream)->bytes_per_frame;
6210 num_rate = GST_SECOND;
6211 denom_rate = (gint) CUR_STREAM (stream)->rate;
6213 } else if (stream->subtype == FOURCC_vide) {
6215 num_rate = CUR_STREAM (stream)->fps_n;
6216 denom_rate = CUR_STREAM (stream)->fps_d;
6221 if (frame_size <= 0)
6222 goto bad_frame_size;
6224 /* we can only clip if we have a valid pts */
6225 pts = GST_BUFFER_PTS (buf);
6226 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
6229 duration = GST_BUFFER_DURATION (buf);
6231 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
6233 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
6237 stop = start + duration;
6239 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
6240 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
6243 /* see if some clipping happened */
6244 diff = cstart - start;
6250 /* bring clipped time to samples and to bytes */
6251 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6254 GST_DEBUG_OBJECT (qtdemux,
6255 "clipping start to %" GST_TIME_FORMAT " %"
6256 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
6262 diff = stop - cstop;
6267 /* bring clipped time to samples and then to bytes */
6268 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6270 GST_DEBUG_OBJECT (qtdemux,
6271 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
6272 " bytes", GST_TIME_ARGS (cstop), diff);
6277 if (offset != 0 || size != osize)
6278 gst_buffer_resize (buf, offset, size);
6280 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
6281 GST_BUFFER_PTS (buf) = pts;
6282 GST_BUFFER_DURATION (buf) = duration;
6286 /* dropped buffer */
6289 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
6294 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
6299 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
6304 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
6305 gst_buffer_unref (buf);
6311 gst_qtdemux_align_buffer (GstQTDemux * demux,
6312 GstBuffer * buffer, gsize alignment)
6316 gst_buffer_map (buffer, &map, GST_MAP_READ);
6318 if (map.size < sizeof (guintptr)) {
6319 gst_buffer_unmap (buffer, &map);
6323 if (((guintptr) map.data) & (alignment - 1)) {
6324 GstBuffer *new_buffer;
6325 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
6327 new_buffer = gst_buffer_new_allocate (NULL,
6328 gst_buffer_get_size (buffer), ¶ms);
6330 /* Copy data "by hand", so ensure alignment is kept: */
6331 gst_buffer_fill (new_buffer, 0, map.data, map.size);
6333 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
6334 GST_DEBUG_OBJECT (demux,
6335 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
6338 gst_buffer_unmap (buffer, &map);
6339 gst_buffer_unref (buffer);
6344 gst_buffer_unmap (buffer, &map);
6349 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
6355 /* We are converting from pairs to triplets */
6356 *res = ccpair_size / 2 * 3;
6357 storage = g_malloc (*res);
6358 for (i = 0; i * 2 < ccpair_size; i += 1) {
6359 /* FIXME: Use line offset 0 as we simply can't know here */
6361 storage[i * 3] = 0x80 | 0x00;
6363 storage[i * 3] = 0x00 | 0x00;
6364 storage[i * 3 + 1] = ccpair[i * 2];
6365 storage[i * 3 + 2] = ccpair[i * 2 + 1];
6372 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
6376 guint32 atom_length, fourcc;
6377 QtDemuxStreamStsdEntry *stsd_entry;
6379 GST_MEMDUMP ("caption atom", data, size);
6381 /* There might be multiple atoms */
6386 atom_length = QT_UINT32 (data);
6387 fourcc = QT_FOURCC (data + 4);
6388 if (G_UNLIKELY (atom_length > size || atom_length == 8))
6391 GST_DEBUG_OBJECT (stream->pad, "here");
6393 /* Check if we have something compatible */
6394 stsd_entry = CUR_STREAM (stream);
6395 switch (stsd_entry->fourcc) {
6397 guint8 *cdat = NULL, *cdt2 = NULL;
6398 gsize cdat_size = 0, cdt2_size = 0;
6399 /* Should be cdat or cdt2 */
6400 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
6401 GST_WARNING_OBJECT (stream->pad,
6402 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
6403 GST_FOURCC_ARGS (fourcc));
6407 /* Convert to S334-1 Annex A byte triplet */
6408 if (fourcc == FOURCC_cdat)
6409 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
6411 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
6412 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
6415 /* Check for another atom ? */
6416 if (size > atom_length + 8) {
6417 guint32 new_atom_length = QT_UINT32 (data + atom_length);
6418 if (size >= atom_length + new_atom_length) {
6419 fourcc = QT_FOURCC (data + atom_length + 4);
6420 if (fourcc == FOURCC_cdat) {
6423 convert_to_s334_1a (data + atom_length + 8,
6424 new_atom_length - 8, 1, &cdat_size);
6426 GST_WARNING_OBJECT (stream->pad,
6427 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6431 convert_to_s334_1a (data + atom_length + 8,
6432 new_atom_length - 8, 2, &cdt2_size);
6434 GST_WARNING_OBJECT (stream->pad,
6435 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6440 *cclen = cdat_size + cdt2_size;
6441 res = g_malloc (*cclen);
6443 memcpy (res, cdat, cdat_size);
6445 memcpy (res + cdat_size, cdt2, cdt2_size);
6451 if (fourcc != FOURCC_ccdp) {
6452 GST_WARNING_OBJECT (stream->pad,
6453 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
6454 GST_FOURCC_ARGS (fourcc));
6457 *cclen = atom_length - 8;
6458 res = g_memdup2 (data + 8, *cclen);
6461 /* Keep this here in case other closed caption formats are added */
6462 g_assert_not_reached ();
6466 GST_MEMDUMP ("Output", res, *cclen);
6471 GST_WARNING ("[cdat] atom is too small or invalid");
6475 /* Handle Closed Caption sample buffers.
6476 * The input buffer metadata must be writable,
6477 * but time/duration etc not yet set and need not be preserved */
6479 gst_qtdemux_process_buffer_clcp (GstQTDemux * qtdemux, QtDemuxStream * stream,
6482 GstBuffer *outbuf = NULL;
6487 gst_buffer_map (buf, &map, GST_MAP_READ);
6489 /* empty buffer is sent to terminate previous subtitle */
6490 if (map.size <= 2) {
6491 gst_buffer_unmap (buf, &map);
6492 gst_buffer_unref (buf);
6496 /* For closed caption, we need to extract the information from the
6497 * [cdat],[cdt2] or [ccdp] atom */
6498 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
6499 gst_buffer_unmap (buf, &map);
6501 outbuf = _gst_buffer_new_wrapped (cc, cclen, g_free);
6502 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6504 /* Conversion failed or there's nothing */
6506 gst_buffer_unref (buf);
6511 /* DVD subpicture specific sample handling.
6512 * the input buffer metadata must be writable,
6513 * but time/duration etc not yet set and need not be preserved */
6515 gst_qtdemux_process_buffer_dvd (GstQTDemux * qtdemux, QtDemuxStream * stream,
6518 /* send a one time dvd clut event */
6519 if (stream->pending_event && stream->pad)
6520 gst_pad_push_event (stream->pad, stream->pending_event);
6521 stream->pending_event = NULL;
6523 /* empty buffer is sent to terminate previous subtitle */
6524 if (gst_buffer_get_size (buf) <= 2) {
6525 gst_buffer_unref (buf);
6529 /* That's all the processing needed for subpictures */
6533 /* Timed text formats
6534 * the input buffer metadata must be writable,
6535 * but time/duration etc not yet set and need not be preserved */
6537 gst_qtdemux_process_buffer_text (GstQTDemux * qtdemux, QtDemuxStream * stream,
6540 GstBuffer *outbuf = NULL;
6545 /* not many cases for now */
6546 if (G_UNLIKELY (stream->subtype != FOURCC_text &&
6547 stream->subtype != FOURCC_sbtl)) {
6551 gst_buffer_map (buf, &map, GST_MAP_READ);
6553 /* empty buffer is sent to terminate previous subtitle */
6554 if (map.size <= 2) {
6555 gst_buffer_unmap (buf, &map);
6556 gst_buffer_unref (buf);
6560 nsize = GST_READ_UINT16_BE (map.data);
6561 nsize = MIN (nsize, map.size - 2);
6563 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6566 /* takes care of UTF-8 validation or UTF-16 recognition,
6567 * no other encoding expected */
6568 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6569 gst_buffer_unmap (buf, &map);
6572 outbuf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
6573 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6575 /* this should not really happen unless the subtitle is corrupted */
6577 gst_buffer_unref (buf);
6579 /* FIXME ? convert optional subsequent style info to markup */
6584 /* WebVTT sample handling according to 14496-30 */
6586 gst_qtdemux_process_buffer_wvtt (GstQTDemux * qtdemux, QtDemuxStream * stream,
6589 GstBuffer *outbuf = NULL;
6592 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
6593 g_assert_not_reached (); /* The buffer must be mappable */
6596 if (qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
6597 GstEvent *gap = NULL;
6598 /* Push a gap event */
6599 stream->segment.position = GST_BUFFER_PTS (buf);
6601 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
6602 gst_pad_push_event (stream->pad, gap);
6604 if (GST_BUFFER_DURATION_IS_VALID (buf))
6605 stream->segment.position += GST_BUFFER_DURATION (buf);
6608 qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
6609 GST_BUFFER_DURATION (buf), map.data, map.size);
6610 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6613 gst_buffer_unmap (buf, &map);
6614 gst_buffer_unref (buf);
6619 static GstFlowReturn
6620 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6623 GstFlowReturn ret = GST_FLOW_OK;
6624 GstClockTime pts, duration;
6626 if (stream->need_clip)
6627 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6629 if (G_UNLIKELY (buf == NULL))
6632 if (G_UNLIKELY (stream->discont)) {
6633 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6634 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6635 stream->discont = FALSE;
6637 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6640 GST_LOG_OBJECT (qtdemux,
6641 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6642 ", duration %" GST_TIME_FORMAT " on pad %s",
6643 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6644 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6645 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
6647 if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
6648 GstStructure *crypto_info;
6649 QtDemuxAavdEncryptionInfo *info =
6650 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
6652 crypto_info = gst_structure_copy (info->default_properties);
6653 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6654 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
6657 if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
6658 || stream->protection_scheme_type == FOURCC_cbcs)) {
6659 GstStructure *crypto_info;
6660 QtDemuxCencSampleSetInfo *info =
6661 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6665 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6666 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6667 GST_PTR_FORMAT, event);
6668 gst_pad_push_event (stream->pad, event);
6671 if (info->crypto_info == NULL) {
6672 if (stream->protection_scheme_type == FOURCC_cbcs) {
6673 if (CUR_STREAM (stream)->fourcc == FOURCC_enca ||
6674 CUR_STREAM (stream)->fourcc == FOURCC_encs ||
6675 CUR_STREAM (stream)->fourcc == FOURCC_enct ||
6676 CUR_STREAM (stream)->fourcc == FOURCC_encv) {
6677 crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
6679 || !gst_buffer_add_protection_meta (buf, crypto_info)) {
6680 GST_ERROR_OBJECT (qtdemux,
6681 "failed to attach cbcs metadata to buffer");
6682 qtdemux_gst_structure_free (crypto_info);
6684 GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
6687 GST_TRACE_OBJECT (qtdemux,
6688 "cbcs stream is not encrypted yet, not adding protection metadata");
6691 GST_DEBUG_OBJECT (qtdemux,
6692 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
6695 /* The end of the crypto_info array matches our n_samples position,
6696 * so count backward from there */
6697 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6698 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6699 /* steal structure from array */
6700 crypto_info = g_ptr_array_index (info->crypto_info, index);
6701 g_ptr_array_index (info->crypto_info, index) = NULL;
6702 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6703 info->crypto_info->len);
6704 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6705 GST_ERROR_OBJECT (qtdemux,
6706 "failed to attach cenc metadata to buffer");
6708 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6709 index, stream->sample_index);
6714 if (stream->alignment > 1)
6715 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6717 pts = GST_BUFFER_PTS (buf);
6718 duration = GST_BUFFER_DURATION (buf);
6720 ret = gst_pad_push (stream->pad, buf);
6722 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6723 /* mark position in stream, we'll need this to know when to send GAP event */
6724 stream->segment.position = pts + duration;
6732 static GstFlowReturn
6733 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6736 GstFlowReturn ret = GST_FLOW_OK;
6738 if (stream->subtype == FOURCC_clcp
6739 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6741 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6742 guint n_triplets, i;
6743 guint field1_off = 0, field2_off = 0;
6745 /* We have to split CEA608 buffers so that each outgoing buffer contains
6746 * one byte pair per field according to the framerate of the video track.
6748 * If there is only a single byte pair per field we don't have to do
6752 gst_buffer_map (buf, &map, GST_MAP_READ);
6754 n_triplets = map.size / 3;
6755 for (i = 0; i < n_triplets; i++) {
6756 if (map.data[3 * i] & 0x80)
6762 g_assert (n_field1 || n_field2);
6764 /* If there's more than 1 frame we have to split, otherwise we can just
6766 if (n_field1 > 1 || n_field2 > 1) {
6768 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6769 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6771 for (i = 0; i < n_output_buffers; i++) {
6773 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6777 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6778 outptr = outmap.data;
6781 gboolean found = FALSE;
6783 while (map.data + field1_off < map.data + map.size) {
6784 if (map.data[field1_off] & 0x80) {
6785 memcpy (outptr, &map.data[field1_off], 3);
6794 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6796 memcpy (outptr, empty, 3);
6803 gboolean found = FALSE;
6805 while (map.data + field2_off < map.data + map.size) {
6806 if ((map.data[field2_off] & 0x80) == 0) {
6807 memcpy (outptr, &map.data[field2_off], 3);
6816 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6818 memcpy (outptr, empty, 3);
6824 gst_buffer_unmap (outbuf, &outmap);
6826 GST_BUFFER_PTS (outbuf) =
6827 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6828 GST_SECOND * CUR_STREAM (stream)->fps_d,
6829 CUR_STREAM (stream)->fps_n);
6830 GST_BUFFER_DURATION (outbuf) =
6831 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6832 CUR_STREAM (stream)->fps_n);
6833 GST_BUFFER_OFFSET (outbuf) = -1;
6834 GST_BUFFER_OFFSET_END (outbuf) = -1;
6836 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6838 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6841 gst_buffer_unmap (buf, &map);
6842 gst_buffer_unref (buf);
6844 gst_buffer_unmap (buf, &map);
6845 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6848 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6854 /* Sets a buffer's attributes properly and pushes it downstream.
6855 * Also checks for additional actions and custom processing that may
6856 * need to be done first.
6858 static GstFlowReturn
6859 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6860 QtDemuxStream * stream, GstBuffer * buf,
6861 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6862 gboolean keyframe, GstClockTime position, guint64 byte_position)
6864 GstFlowReturn ret = GST_FLOW_OK;
6866 /* offset the timestamps according to the edit list */
6868 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6872 gst_buffer_map (buf, &map, GST_MAP_READ);
6873 url = g_strndup ((gchar *) map.data, map.size);
6874 gst_buffer_unmap (buf, &map);
6875 if (url != NULL && strlen (url) != 0) {
6876 /* we have RTSP redirect now */
6877 g_free (qtdemux->redirect_location);
6878 qtdemux->redirect_location = g_strdup (url);
6879 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6880 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6881 gst_structure_new ("redirect",
6882 "new-location", G_TYPE_STRING, url, NULL)));
6884 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6890 /* position reporting */
6891 if (qtdemux->segment.rate >= 0) {
6892 qtdemux->segment.position = position;
6893 gst_qtdemux_sync_streams (qtdemux);
6896 if (G_UNLIKELY (!stream->pad)) {
6897 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6898 gst_buffer_unref (buf);
6902 /* send out pending buffers */
6903 while (stream->buffers) {
6904 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6906 if (G_UNLIKELY (stream->discont)) {
6907 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6908 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6909 stream->discont = FALSE;
6911 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6914 if (stream->alignment > 1)
6915 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6916 gst_pad_push (stream->pad, buffer);
6918 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6921 /* we're going to modify the metadata */
6922 buf = gst_buffer_make_writable (buf);
6924 if (qtdemux->start_utc_time != GST_CLOCK_TIME_NONE) {
6925 static GstStaticCaps unix_caps = GST_STATIC_CAPS ("timestamp/x-unix");
6926 GstCaps *caps = gst_static_caps_get (&unix_caps);
6927 gst_buffer_add_reference_timestamp_meta (buf, caps,
6928 pts + qtdemux->start_utc_time - stream->cslg_shift,
6929 GST_CLOCK_TIME_NONE);
6930 gst_caps_unref (caps);
6933 GST_BUFFER_DTS (buf) = dts;
6934 GST_BUFFER_PTS (buf) = pts;
6935 GST_BUFFER_DURATION (buf) = duration;
6936 GST_BUFFER_OFFSET (buf) = -1;
6937 GST_BUFFER_OFFSET_END (buf) = -1;
6939 if (G_UNLIKELY (stream->process_func))
6940 buf = stream->process_func (qtdemux, stream, buf);
6947 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6948 stream->on_keyframe = FALSE;
6950 stream->on_keyframe = TRUE;
6953 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6954 gst_buffer_append_memory (buf,
6955 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6957 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6958 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6961 if (G_UNLIKELY (qtdemux->element_index)) {
6962 GstClockTime stream_time;
6965 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6967 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6968 GST_LOG_OBJECT (qtdemux,
6969 "adding association %" GST_TIME_FORMAT "-> %"
6970 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6971 gst_index_add_association (qtdemux->element_index,
6973 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6974 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6975 GST_FORMAT_BYTES, byte_position, NULL);
6980 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6986 static const QtDemuxRandomAccessEntry *
6987 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6988 GstClockTime pos, gboolean after)
6990 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6991 guint n_entries = stream->n_ra_entries;
6994 /* we assume the table is sorted */
6995 for (i = 0; i < n_entries; ++i) {
6996 if (entries[i].ts > pos)
7000 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
7001 * probably okay to assume that the index lists the very first fragment */
7008 return &entries[i - 1];
7012 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
7014 const QtDemuxRandomAccessEntry *best_entry = NULL;
7017 GST_OBJECT_LOCK (qtdemux);
7019 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
7021 /* first see if we can determine where to go to using mfra,
7022 * before we start clearing things */
7023 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7024 const QtDemuxRandomAccessEntry *entry;
7025 QtDemuxStream *stream;
7026 gboolean is_audio_or_video;
7028 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7030 if (stream->ra_entries == NULL)
7033 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
7034 is_audio_or_video = TRUE;
7036 is_audio_or_video = FALSE;
7039 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
7040 stream->time_position, !is_audio_or_video);
7042 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
7043 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
7045 stream->pending_seek = entry;
7047 /* decide position to jump to just based on audio/video tracks, not subs */
7048 if (!is_audio_or_video)
7051 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
7055 /* no luck, will handle seek otherwise */
7056 if (best_entry == NULL) {
7057 GST_OBJECT_UNLOCK (qtdemux);
7061 /* ok, now we can prepare for processing as of located moof */
7062 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7063 QtDemuxStream *stream;
7065 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7067 g_free (stream->samples);
7068 stream->samples = NULL;
7069 stream->n_samples = 0;
7070 stream->stbl_index = -1; /* no samples have yet been parsed */
7071 stream->sample_index = -1;
7073 if (stream->protection_scheme_info) {
7074 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
7075 if (stream->protection_scheme_type == FOURCC_cenc
7076 || stream->protection_scheme_type == FOURCC_cbcs) {
7077 QtDemuxCencSampleSetInfo *info =
7078 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
7079 if (info->crypto_info) {
7080 g_ptr_array_free (info->crypto_info, TRUE);
7081 info->crypto_info = NULL;
7087 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
7088 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
7089 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
7090 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
7092 qtdemux->moof_offset = best_entry->moof_offset;
7094 qtdemux_add_fragmented_samples (qtdemux);
7096 GST_OBJECT_UNLOCK (qtdemux);
7100 static GstFlowReturn
7101 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
7103 GstFlowReturn ret = GST_FLOW_OK;
7104 GstBuffer *buf = NULL;
7105 QtDemuxStream *stream, *target_stream = NULL;
7106 GstClockTime min_time;
7108 GstClockTime dts = GST_CLOCK_TIME_NONE;
7109 GstClockTime pts = GST_CLOCK_TIME_NONE;
7110 GstClockTime duration = 0;
7111 gboolean keyframe = FALSE;
7112 guint sample_size = 0;
7113 guint num_samples = 1;
7118 if (qtdemux->fragmented_seek_pending) {
7119 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
7120 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
7121 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
7122 qtdemux->fragmented_seek_pending = FALSE;
7124 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
7128 /* Figure out the next stream sample to output, min_time is expressed in
7129 * global time and runs over the edit list segments. */
7130 min_time = G_MAXUINT64;
7131 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7132 GstClockTime position;
7134 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7135 position = stream->time_position;
7137 /* position of -1 is EOS */
7138 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
7139 min_time = position;
7140 target_stream = stream;
7144 if (G_UNLIKELY (target_stream == NULL)) {
7145 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
7149 /* check for segment end */
7150 if (G_UNLIKELY (qtdemux->segment.stop != -1
7151 && qtdemux->segment.rate >= 0
7152 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
7153 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
7154 target_stream->time_position = GST_CLOCK_TIME_NONE;
7158 /* fetch info for the current sample of this stream */
7159 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, target_stream,
7160 &empty, &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
7163 /* Send catche-up GAP event for each other stream if required.
7164 * This logic will be applied only for positive rate */
7165 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux) &&
7166 qtdemux->segment.rate >= 0; i++) {
7167 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7169 if (stream == target_stream ||
7170 !GST_CLOCK_TIME_IS_VALID (stream->segment.stop) ||
7171 !GST_CLOCK_TIME_IS_VALID (stream->segment.position))
7175 GstClockTime gap_threshold;
7176 /* kind of running time with offset segment.base and segment.start */
7177 GstClockTime pseudo_target_time = target_stream->segment.base;
7178 GstClockTime pseudo_cur_time = stream->segment.base;
7180 /* make sure positive offset, segment.position can be smallr than
7181 * segment.start for some reasons */
7182 if (target_stream->segment.position >= target_stream->segment.start) {
7183 pseudo_target_time +=
7184 (target_stream->segment.position - target_stream->segment.start);
7187 if (stream->segment.position >= stream->segment.start)
7188 pseudo_cur_time += (stream->segment.position - stream->segment.start);
7190 /* Only send gap events on non-subtitle streams if lagging way behind. */
7191 if (stream->subtype == FOURCC_subp
7192 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl ||
7193 stream->subtype == FOURCC_wvtt)
7194 gap_threshold = 1 * GST_SECOND;
7196 gap_threshold = 3 * GST_SECOND;
7198 /* send gap events until the stream catches up */
7199 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
7200 while (GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
7201 pseudo_cur_time < (G_MAXUINT64 - gap_threshold) &&
7202 pseudo_cur_time + gap_threshold < pseudo_target_time) {
7204 gst_event_new_gap (stream->segment.position, gap_threshold);
7205 GST_LOG_OBJECT (stream->pad, "Sending %" GST_PTR_FORMAT, gap);
7207 gst_pad_push_event (stream->pad, gap);
7208 stream->segment.position += gap_threshold;
7209 pseudo_cur_time += gap_threshold;
7214 stream = target_stream;
7216 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
7217 if (stream->new_caps) {
7218 gst_qtdemux_configure_stream (qtdemux, stream);
7219 qtdemux_do_allocation (stream, qtdemux);
7222 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
7223 if (G_UNLIKELY (qtdemux->segment.
7224 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
7225 if (stream->subtype == FOURCC_vide) {
7227 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
7230 } else if (qtdemux->trickmode_interval > 0) {
7231 GstClockTimeDiff interval;
7233 if (qtdemux->segment.rate > 0)
7234 interval = stream->time_position - stream->last_keyframe_dts;
7236 interval = stream->last_keyframe_dts - stream->time_position;
7238 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
7239 && interval < qtdemux->trickmode_interval) {
7240 GST_LOG_OBJECT (qtdemux,
7241 "Skipping keyframe within interval on track-id %u",
7245 stream->last_keyframe_dts = stream->time_position;
7251 GST_DEBUG_OBJECT (qtdemux,
7252 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
7253 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
7254 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
7255 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
7256 GST_TIME_ARGS (duration));
7258 if (G_UNLIKELY (empty)) {
7259 /* empty segment, push a gap if there's a second or more
7260 * difference and move to the next one */
7261 if ((pts + duration - stream->segment.position) >= GST_SECOND)
7262 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
7263 stream->segment.position = pts + duration;
7267 /* hmm, empty sample, skip and move to next sample */
7268 if (G_UNLIKELY (sample_size <= 0))
7271 /* last pushed sample was out of boundary, goto next sample */
7272 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
7275 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
7276 GST_DEBUG_OBJECT (qtdemux,
7277 "size %d larger than stream max_buffer_size %d, trimming",
7278 sample_size, stream->max_buffer_size);
7280 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
7281 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
7282 && sample_size < stream->min_buffer_size) {
7283 guint start_sample_index = stream->sample_index;
7284 guint accumulated_size = sample_size;
7285 guint64 expected_next_offset = offset + sample_size;
7287 GST_DEBUG_OBJECT (qtdemux,
7288 "size %d smaller than stream min_buffer_size %d, combining with the next",
7289 sample_size, stream->min_buffer_size);
7291 while (stream->sample_index < stream->to_sample
7292 && stream->sample_index + 1 < stream->n_samples) {
7293 const QtDemuxSample *next_sample;
7295 /* Increment temporarily */
7296 stream->sample_index++;
7298 /* Failed to parse sample so let's go back to the previous one that was
7299 * still successful */
7300 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
7301 stream->sample_index--;
7305 next_sample = &stream->samples[stream->sample_index];
7307 /* Not contiguous with the previous sample so let's go back to the
7308 * previous one that was still successful */
7309 if (next_sample->offset != expected_next_offset) {
7310 stream->sample_index--;
7314 accumulated_size += next_sample->size;
7315 expected_next_offset += next_sample->size;
7316 if (accumulated_size >= stream->min_buffer_size)
7320 num_samples = stream->sample_index + 1 - start_sample_index;
7321 stream->sample_index = start_sample_index;
7322 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
7323 num_samples, accumulated_size);
7324 size = accumulated_size;
7329 if (qtdemux->cenc_aux_info_offset > 0) {
7332 GstBuffer *aux_info = NULL;
7334 /* pull the data stored before the sample */
7336 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
7337 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
7338 if (G_UNLIKELY (ret != GST_FLOW_OK))
7340 gst_buffer_map (aux_info, &map, GST_MAP_READ);
7341 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
7342 gst_byte_reader_init (&br, map.data + 8, map.size);
7343 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
7344 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
7345 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
7346 gst_buffer_unmap (aux_info, &map);
7347 gst_buffer_unref (aux_info);
7348 ret = GST_FLOW_ERROR;
7351 gst_buffer_unmap (aux_info, &map);
7352 gst_buffer_unref (aux_info);
7355 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
7358 if (stream->use_allocator) {
7359 /* if we have a per-stream allocator, use it */
7360 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
7363 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
7365 if (G_UNLIKELY (ret != GST_FLOW_OK))
7368 /* Update for both splitting and combining of samples */
7369 if (size != sample_size) {
7370 pts += gst_util_uint64_scale_int (GST_SECOND,
7371 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7374 gst_util_uint64_scale_int (GST_SECOND,
7375 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7378 gst_util_uint64_scale_int (GST_SECOND,
7379 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
7382 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
7383 dts, pts, duration, keyframe, min_time, offset);
7385 if (size < sample_size) {
7386 QtDemuxSample *sample = &stream->samples[stream->sample_index];
7387 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
7389 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
7391 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
7392 if (time_position >= segment->media_start) {
7393 /* inside the segment, update time_position, looks very familiar to
7394 * GStreamer segments, doesn't it? */
7395 stream->time_position = (time_position - segment->media_start) +
7398 /* not yet in segment, time does not yet increment. This means
7399 * that we are still prerolling keyframes to the decoder so it can
7400 * decode the first sample of the segment. */
7401 stream->time_position = segment->time;
7403 } else if (size > sample_size) {
7404 /* Increase to the last sample we already pulled so that advancing
7405 * below brings us to the next sample we need to pull */
7406 stream->sample_index += num_samples - 1;
7410 GST_OBJECT_LOCK (qtdemux);
7411 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
7412 GST_OBJECT_UNLOCK (qtdemux);
7413 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
7414 * we have no more data for the pad to push */
7415 if (ret == GST_FLOW_EOS)
7418 stream->offset_in_sample += size;
7419 if (stream->offset_in_sample >= sample_size) {
7420 gst_qtdemux_advance_sample (qtdemux, stream);
7425 gst_qtdemux_advance_sample (qtdemux, stream);
7433 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
7439 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
7440 /* EOS will be raised if all are EOS */
7447 gst_qtdemux_loop (GstPad * pad)
7449 GstQTDemux *qtdemux;
7453 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
7455 cur_offset = qtdemux->offset;
7456 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
7457 cur_offset, qt_demux_state_string (qtdemux->state));
7459 switch (qtdemux->state) {
7460 case QTDEMUX_STATE_INITIAL:
7461 case QTDEMUX_STATE_HEADER:
7462 ret = gst_qtdemux_loop_state_header (qtdemux);
7464 case QTDEMUX_STATE_MOVIE:
7465 ret = gst_qtdemux_loop_state_movie (qtdemux);
7466 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
7467 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
7475 /* if something went wrong, pause */
7476 if (ret != GST_FLOW_OK)
7480 gst_object_unref (qtdemux);
7486 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7487 (NULL), ("streaming stopped, invalid state"));
7488 gst_pad_pause_task (pad);
7489 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7494 const gchar *reason = gst_flow_get_name (ret);
7496 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7498 gst_pad_pause_task (pad);
7500 /* fatal errors need special actions */
7502 if (ret == GST_FLOW_EOS) {
7503 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
7504 /* we have no streams, post an error */
7505 gst_qtdemux_post_no_playable_stream_error (qtdemux);
7507 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7510 if ((stop = qtdemux->segment.stop) == -1)
7511 stop = qtdemux->segment.duration;
7513 if (qtdemux->segment.rate >= 0) {
7514 GstMessage *message;
7517 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7518 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7519 GST_FORMAT_TIME, stop);
7520 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7521 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7522 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7523 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7525 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7526 gst_qtdemux_push_event (qtdemux, event);
7528 GstMessage *message;
7531 /* For Reverse Playback */
7532 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7533 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7534 GST_FORMAT_TIME, qtdemux->segment.start);
7535 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7536 qtdemux->segment.start);
7537 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7538 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7539 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7541 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7542 gst_qtdemux_push_event (qtdemux, event);
7547 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7548 event = gst_event_new_eos ();
7549 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7550 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7551 gst_qtdemux_push_event (qtdemux, event);
7553 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7554 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
7555 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7564 * Returns if there are samples to be played.
7567 has_next_entry (GstQTDemux * demux)
7569 QtDemuxStream *stream;
7572 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7574 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7575 stream = QTDEMUX_NTH_STREAM (demux, i);
7577 if (stream->sample_index == -1) {
7578 stream->sample_index = 0;
7579 stream->offset_in_sample = 0;
7582 if (stream->sample_index >= stream->n_samples) {
7583 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7586 GST_DEBUG_OBJECT (demux, "Found a sample");
7590 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7597 * Returns the size of the first entry at the current offset.
7598 * If -1, there are none (which means EOS or empty file).
7601 next_entry_size (GstQTDemux * demux)
7603 QtDemuxStream *stream, *target_stream = NULL;
7604 guint64 smalloffs = (guint64) - 1;
7605 QtDemuxSample *sample;
7608 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7611 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7612 stream = QTDEMUX_NTH_STREAM (demux, i);
7614 if (stream->sample_index == -1) {
7615 stream->sample_index = 0;
7616 stream->offset_in_sample = 0;
7619 if (stream->sample_index >= stream->n_samples) {
7620 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7624 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
7625 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7626 stream->sample_index);
7630 sample = &stream->samples[stream->sample_index];
7632 GST_LOG_OBJECT (demux,
7633 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7634 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7635 stream->sample_index, sample->offset, sample->size);
7637 if (((smalloffs == -1)
7638 || (sample->offset < smalloffs)) && (sample->size)) {
7639 smalloffs = sample->offset;
7640 target_stream = stream;
7647 GST_LOG_OBJECT (demux,
7648 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7649 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7651 stream = target_stream;
7652 sample = &stream->samples[stream->sample_index];
7654 if (sample->offset >= demux->offset) {
7655 demux->todrop = sample->offset - demux->offset;
7656 return sample->size + demux->todrop;
7659 GST_DEBUG_OBJECT (demux,
7660 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7665 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
7667 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7669 gst_element_post_message (GST_ELEMENT_CAST (demux),
7670 gst_message_new_element (GST_OBJECT_CAST (demux),
7671 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7675 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
7680 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7683 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7684 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7685 GST_SEEK_TYPE_NONE, -1);
7687 /* store seqnum to drop flush events, they don't need to reach downstream */
7688 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7689 res = gst_pad_push_event (demux->sinkpad, event);
7690 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7695 /* check for seekable upstream, above and beyond a mere query */
7697 gst_qtdemux_check_seekability (GstQTDemux * demux)
7700 gboolean seekable = FALSE;
7701 gint64 start = -1, stop = -1;
7703 if (demux->upstream_size)
7706 if (demux->upstream_format_is_time)
7709 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7710 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7711 GST_DEBUG_OBJECT (demux, "seeking query failed");
7715 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7717 /* try harder to query upstream size if we didn't get it the first time */
7718 if (seekable && stop == -1) {
7719 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7720 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7723 /* if upstream doesn't know the size, it's likely that it's not seekable in
7724 * practice even if it technically may be seekable */
7725 if (seekable && (start != 0 || stop <= start)) {
7726 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7731 gst_query_unref (query);
7733 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7734 G_GUINT64_FORMAT ")", seekable, start, stop);
7735 demux->upstream_seekable = seekable;
7736 demux->upstream_size = seekable ? stop : -1;
7740 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7742 g_return_if_fail (bytes <= demux->todrop);
7744 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7745 gst_adapter_flush (demux->adapter, bytes);
7746 demux->neededbytes -= bytes;
7747 demux->offset += bytes;
7748 demux->todrop -= bytes;
7751 /* PUSH-MODE only: Send a segment, if not done already. */
7753 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7755 if (G_UNLIKELY (demux->need_segment)) {
7758 if (!demux->upstream_format_is_time) {
7759 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7761 GstEvent *segment_event;
7762 segment_event = gst_event_new_segment (&demux->segment);
7763 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7764 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7765 gst_qtdemux_push_event (demux, segment_event);
7768 demux->need_segment = FALSE;
7770 /* clear to send tags on all streams */
7771 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7772 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7773 gst_qtdemux_push_tags (demux, stream);
7774 if (CUR_STREAM (stream)->sparse) {
7775 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7776 gst_pad_push_event (stream->pad,
7777 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7783 /* Used for push mode only. */
7785 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7786 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7788 GstClockTime ts, dur;
7792 stream->segments[segment_index].duration - (pos -
7793 stream->segments[segment_index].time);
7794 stream->time_position += dur;
7796 /* Only gaps with a duration of at least one second are propagated.
7797 * Same workaround as in pull mode.
7798 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7799 if (dur >= GST_SECOND) {
7801 gap = gst_event_new_gap (ts, dur);
7803 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7804 "segment: %" GST_PTR_FORMAT, gap);
7805 gst_pad_push_event (stream->pad, gap);
7809 static GstFlowReturn
7810 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7814 demux = GST_QTDEMUX (parent);
7816 GST_DEBUG_OBJECT (demux,
7817 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7818 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7819 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7820 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7821 gst_buffer_get_size (inbuf), demux->offset);
7823 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7824 gboolean is_gap_input = FALSE;
7827 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7829 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7830 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7833 /* Check if we can land back on our feet in the case where upstream is
7834 * handling the seeking/pushing of samples with gaps in between (like
7835 * in the case of trick-mode DASH for example) */
7836 if (demux->upstream_format_is_time
7837 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7838 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7840 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7841 GST_LOG_OBJECT (demux,
7842 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7843 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7845 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7846 stream, GST_BUFFER_OFFSET (inbuf));
7848 QtDemuxSample *sample = &stream->samples[res];
7849 GST_LOG_OBJECT (demux,
7850 "Checking if sample %d from track-id %u is valid (offset:%"
7851 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7852 stream->track_id, sample->offset, sample->size);
7853 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7854 GST_LOG_OBJECT (demux,
7855 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7857 is_gap_input = TRUE;
7858 /* We can go back to standard playback mode */
7859 demux->state = QTDEMUX_STATE_MOVIE;
7860 /* Remember which sample this stream is at */
7861 stream->sample_index = res;
7862 /* Finally update all push-based values to the expected values */
7863 demux->neededbytes = stream->samples[res].size;
7864 demux->offset = GST_BUFFER_OFFSET (inbuf);
7866 demux->mdatsize - demux->offset + demux->mdatoffset;
7871 if (!is_gap_input) {
7872 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7873 /* Reset state if it's a real discont */
7874 demux->neededbytes = 16;
7875 demux->state = QTDEMUX_STATE_INITIAL;
7876 demux->offset = GST_BUFFER_OFFSET (inbuf);
7877 gst_adapter_clear (demux->adapter);
7880 /* Reverse fragmented playback, need to flush all we have before
7881 * consuming a new fragment.
7882 * The samples array have the timestamps calculated by accumulating the
7883 * durations but this won't work for reverse playback of fragments as
7884 * the timestamps of a subsequent fragment should be smaller than the
7885 * previously received one. */
7886 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7887 gst_qtdemux_process_adapter (demux, TRUE);
7888 g_ptr_array_foreach (demux->active_streams,
7889 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7893 gst_adapter_push (demux->adapter, inbuf);
7895 GST_DEBUG_OBJECT (demux,
7896 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7897 demux->neededbytes, gst_adapter_available (demux->adapter));
7899 return gst_qtdemux_process_adapter (demux, FALSE);
7903 gst_segment_to_stream_time_clamped (const GstSegment * segment,
7906 guint64 segment_stream_time_start;
7907 guint64 segment_stream_time_stop = GST_CLOCK_TIME_NONE;
7908 guint64 stream_pts_unsigned;
7911 g_return_val_if_fail (segment != NULL, GST_CLOCK_TIME_NONE);
7912 g_return_val_if_fail (segment->format == GST_FORMAT_TIME,
7913 GST_CLOCK_TIME_NONE);
7915 segment_stream_time_start = segment->time;
7916 if (segment->stop != GST_CLOCK_TIME_NONE)
7917 segment_stream_time_stop =
7918 gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
7921 gst_segment_to_stream_time_full (segment, GST_FORMAT_TIME, position,
7922 &stream_pts_unsigned);
7923 /* ret == 0 if the segment is invalid (either position, segment->time or the segment start are -1). */
7924 g_return_val_if_fail (ret != 0, GST_CLOCK_TIME_NONE);
7926 if (ret == -1 || stream_pts_unsigned < segment_stream_time_start) {
7927 /* Negative or prior to segment start stream time, clamp to segment start. */
7928 return segment_stream_time_start;
7929 } else if (segment_stream_time_stop != GST_CLOCK_TIME_NONE
7930 && stream_pts_unsigned > segment_stream_time_stop) {
7931 /* Clamp to segment end. */
7932 return segment_stream_time_stop;
7934 return stream_pts_unsigned;
7938 static GstFlowReturn
7939 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7941 GstFlowReturn ret = GST_FLOW_OK;
7943 /* we never really mean to buffer that much */
7944 if (demux->neededbytes == -1) {
7948 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7949 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7951 #ifndef GST_DISABLE_GST_DEBUG
7953 guint64 discont_offset, distance_from_discont;
7955 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7956 distance_from_discont =
7957 gst_adapter_distance_from_discont (demux->adapter);
7959 GST_DEBUG_OBJECT (demux,
7960 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7961 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7962 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7963 demux->offset, discont_offset, distance_from_discont);
7967 switch (demux->state) {
7968 case QTDEMUX_STATE_INITIAL:{
7973 gst_qtdemux_check_seekability (demux);
7975 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7977 /* get fourcc/length, set neededbytes */
7978 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7980 gst_adapter_unmap (demux->adapter);
7982 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7983 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7985 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7986 (_("This file is invalid and cannot be played.")),
7987 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7988 GST_FOURCC_ARGS (fourcc)));
7989 ret = GST_FLOW_ERROR;
7992 if (fourcc == FOURCC_mdat) {
7993 gint next_entry = next_entry_size (demux);
7994 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7995 || !demux->fragmented)) {
7996 /* we have the headers, start playback */
7997 demux->state = QTDEMUX_STATE_MOVIE;
7998 demux->neededbytes = next_entry;
7999 demux->mdatleft = size;
8000 demux->mdatsize = demux->mdatleft;
8002 /* no headers yet, try to get them */
8005 guint64 old, target;
8008 old = demux->offset;
8009 target = old + size;
8011 /* try to jump over the atom with a seek */
8012 /* only bother if it seems worth doing so,
8013 * and avoids possible upstream/server problems */
8014 if (demux->upstream_seekable &&
8015 demux->upstream_size > 4 * (1 << 20)) {
8016 res = qtdemux_seek_offset (demux, target);
8018 GST_DEBUG_OBJECT (demux, "skipping seek");
8023 GST_DEBUG_OBJECT (demux, "seek success");
8024 /* remember the offset fo the first mdat so we can seek back to it
8025 * after we have the headers */
8026 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
8027 demux->first_mdat = old;
8028 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
8031 /* seek worked, continue reading */
8032 demux->offset = target;
8033 demux->neededbytes = 16;
8034 demux->state = QTDEMUX_STATE_INITIAL;
8036 /* seek failed, need to buffer */
8037 demux->offset = old;
8038 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
8039 /* there may be multiple mdat (or alike) buffers */
8041 if (demux->mdatbuffer)
8042 bs = gst_buffer_get_size (demux->mdatbuffer);
8045 if (size + bs > 10 * (1 << 20))
8047 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
8048 demux->neededbytes = size;
8049 if (!demux->mdatbuffer)
8050 demux->mdatoffset = demux->offset;
8053 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
8054 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8055 (_("This file is invalid and cannot be played.")),
8056 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
8057 GST_FOURCC_ARGS (fourcc), size));
8058 ret = GST_FLOW_ERROR;
8061 /* this means we already started buffering and still no moov header,
8062 * let's continue buffering everything till we get moov */
8063 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
8064 || fourcc == FOURCC_moof))
8066 demux->neededbytes = size;
8067 demux->state = QTDEMUX_STATE_HEADER;
8071 case QTDEMUX_STATE_HEADER:{
8075 GST_DEBUG_OBJECT (demux, "In header");
8077 data = gst_adapter_map (demux->adapter, demux->neededbytes);
8079 /* parse the header */
8080 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
8082 if (fourcc == FOURCC_moov) {
8083 /* in usual fragmented setup we could try to scan for more
8084 * and end up at the the moov (after mdat) again */
8085 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
8087 || demux->last_moov_offset == demux->offset)) {
8088 GST_DEBUG_OBJECT (demux,
8089 "Skipping moov atom as we have (this) one already");
8091 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
8093 if (demux->got_moov && demux->fragmented) {
8094 GST_DEBUG_OBJECT (demux,
8095 "Got a second moov, clean up data from old one");
8096 if (demux->moov_node_compressed) {
8097 g_node_destroy (demux->moov_node_compressed);
8098 if (demux->moov_node)
8099 g_free (demux->moov_node->data);
8101 demux->moov_node_compressed = NULL;
8102 if (demux->moov_node)
8103 g_node_destroy (demux->moov_node);
8104 demux->moov_node = NULL;
8105 demux->start_utc_time = GST_CLOCK_TIME_NONE;
8108 demux->last_moov_offset = demux->offset;
8110 /* Update streams with new moov */
8111 gst_qtdemux_stream_concat (demux,
8112 demux->old_streams, demux->active_streams);
8114 qtdemux_parse_moov (demux, data, demux->neededbytes);
8115 qtdemux_node_dump (demux, demux->moov_node);
8116 qtdemux_parse_tree (demux);
8117 qtdemux_prepare_streams (demux);
8118 QTDEMUX_EXPOSE_LOCK (demux);
8119 qtdemux_expose_streams (demux);
8120 QTDEMUX_EXPOSE_UNLOCK (demux);
8122 demux->got_moov = TRUE;
8124 gst_qtdemux_check_send_pending_segment (demux);
8126 if (demux->moov_node_compressed) {
8127 g_node_destroy (demux->moov_node_compressed);
8128 g_free (demux->moov_node->data);
8130 demux->moov_node_compressed = NULL;
8131 g_node_destroy (demux->moov_node);
8132 demux->moov_node = NULL;
8133 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
8135 } else if (fourcc == FOURCC_moof) {
8136 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
8138 GstClockTime prev_pts;
8139 guint64 prev_offset;
8140 guint64 adapter_discont_offset, adapter_discont_dist;
8142 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
8145 * The timestamp of the moof buffer is relevant as some scenarios
8146 * won't have the initial timestamp in the atoms. Whenever a new
8147 * buffer has started, we get that buffer's PTS and use it as a base
8148 * timestamp for the trun entries.
8150 * To keep track of the current buffer timestamp and starting point
8151 * we use gst_adapter_prev_pts that gives us the PTS and the distance
8152 * from the beginning of the buffer, with the distance and demux->offset
8153 * we know if it is still the same buffer or not.
8155 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
8156 prev_offset = demux->offset - dist;
8157 if (demux->fragment_start_offset == -1
8158 || prev_offset > demux->fragment_start_offset) {
8159 demux->fragment_start_offset = prev_offset;
8160 demux->fragment_start = prev_pts;
8161 GST_DEBUG_OBJECT (demux,
8162 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
8163 GST_TIME_FORMAT, demux->fragment_start_offset,
8164 GST_TIME_ARGS (demux->fragment_start));
8167 /* We can't use prev_offset() here because this would require
8168 * upstream to set consistent and correct offsets on all buffers
8169 * since the discont. Nothing ever did that in the past and we
8170 * would break backwards compatibility here then.
8171 * Instead take the offset we had at the last discont and count
8172 * the bytes from there. This works with old code as there would
8173 * be no discont between moov and moof, and also works with
8174 * adaptivedemux which correctly sets offset and will set the
8175 * DISCONT flag accordingly when needed.
8177 * We also only do this for upstream TIME segments as otherwise
8178 * there are potential backwards compatibility problems with
8179 * seeking in PUSH mode and upstream providing inconsistent
8181 adapter_discont_offset =
8182 gst_adapter_offset_at_discont (demux->adapter);
8183 adapter_discont_dist =
8184 gst_adapter_distance_from_discont (demux->adapter);
8186 GST_DEBUG_OBJECT (demux,
8187 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
8188 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
8189 demux->offset, adapter_discont_offset, adapter_discont_dist);
8191 if (demux->upstream_format_is_time) {
8192 demux->moof_offset = adapter_discont_offset;
8193 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
8194 demux->moof_offset += adapter_discont_dist;
8195 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
8196 demux->moof_offset = demux->offset;
8198 demux->moof_offset = demux->offset;
8201 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
8202 demux->moof_offset, NULL)) {
8203 gst_adapter_unmap (demux->adapter);
8204 ret = GST_FLOW_ERROR;
8208 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
8209 if (demux->variant == VARIANT_MSS_FRAGMENTED && !demux->exposed) {
8210 QTDEMUX_EXPOSE_LOCK (demux);
8211 qtdemux_expose_streams (demux);
8212 QTDEMUX_EXPOSE_UNLOCK (demux);
8215 gst_qtdemux_check_send_pending_segment (demux);
8217 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
8219 } else if (fourcc == FOURCC_ftyp) {
8220 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
8221 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
8222 } else if (fourcc == FOURCC_uuid) {
8223 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
8224 qtdemux_parse_uuid (demux, data, demux->neededbytes);
8225 } else if (fourcc == FOURCC_sidx) {
8226 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
8227 qtdemux_parse_sidx (demux, data, demux->neededbytes);
8228 } else if (fourcc == FOURCC_meta) {
8229 GNode *node, *child;
8230 GstByteReader child_data;
8232 node = g_node_new ((gpointer) data);
8233 qtdemux_parse_node (demux, node, data, demux->neededbytes);
8235 /* Parse ONVIF Export File Format CorrectStartTime box if available */
8237 qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb,
8239 qtdemux_parse_cstb (demux, &child_data);
8242 g_node_destroy (node);
8246 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
8250 /* [free] and [skip] are padding atoms */
8251 GST_DEBUG_OBJECT (demux,
8252 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
8253 GST_FOURCC_ARGS (fourcc));
8256 GST_WARNING_OBJECT (demux,
8257 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
8258 GST_FOURCC_ARGS (fourcc));
8259 /* Let's jump that one and go back to initial state */
8263 gst_adapter_unmap (demux->adapter);
8266 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
8267 gsize remaining_data_size = 0;
8269 /* the mdat was before the header */
8270 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
8271 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
8272 /* restore our adapter/offset view of things with upstream;
8273 * put preceding buffered data ahead of current moov data.
8274 * This should also handle evil mdat, moov, mdat cases and alike */
8275 gst_adapter_flush (demux->adapter, demux->neededbytes);
8277 /* Store any remaining data after the mdat for later usage */
8278 remaining_data_size = gst_adapter_available (demux->adapter);
8279 if (remaining_data_size > 0) {
8280 g_assert (demux->restoredata_buffer == NULL);
8281 demux->restoredata_buffer =
8282 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
8283 demux->restoredata_offset = demux->offset + demux->neededbytes;
8284 GST_DEBUG_OBJECT (demux,
8285 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
8286 G_GUINT64_FORMAT, remaining_data_size,
8287 demux->restoredata_offset);
8290 gst_adapter_push (demux->adapter, demux->mdatbuffer);
8291 demux->mdatbuffer = NULL;
8292 demux->offset = demux->mdatoffset;
8293 demux->neededbytes = next_entry_size (demux);
8294 demux->state = QTDEMUX_STATE_MOVIE;
8295 demux->mdatleft = gst_adapter_available (demux->adapter);
8296 demux->mdatsize = demux->mdatleft;
8298 GST_DEBUG_OBJECT (demux, "Carrying on normally");
8299 gst_adapter_flush (demux->adapter, demux->neededbytes);
8301 /* only go back to the mdat if there are samples to play */
8302 if (demux->got_moov && demux->first_mdat != -1
8303 && has_next_entry (demux)) {
8306 /* we need to seek back */
8307 res = qtdemux_seek_offset (demux, demux->first_mdat);
8309 demux->offset = demux->first_mdat;
8311 GST_DEBUG_OBJECT (demux, "Seek back failed");
8314 demux->offset += demux->neededbytes;
8316 demux->neededbytes = 16;
8317 demux->state = QTDEMUX_STATE_INITIAL;
8322 case QTDEMUX_STATE_BUFFER_MDAT:{
8326 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
8328 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8329 gst_buffer_extract (buf, 0, fourcc, 4);
8330 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
8331 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
8332 if (demux->mdatbuffer)
8333 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
8335 demux->mdatbuffer = buf;
8336 demux->offset += demux->neededbytes;
8337 demux->neededbytes = 16;
8338 demux->state = QTDEMUX_STATE_INITIAL;
8339 gst_qtdemux_post_progress (demux, 1, 1);
8343 case QTDEMUX_STATE_MOVIE:{
8344 QtDemuxStream *stream = NULL;
8345 QtDemuxSample *sample;
8346 GstClockTime dts, pts, stream_pts, duration;
8350 GST_DEBUG_OBJECT (demux,
8351 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
8353 if (demux->fragmented) {
8354 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
8356 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
8357 /* if needed data starts within this atom,
8358 * then it should not exceed this atom */
8359 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
8360 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8361 (_("This file is invalid and cannot be played.")),
8362 ("sample data crosses atom boundary"));
8363 ret = GST_FLOW_ERROR;
8366 demux->mdatleft -= demux->neededbytes;
8368 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
8369 /* so we are dropping more than left in this atom */
8370 gst_qtdemux_drop_data (demux, demux->mdatleft);
8371 demux->mdatleft = 0;
8373 /* need to resume atom parsing so we do not miss any other pieces */
8374 demux->state = QTDEMUX_STATE_INITIAL;
8375 demux->neededbytes = 16;
8377 /* check if there was any stored post mdat data from previous buffers */
8378 if (demux->restoredata_buffer) {
8379 g_assert (gst_adapter_available (demux->adapter) == 0);
8381 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
8382 demux->restoredata_buffer = NULL;
8383 demux->offset = demux->restoredata_offset;
8390 if (demux->todrop) {
8391 if (demux->cenc_aux_info_offset > 0) {
8395 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
8396 data = gst_adapter_map (demux->adapter, demux->todrop);
8397 gst_byte_reader_init (&br, data + 8, demux->todrop);
8398 if (!qtdemux_parse_cenc_aux_info (demux,
8399 QTDEMUX_NTH_STREAM (demux, 0), &br,
8400 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
8401 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
8402 ret = GST_FLOW_ERROR;
8403 gst_adapter_unmap (demux->adapter);
8404 g_free (demux->cenc_aux_info_sizes);
8405 demux->cenc_aux_info_sizes = NULL;
8408 demux->cenc_aux_info_offset = 0;
8409 g_free (demux->cenc_aux_info_sizes);
8410 demux->cenc_aux_info_sizes = NULL;
8411 gst_adapter_unmap (demux->adapter);
8413 gst_qtdemux_drop_data (demux, demux->todrop);
8417 /* initial newsegment sent here after having added pads,
8418 * possible others in sink_event */
8419 gst_qtdemux_check_send_pending_segment (demux);
8421 /* Figure out which stream this packet belongs to */
8422 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8423 stream = QTDEMUX_NTH_STREAM (demux, i);
8424 if (stream->sample_index >= stream->n_samples) {
8425 /* reset to be checked below G_UNLIKELY (stream == NULL) */
8429 GST_LOG_OBJECT (demux,
8430 "Checking track-id %u (sample_index:%d / offset:%"
8431 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
8432 stream->sample_index,
8433 stream->samples[stream->sample_index].offset,
8434 stream->samples[stream->sample_index].size);
8436 if (stream->samples[stream->sample_index].offset == demux->offset)
8440 if (G_UNLIKELY (stream == NULL))
8441 goto unknown_stream;
8443 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
8445 if (stream->new_caps) {
8446 gst_qtdemux_configure_stream (demux, stream);
8449 /* Put data in a buffer, set timestamps, caps, ... */
8450 sample = &stream->samples[stream->sample_index];
8452 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
8453 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
8454 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
8456 dts = QTSAMPLE_DTS (stream, sample);
8457 pts = QTSAMPLE_PTS (stream, sample);
8459 gst_segment_to_stream_time_clamped (&stream->segment, pts);
8460 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
8461 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
8463 /* check for segment end */
8464 if (G_UNLIKELY (demux->segment.stop != -1
8465 && demux->segment.stop <= stream_pts && keyframe)
8466 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
8467 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
8468 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
8470 /* skip this data, stream is EOS */
8471 gst_adapter_flush (demux->adapter, demux->neededbytes);
8472 demux->offset += demux->neededbytes;
8474 /* check if all streams are eos */
8476 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8477 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
8486 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8488 /* FIXME: should either be an assert or a plain check */
8489 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8491 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
8492 dts, pts, duration, keyframe, dts, demux->offset);
8496 GST_OBJECT_LOCK (demux);
8497 ret = gst_qtdemux_combine_flows (demux, stream, ret);
8498 GST_OBJECT_UNLOCK (demux);
8500 /* skip this data, stream is EOS */
8501 gst_adapter_flush (demux->adapter, demux->neededbytes);
8504 stream->sample_index++;
8505 stream->offset_in_sample = 0;
8507 /* update current offset and figure out size of next buffer */
8508 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8509 demux->offset, demux->neededbytes);
8510 demux->offset += demux->neededbytes;
8511 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8515 if (ret == GST_FLOW_EOS) {
8516 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8517 demux->neededbytes = -1;
8521 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
8522 if (demux->fragmented) {
8523 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8524 /* there may be more to follow, only finish this atom */
8525 demux->todrop = demux->mdatleft;
8526 demux->neededbytes = demux->todrop;
8531 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8532 goto non_ok_unlinked_flow;
8541 /* when buffering movie data, at least show user something is happening */
8542 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
8543 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
8544 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
8545 demux->neededbytes);
8552 non_ok_unlinked_flow:
8554 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8555 gst_flow_get_name (ret));
8560 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8561 ret = GST_FLOW_ERROR;
8566 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8572 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8573 (NULL), ("qtdemuxer invalid state %d", demux->state));
8574 ret = GST_FLOW_ERROR;
8579 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8580 (NULL), ("no 'moov' atom within the first 10 MB"));
8581 ret = GST_FLOW_ERROR;
8587 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
8592 query = gst_query_new_scheduling ();
8594 if (!gst_pad_peer_query (sinkpad, query)) {
8595 gst_query_unref (query);
8599 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8600 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8601 gst_query_unref (query);
8606 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8607 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8611 GST_DEBUG_OBJECT (sinkpad, "activating push");
8612 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8617 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
8618 GstPadMode mode, gboolean active)
8621 GstQTDemux *demux = GST_QTDEMUX (parent);
8624 case GST_PAD_MODE_PUSH:
8625 demux->pullbased = FALSE;
8628 case GST_PAD_MODE_PULL:
8630 demux->pullbased = TRUE;
8631 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
8634 res = gst_pad_stop_task (sinkpad);
8646 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8652 memset (&z, 0, sizeof (z));
8657 if ((ret = inflateInit (&z)) != Z_OK) {
8658 GST_ERROR ("inflateInit() returned %d", ret);
8662 z.next_in = z_buffer;
8663 z.avail_in = z_length;
8665 buffer = (guint8 *) g_malloc (*length);
8666 z.avail_out = *length;
8667 z.next_out = (Bytef *) buffer;
8669 ret = inflate (&z, Z_NO_FLUSH);
8670 if (ret == Z_STREAM_END) {
8672 } else if (ret != Z_OK) {
8673 GST_WARNING ("inflate() returned %d", ret);
8677 if (*length > G_MAXUINT - 4096 || *length > QTDEMUX_MAX_SAMPLE_INDEX_SIZE) {
8678 GST_WARNING ("too big decompressed data");
8684 buffer = (guint8 *) g_realloc (buffer, *length);
8685 z.next_out = (Bytef *) (buffer + z.total_out);
8686 z.avail_out += *length - z.total_out;
8687 } while (z.avail_in > 0);
8689 if (ret != Z_STREAM_END) {
8694 *length = z.total_out;
8701 #endif /* HAVE_ZLIB */
8704 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
8708 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8710 /* counts as header data */
8711 qtdemux->header_size += length;
8713 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
8714 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
8716 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
8723 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
8724 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
8725 if (dcom == NULL || cmvd == NULL)
8726 goto invalid_compression;
8728 dcom_len = QT_UINT32 (dcom->data);
8730 goto invalid_compression;
8732 method = QT_FOURCC ((guint8 *) dcom->data + 8);
8736 guint uncompressed_length;
8737 guint compressed_length;
8741 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
8743 goto invalid_compression;
8745 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8746 compressed_length = cmvd_len - 12;
8747 GST_LOG ("length = %u", uncompressed_length);
8750 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8751 compressed_length, &uncompressed_length);
8754 qtdemux->moov_node_compressed = qtdemux->moov_node;
8755 qtdemux->moov_node = g_node_new (buf);
8757 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8758 uncompressed_length);
8762 #endif /* HAVE_ZLIB */
8764 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8765 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8772 invalid_compression:
8774 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8780 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8783 while (G_UNLIKELY (buf < end)) {
8787 if (G_UNLIKELY (buf + 4 > end)) {
8788 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8791 len = QT_UINT32 (buf);
8792 if (G_UNLIKELY (len == 0)) {
8793 GST_LOG_OBJECT (qtdemux, "empty container");
8796 if (G_UNLIKELY (len < 8)) {
8797 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8800 if (G_UNLIKELY (len > (end - buf))) {
8801 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8802 (gint) (end - buf));
8806 child = g_node_new ((guint8 *) buf);
8807 g_node_append (node, child);
8808 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8809 qtdemux_parse_node (qtdemux, child, buf, len);
8817 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8820 int len = QT_UINT32 (xdxt->data);
8821 guint8 *buf = xdxt->data;
8822 guint8 *end = buf + len;
8825 /* skip size and type */
8833 size = QT_UINT32 (buf);
8834 type = QT_FOURCC (buf + 4);
8836 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8838 if (buf + size > end || size <= 0)
8844 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8845 GST_FOURCC_ARGS (type));
8849 buffer = gst_buffer_new_and_alloc (size);
8850 gst_buffer_fill (buffer, 0, buf, size);
8851 stream->buffers = g_slist_append (stream->buffers, buffer);
8852 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8855 buffer = gst_buffer_new_and_alloc (size);
8856 gst_buffer_fill (buffer, 0, buf, size);
8857 stream->buffers = g_slist_append (stream->buffers, buffer);
8858 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8861 buffer = gst_buffer_new_and_alloc (size);
8862 gst_buffer_fill (buffer, 0, buf, size);
8863 stream->buffers = g_slist_append (stream->buffers, buffer);
8864 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8867 GST_WARNING_OBJECT (qtdemux,
8868 "unknown theora cookie %" GST_FOURCC_FORMAT,
8869 GST_FOURCC_ARGS (type));
8878 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8882 guint32 node_length = 0;
8883 const QtNodeType *type;
8886 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8888 if (G_UNLIKELY (length < 8))
8889 goto not_enough_data;
8891 node_length = QT_UINT32 (buffer);
8892 fourcc = QT_FOURCC (buffer + 4);
8894 /* ignore empty nodes */
8895 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8898 type = qtdemux_type_get (fourcc);
8900 end = buffer + length;
8902 GST_LOG_OBJECT (qtdemux,
8903 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8904 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8906 if (node_length > length)
8907 goto broken_atom_size;
8909 if (type->flags & QT_FLAG_CONTAINER) {
8910 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8915 if (node_length < 20) {
8916 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8919 GST_DEBUG_OBJECT (qtdemux,
8920 "parsing stsd (sample table, sample description) atom");
8921 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8922 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8935 /* also read alac (or whatever) in stead of mp4a in the following,
8936 * since a similar layout is used in other cases as well */
8937 if (fourcc == FOURCC_mp4a)
8939 else if (fourcc == FOURCC_fLaC)
8941 else if (fourcc == FOURCC_opus)
8946 /* There are two things we might encounter here: a true mp4a atom, and
8947 an mp4a entry in an stsd atom. The latter is what we're interested
8948 in, and it looks like an atom, but isn't really one. The true mp4a
8949 atom is short, so we detect it based on length here. */
8950 if (length < min_size) {
8951 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8952 GST_FOURCC_ARGS (fourcc));
8956 /* 'version' here is the sound sample description version. Types 0 and
8957 1 are documented in the QTFF reference, but type 2 is not: it's
8958 described in Apple header files instead (struct SoundDescriptionV2
8960 version = QT_UINT16 (buffer + 16);
8962 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8963 GST_FOURCC_ARGS (fourcc), version);
8965 /* parse any esds descriptors */
8977 GST_WARNING_OBJECT (qtdemux,
8978 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8979 GST_FOURCC_ARGS (fourcc), version);
8984 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
9012 /* codec_data is contained inside these atoms, which all have
9013 * the same format. */
9014 /* video sample description size is 86 bytes without extension.
9015 * node_length have to be bigger than 86 bytes because video sample
9016 * description can include extensions such as esds, fiel, glbl, etc. */
9017 if (node_length < 86) {
9018 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
9019 " sample description length too short (%u < 86)",
9020 GST_FOURCC_ARGS (fourcc), node_length);
9024 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
9025 GST_FOURCC_ARGS (fourcc));
9027 /* version (2 bytes) : this is set to 0, unless a compressor has changed
9029 * revision level (2 bytes) : must be set to 0. */
9030 version = QT_UINT32 (buffer + 16);
9031 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
9033 /* compressor name : PASCAL string and informative purposes
9034 * first byte : the number of bytes to be displayed.
9035 * it has to be less than 32 because it is reserved
9036 * space of 32 bytes total including itself. */
9037 str_len = QT_UINT8 (buffer + 50);
9039 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
9040 (char *) buffer + 51);
9042 GST_WARNING_OBJECT (qtdemux,
9043 "compressorname length too big (%u > 31)", str_len);
9045 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
9047 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
9052 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
9054 /* You are reading this correctly. QTFF specifies that the
9055 * metadata atom is a short atom, whereas ISO BMFF specifies
9056 * it's a full atom. But since so many people are doing things
9057 * differently, we actually peek into the atom to see which
9060 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
9061 GST_FOURCC_ARGS (fourcc));
9064 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
9065 /* Variant 1: What QTFF specifies. 'meta' is a short header which
9066 * starts with a 'hdlr' atom */
9067 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
9068 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
9069 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
9070 * with version/flags both set to zero */
9071 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
9073 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
9078 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
9079 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
9080 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
9089 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
9090 GST_FOURCC_ARGS (fourcc));
9094 version = QT_UINT32 (buffer + 12);
9095 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
9102 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
9107 if (length < offset) {
9108 GST_WARNING_OBJECT (qtdemux,
9109 "skipping too small %" GST_FOURCC_FORMAT " box",
9110 GST_FOURCC_ARGS (fourcc));
9113 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
9119 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
9124 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
9129 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
9132 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9135 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
9138 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
9140 if (!strcmp (type->name, "unknown"))
9141 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
9145 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
9146 GST_FOURCC_ARGS (fourcc));
9152 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9153 (_("This file is corrupt and cannot be played.")),
9154 ("Not enough data for an atom header, got only %u bytes", length));
9159 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9160 (_("This file is corrupt and cannot be played.")),
9161 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
9162 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
9169 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
9171 /* FIXME: This can only reliably work if demuxers have a
9172 * separate streaming thread per srcpad. This should be
9173 * done in a demuxer base class, which integrates parts
9176 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
9181 query = gst_query_new_allocation (stream->caps, FALSE);
9183 if (!gst_pad_peer_query (stream->pad, query)) {
9184 /* not a problem, just debug a little */
9185 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
9188 if (stream->allocator)
9189 gst_object_unref (stream->allocator);
9191 if (gst_query_get_n_allocation_params (query) > 0) {
9192 /* try the allocator */
9193 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
9195 stream->use_allocator = TRUE;
9197 stream->allocator = NULL;
9198 gst_allocation_params_init (&stream->params);
9199 stream->use_allocator = FALSE;
9201 gst_query_unref (query);
9206 pad_query (const GValue * item, GValue * value, gpointer user_data)
9208 GstPad *pad = g_value_get_object (item);
9209 GstQuery *query = user_data;
9212 res = gst_pad_peer_query (pad, query);
9215 g_value_set_boolean (value, TRUE);
9219 GST_INFO_OBJECT (pad, "pad peer query failed");
9224 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
9225 GstPadDirection direction)
9228 GstIteratorFoldFunction func = pad_query;
9229 GValue res = { 0, };
9231 g_value_init (&res, G_TYPE_BOOLEAN);
9232 g_value_set_boolean (&res, FALSE);
9235 if (direction == GST_PAD_SRC)
9236 it = gst_element_iterate_src_pads (element);
9238 it = gst_element_iterate_sink_pads (element);
9240 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
9241 gst_iterator_resync (it);
9243 gst_iterator_free (it);
9245 return g_value_get_boolean (&res);
9249 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
9250 QtDemuxStream * stream)
9254 GstElement *element = GST_ELEMENT (qtdemux);
9256 gchar **filtered_sys_ids;
9257 GValue event_list = G_VALUE_INIT;
9260 /* 1. Check if we already have the context. */
9261 if (qtdemux->preferred_protection_system_id != NULL) {
9262 GST_LOG_OBJECT (element,
9263 "already have the protection context, no need to request it again");
9267 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9268 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
9269 (const gchar **) qtdemux->protection_system_ids->pdata);
9271 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9272 qtdemux->protection_system_ids->len - 1);
9273 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
9274 "decryptors for %u of them, running context request",
9275 qtdemux->protection_system_ids->len,
9276 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
9279 if (stream->protection_scheme_event_queue.length) {
9280 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
9281 stream->protection_scheme_event_queue.length);
9282 walk = stream->protection_scheme_event_queue.tail;
9284 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
9285 qtdemux->protection_event_queue.length);
9286 walk = qtdemux->protection_event_queue.tail;
9289 g_value_init (&event_list, GST_TYPE_LIST);
9290 for (; walk; walk = g_list_previous (walk)) {
9291 GValue event_value = G_VALUE_INIT;
9292 g_value_init (&event_value, GST_TYPE_EVENT);
9293 g_value_set_boxed (&event_value, walk->data);
9294 gst_value_list_append_and_take_value (&event_list, &event_value);
9297 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
9298 * check if downstream already has a context of the specific type
9299 * 2b) Query upstream as above.
9301 query = gst_query_new_context ("drm-preferred-decryption-system-id");
9302 st = gst_query_writable_structure (query);
9303 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9304 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9306 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9307 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
9308 gst_query_parse_context (query, &ctxt);
9309 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
9310 gst_element_set_context (element, ctxt);
9311 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
9312 gst_query_parse_context (query, &ctxt);
9313 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
9314 gst_element_set_context (element, ctxt);
9316 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
9317 * the required context type and afterwards check if a
9318 * usable context was set now as in 1). The message could
9319 * be handled by the parent bins of the element and the
9324 GST_INFO_OBJECT (element, "posting need context message");
9325 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
9326 "drm-preferred-decryption-system-id");
9327 st = (GstStructure *) gst_message_get_structure (msg);
9328 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9329 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9332 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9333 gst_element_post_message (element, msg);
9336 g_strfreev (filtered_sys_ids);
9337 g_value_unset (&event_list);
9338 gst_query_unref (query);
9342 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
9343 QtDemuxStream * stream)
9346 const gchar *selected_system = NULL;
9348 g_return_val_if_fail (qtdemux != NULL, FALSE);
9349 g_return_val_if_fail (stream != NULL, FALSE);
9350 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
9353 if (stream->protection_scheme_type == FOURCC_aavd) {
9354 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9355 if (!gst_structure_has_name (s, "application/x-aavd")) {
9356 gst_structure_set (s,
9357 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
9359 gst_structure_set_name (s, "application/x-aavd");
9364 if (stream->protection_scheme_type != FOURCC_cenc
9365 && stream->protection_scheme_type != FOURCC_cbcs) {
9366 GST_ERROR_OBJECT (qtdemux,
9367 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
9368 GST_FOURCC_ARGS (stream->protection_scheme_type));
9372 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9373 if (!gst_structure_has_name (s, "application/x-cenc")) {
9374 gst_structure_set (s,
9375 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
9376 gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
9377 (stream->protection_scheme_type == FOURCC_cbcs) ? "cbcs" : "cenc",
9379 gst_structure_set_name (s, "application/x-cenc");
9382 if (qtdemux->protection_system_ids == NULL) {
9383 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
9384 "cenc protection system information has been found, not setting a "
9385 "protection system UUID");
9389 gst_qtdemux_request_protection_context (qtdemux, stream);
9390 if (qtdemux->preferred_protection_system_id != NULL) {
9391 const gchar *preferred_system_array[] =
9392 { qtdemux->preferred_protection_system_id, NULL };
9394 selected_system = gst_protection_select_system (preferred_system_array);
9396 if (selected_system) {
9397 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
9398 qtdemux->preferred_protection_system_id);
9400 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
9401 "because there is no available decryptor",
9402 qtdemux->preferred_protection_system_id);
9406 if (!selected_system) {
9407 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9408 selected_system = gst_protection_select_system ((const gchar **)
9409 qtdemux->protection_system_ids->pdata);
9410 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9411 qtdemux->protection_system_ids->len - 1);
9414 if (!selected_system) {
9415 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
9416 "suitable decryptor element has been found");
9420 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
9423 gst_structure_set (s,
9424 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
9431 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
9433 /* fps is calculated base on the duration of the average framerate since
9434 * qt does not have a fixed framerate. */
9435 gboolean fps_available = TRUE;
9436 guint32 first_duration = 0;
9438 if (stream->n_samples > 0)
9439 first_duration = stream->samples[0].duration;
9441 if ((stream->n_samples == 1 && first_duration == 0)
9442 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
9444 CUR_STREAM (stream)->fps_n = 0;
9445 CUR_STREAM (stream)->fps_d = 1;
9447 if (stream->duration == 0 || stream->n_samples < 2) {
9448 CUR_STREAM (stream)->fps_n = stream->timescale;
9449 CUR_STREAM (stream)->fps_d = 1;
9450 fps_available = FALSE;
9452 GstClockTime avg_duration;
9456 /* duration and n_samples can be updated for fragmented format
9457 * so, framerate of fragmented format is calculated using data in a moof */
9458 if (qtdemux->fragmented && stream->n_samples_moof > 0
9459 && stream->duration_moof > 0) {
9460 n_samples = stream->n_samples_moof;
9461 duration = stream->duration_moof;
9463 n_samples = stream->n_samples;
9464 duration = stream->duration;
9467 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
9468 /* stream->duration is guint64, timescale, n_samples are guint32 */
9470 gst_util_uint64_scale_round (duration -
9471 first_duration, GST_SECOND,
9472 (guint64) (stream->timescale) * (n_samples - 1));
9474 GST_LOG_OBJECT (qtdemux,
9475 "Calculating avg sample duration based on stream (or moof) duration %"
9477 " minus first sample %u, leaving %d samples gives %"
9478 GST_TIME_FORMAT, duration, first_duration,
9479 n_samples - 1, GST_TIME_ARGS (avg_duration));
9481 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9482 gst_video_guess_framerate (avg_duration,
9483 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9484 if (CUR_STREAM (stream)->fps_d == 0)
9485 fps_available = FALSE;
9488 gst_video_guess_framerate (avg_duration,
9489 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9492 GST_DEBUG_OBJECT (qtdemux,
9493 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
9494 stream->timescale, CUR_STREAM (stream)->fps_n,
9495 CUR_STREAM (stream)->fps_d);
9499 return fps_available;
9503 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
9505 if (stream->subtype == FOURCC_vide) {
9506 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9508 if (CUR_STREAM (stream)->caps) {
9509 CUR_STREAM (stream)->caps =
9510 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9512 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
9513 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9514 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
9515 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
9517 /* set framerate if calculated framerate is reliable */
9518 if (fps_available) {
9519 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9520 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9521 CUR_STREAM (stream)->fps_d, NULL);
9524 /* calculate pixel-aspect-ratio using display width and height */
9525 GST_DEBUG_OBJECT (qtdemux,
9526 "video size %dx%d, target display size %dx%d",
9527 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
9528 stream->display_width, stream->display_height);
9529 /* qt file might have pasp atom */
9530 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9531 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
9532 CUR_STREAM (stream)->par_h);
9533 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9534 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9535 CUR_STREAM (stream)->par_h, NULL);
9536 } else if (stream->display_width > 0 && stream->display_height > 0
9537 && CUR_STREAM (stream)->width > 0
9538 && CUR_STREAM (stream)->height > 0) {
9541 /* calculate the pixel aspect ratio using the display and pixel w/h */
9542 n = stream->display_width * CUR_STREAM (stream)->height;
9543 d = stream->display_height * CUR_STREAM (stream)->width;
9546 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
9547 CUR_STREAM (stream)->par_w = n;
9548 CUR_STREAM (stream)->par_h = d;
9549 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9550 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9551 CUR_STREAM (stream)->par_h, NULL);
9554 if (CUR_STREAM (stream)->interlace_mode > 0) {
9555 if (CUR_STREAM (stream)->interlace_mode == 1) {
9556 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9557 G_TYPE_STRING, "progressive", NULL);
9558 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
9559 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9560 G_TYPE_STRING, "interleaved", NULL);
9561 if (CUR_STREAM (stream)->field_order == 9) {
9562 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9563 G_TYPE_STRING, "top-field-first", NULL);
9564 } else if (CUR_STREAM (stream)->field_order == 14) {
9565 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9566 G_TYPE_STRING, "bottom-field-first", NULL);
9571 /* Create incomplete colorimetry here if needed */
9572 if (CUR_STREAM (stream)->colorimetry.range ||
9573 CUR_STREAM (stream)->colorimetry.matrix ||
9574 CUR_STREAM (stream)->colorimetry.transfer
9575 || CUR_STREAM (stream)->colorimetry.primaries) {
9576 gchar *colorimetry =
9577 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
9578 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
9579 G_TYPE_STRING, colorimetry, NULL);
9580 g_free (colorimetry);
9583 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9584 guint par_w = 1, par_h = 1;
9586 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9587 par_w = CUR_STREAM (stream)->par_w;
9588 par_h = CUR_STREAM (stream)->par_h;
9591 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
9592 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
9594 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9597 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9598 "multiview-mode", G_TYPE_STRING,
9599 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9600 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9601 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9606 else if (stream->subtype == FOURCC_soun) {
9607 if (CUR_STREAM (stream)->caps) {
9608 CUR_STREAM (stream)->caps =
9609 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9610 if (CUR_STREAM (stream)->rate > 0)
9611 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9612 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
9613 if (CUR_STREAM (stream)->n_channels > 0)
9614 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9615 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
9616 if (CUR_STREAM (stream)->n_channels > 2) {
9617 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9618 * correctly; this is just the minimum we can do - assume
9619 * we don't actually have any channel positions. */
9620 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9621 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9626 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
9627 const GstStructure *s;
9628 QtDemuxStream *fps_stream = NULL;
9629 gboolean fps_available = FALSE;
9631 /* CEA608 closed caption tracks are a bit special in that each sample
9632 * can contain CCs for multiple frames, and CCs can be omitted and have to
9633 * be inferred from the duration of the sample then.
9635 * As such we take the framerate from the (first) video track here for
9636 * CEA608 as there must be one CC byte pair for every video frame
9637 * according to the spec.
9639 * For CEA708 all is fine and there is one sample per frame.
9642 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9643 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9646 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
9647 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
9649 if (tmp->subtype == FOURCC_vide) {
9656 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
9657 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
9658 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
9661 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9662 fps_stream = stream;
9665 CUR_STREAM (stream)->caps =
9666 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9668 /* set framerate if calculated framerate is reliable */
9669 if (fps_available) {
9670 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9671 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9672 CUR_STREAM (stream)->fps_d, NULL);
9677 gboolean forward_collection = FALSE;
9678 GstCaps *prev_caps = NULL;
9680 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
9681 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
9682 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
9683 gst_pad_set_active (stream->pad, TRUE);
9685 gst_pad_use_fixed_caps (stream->pad);
9687 if (stream->protected) {
9688 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
9689 GST_ERROR_OBJECT (qtdemux,
9690 "Failed to configure protected stream caps.");
9695 if (stream->new_stream) {
9697 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9700 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9703 gst_event_parse_stream_flags (event, &stream_flags);
9704 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9705 qtdemux->have_group_id = TRUE;
9707 qtdemux->have_group_id = FALSE;
9708 gst_event_unref (event);
9709 } else if (!qtdemux->have_group_id) {
9710 qtdemux->have_group_id = TRUE;
9711 qtdemux->group_id = gst_util_group_id_next ();
9714 stream->new_stream = FALSE;
9715 event = gst_event_new_stream_start (stream->stream_id);
9716 if (qtdemux->have_group_id)
9717 gst_event_set_group_id (event, qtdemux->group_id);
9718 if (stream->disabled)
9719 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9720 if (CUR_STREAM (stream)->sparse) {
9721 stream_flags |= GST_STREAM_FLAG_SPARSE;
9723 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9725 gst_event_set_stream_flags (event, stream_flags);
9726 gst_pad_push_event (stream->pad, event);
9728 forward_collection = TRUE;
9731 prev_caps = gst_pad_get_current_caps (stream->pad);
9733 if (CUR_STREAM (stream)->caps) {
9735 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9736 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9737 CUR_STREAM (stream)->caps);
9738 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9740 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9743 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9747 gst_caps_unref (prev_caps);
9748 stream->new_caps = FALSE;
9750 if (forward_collection) {
9751 /* Forward upstream collection and selection if any */
9752 GstEvent *upstream_event = gst_pad_get_sticky_event (qtdemux->sinkpad,
9753 GST_EVENT_STREAM_COLLECTION, 0);
9755 gst_pad_push_event (stream->pad, upstream_event);
9762 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9763 QtDemuxStream * stream)
9765 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9768 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9769 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9770 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9771 stream->stsd_entries_length)) {
9772 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9773 (_("This file is invalid and cannot be played.")),
9774 ("New sample description id is out of bounds (%d >= %d)",
9775 stream->stsd_sample_description_id, stream->stsd_entries_length));
9777 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9778 stream->new_caps = TRUE;
9783 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9784 QtDemuxStream * stream, GstTagList * list)
9786 gboolean ret = TRUE;
9788 if (stream->subtype == FOURCC_vide) {
9789 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9792 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9795 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9796 gst_object_unref (stream->pad);
9802 qtdemux->n_video_streams++;
9803 } else if (stream->subtype == FOURCC_soun) {
9804 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9807 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9809 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9810 gst_object_unref (stream->pad);
9815 qtdemux->n_audio_streams++;
9816 } else if (stream->subtype == FOURCC_strm) {
9817 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9818 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9819 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9820 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
9821 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9824 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9826 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9827 gst_object_unref (stream->pad);
9832 qtdemux->n_sub_streams++;
9833 } else if (stream->subtype == FOURCC_meta) {
9834 gchar *name = g_strdup_printf ("meta_%u", qtdemux->n_meta_streams);
9837 gst_pad_new_from_static_template (&gst_qtdemux_metasrc_template, name);
9839 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9840 gst_object_unref (stream->pad);
9845 qtdemux->n_meta_streams++;
9846 } else if (CUR_STREAM (stream)->caps) {
9847 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9850 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9852 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9853 gst_object_unref (stream->pad);
9858 qtdemux->n_video_streams++;
9860 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9867 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9868 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9869 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9870 GST_OBJECT_LOCK (qtdemux);
9871 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9872 GST_OBJECT_UNLOCK (qtdemux);
9874 if (stream->stream_tags)
9875 gst_tag_list_unref (stream->stream_tags);
9876 stream->stream_tags = list;
9878 /* global tags go on each pad anyway */
9879 stream->send_global_tags = TRUE;
9880 /* send upstream GST_EVENT_PROTECTION events that were received before
9881 this source pad was created */
9882 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9883 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9887 gst_tag_list_unref (list);
9891 /* find next atom with @fourcc starting at @offset */
9892 static GstFlowReturn
9893 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9894 guint64 * length, guint32 fourcc)
9900 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9901 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9907 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9908 if (G_UNLIKELY (ret != GST_FLOW_OK))
9910 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9913 gst_buffer_unref (buf);
9916 gst_buffer_map (buf, &map, GST_MAP_READ);
9917 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9918 gst_buffer_unmap (buf, &map);
9919 gst_buffer_unref (buf);
9921 if (G_UNLIKELY (*length == 0)) {
9922 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9923 ret = GST_FLOW_ERROR;
9927 if (lfourcc == fourcc) {
9928 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9929 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9932 GST_LOG_OBJECT (qtdemux,
9933 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9934 GST_FOURCC_ARGS (lfourcc), *offset);
9935 if (*offset == G_MAXUINT64)
9945 /* might simply have had last one */
9946 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9951 /* should only do something in pull mode */
9952 /* call with OBJECT lock */
9953 static GstFlowReturn
9954 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9956 guint64 length, offset;
9957 GstBuffer *buf = NULL;
9958 GstFlowReturn ret = GST_FLOW_OK;
9959 GstFlowReturn res = GST_FLOW_OK;
9962 offset = qtdemux->moof_offset;
9963 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9966 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9967 return GST_FLOW_EOS;
9970 /* best not do pull etc with lock held */
9971 GST_OBJECT_UNLOCK (qtdemux);
9973 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9974 if (ret != GST_FLOW_OK)
9977 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9978 if (G_UNLIKELY (ret != GST_FLOW_OK))
9980 gst_buffer_map (buf, &map, GST_MAP_READ);
9981 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9982 gst_buffer_unmap (buf, &map);
9983 gst_buffer_unref (buf);
9988 gst_buffer_unmap (buf, &map);
9989 gst_buffer_unref (buf);
9993 /* look for next moof */
9994 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9995 if (G_UNLIKELY (ret != GST_FLOW_OK))
9999 GST_OBJECT_LOCK (qtdemux);
10001 qtdemux->moof_offset = offset;
10007 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
10009 res = GST_FLOW_ERROR;
10014 /* maybe upstream temporarily flushing */
10015 if (ret != GST_FLOW_FLUSHING) {
10016 GST_DEBUG_OBJECT (qtdemux, "no next moof");
10019 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
10020 /* resume at current position next time */
10028 qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
10031 guint32 num_chunks;
10032 gint32 stts_duration;
10033 GstByteWriter stsc, stts, stsz;
10035 /* Each sample has a different size, which we don't support for merging */
10036 if (stream->sample_size == 0) {
10037 GST_DEBUG_OBJECT (qtdemux,
10038 "Not all samples have the same size, not merging");
10042 /* The stream has a ctts table, we don't support that */
10043 if (stream->ctts_present) {
10044 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
10048 /* If there's a sync sample table also ignore this stream */
10049 if (stream->stps_present || stream->stss_present) {
10050 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
10054 /* If chunks are considered samples already ignore this stream */
10055 if (stream->chunks_are_samples) {
10056 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
10060 /* Require that all samples have the same duration */
10061 if (stream->n_sample_times > 1) {
10062 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
10066 /* Parse the stts to get the sample duration and number of samples */
10067 gst_byte_reader_skip_unchecked (&stream->stts, 4);
10068 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10070 /* Parse the number of chunks from the stco manually because the
10071 * reader is already behind that */
10072 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
10074 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
10077 /* Now parse stsc, convert chunks into single samples and generate a
10078 * new stsc, stts and stsz from this information */
10079 gst_byte_writer_init (&stsc);
10080 gst_byte_writer_init (&stts);
10081 gst_byte_writer_init (&stsz);
10083 /* Note: we skip fourccs, size, version, flags and other fields of the new
10084 * atoms as the byte readers with them are already behind that position
10085 * anyway and only update the values of those inside the stream directly.
10087 stream->n_sample_times = 0;
10088 stream->n_samples = 0;
10089 for (i = 0; i < stream->n_samples_per_chunk; i++) {
10091 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
10093 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10094 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10095 sample_description_id =
10096 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10098 if (i == stream->n_samples_per_chunk - 1) {
10099 /* +1 because first_chunk is 1-based */
10100 last_chunk = num_chunks + 1;
10102 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10105 GST_DEBUG_OBJECT (qtdemux,
10106 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
10107 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
10109 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
10110 /* One sample in this chunk */
10111 gst_byte_writer_put_uint32_be (&stsc, 1);
10112 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
10114 /* For each chunk write a stts and stsz entry now */
10115 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
10116 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
10117 for (j = first_chunk; j < last_chunk; j++) {
10118 gst_byte_writer_put_uint32_be (&stsz,
10119 stream->sample_size * samples_per_chunk);
10122 stream->n_sample_times += 1;
10123 stream->n_samples += last_chunk - first_chunk;
10126 g_assert_cmpint (stream->n_samples, ==, num_chunks);
10128 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
10129 stream->n_samples, stream->n_sample_times);
10131 /* We don't have a fixed sample size anymore */
10132 stream->sample_size = 0;
10134 /* Free old data for the atoms */
10135 g_free ((gpointer) stream->stsz.data);
10136 stream->stsz.data = NULL;
10137 g_free ((gpointer) stream->stsc.data);
10138 stream->stsc.data = NULL;
10139 g_free ((gpointer) stream->stts.data);
10140 stream->stts.data = NULL;
10142 /* Store new data and replace byte readers */
10143 stream->stsz.size = gst_byte_writer_get_size (&stsz);
10144 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
10145 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
10146 stream->stts.size = gst_byte_writer_get_size (&stts);
10147 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
10148 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
10149 stream->stsc.size = gst_byte_writer_get_size (&stsc);
10150 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
10151 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
10154 /* initialise bytereaders for stbl sub-atoms */
10156 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
10158 stream->stbl_index = -1; /* no samples have yet been parsed */
10159 stream->sample_index = -1;
10161 /* time-to-sample atom */
10162 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
10165 /* copy atom data into a new buffer for later use */
10166 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
10168 /* skip version + flags */
10169 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
10170 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
10172 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
10174 /* make sure there's enough data */
10175 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
10176 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
10177 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
10178 stream->n_sample_times);
10179 if (!stream->n_sample_times)
10183 /* sync sample atom */
10184 stream->stps_present = FALSE;
10185 if ((stream->stss_present =
10186 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
10187 &stream->stss) ? TRUE : FALSE) == TRUE) {
10188 /* copy atom data into a new buffer for later use */
10189 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
10191 /* skip version + flags */
10192 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
10193 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
10196 if (stream->n_sample_syncs) {
10197 /* make sure there's enough data */
10198 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
10202 /* partial sync sample atom */
10203 if ((stream->stps_present =
10204 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
10205 &stream->stps) ? TRUE : FALSE) == TRUE) {
10206 /* copy atom data into a new buffer for later use */
10207 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
10209 /* skip version + flags */
10210 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
10211 !gst_byte_reader_get_uint32_be (&stream->stps,
10212 &stream->n_sample_partial_syncs))
10215 /* if there are no entries, the stss table contains the real
10217 if (stream->n_sample_partial_syncs) {
10218 /* make sure there's enough data */
10219 if (!qt_atom_parser_has_chunks (&stream->stps,
10220 stream->n_sample_partial_syncs, 4))
10227 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
10230 /* copy atom data into a new buffer for later use */
10231 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
10233 /* skip version + flags */
10234 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
10235 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
10238 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
10241 if (!stream->n_samples)
10244 /* sample-to-chunk atom */
10245 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
10248 /* copy atom data into a new buffer for later use */
10249 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
10251 /* skip version + flags */
10252 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
10253 !gst_byte_reader_get_uint32_be (&stream->stsc,
10254 &stream->n_samples_per_chunk))
10257 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
10258 stream->n_samples_per_chunk);
10260 /* make sure there's enough data */
10261 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
10267 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
10268 stream->co_size = sizeof (guint32);
10269 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
10271 stream->co_size = sizeof (guint64);
10275 /* copy atom data into a new buffer for later use */
10276 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
10278 /* skip version + flags */
10279 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
10282 /* chunks_are_samples == TRUE means treat chunks as samples */
10283 stream->chunks_are_samples = stream->sample_size
10284 && !CUR_STREAM (stream)->sampled;
10285 if (stream->chunks_are_samples) {
10286 /* treat chunks as samples */
10287 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
10290 /* skip number of entries */
10291 if (!gst_byte_reader_skip (&stream->stco, 4))
10294 /* make sure there are enough data in the stsz atom */
10295 if (!stream->sample_size) {
10296 /* different sizes for each sample */
10297 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
10302 /* composition time-to-sample */
10303 if ((stream->ctts_present =
10304 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
10305 &stream->ctts) ? TRUE : FALSE) == TRUE) {
10306 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
10307 guint8 ctts_version;
10308 gboolean checked_ctts = FALSE;
10310 /* copy atom data into a new buffer for later use */
10311 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
10313 /* version 1 has signed offsets */
10314 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
10318 if (!gst_byte_reader_skip (&stream->ctts, 3)
10319 || !gst_byte_reader_get_uint32_be (&stream->ctts,
10320 &stream->n_composition_times))
10323 /* make sure there's enough data */
10324 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
10328 /* This is optional, if missing we iterate the ctts */
10329 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
10330 guint8 cslg_version;
10332 /* cslg version 1 has 64 bit fields */
10333 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
10337 if (!gst_byte_reader_skip (&cslg, 3))
10340 if (cslg_version == 0) {
10341 gint32 composition_to_dts_shift;
10343 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
10346 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10348 gint64 composition_to_dts_shift;
10350 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
10353 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10356 gint32 cslg_least = 0;
10357 guint num_entries, pos;
10360 pos = gst_byte_reader_get_pos (&stream->ctts);
10361 num_entries = stream->n_composition_times;
10363 checked_ctts = TRUE;
10365 stream->cslg_shift = 0;
10367 for (i = 0; i < num_entries; i++) {
10370 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10371 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10372 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10373 * slightly inaccurate PTS could be more usable than corrupted one */
10374 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10375 && ABS (offset) / 2 > stream->duration)) {
10376 GST_WARNING_OBJECT (qtdemux,
10377 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10378 " larger than duration %" G_GUINT64_FORMAT, offset,
10381 stream->cslg_shift = 0;
10382 stream->ctts_present = FALSE;
10386 /* Don't consider "no decode samples" with offset G_MININT32
10387 * for the DTS/PTS shift */
10388 if (offset != G_MININT32 && offset < cslg_least)
10389 cslg_least = offset;
10392 if (cslg_least < 0)
10393 stream->cslg_shift = -cslg_least;
10395 stream->cslg_shift = 0;
10397 /* reset the reader so we can generate sample table */
10398 gst_byte_reader_set_pos (&stream->ctts, pos);
10401 /* Check if ctts values are looking reasonable if that didn't happen above */
10402 if (!checked_ctts) {
10403 guint num_entries, pos;
10406 pos = gst_byte_reader_get_pos (&stream->ctts);
10407 num_entries = stream->n_composition_times;
10409 for (i = 0; i < num_entries; i++) {
10412 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10413 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10414 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10415 * slightly inaccurate PTS could be more usable than corrupted one */
10416 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10417 && ABS (offset) / 2 > stream->duration)) {
10418 GST_WARNING_OBJECT (qtdemux,
10419 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10420 " larger than duration %" G_GUINT64_FORMAT, offset,
10423 stream->cslg_shift = 0;
10424 stream->ctts_present = FALSE;
10429 /* reset the reader so we can generate sample table */
10430 gst_byte_reader_set_pos (&stream->ctts, pos);
10433 /* Ensure the cslg_shift value is consistent so we can use it
10434 * unconditionally to produce TS and Segment */
10435 stream->cslg_shift = 0;
10438 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
10439 stream->cslg_shift);
10441 /* For raw audio streams especially we might want to merge the samples
10442 * to not output one audio sample per buffer. We're doing this here
10443 * before allocating the sample tables so that from this point onwards
10444 * the number of container samples are static */
10445 if (stream->min_buffer_size > 0) {
10446 qtdemux_merge_sample_table (qtdemux, stream);
10450 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
10451 stream->n_samples, (guint) sizeof (QtDemuxSample),
10452 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
10454 if (stream->n_samples >=
10455 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
10456 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
10457 "be larger than %uMB (broken file?)", stream->n_samples,
10458 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
10462 g_assert (stream->samples == NULL);
10463 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
10464 if (!stream->samples) {
10465 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
10466 stream->n_samples);
10474 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10475 (_("This file is corrupt and cannot be played.")), (NULL));
10480 gst_qtdemux_stbl_free (stream);
10481 if (!qtdemux->fragmented) {
10482 /* not quite good */
10483 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
10486 /* may pick up samples elsewhere */
10492 /* collect samples from the next sample to be parsed up to sample @n for @stream
10493 * by reading the info from @stbl
10495 * This code can be executed from both the streaming thread and the seeking
10496 * thread so it takes the object lock to protect itself
10499 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
10502 QtDemuxSample *samples, *first, *cur, *last;
10503 guint32 n_samples_per_chunk;
10506 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
10507 GST_FOURCC_FORMAT ", pad %s",
10508 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
10509 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
10511 n_samples = stream->n_samples;
10513 if (n >= n_samples)
10514 goto out_of_samples;
10516 GST_OBJECT_LOCK (qtdemux);
10517 if (n <= stream->stbl_index)
10518 goto already_parsed;
10520 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
10522 if (!stream->stsz.data) {
10523 /* so we already parsed and passed all the moov samples;
10524 * onto fragmented ones */
10525 g_assert (qtdemux->fragmented);
10529 /* pointer to the sample table */
10530 samples = stream->samples;
10532 /* starts from -1, moves to the next sample index to parse */
10533 stream->stbl_index++;
10535 /* keep track of the first and last sample to fill */
10536 first = &samples[stream->stbl_index];
10537 last = &samples[n];
10539 if (!stream->chunks_are_samples) {
10540 /* set the sample sizes */
10541 if (stream->sample_size == 0) {
10542 /* different sizes for each sample */
10543 for (cur = first; cur <= last; cur++) {
10544 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
10545 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
10546 (guint) (cur - samples), cur->size);
10549 /* samples have the same size */
10550 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
10551 for (cur = first; cur <= last; cur++)
10552 cur->size = stream->sample_size;
10556 n_samples_per_chunk = stream->n_samples_per_chunk;
10559 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
10560 guint32 last_chunk;
10562 if (stream->stsc_chunk_index >= stream->last_chunk
10563 || stream->stsc_chunk_index < stream->first_chunk) {
10564 stream->first_chunk =
10565 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10566 stream->samples_per_chunk =
10567 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10568 /* starts from 1 */
10569 stream->stsd_sample_description_id =
10570 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
10572 /* chunk numbers are counted from 1 it seems */
10573 if (G_UNLIKELY (stream->first_chunk == 0))
10576 --stream->first_chunk;
10578 /* the last chunk of each entry is calculated by taking the first chunk
10579 * of the next entry; except if there is no next, where we fake it with
10581 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
10582 stream->last_chunk = G_MAXUINT32;
10584 stream->last_chunk =
10585 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10586 if (G_UNLIKELY (stream->last_chunk == 0))
10589 --stream->last_chunk;
10592 GST_LOG_OBJECT (qtdemux,
10593 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
10594 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
10595 stream->samples_per_chunk, stream->stsd_sample_description_id);
10597 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
10600 if (stream->last_chunk != G_MAXUINT32) {
10601 if (!qt_atom_parser_peek_sub (&stream->stco,
10602 stream->first_chunk * stream->co_size,
10603 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10604 &stream->co_chunk))
10608 stream->co_chunk = stream->stco;
10609 if (!gst_byte_reader_skip (&stream->co_chunk,
10610 stream->first_chunk * stream->co_size))
10614 stream->stsc_chunk_index = stream->first_chunk;
10617 last_chunk = stream->last_chunk;
10619 if (stream->chunks_are_samples) {
10620 cur = &samples[stream->stsc_chunk_index];
10622 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10625 stream->stsc_chunk_index = j;
10630 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
10633 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10634 "%" G_GUINT64_FORMAT, j, cur->offset);
10636 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
10637 CUR_STREAM (stream)->bytes_per_frame > 0) {
10639 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
10640 CUR_STREAM (stream)->samples_per_frame *
10641 CUR_STREAM (stream)->bytes_per_frame;
10643 cur->size = stream->samples_per_chunk;
10646 GST_DEBUG_OBJECT (qtdemux,
10647 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
10648 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
10649 stream->stco_sample_index)), cur->size);
10651 cur->timestamp = stream->stco_sample_index;
10652 cur->duration = stream->samples_per_chunk;
10653 cur->keyframe = TRUE;
10656 stream->stco_sample_index += stream->samples_per_chunk;
10658 stream->stsc_chunk_index = j;
10660 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10661 guint32 samples_per_chunk;
10662 guint64 chunk_offset;
10664 if (!stream->stsc_sample_index
10665 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
10666 &stream->chunk_offset))
10669 samples_per_chunk = stream->samples_per_chunk;
10670 chunk_offset = stream->chunk_offset;
10672 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10673 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10674 G_GUINT64_FORMAT " and size %d",
10675 (guint) (cur - samples), chunk_offset, cur->size);
10677 cur->offset = chunk_offset;
10678 chunk_offset += cur->size;
10681 if (G_UNLIKELY (cur > last)) {
10683 stream->stsc_sample_index = k + 1;
10684 stream->chunk_offset = chunk_offset;
10685 stream->stsc_chunk_index = j;
10689 stream->stsc_sample_index = 0;
10691 stream->stsc_chunk_index = j;
10693 stream->stsc_index++;
10696 if (stream->chunks_are_samples)
10700 guint32 n_sample_times;
10702 n_sample_times = stream->n_sample_times;
10705 for (i = stream->stts_index; i < n_sample_times; i++) {
10706 guint32 stts_samples;
10707 gint32 stts_duration;
10710 if (stream->stts_sample_index >= stream->stts_samples
10711 || !stream->stts_sample_index) {
10713 stream->stts_samples =
10714 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10715 stream->stts_duration =
10716 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10718 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10719 i, stream->stts_samples, stream->stts_duration);
10721 stream->stts_sample_index = 0;
10724 stts_samples = stream->stts_samples;
10725 stts_duration = stream->stts_duration;
10726 stts_time = stream->stts_time;
10728 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10729 GST_DEBUG_OBJECT (qtdemux,
10730 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10731 (guint) (cur - samples), j,
10732 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10734 cur->timestamp = stts_time;
10735 cur->duration = stts_duration;
10737 /* avoid 32-bit wrap-around,
10738 * but still mind possible 'negative' duration */
10739 stts_time += (gint64) stts_duration;
10742 if (G_UNLIKELY (cur > last)) {
10744 stream->stts_time = stts_time;
10745 stream->stts_sample_index = j + 1;
10746 if (stream->stts_sample_index >= stream->stts_samples)
10747 stream->stts_index++;
10751 stream->stts_sample_index = 0;
10752 stream->stts_time = stts_time;
10753 stream->stts_index++;
10755 /* fill up empty timestamps with the last timestamp, this can happen when
10756 * the last samples do not decode and so we don't have timestamps for them.
10757 * We however look at the last timestamp to estimate the track length so we
10758 * need something in here. */
10759 for (; cur < last; cur++) {
10760 GST_DEBUG_OBJECT (qtdemux,
10761 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10762 (guint) (cur - samples),
10763 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10764 cur->timestamp = stream->stts_time;
10765 cur->duration = -1;
10770 /* sample sync, can be NULL */
10771 if (stream->stss_present == TRUE) {
10772 guint32 n_sample_syncs;
10774 n_sample_syncs = stream->n_sample_syncs;
10776 if (!n_sample_syncs) {
10777 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10778 stream->all_keyframe = TRUE;
10780 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10781 /* note that the first sample is index 1, not 0 */
10784 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10786 if (G_LIKELY (index > 0 && index <= n_samples)) {
10788 samples[index].keyframe = TRUE;
10789 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10790 /* and exit if we have enough samples */
10791 if (G_UNLIKELY (index >= n)) {
10798 stream->stss_index = i;
10801 /* stps marks partial sync frames like open GOP I-Frames */
10802 if (stream->stps_present == TRUE) {
10803 guint32 n_sample_partial_syncs;
10805 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10807 /* if there are no entries, the stss table contains the real
10809 if (n_sample_partial_syncs) {
10810 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10811 /* note that the first sample is index 1, not 0 */
10814 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10816 if (G_LIKELY (index > 0 && index <= n_samples)) {
10818 samples[index].keyframe = TRUE;
10819 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10820 /* and exit if we have enough samples */
10821 if (G_UNLIKELY (index >= n)) {
10828 stream->stps_index = i;
10832 /* no stss, all samples are keyframes */
10833 stream->all_keyframe = TRUE;
10834 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10839 /* composition time to sample */
10840 if (stream->ctts_present == TRUE) {
10841 guint32 n_composition_times;
10842 guint32 ctts_count;
10843 gint32 ctts_soffset;
10845 /* Fill in the pts_offsets */
10847 n_composition_times = stream->n_composition_times;
10849 for (i = stream->ctts_index; i < n_composition_times; i++) {
10850 if (stream->ctts_sample_index >= stream->ctts_count
10851 || !stream->ctts_sample_index) {
10852 stream->ctts_count =
10853 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10854 stream->ctts_soffset =
10855 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10856 stream->ctts_sample_index = 0;
10859 ctts_count = stream->ctts_count;
10860 ctts_soffset = stream->ctts_soffset;
10862 /* FIXME: Set offset to 0 for "no decode samples". This needs
10863 * to be handled in a codec specific manner ideally. */
10864 if (ctts_soffset == G_MININT32)
10867 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10868 cur->pts_offset = ctts_soffset;
10871 if (G_UNLIKELY (cur > last)) {
10873 stream->ctts_sample_index = j + 1;
10877 stream->ctts_sample_index = 0;
10878 stream->ctts_index++;
10882 stream->stbl_index = n;
10883 /* if index has been completely parsed, free data that is no-longer needed */
10884 if (n + 1 == stream->n_samples) {
10885 gst_qtdemux_stbl_free (stream);
10886 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10887 if (qtdemux->pullbased) {
10888 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10889 while (n + 1 == stream->n_samples)
10890 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10894 GST_OBJECT_UNLOCK (qtdemux);
10901 GST_LOG_OBJECT (qtdemux,
10902 "Tried to parse up to sample %u but this sample has already been parsed",
10904 /* if fragmented, there may be more */
10905 if (qtdemux->fragmented && n == stream->stbl_index)
10907 GST_OBJECT_UNLOCK (qtdemux);
10913 GST_LOG_OBJECT (qtdemux,
10914 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10915 stream->n_samples);
10916 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10917 (_("This file is corrupt and cannot be played.")), (NULL));
10922 GST_OBJECT_UNLOCK (qtdemux);
10923 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10924 (_("This file is corrupt and cannot be played.")), (NULL));
10929 /* collect all segment info for @stream.
10932 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10936 /* accept edts if they contain gaps at start and there is only
10937 * one media segment */
10938 gboolean allow_pushbased_edts = TRUE;
10939 gint media_segments_count = 0;
10941 /* parse and prepare segment info from the edit list */
10942 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10943 stream->n_segments = 0;
10944 stream->segments = NULL;
10945 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10948 guint segment_number, entry_size;
10950 GstClockTime stime;
10951 const guint8 *buffer;
10955 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10956 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10959 buffer = elst->data;
10961 size = QT_UINT32 (buffer);
10962 /* version, flags, n_segments */
10964 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10967 version = QT_UINT8 (buffer + 8);
10968 entry_size = (version == 1) ? 20 : 12;
10970 n_segments = QT_UINT32 (buffer + 12);
10972 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10973 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10977 /* we might allocate a bit too much, at least allocate 1 segment */
10978 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10980 /* segments always start from 0 */
10984 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10986 guint64 media_time;
10987 gboolean empty_edit = FALSE;
10988 QtDemuxSegment *segment;
10990 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10992 if (version == 1) {
10993 media_time = QT_UINT64 (buffer + 8);
10994 duration = QT_UINT64 (buffer);
10995 if (media_time == G_MAXUINT64)
10998 media_time = QT_UINT32 (buffer + 4);
10999 duration = QT_UINT32 (buffer);
11000 if (media_time == G_MAXUINT32)
11005 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
11007 segment = &stream->segments[segment_number];
11009 /* time and duration expressed in global timescale */
11010 segment->time = stime;
11011 if (duration != 0 || empty_edit) {
11012 /* edge case: empty edits with duration=zero are treated here.
11013 * (files should not have these anyway). */
11015 /* add non scaled values so we don't cause roundoff errors */
11017 stime = QTTIME_TO_GSTTIME (qtdemux, time);
11018 segment->duration = stime - segment->time;
11020 /* zero duration does not imply media_start == media_stop
11021 * but, only specify media_start. The edit ends with the track. */
11022 stime = segment->duration = GST_CLOCK_TIME_NONE;
11023 /* Don't allow more edits after this one. */
11024 n_segments = segment_number + 1;
11026 segment->stop_time = stime;
11028 segment->trak_media_start = media_time;
11029 /* media_time expressed in stream timescale */
11031 segment->media_start = media_start;
11032 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
11033 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
11034 media_segments_count++;
11036 segment->media_start = GST_CLOCK_TIME_NONE;
11037 segment->media_stop = GST_CLOCK_TIME_NONE;
11039 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
11041 if (rate_int <= 1) {
11042 /* 0 is not allowed, some programs write 1 instead of the floating point
11044 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
11048 segment->rate = rate_int / 65536.0;
11051 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
11052 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
11053 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
11054 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
11055 segment_number, GST_TIME_ARGS (segment->time),
11056 GST_TIME_ARGS (segment->duration),
11057 GST_TIME_ARGS (segment->media_start), media_time,
11058 GST_TIME_ARGS (segment->media_stop),
11059 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
11060 stream->timescale);
11061 if (segment->stop_time > qtdemux->segment.stop &&
11062 !qtdemux->upstream_format_is_time) {
11063 GST_WARNING_OBJECT (qtdemux, "Segment %d "
11064 " extends to %" GST_TIME_FORMAT
11065 " past the end of the declared movie duration %" GST_TIME_FORMAT
11066 " movie segment will be extended", segment_number,
11067 GST_TIME_ARGS (segment->stop_time),
11068 GST_TIME_ARGS (qtdemux->segment.stop));
11069 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
11072 buffer += entry_size;
11074 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
11075 stream->n_segments = n_segments;
11076 if (media_segments_count != 1)
11077 allow_pushbased_edts = FALSE;
11081 /* push based does not handle segments, so act accordingly here,
11082 * and warn if applicable */
11083 if (!qtdemux->pullbased && !allow_pushbased_edts) {
11084 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
11085 /* remove and use default one below, we stream like it anyway */
11086 g_free (stream->segments);
11087 stream->segments = NULL;
11088 stream->n_segments = 0;
11091 /* no segments, create one to play the complete trak */
11092 if (stream->n_segments == 0) {
11093 GstClockTime stream_duration =
11094 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
11096 if (stream->segments == NULL)
11097 stream->segments = g_new (QtDemuxSegment, 1);
11099 /* represent unknown our way */
11100 if (stream_duration == 0)
11101 stream_duration = GST_CLOCK_TIME_NONE;
11103 stream->segments[0].time = 0;
11104 stream->segments[0].stop_time = stream_duration;
11105 stream->segments[0].duration = stream_duration;
11106 stream->segments[0].media_start = 0;
11107 stream->segments[0].media_stop = stream_duration;
11108 stream->segments[0].rate = 1.0;
11109 stream->segments[0].trak_media_start = 0;
11111 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
11112 GST_TIME_ARGS (stream_duration));
11113 stream->n_segments = 1;
11114 stream->dummy_segment = TRUE;
11116 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
11122 * Parses the stsd atom of a svq3 trak looking for
11123 * the SMI and gama atoms.
11126 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
11127 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
11129 const guint8 *_gamma = NULL;
11130 GstBuffer *_seqh = NULL;
11131 const guint8 *stsd_data = stsd_entry_data;
11132 guint32 length = QT_UINT32 (stsd_data);
11136 GST_WARNING_OBJECT (qtdemux, "stsd too short");
11142 version = QT_UINT16 (stsd_data);
11143 if (version == 3) {
11144 if (length >= 70) {
11147 while (length > 8) {
11148 guint32 fourcc, size;
11149 const guint8 *data;
11150 size = QT_UINT32 (stsd_data);
11151 fourcc = QT_FOURCC (stsd_data + 4);
11152 data = stsd_data + 8;
11155 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
11156 "svq3 atom parsing");
11165 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
11166 " for gama atom, expected 12", size);
11171 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
11173 if (_seqh != NULL) {
11174 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
11175 " found, ignoring");
11177 seqh_size = QT_UINT32 (data + 4);
11178 if (seqh_size > 0) {
11179 _seqh = gst_buffer_new_and_alloc (seqh_size);
11180 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
11187 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
11188 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
11192 if (size <= length) {
11198 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
11201 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
11202 G_GUINT16_FORMAT, version);
11212 } else if (_seqh) {
11213 gst_buffer_unref (_seqh);
11218 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
11221 GstByteReader dref;
11225 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
11226 * atom that might contain a 'data' atom with the rtsp uri.
11227 * This case was reported in bug #597497, some info about
11228 * the hndl atom can be found in TN1195
11230 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
11231 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
11234 guint32 dref_num_entries = 0;
11235 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
11236 gst_byte_reader_skip (&dref, 4) &&
11237 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
11240 /* search dref entries for hndl atom */
11241 for (i = 0; i < dref_num_entries; i++) {
11242 guint32 size = 0, type;
11243 guint8 string_len = 0;
11244 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
11245 qt_atom_parser_get_fourcc (&dref, &type)) {
11246 if (type == FOURCC_hndl) {
11247 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
11249 /* skip data reference handle bytes and the
11250 * following pascal string and some extra 4
11251 * bytes I have no idea what are */
11252 if (!gst_byte_reader_skip (&dref, 4) ||
11253 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
11254 !gst_byte_reader_skip (&dref, string_len + 4)) {
11255 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
11259 /* iterate over the atoms to find the data atom */
11260 while (gst_byte_reader_get_remaining (&dref) >= 8) {
11264 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
11265 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
11266 if (atom_type == FOURCC_data) {
11267 const guint8 *uri_aux = NULL;
11269 /* found the data atom that might contain the rtsp uri */
11270 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
11271 "hndl atom, interpreting it as an URI");
11272 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
11274 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
11275 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
11277 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
11278 "didn't contain a rtsp address");
11280 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
11285 /* skipping to the next entry */
11286 if (!gst_byte_reader_skip (&dref, atom_size - 8))
11289 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
11296 /* skip to the next entry */
11297 if (!gst_byte_reader_skip (&dref, size - 8))
11300 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
11303 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
11309 #define AMR_NB_ALL_MODES 0x81ff
11310 #define AMR_WB_ALL_MODES 0x83ff
11312 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
11314 /* The 'damr' atom is of the form:
11316 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
11317 * 32 b 8 b 16 b 8 b 8 b
11319 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
11320 * represents the highest mode used in the stream (and thus the maximum
11321 * bitrate), with a couple of special cases as seen below.
11324 /* Map of frame type ID -> bitrate */
11325 static const guint nb_bitrates[] = {
11326 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
11328 static const guint wb_bitrates[] = {
11329 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
11335 gst_buffer_map (buf, &map, GST_MAP_READ);
11337 if (map.size != 0x11) {
11338 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
11342 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
11343 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
11344 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
11348 mode_set = QT_UINT16 (map.data + 13);
11350 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
11351 max_mode = 7 + (wb ? 1 : 0);
11353 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
11354 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
11356 if (max_mode == -1) {
11357 GST_DEBUG ("No mode indication was found (mode set) = %x",
11362 gst_buffer_unmap (buf, &map);
11363 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
11366 gst_buffer_unmap (buf, &map);
11371 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
11372 GstByteReader * reader, guint32 * matrix, const gchar * atom)
11375 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
11381 if (gst_byte_reader_get_remaining (reader) < 36)
11384 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
11385 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
11386 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
11387 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
11388 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
11389 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
11390 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
11391 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
11392 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
11394 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
11395 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
11396 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
11398 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
11399 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
11401 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
11402 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
11409 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
11410 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
11417 * This macro will only compare value abdegh, it expects cfi to have already
11420 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
11421 (m)[3] == (d << 16) && (m)[4] == (e << 16))
11423 /* only handle the cases where the last column has standard values */
11424 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
11425 const gchar *rotation_tag = NULL;
11427 /* no rotation needed */
11428 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
11430 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
11431 rotation_tag = "rotate-90";
11432 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
11433 rotation_tag = "rotate-180";
11434 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
11435 rotation_tag = "rotate-270";
11437 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11440 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
11441 GST_STR_NULL (rotation_tag));
11442 if (rotation_tag != NULL) {
11443 if (*taglist == NULL)
11444 *taglist = gst_tag_list_new_empty ();
11445 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
11446 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
11449 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11454 qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
11455 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11459 GstBuffer *adrm_buf = NULL;
11460 QtDemuxAavdEncryptionInfo *info;
11462 adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
11463 if (G_UNLIKELY (!adrm)) {
11464 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
11467 adrm_size = QT_UINT32 (adrm->data);
11468 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
11470 stream->protection_scheme_type = FOURCC_aavd;
11472 if (!stream->protection_scheme_info)
11473 stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
11475 info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
11477 if (info->default_properties)
11478 gst_structure_free (info->default_properties);
11479 info->default_properties = gst_structure_new ("application/x-aavd",
11480 "encrypted", G_TYPE_BOOLEAN, TRUE,
11481 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
11482 gst_buffer_unref (adrm_buf);
11484 *original_fmt = FOURCC_mp4a;
11488 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
11489 * protected streams (sinf, frma, schm and schi); if the protection scheme is
11490 * Common Encryption (cenc), the function will also parse the tenc box (defined
11491 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
11492 * (typically an enc[v|a|t|s] sample entry); the function will set
11493 * @original_fmt to the fourcc of the original unencrypted stream format.
11494 * Returns TRUE if successful; FALSE otherwise. */
11496 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
11497 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11503 QtDemuxCencSampleSetInfo *info;
11505 const guint8 *tenc_data;
11507 g_return_val_if_fail (qtdemux != NULL, FALSE);
11508 g_return_val_if_fail (stream != NULL, FALSE);
11509 g_return_val_if_fail (container != NULL, FALSE);
11510 g_return_val_if_fail (original_fmt != NULL, FALSE);
11512 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
11513 if (G_UNLIKELY (!sinf)) {
11514 if (stream->protection_scheme_type == FOURCC_cenc
11515 || stream->protection_scheme_type == FOURCC_cbcs) {
11516 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
11517 "mandatory for Common Encryption");
11523 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
11524 if (G_UNLIKELY (!frma)) {
11525 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
11529 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
11530 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
11531 GST_FOURCC_ARGS (*original_fmt));
11533 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
11535 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
11538 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
11539 stream->protection_scheme_version =
11540 QT_UINT32 ((const guint8 *) schm->data + 16);
11542 GST_DEBUG_OBJECT (qtdemux,
11543 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
11544 "protection_scheme_version: %#010x",
11545 GST_FOURCC_ARGS (stream->protection_scheme_type),
11546 stream->protection_scheme_version);
11548 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
11550 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
11553 if (stream->protection_scheme_type != FOURCC_cenc &&
11554 stream->protection_scheme_type != FOURCC_piff &&
11555 stream->protection_scheme_type != FOURCC_cbcs) {
11556 GST_ERROR_OBJECT (qtdemux,
11557 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
11558 GST_FOURCC_ARGS (stream->protection_scheme_type));
11562 if (G_UNLIKELY (!stream->protection_scheme_info))
11563 stream->protection_scheme_info =
11564 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
11566 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
11568 if (stream->protection_scheme_type == FOURCC_cenc
11569 || stream->protection_scheme_type == FOURCC_cbcs) {
11570 guint8 is_encrypted;
11572 guint8 constant_iv_size = 0;
11573 const guint8 *default_kid;
11574 guint8 crypt_byte_block = 0;
11575 guint8 skip_byte_block = 0;
11576 const guint8 *constant_iv = NULL;
11578 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
11580 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11581 "which is mandatory for Common Encryption");
11584 tenc_data = (const guint8 *) tenc->data + 12;
11585 is_encrypted = QT_UINT8 (tenc_data + 2);
11586 iv_size = QT_UINT8 (tenc_data + 3);
11587 default_kid = (tenc_data + 4);
11588 if (stream->protection_scheme_type == FOURCC_cbcs) {
11589 guint8 possible_pattern_info;
11590 if (iv_size == 0) {
11591 constant_iv_size = QT_UINT8 (tenc_data + 20);
11592 if (constant_iv_size != 8 && constant_iv_size != 16) {
11593 GST_ERROR_OBJECT (qtdemux,
11594 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
11597 constant_iv = (tenc_data + 21);
11599 possible_pattern_info = QT_UINT8 (tenc_data + 1);
11600 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
11601 skip_byte_block = possible_pattern_info & 0x0f;
11603 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
11604 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
11605 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
11606 } else if (stream->protection_scheme_type == FOURCC_piff) {
11608 static const guint8 piff_track_encryption_uuid[] = {
11609 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
11610 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
11613 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
11615 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11616 "which is mandatory for Common Encryption");
11620 tenc_data = (const guint8 *) tenc->data + 8;
11621 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
11622 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
11623 GST_ERROR_OBJECT (qtdemux,
11624 "Unsupported track encryption box with uuid: %s", box_uuid);
11628 tenc_data = (const guint8 *) tenc->data + 16 + 12;
11629 gst_byte_reader_init (&br, tenc_data, 20);
11630 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
11631 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
11634 stream->protection_scheme_type = FOURCC_cenc;
11641 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
11642 QtDemuxStream ** stream2)
11644 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
11648 qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
11653 /*parse svmi header if existing */
11654 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11656 guint32 len = QT_UINT32 ((guint8 *) svmi->data);
11657 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11659 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11660 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11661 guint8 frame_type, frame_layout;
11662 guint32 stereo_mono_change_count;
11667 /* MPEG-A stereo video */
11668 if (qtdemux->major_brand == FOURCC_ss02)
11669 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11671 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11672 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11673 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
11675 switch (frame_type) {
11677 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11680 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11683 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11686 /* mode 3 is primary/secondary view sequence, ie
11687 * left/right views in separate tracks. See section 7.2
11688 * of ISO/IEC 23000-11:2009 */
11689 /* In the future this might be supported using related
11690 * streams, like an enhancement track - if files like this
11692 GST_FIXME_OBJECT (qtdemux,
11693 "Implement stereo video in separate streams");
11696 if ((frame_layout & 0x1) == 0)
11697 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11699 GST_LOG_OBJECT (qtdemux,
11700 "StereoVideo: composition type: %u, is_left_first: %u",
11701 frame_type, frame_layout);
11703 if (stereo_mono_change_count > 1) {
11704 GST_FIXME_OBJECT (qtdemux,
11705 "Mixed-mono flags are not yet supported in qtdemux.");
11708 stream->multiview_mode = mode;
11709 stream->multiview_flags = flags;
11716 /* parse the traks.
11717 * With each track we associate a new QtDemuxStream that contains all the info
11719 * traks that do not decode to something (like strm traks) will not have a pad.
11722 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
11724 GstByteReader tkhd;
11738 QtDemuxStream *stream = NULL;
11739 const guint8 *stsd_data;
11740 const guint8 *stsd_entry_data;
11741 guint remaining_stsd_len;
11742 guint stsd_entry_count;
11744 guint16 lang_code; /* quicktime lang code or packed iso code */
11746 guint32 tkhd_flags = 0;
11747 guint8 tkhd_version = 0;
11748 guint32 w = 0, h = 0;
11749 guint value_size, stsd_len, len;
11753 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11755 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11756 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11757 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11760 /* pick between 64 or 32 bits */
11761 value_size = tkhd_version == 1 ? 8 : 4;
11762 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11763 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11766 /* Check if current moov has duplicated track_id */
11767 if (qtdemux_find_stream (qtdemux, track_id))
11768 goto existing_stream;
11770 stream = _create_stream (qtdemux, track_id);
11771 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11773 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11774 if (!gst_byte_reader_skip (&tkhd, 4))
11777 if (tkhd_version == 1) {
11778 if (!gst_byte_reader_get_uint64_be (&tkhd, &stream->tkhd_duration))
11782 if (!gst_byte_reader_get_uint32_be (&tkhd, &dur))
11784 stream->tkhd_duration = dur;
11786 GST_INFO_OBJECT (qtdemux, "tkhd duration: %" G_GUINT64_FORMAT,
11787 stream->tkhd_duration);
11789 /* need defaults for fragments */
11790 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11792 if ((tkhd_flags & 1) == 0)
11793 stream->disabled = TRUE;
11795 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11796 tkhd_version, tkhd_flags, stream->track_id);
11798 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11801 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11802 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11803 if (qtdemux->major_brand != FOURCC_mjp2 ||
11804 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11808 len = QT_UINT32 ((guint8 *) mdhd->data);
11809 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11810 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11811 if (version == 0x01000000) {
11814 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11815 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11816 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11820 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11821 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11822 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11825 if (lang_code < 0x400) {
11826 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11827 } else if (lang_code == 0x7fff) {
11828 stream->lang_id[0] = 0; /* unspecified */
11830 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11831 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11832 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11833 stream->lang_id[3] = 0;
11836 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11837 stream->timescale);
11838 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11840 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11841 lang_code, stream->lang_id);
11843 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11846 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11847 /* chapters track reference */
11848 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11850 gsize length = GST_READ_UINT32_BE (chap->data);
11851 if (qtdemux->chapters_track_id)
11852 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11854 if (length >= 12) {
11855 qtdemux->chapters_track_id =
11856 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11861 /* fragmented files may have bogus duration in moov */
11862 if (!qtdemux->fragmented &&
11863 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11864 guint64 tdur1, tdur2;
11866 /* don't overflow */
11867 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11868 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11871 * some of those trailers, nowadays, have prologue images that are
11872 * themselves video tracks as well. I haven't really found a way to
11873 * identify those yet, except for just looking at their duration. */
11874 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11875 GST_WARNING_OBJECT (qtdemux,
11876 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11877 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11878 "found, assuming preview image or something; skipping track",
11879 stream->duration, stream->timescale, qtdemux->duration,
11880 qtdemux->timescale);
11881 gst_qtdemux_stream_unref (stream);
11886 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11889 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11890 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11892 len = QT_UINT32 ((guint8 *) hdlr->data);
11894 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11895 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11896 GST_FOURCC_ARGS (stream->subtype));
11898 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11901 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11904 /* Parse out svmi (and later st3d/sv3d) atoms */
11905 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
11908 /* parse rest of tkhd */
11909 if (stream->subtype == FOURCC_vide) {
11912 /* version 1 uses some 64-bit ints */
11913 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11914 if (!gst_byte_reader_skip (&tkhd, 16))
11916 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11920 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11923 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11924 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11927 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11928 &stream->stream_tags);
11932 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11934 stsd_data = (const guint8 *) stsd->data;
11936 /* stsd should at least have one entry */
11937 stsd_len = QT_UINT32 (stsd_data);
11938 if (stsd_len < 24) {
11939 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11940 if (stream->subtype == FOURCC_vivo) {
11941 gst_qtdemux_stream_unref (stream);
11948 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11949 /* each stsd entry must contain at least 8 bytes */
11950 if (stream->stsd_entries_length == 0
11951 || stream->stsd_entries_length > stsd_len / 8) {
11952 stream->stsd_entries_length = 0;
11955 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11956 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11957 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11959 stsd_entry_data = stsd_data + 16;
11960 remaining_stsd_len = stsd_len - 16;
11961 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11963 gchar *codec = NULL;
11964 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11966 /* and that entry should fit within stsd */
11967 len = QT_UINT32 (stsd_entry_data);
11968 if (len > remaining_stsd_len)
11971 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11972 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11973 GST_FOURCC_ARGS (entry->fourcc));
11974 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11976 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11977 goto error_encrypted;
11979 if (fourcc == FOURCC_aavd) {
11980 if (stream->subtype != FOURCC_soun) {
11981 GST_ERROR_OBJECT (qtdemux,
11982 "Unexpeced stsd type 'aavd' outside 'soun' track");
11984 /* encrypted audio with sound sample description v0 */
11985 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11986 stream->protected = TRUE;
11987 if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
11988 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11992 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11993 /* FIXME this looks wrong, there might be multiple children
11994 * with the same type */
11995 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11996 stream->protected = TRUE;
11997 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11998 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
12001 if (stream->subtype == FOURCC_vide) {
12006 gint depth, palette_size, palette_count;
12007 guint32 *palette_data = NULL;
12009 entry->sampled = TRUE;
12011 stream->display_width = w >> 16;
12012 stream->display_height = h >> 16;
12015 if (len < 86) /* TODO verify */
12018 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
12019 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
12020 entry->fps_n = 0; /* this is filled in later */
12021 entry->fps_d = 0; /* this is filled in later */
12022 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
12023 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
12025 /* if color_table_id is 0, ctab atom must follow; however some files
12026 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
12027 * if color table is not present we'll correct the value */
12028 if (entry->color_table_id == 0 &&
12030 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
12031 entry->color_table_id = -1;
12034 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
12035 entry->width, entry->height, entry->bits_per_sample,
12036 entry->color_table_id);
12038 depth = entry->bits_per_sample;
12040 /* more than 32 bits means grayscale */
12041 gray = (depth > 32);
12042 /* low 32 bits specify the depth */
12045 /* different number of palette entries is determined by depth. */
12047 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
12048 palette_count = (1 << depth);
12049 palette_size = palette_count * 4;
12051 if (entry->color_table_id) {
12052 switch (palette_count) {
12056 palette_data = g_memdup2 (ff_qt_default_palette_2, palette_size);
12059 palette_data = g_memdup2 (ff_qt_default_palette_4, palette_size);
12064 g_memdup2 (ff_qt_grayscale_palette_16, palette_size);
12066 palette_data = g_memdup2 (ff_qt_default_palette_16, palette_size);
12071 g_memdup2 (ff_qt_grayscale_palette_256, palette_size);
12074 g_memdup2 (ff_qt_default_palette_256, palette_size);
12077 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
12078 (_("The video in this file might not play correctly.")),
12079 ("unsupported palette depth %d", depth));
12083 guint i, j, start, end;
12089 start = QT_UINT32 (stsd_entry_data + offset + 70);
12090 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
12091 end = QT_UINT16 (stsd_entry_data + offset + 76);
12093 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
12094 start, end, palette_count);
12101 if (len < 94 + (end - start) * 8)
12104 /* palette is always the same size */
12105 palette_data = g_malloc0 (256 * 4);
12106 palette_size = 256 * 4;
12108 for (j = 0, i = start; i <= end; j++, i++) {
12109 guint32 a, r, g, b;
12111 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
12112 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
12113 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
12114 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
12116 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
12117 (g & 0xff00) | (b >> 8);
12122 gst_caps_unref (entry->caps);
12125 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12127 if (G_UNLIKELY (!entry->caps)) {
12128 g_free (palette_data);
12129 goto unknown_stream;
12133 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12134 GST_TAG_VIDEO_CODEC, codec, NULL);
12139 if (palette_data) {
12142 if (entry->rgb8_palette)
12143 gst_memory_unref (entry->rgb8_palette);
12144 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
12145 palette_data, palette_size, 0, palette_size, palette_data, g_free);
12147 s = gst_caps_get_structure (entry->caps, 0);
12149 /* non-raw video has a palette_data property. raw video has the palette as
12150 * an extra plane that we append to the output buffers before we push
12152 if (!gst_structure_has_name (s, "video/x-raw")) {
12153 GstBuffer *palette;
12155 palette = gst_buffer_new ();
12156 gst_buffer_append_memory (palette, entry->rgb8_palette);
12157 entry->rgb8_palette = NULL;
12159 gst_caps_set_simple (entry->caps, "palette_data",
12160 GST_TYPE_BUFFER, palette, NULL);
12161 gst_buffer_unref (palette);
12163 } else if (palette_count != 0) {
12164 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
12165 (NULL), ("Unsupported palette depth %d", depth));
12168 GST_LOG_OBJECT (qtdemux, "frame count: %u",
12169 QT_UINT16 (stsd_entry_data + offset + 32));
12175 /* pick 'the' stsd child */
12176 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12177 // We should skip parsing the stsd for non-protected streams if
12178 // the entry doesn't match the fourcc, since they don't change
12179 // format. However, for protected streams we can have partial
12180 // encryption, where parts of the stream are encrypted and parts
12181 // not. For both parts of such streams, we should ensure the
12182 // esds overrides are parsed for both from the stsd.
12183 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
12184 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
12186 else if (!stream->protected)
12191 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
12192 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
12193 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
12194 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
12198 const guint8 *pasp_data = (const guint8 *) pasp->data;
12199 guint len = QT_UINT32 (pasp_data);
12202 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
12203 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
12205 CUR_STREAM (stream)->par_w = 0;
12206 CUR_STREAM (stream)->par_h = 0;
12209 CUR_STREAM (stream)->par_w = 0;
12210 CUR_STREAM (stream)->par_h = 0;
12214 const guint8 *fiel_data = (const guint8 *) fiel->data;
12215 guint len = QT_UINT32 (fiel_data);
12218 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
12219 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
12224 const guint8 *colr_data = (const guint8 *) colr->data;
12225 guint len = QT_UINT32 (colr_data);
12227 if (len == 19 || len == 18) {
12228 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
12230 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
12231 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
12232 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
12233 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
12234 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
12236 CUR_STREAM (stream)->colorimetry.primaries =
12237 gst_video_color_primaries_from_iso (primaries);
12238 CUR_STREAM (stream)->colorimetry.transfer =
12239 gst_video_transfer_function_from_iso (transfer_function);
12240 CUR_STREAM (stream)->colorimetry.matrix =
12241 gst_video_color_matrix_from_iso (matrix);
12242 CUR_STREAM (stream)->colorimetry.range =
12243 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
12244 GST_VIDEO_COLOR_RANGE_16_235;
12246 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
12249 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
12254 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12255 stream->stream_tags);
12262 guint len = QT_UINT32 (stsd_entry_data);
12263 len = len <= 0x56 ? 0 : len - 0x56;
12264 const guint8 *avc_data = stsd_entry_data + 0x56;
12267 while (len >= 0x8) {
12270 if (QT_UINT32 (avc_data) <= 0x8)
12272 else if (QT_UINT32 (avc_data) <= len)
12273 size = QT_UINT32 (avc_data) - 0x8;
12278 /* No real data, so break out */
12281 switch (QT_FOURCC (avc_data + 0x4)) {
12284 /* parse, if found */
12287 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
12289 /* First 4 bytes are the length of the atom, the next 4 bytes
12290 * are the fourcc, the next 1 byte is the version, and the
12291 * subsequent bytes are profile_tier_level structure like data. */
12292 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
12293 avc_data + 8 + 1, size - 1);
12294 buf = gst_buffer_new_and_alloc (size);
12295 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
12296 gst_caps_set_simple (entry->caps,
12297 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12298 gst_buffer_unref (buf);
12306 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
12308 /* First 4 bytes are the length of the atom, the next 4 bytes
12309 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
12310 * next 1 byte is the version, and the
12311 * subsequent bytes are sequence parameter set like data. */
12313 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
12315 gst_codec_utils_h264_caps_set_level_and_profile
12316 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
12318 buf = gst_buffer_new_and_alloc (size);
12319 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
12320 gst_caps_set_simple (entry->caps,
12321 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12322 gst_buffer_unref (buf);
12328 guint avg_bitrate, max_bitrate;
12330 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
12334 max_bitrate = QT_UINT32 (avc_data + 0xc);
12335 avg_bitrate = QT_UINT32 (avc_data + 0x10);
12337 if (!max_bitrate && !avg_bitrate)
12340 /* Some muxers seem to swap the average and maximum bitrates
12341 * (I'm looking at you, YouTube), so we swap for sanity. */
12342 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
12343 guint temp = avg_bitrate;
12345 avg_bitrate = max_bitrate;
12346 max_bitrate = temp;
12349 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12350 gst_tag_list_add (stream->stream_tags,
12351 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
12352 max_bitrate, NULL);
12354 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12355 gst_tag_list_add (stream->stream_tags,
12356 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
12368 avc_data += size + 8;
12379 guint len = QT_UINT32 (stsd_entry_data);
12380 len = len <= 0x56 ? 0 : len - 0x56;
12381 const guint8 *hevc_data = stsd_entry_data + 0x56;
12384 while (len >= 0x8) {
12387 if (QT_UINT32 (hevc_data) <= 0x8)
12389 else if (QT_UINT32 (hevc_data) <= len)
12390 size = QT_UINT32 (hevc_data) - 0x8;
12395 /* No real data, so break out */
12398 switch (QT_FOURCC (hevc_data + 0x4)) {
12401 /* parse, if found */
12404 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
12406 /* First 4 bytes are the length of the atom, the next 4 bytes
12407 * are the fourcc, the next 1 byte is the version, and the
12408 * subsequent bytes are sequence parameter set like data. */
12409 gst_codec_utils_h265_caps_set_level_tier_and_profile
12410 (entry->caps, hevc_data + 8 + 1, size - 1);
12412 buf = gst_buffer_new_and_alloc (size);
12413 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
12414 gst_caps_set_simple (entry->caps,
12415 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12416 gst_buffer_unref (buf);
12423 hevc_data += size + 8;
12436 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
12437 GST_FOURCC_ARGS (fourcc));
12439 /* codec data might be in glbl extension atom */
12441 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
12447 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
12449 len = QT_UINT32 (data);
12452 buf = gst_buffer_new_and_alloc (len);
12453 gst_buffer_fill (buf, 0, data + 8, len);
12454 gst_caps_set_simple (entry->caps,
12455 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12456 gst_buffer_unref (buf);
12463 /* see annex I of the jpeg2000 spec */
12464 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
12465 const guint8 *data;
12466 const gchar *colorspace = NULL;
12468 guint32 ncomp_map = 0;
12469 gint32 *comp_map = NULL;
12470 guint32 nchan_def = 0;
12471 gint32 *chan_def = NULL;
12473 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12474 /* some required atoms */
12475 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12478 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
12482 /* number of components; redundant with info in codestream, but useful
12484 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
12485 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
12487 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
12489 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
12492 GST_DEBUG_OBJECT (qtdemux, "found colr");
12493 /* extract colour space info */
12494 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12495 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
12497 colorspace = "sRGB";
12500 colorspace = "GRAY";
12503 colorspace = "sYUV";
12511 /* colr is required, and only values 16, 17, and 18 are specified,
12512 so error if we have no colorspace */
12515 /* extract component mapping */
12516 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
12518 guint32 cmap_len = 0;
12520 cmap_len = QT_UINT32 (cmap->data);
12521 if (cmap_len >= 8) {
12522 /* normal box, subtract off header */
12524 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12525 if (cmap_len % 4 == 0) {
12526 ncomp_map = (cmap_len / 4);
12527 comp_map = g_new0 (gint32, ncomp_map);
12528 for (i = 0; i < ncomp_map; i++) {
12531 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12532 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12533 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
12534 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12539 /* extract channel definitions */
12540 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
12542 guint32 cdef_len = 0;
12544 cdef_len = QT_UINT32 (cdef->data);
12545 if (cdef_len >= 10) {
12546 /* normal box, subtract off header and len */
12548 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12549 if (cdef_len % 6 == 0) {
12550 nchan_def = (cdef_len / 6);
12551 chan_def = g_new0 (gint32, nchan_def);
12552 for (i = 0; i < nchan_def; i++)
12554 for (i = 0; i < nchan_def; i++) {
12555 guint16 cn, typ, asoc;
12556 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12557 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12558 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
12559 if (cn < nchan_def) {
12562 chan_def[cn] = asoc;
12565 chan_def[cn] = 0; /* alpha */
12568 chan_def[cn] = -typ;
12576 gst_caps_set_simple (entry->caps,
12577 "num-components", G_TYPE_INT, ncomp, NULL);
12578 gst_caps_set_simple (entry->caps,
12579 "colorspace", G_TYPE_STRING, colorspace, NULL);
12582 GValue arr = { 0, };
12583 GValue elt = { 0, };
12585 g_value_init (&arr, GST_TYPE_ARRAY);
12586 g_value_init (&elt, G_TYPE_INT);
12587 for (i = 0; i < ncomp_map; i++) {
12588 g_value_set_int (&elt, comp_map[i]);
12589 gst_value_array_append_value (&arr, &elt);
12591 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12592 "component-map", &arr);
12593 g_value_unset (&elt);
12594 g_value_unset (&arr);
12599 GValue arr = { 0, };
12600 GValue elt = { 0, };
12602 g_value_init (&arr, GST_TYPE_ARRAY);
12603 g_value_init (&elt, G_TYPE_INT);
12604 for (i = 0; i < nchan_def; i++) {
12605 g_value_set_int (&elt, chan_def[i]);
12606 gst_value_array_append_value (&arr, &elt);
12608 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12609 "channel-definitions", &arr);
12610 g_value_unset (&elt);
12611 g_value_unset (&arr);
12615 /* some optional atoms */
12616 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
12617 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
12619 /* indicate possible fields in caps */
12621 data = (guint8 *) field->data + 8;
12623 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12624 (gint) * data, NULL);
12626 /* add codec_data if provided */
12631 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12632 data = prefix->data;
12633 len = QT_UINT32 (data);
12636 buf = gst_buffer_new_and_alloc (len);
12637 gst_buffer_fill (buf, 0, data + 8, len);
12638 gst_caps_set_simple (entry->caps,
12639 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12640 gst_buffer_unref (buf);
12649 GstBuffer *seqh = NULL;
12650 const guint8 *gamma_data = NULL;
12651 guint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
12653 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
12656 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
12657 QT_FP32 (gamma_data), NULL);
12660 /* sorry for the bad name, but we don't know what this is, other
12661 * than its own fourcc */
12662 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12664 gst_buffer_unref (seqh);
12667 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12668 buf = gst_buffer_new_and_alloc (len);
12669 gst_buffer_fill (buf, 0, stsd_data, len);
12670 gst_caps_set_simple (entry->caps,
12671 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12672 gst_buffer_unref (buf);
12677 /* https://developer.apple.com/standards/qtff-2001.pdf,
12678 * page 92, "Video Sample Description", under table 3.1 */
12681 const gint compressor_offset =
12682 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12683 const gint min_size = compressor_offset + 32 + 2 + 2;
12686 guint16 color_table_id = 0;
12689 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12691 /* recover information on interlaced/progressive */
12692 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
12696 len = QT_UINT32 (jpeg->data);
12697 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12699 if (len >= min_size) {
12700 gst_byte_reader_init (&br, jpeg->data, len);
12702 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12703 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12704 if (color_table_id != 0) {
12705 /* the spec says there can be concatenated chunks in the data, and we want
12706 * to find one called field. Walk through them. */
12707 gint offset = min_size;
12708 while (offset + 8 < len) {
12709 guint32 size = 0, tag;
12710 ok = gst_byte_reader_get_uint32_le (&br, &size);
12711 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12712 if (!ok || size < 8) {
12713 GST_WARNING_OBJECT (qtdemux,
12714 "Failed to walk optional chunk list");
12717 GST_DEBUG_OBJECT (qtdemux,
12718 "Found optional %4.4s chunk, size %u",
12719 (const char *) &tag, size);
12720 if (tag == FOURCC_fiel) {
12721 guint8 n_fields = 0, ordering = 0;
12722 gst_byte_reader_get_uint8 (&br, &n_fields);
12723 gst_byte_reader_get_uint8 (&br, &ordering);
12724 if (n_fields == 1 || n_fields == 2) {
12725 GST_DEBUG_OBJECT (qtdemux,
12726 "Found fiel tag with %u fields, ordering %u",
12727 n_fields, ordering);
12729 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12730 "interlace-mode", G_TYPE_STRING, "interleaved",
12733 GST_WARNING_OBJECT (qtdemux,
12734 "Found fiel tag with invalid fields (%u)", n_fields);
12740 GST_DEBUG_OBJECT (qtdemux,
12741 "Color table ID is 0, not trying to get interlacedness");
12744 GST_WARNING_OBJECT (qtdemux,
12745 "Length of jpeg chunk is too small, not trying to get interlacedness");
12753 gst_caps_set_simple (entry->caps,
12754 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12760 GNode *xith, *xdxt;
12762 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12763 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12767 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12771 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12772 /* collect the headers and store them in a stream list so that we can
12773 * send them out first */
12774 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12784 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12785 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12788 ovc1_data = ovc1->data;
12789 ovc1_len = QT_UINT32 (ovc1_data);
12790 if (ovc1_len <= 198) {
12791 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12794 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12795 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12796 gst_caps_set_simple (entry->caps,
12797 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12798 gst_buffer_unref (buf);
12803 guint len = QT_UINT32 (stsd_entry_data);
12804 len = len <= 0x56 ? 0 : len - 0x56;
12805 const guint8 *vc1_data = stsd_entry_data + 0x56;
12811 if (QT_UINT32 (vc1_data) <= 8)
12813 else if (QT_UINT32 (vc1_data) <= len)
12814 size = QT_UINT32 (vc1_data) - 8;
12819 /* No real data, so break out */
12822 switch (QT_FOURCC (vc1_data + 0x4)) {
12823 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12827 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12828 buf = gst_buffer_new_and_alloc (size);
12829 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12830 gst_caps_set_simple (entry->caps,
12831 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12832 gst_buffer_unref (buf);
12839 vc1_data += size + 8;
12845 guint len = QT_UINT32 (stsd_entry_data);
12846 len = len <= 0x56 ? 0 : len - 0x56;
12847 const guint8 *av1_data = stsd_entry_data + 0x56;
12850 while (len >= 0x8) {
12853 if (QT_UINT32 (av1_data) <= 0x8)
12855 else if (QT_UINT32 (av1_data) <= len)
12856 size = QT_UINT32 (av1_data) - 0x8;
12861 /* No real data, so break out */
12864 switch (QT_FOURCC (av1_data + 0x4)) {
12867 /* parse, if found */
12870 GST_DEBUG_OBJECT (qtdemux,
12871 "found av1C codec_data in stsd of size %d", size);
12873 /* not enough data, just ignore and hope for the best */
12878 * 4 bytes: atom length
12881 * version 1 (marker=1):
12883 * unsigned int (1) marker = 1;
12884 * unsigned int (7) version = 1;
12885 * unsigned int (3) seq_profile;
12886 * unsigned int (5) seq_level_idx_0;
12887 * unsigned int (1) seq_tier_0;
12888 * unsigned int (1) high_bitdepth;
12889 * unsigned int (1) twelve_bit;
12890 * unsigned int (1) monochrome;
12891 * unsigned int (1) chroma_subsampling_x;
12892 * unsigned int (1) chroma_subsampling_y;
12893 * unsigned int (2) chroma_sample_position;
12894 * unsigned int (3) reserved = 0;
12896 * unsigned int (1) initial_presentation_delay_present;
12897 * if (initial_presentation_delay_present) {
12898 * unsigned int (4) initial_presentation_delay_minus_one;
12900 * unsigned int (4) reserved = 0;
12903 * unsigned int (8) configOBUs[];
12908 switch (av1_data[8]) {
12910 guint8 pres_delay_field;
12912 /* We let profile and the other parts be figured out by
12913 * av1parse and only include the presentation delay here
12915 /* We skip initial_presentation_delay* for now */
12916 pres_delay_field = *(av1_data + 11);
12917 if (pres_delay_field & (1 << 5)) {
12918 gst_caps_set_simple (entry->caps,
12919 "presentation-delay", G_TYPE_INT,
12920 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12923 buf = gst_buffer_new_and_alloc (size);
12924 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12925 gst_buffer_fill (buf, 0, av1_data + 8, size);
12926 gst_caps_set_simple (entry->caps,
12927 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12928 gst_buffer_unref (buf);
12932 GST_WARNING ("Unknown version 0x%02x of av1C box",
12944 av1_data += size + 8;
12950 /* TODO: Need to parse vpcC for VP8 codec too.
12951 * Note that VPCodecConfigurationBox (vpcC) is defined for
12952 * vp08, vp09, and vp10 fourcc. */
12955 guint len = QT_UINT32 (stsd_entry_data);
12956 len = len <= 0x56 ? 0 : len - 0x56;
12957 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12960 while (len >= 0x8) {
12963 if (QT_UINT32 (vpcc_data) <= 0x8)
12965 else if (QT_UINT32 (vpcc_data) <= len)
12966 size = QT_UINT32 (vpcc_data) - 0x8;
12971 /* No real data, so break out */
12974 switch (QT_FOURCC (vpcc_data + 0x4)) {
12977 const gchar *profile_str = NULL;
12978 const gchar *chroma_format_str = NULL;
12981 guint8 chroma_format;
12982 GstVideoColorimetry cinfo;
12984 /* parse, if found */
12985 GST_DEBUG_OBJECT (qtdemux,
12986 "found vp codec_data in stsd of size %d", size);
12988 /* the meaning of "size" is length of the atom body, excluding
12989 * atom length and fourcc fields */
12994 * 4 bytes: atom length
13001 * 3 bits: chromaSubsampling
13002 * 1 bit: videoFullRangeFlag
13003 * 1 byte: colourPrimaries
13004 * 1 byte: transferCharacteristics
13005 * 1 byte: matrixCoefficients
13006 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
13007 * rest: codecIntializationData (not used for vp8 and vp9)
13010 if (vpcc_data[8] != 1) {
13011 GST_WARNING_OBJECT (qtdemux,
13012 "unknown vpcC version %d", vpcc_data[8]);
13016 profile = vpcc_data[12];
13035 gst_caps_set_simple (entry->caps,
13036 "profile", G_TYPE_STRING, profile_str, NULL);
13039 /* skip level, the VP9 spec v0.6 defines only one level atm,
13040 * but webm spec define various ones. Add level to caps
13041 * if we really need it then */
13043 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
13044 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
13045 gst_caps_set_simple (entry->caps,
13046 "bit-depth-luma", G_TYPE_UINT, bitdepth,
13047 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
13050 chroma_format = (vpcc_data[14] & 0xe) >> 1;
13051 switch (chroma_format) {
13054 chroma_format_str = "4:2:0";
13057 chroma_format_str = "4:2:2";
13060 chroma_format_str = "4:4:4";
13066 if (chroma_format_str) {
13067 gst_caps_set_simple (entry->caps,
13068 "chroma-format", G_TYPE_STRING, chroma_format_str,
13072 if ((vpcc_data[14] & 0x1) != 0)
13073 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
13075 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
13077 gst_video_color_primaries_from_iso (vpcc_data[15]);
13079 gst_video_transfer_function_from_iso (vpcc_data[16]);
13081 gst_video_color_matrix_from_iso (vpcc_data[17]);
13083 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
13084 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
13085 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
13086 /* set this only if all values are known, otherwise this
13087 * might overwrite valid ones parsed from other color box */
13088 CUR_STREAM (stream)->colorimetry = cinfo;
13097 vpcc_data += size + 8;
13107 GST_INFO_OBJECT (qtdemux,
13108 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13109 GST_FOURCC_ARGS (fourcc), entry->caps);
13111 } else if (stream->subtype == FOURCC_soun) {
13113 guint version, samplesize;
13114 guint16 compression_id;
13115 gboolean amrwb = FALSE;
13118 /* sample description entry (16) + sound sample description v0 (20) */
13122 version = QT_UINT32 (stsd_entry_data + offset);
13123 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
13124 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
13125 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
13126 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
13128 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
13129 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
13130 QT_UINT32 (stsd_entry_data + offset + 4));
13131 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13132 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
13133 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
13134 GST_LOG_OBJECT (qtdemux, "packet size: %d",
13135 QT_UINT16 (stsd_entry_data + offset + 14));
13136 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13138 if (compression_id == 0xfffe)
13139 entry->sampled = TRUE;
13141 /* first assume uncompressed audio */
13142 entry->bytes_per_sample = samplesize / 8;
13143 entry->samples_per_frame = entry->n_channels;
13144 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13145 entry->samples_per_packet = entry->samples_per_frame;
13146 entry->bytes_per_packet = entry->bytes_per_sample;
13150 if (version == 0x00010000) {
13151 /* sample description entry (16) + sound sample description v1 (20+16) */
13155 /* take information from here over the normal sample description */
13156 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
13157 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
13158 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
13159 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
13161 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
13162 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
13163 entry->samples_per_packet);
13164 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13165 entry->bytes_per_packet);
13166 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
13167 entry->bytes_per_frame);
13168 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
13169 entry->bytes_per_sample);
13171 if (!entry->sampled && entry->bytes_per_packet) {
13172 entry->samples_per_frame = (entry->bytes_per_frame /
13173 entry->bytes_per_packet) * entry->samples_per_packet;
13174 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
13175 entry->samples_per_frame);
13177 } else if (version == 0x00020000) {
13178 /* sample description entry (16) + sound sample description v2 (56) */
13182 /* take information from here over the normal sample description */
13183 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
13184 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
13185 entry->samples_per_frame = entry->n_channels;
13186 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
13187 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
13188 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
13189 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
13191 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
13192 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13193 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13194 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
13195 entry->bytes_per_sample * 8);
13196 GST_LOG_OBJECT (qtdemux, "format flags: %X",
13197 QT_UINT32 (stsd_entry_data + offset + 24));
13198 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13199 entry->bytes_per_packet);
13200 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
13201 entry->samples_per_packet);
13202 } else if (version != 0x00000) {
13203 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
13208 /* Yes, these have to be hard-coded */
13211 entry->samples_per_packet = 6;
13212 entry->bytes_per_packet = 1;
13213 entry->bytes_per_frame = 1 * entry->n_channels;
13214 entry->bytes_per_sample = 1;
13215 entry->samples_per_frame = 6 * entry->n_channels;
13220 entry->samples_per_packet = 3;
13221 entry->bytes_per_packet = 1;
13222 entry->bytes_per_frame = 1 * entry->n_channels;
13223 entry->bytes_per_sample = 1;
13224 entry->samples_per_frame = 3 * entry->n_channels;
13229 entry->samples_per_packet = 64;
13230 entry->bytes_per_packet = 34;
13231 entry->bytes_per_frame = 34 * entry->n_channels;
13232 entry->bytes_per_sample = 2;
13233 entry->samples_per_frame = 64 * entry->n_channels;
13239 entry->samples_per_packet = 1;
13240 entry->bytes_per_packet = 1;
13241 entry->bytes_per_frame = 1 * entry->n_channels;
13242 entry->bytes_per_sample = 1;
13243 entry->samples_per_frame = 1 * entry->n_channels;
13248 entry->samples_per_packet = 160;
13249 entry->bytes_per_packet = 33;
13250 entry->bytes_per_frame = 33 * entry->n_channels;
13251 entry->bytes_per_sample = 2;
13252 entry->samples_per_frame = 160 * entry->n_channels;
13255 /* fix up any invalid header information from above */
13260 /* Sometimes these are set to 0 in the sound sample descriptions so
13261 * let's try to infer useful values from the other information we
13262 * have available */
13263 if (entry->bytes_per_sample == 0)
13264 entry->bytes_per_sample =
13265 entry->bytes_per_frame / entry->n_channels;
13266 if (entry->bytes_per_sample == 0)
13267 entry->bytes_per_sample = samplesize / 8;
13269 if (entry->bytes_per_frame == 0)
13270 entry->bytes_per_frame =
13271 entry->bytes_per_sample * entry->n_channels;
13273 if (entry->bytes_per_packet == 0)
13274 entry->bytes_per_packet = entry->bytes_per_sample;
13276 if (entry->samples_per_frame == 0)
13277 entry->samples_per_frame = entry->n_channels;
13279 if (entry->samples_per_packet == 0)
13280 entry->samples_per_packet = entry->samples_per_frame;
13290 entry->bytes_per_sample = 3;
13294 entry->bytes_per_sample = 4;
13297 entry->bytes_per_sample = 8;
13300 entry->bytes_per_sample = 2;
13303 g_assert_not_reached ();
13306 entry->samples_per_frame = entry->n_channels;
13307 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13308 entry->samples_per_packet = entry->samples_per_frame;
13309 entry->bytes_per_packet = entry->bytes_per_sample;
13313 /* According to TS 102 366, the channel count in
13314 * a (E)AC3SampleEntry box is to be ignored */
13316 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
13317 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
13319 entry->n_channels = 0;
13327 gst_caps_unref (entry->caps);
13329 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
13330 stsd_entry_data + 32, len - 16, &codec);
13341 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
13343 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
13345 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
13347 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
13350 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
13351 const gchar *format_str;
13355 format_str = (enda_value) ? "S24LE" : "S24BE";
13358 format_str = (enda_value) ? "S32LE" : "S32BE";
13361 format_str = (enda_value) ? "F32LE" : "F32BE";
13364 format_str = (enda_value) ? "F64LE" : "F64BE";
13367 g_assert_not_reached ();
13370 gst_caps_set_simple (entry->caps,
13371 "format", G_TYPE_STRING, format_str, NULL);
13377 const guint8 *owma_data;
13378 const gchar *codec_name = NULL;
13382 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13383 /* FIXME this should also be gst_riff_strf_auds,
13384 * but the latter one is actually missing bits-per-sample :( */
13389 gint32 nSamplesPerSec;
13390 gint32 nAvgBytesPerSec;
13391 gint16 nBlockAlign;
13392 gint16 wBitsPerSample;
13395 WAVEFORMATEX *wfex;
13397 GST_DEBUG_OBJECT (qtdemux, "parse owma");
13398 owma_data = stsd_entry_data;
13399 owma_len = QT_UINT32 (owma_data);
13400 if (owma_len <= 54) {
13401 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
13404 wfex = (WAVEFORMATEX *) (owma_data + 36);
13405 buf = gst_buffer_new_and_alloc (owma_len - 54);
13406 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
13407 if (wfex->wFormatTag == 0x0161) {
13408 codec_name = "Windows Media Audio";
13410 } else if (wfex->wFormatTag == 0x0162) {
13411 codec_name = "Windows Media Audio 9 Pro";
13413 } else if (wfex->wFormatTag == 0x0163) {
13414 codec_name = "Windows Media Audio 9 Lossless";
13415 /* is that correct? gstffmpegcodecmap.c is missing it, but
13416 * fluendo codec seems to support it */
13420 gst_caps_set_simple (entry->caps,
13421 "codec_data", GST_TYPE_BUFFER, buf,
13422 "wmaversion", G_TYPE_INT, version,
13423 "block_align", G_TYPE_INT,
13424 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
13425 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13426 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13427 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13428 gst_buffer_unref (buf);
13432 codec = g_strdup (codec_name);
13438 guint len = QT_UINT32 (stsd_entry_data);
13439 len = len <= offset ? 0 : len - offset;
13440 const guint8 *wfex_data = stsd_entry_data + offset;
13441 const gchar *codec_name = NULL;
13443 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13444 /* FIXME this should also be gst_riff_strf_auds,
13445 * but the latter one is actually missing bits-per-sample :( */
13450 gint32 nSamplesPerSec;
13451 gint32 nAvgBytesPerSec;
13452 gint16 nBlockAlign;
13453 gint16 wBitsPerSample;
13458 /* FIXME: unify with similar wavformatex parsing code above */
13459 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13465 if (QT_UINT32 (wfex_data) <= 0x8)
13467 else if (QT_UINT32 (wfex_data) <= len)
13468 size = QT_UINT32 (wfex_data) - 8;
13473 /* No real data, so break out */
13476 switch (QT_FOURCC (wfex_data + 4)) {
13477 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13479 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13484 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13485 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13486 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13487 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13488 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13489 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13490 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13492 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13493 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13494 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13495 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13496 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13497 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13499 if (wfex.wFormatTag == 0x0161) {
13500 codec_name = "Windows Media Audio";
13502 } else if (wfex.wFormatTag == 0x0162) {
13503 codec_name = "Windows Media Audio 9 Pro";
13505 } else if (wfex.wFormatTag == 0x0163) {
13506 codec_name = "Windows Media Audio 9 Lossless";
13507 /* is that correct? gstffmpegcodecmap.c is missing it, but
13508 * fluendo codec seems to support it */
13512 gst_caps_set_simple (entry->caps,
13513 "wmaversion", G_TYPE_INT, version,
13514 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13515 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13516 "width", G_TYPE_INT, wfex.wBitsPerSample,
13517 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13519 if (size > wfex.cbSize) {
13522 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13523 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13524 size - wfex.cbSize);
13525 gst_caps_set_simple (entry->caps,
13526 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13527 gst_buffer_unref (buf);
13529 GST_WARNING_OBJECT (qtdemux, "no codec data");
13534 codec = g_strdup (codec_name);
13542 wfex_data += size + 8;
13548 guint8 *channel_mapping = NULL;
13549 guint32 dops_len, rate;
13551 guint8 channel_mapping_family;
13552 guint8 stream_count;
13553 guint8 coupled_count;
13559 opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
13560 if (opus == NULL) {
13561 GST_WARNING_OBJECT (qtdemux, "Opus Sample Entry not found");
13565 dops = qtdemux_tree_get_child_by_type (opus, FOURCC_dops);
13566 if (dops == NULL) {
13567 GST_WARNING_OBJECT (qtdemux, "Opus Specific Box not found");
13571 /* Opus Specific Box content:
13575 * 1 byte: OutputChannelCount;
13576 * 2 bytes: PreSkip (big-endians);
13577 * 4 bytes: InputSampleRate (big-endians);
13578 * 2 bytes: OutputGain (big-endians);
13579 * 1 byte: ChannelMappingFamily;
13580 * if (ChannelMappingFamily != 0) {
13581 * 1 byte: StreamCount;
13582 * 1 byte: CoupledCount;
13583 * for (OutputChannel in 0..OutputChannelCount) {
13584 * 1 byte: ChannelMapping;
13589 dops_len = QT_UINT32 ((guint8 *) dops->data);
13590 if (len < offset + dops_len) {
13591 GST_WARNING_OBJECT (qtdemux,
13592 "Opus Sample Entry has bogus size %" G_GUINT32_FORMAT, len);
13595 if (dops_len < 19) {
13596 GST_WARNING_OBJECT (qtdemux,
13597 "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
13602 n_channels = GST_READ_UINT8 ((guint8 *) dops->data + 9);
13603 rate = GST_READ_UINT32_BE ((guint8 *) dops->data + 12);
13604 channel_mapping_family = GST_READ_UINT8 ((guint8 *) dops->data + 18);
13606 if (channel_mapping_family != 0) {
13607 if (dops_len < 21 + n_channels) {
13608 GST_WARNING_OBJECT (qtdemux,
13609 "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
13614 stream_count = GST_READ_UINT8 ((guint8 *) dops->data + 19);
13615 coupled_count = GST_READ_UINT8 ((guint8 *) dops->data + 20);
13617 if (n_channels > 0) {
13618 channel_mapping = g_malloc (n_channels * sizeof (guint8));
13619 for (i = 0; i < n_channels; i++)
13620 channel_mapping[i] =
13621 GST_READ_UINT8 ((guint8 *) dops->data + i + 21);
13623 } else if (n_channels == 1) {
13626 } else if (n_channels == 2) {
13630 GST_WARNING_OBJECT (qtdemux,
13631 "Opus unexpected nb of channels %d without channel mapping",
13636 entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
13637 channel_mapping_family, stream_count, coupled_count,
13639 g_free (channel_mapping);
13641 entry->sampled = TRUE;
13653 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13654 GST_TAG_AUDIO_CODEC, codec, NULL);
13658 /* some bitrate info may have ended up in caps */
13659 s = gst_caps_get_structure (entry->caps, 0);
13660 gst_structure_get_int (s, "bitrate", &bitrate);
13662 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13663 GST_TAG_BITRATE, bitrate, NULL);
13667 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
13668 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
13669 if (stream->protected) {
13670 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
13671 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13673 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
13683 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
13685 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
13687 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13691 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13692 16 bits is a byte-swapped wave-style codec identifier,
13693 and we can find a WAVE header internally to a 'wave' atom here.
13694 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13695 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13698 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13699 if (len < offset + 20) {
13700 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13702 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
13703 const guint8 *data = stsd_entry_data + offset + 16;
13705 GNode *waveheadernode;
13707 wavenode = g_node_new ((guint8 *) data);
13708 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
13709 const guint8 *waveheader;
13712 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
13713 if (waveheadernode) {
13714 waveheader = (const guint8 *) waveheadernode->data;
13715 headerlen = QT_UINT32 (waveheader);
13717 if (headerlen > 8) {
13718 gst_riff_strf_auds *header = NULL;
13719 GstBuffer *headerbuf;
13725 headerbuf = gst_buffer_new_and_alloc (headerlen);
13726 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13728 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13729 headerbuf, &header, &extra)) {
13730 gst_caps_unref (entry->caps);
13731 /* FIXME: Need to do something with the channel reorder map */
13733 gst_riff_create_audio_caps (header->format, NULL, header,
13734 extra, NULL, NULL, NULL);
13737 gst_buffer_unref (extra);
13742 GST_DEBUG ("Didn't find waveheadernode for this codec");
13744 g_node_destroy (wavenode);
13747 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13748 stream->stream_tags);
13752 /* FIXME: what is in the chunk? */
13755 gint len = QT_UINT32 (stsd_data);
13757 /* seems to be always = 116 = 0x74 */
13763 gint len = QT_UINT32 (stsd_entry_data);
13766 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13768 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13769 gst_caps_set_simple (entry->caps,
13770 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13771 gst_buffer_unref (buf);
13773 gst_caps_set_simple (entry->caps,
13774 "samplesize", G_TYPE_INT, samplesize, NULL);
13779 GNode *alac, *wave = NULL;
13781 /* apparently, m4a has this atom appended directly in the stsd entry,
13782 * while mov has it in a wave atom */
13783 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
13785 /* alac now refers to stsd entry atom */
13786 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
13788 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
13790 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
13793 const guint8 *alac_data = alac->data;
13794 gint len = QT_UINT32 (alac->data);
13798 GST_DEBUG_OBJECT (qtdemux,
13799 "discarding alac atom with unexpected len %d", len);
13801 /* codec-data contains alac atom size and prefix,
13802 * ffmpeg likes it that way, not quite gst-ish though ...*/
13803 buf = gst_buffer_new_and_alloc (len);
13804 gst_buffer_fill (buf, 0, alac->data, len);
13805 gst_caps_set_simple (entry->caps,
13806 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13807 gst_buffer_unref (buf);
13809 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
13810 entry->n_channels = QT_UINT8 (alac_data + 21);
13811 entry->rate = QT_UINT32 (alac_data + 32);
13812 samplesize = QT_UINT8 (alac_data + 16 + 1);
13815 gst_caps_set_simple (entry->caps,
13816 "samplesize", G_TYPE_INT, samplesize, NULL);
13821 /* The codingname of the sample entry is 'fLaC' */
13822 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
13825 /* The 'dfLa' box is added to the sample entry to convey
13826 initializing information for the decoder. */
13827 const GNode *dfla =
13828 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
13831 const guint32 len = QT_UINT32 (dfla->data);
13833 /* Must contain at least dfLa box header (12),
13834 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13836 GST_DEBUG_OBJECT (qtdemux,
13837 "discarding dfla atom with unexpected len %d", len);
13839 /* skip dfLa header to get the METADATA_BLOCKs */
13840 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13841 const guint32 metadata_blocks_len = len - 12;
13843 gchar *stream_marker = g_strdup ("fLaC");
13844 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13845 strlen (stream_marker));
13848 guint32 remainder = 0;
13849 guint32 block_size = 0;
13850 gboolean is_last = FALSE;
13852 GValue array = G_VALUE_INIT;
13853 GValue value = G_VALUE_INIT;
13855 g_value_init (&array, GST_TYPE_ARRAY);
13856 g_value_init (&value, GST_TYPE_BUFFER);
13858 gst_value_set_buffer (&value, block);
13859 gst_value_array_append_value (&array, &value);
13860 g_value_reset (&value);
13862 gst_buffer_unref (block);
13864 /* check there's at least one METADATA_BLOCK_HEADER's worth
13865 * of data, and we haven't already finished parsing */
13866 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13867 remainder = metadata_blocks_len - index;
13869 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13871 (metadata_blocks[index + 1] << 16) +
13872 (metadata_blocks[index + 2] << 8) +
13873 metadata_blocks[index + 3];
13875 /* be careful not to read off end of box */
13876 if (block_size > remainder) {
13880 is_last = metadata_blocks[index] >> 7;
13882 block = gst_buffer_new_and_alloc (block_size);
13884 gst_buffer_fill (block, 0, &metadata_blocks[index],
13887 gst_value_set_buffer (&value, block);
13888 gst_value_array_append_value (&array, &value);
13889 g_value_reset (&value);
13891 gst_buffer_unref (block);
13893 index += block_size;
13896 /* only append the metadata if we successfully read all of it */
13898 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
13899 (stream)->caps, 0), "streamheader", &array);
13901 GST_WARNING_OBJECT (qtdemux,
13902 "discarding all METADATA_BLOCKs due to invalid "
13903 "block_size %d at idx %d, rem %d", block_size, index,
13907 g_value_unset (&value);
13908 g_value_unset (&array);
13910 /* The sample rate obtained from the stsd may not be accurate
13911 * since it cannot represent rates greater than 65535Hz, so
13912 * override that value with the sample rate from the
13913 * METADATA_BLOCK_STREAMINFO block */
13914 CUR_STREAM (stream)->rate =
13915 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
13926 gint len = QT_UINT32 (stsd_entry_data);
13929 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13932 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13934 /* If we have enough data, let's try to get the 'damr' atom. See
13935 * the 3GPP container spec (26.244) for more details. */
13936 if ((len - 0x34) > 8 &&
13937 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
13938 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13939 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13942 gst_caps_set_simple (entry->caps,
13943 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13944 gst_buffer_unref (buf);
13950 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
13951 gint len = QT_UINT32 (stsd_entry_data);
13952 guint16 sound_version = 0;
13953 /* FIXME: Can this be determined somehow? There doesn't seem to be
13954 * anything in mp4a atom that specifis compression */
13956 guint16 channels = entry->n_channels;
13957 guint32 time_scale = (guint32) entry->rate;
13958 gint sample_rate_index = -1;
13961 sound_version = QT_UINT16 (stsd_entry_data + 16);
13963 if (sound_version == 1) {
13964 channels = QT_UINT16 (stsd_entry_data + 24);
13965 time_scale = QT_UINT32 (stsd_entry_data + 30);
13967 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13971 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13975 sample_rate_index =
13976 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13977 if (sample_rate_index >= 0 && channels > 0) {
13978 guint8 codec_data[2];
13981 /* build AAC codec data */
13982 codec_data[0] = profile << 3;
13983 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13984 codec_data[1] = (sample_rate_index & 0x01) << 7;
13985 codec_data[1] |= (channels & 0xF) << 3;
13987 buf = gst_buffer_new_and_alloc (2);
13988 gst_buffer_fill (buf, 0, codec_data, 2);
13989 gst_caps_set_simple (entry->caps,
13990 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13991 gst_buffer_unref (buf);
14002 /* Fully handled elsewhere */
14005 GST_INFO_OBJECT (qtdemux,
14006 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14010 GST_INFO_OBJECT (qtdemux,
14011 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14012 GST_FOURCC_ARGS (fourcc), entry->caps);
14014 } else if (stream->subtype == FOURCC_strm) {
14015 if (fourcc == FOURCC_rtsp) {
14016 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
14018 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
14019 GST_FOURCC_ARGS (fourcc));
14020 goto unknown_stream;
14022 entry->sampled = TRUE;
14023 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
14024 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
14025 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
14027 entry->sampled = TRUE;
14028 entry->sparse = TRUE;
14031 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14034 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14035 GST_TAG_SUBTITLE_CODEC, codec, NULL);
14040 /* hunt for sort-of codec data */
14044 GNode *mp4s = NULL;
14045 GNode *esds = NULL;
14047 /* look for palette in a stsd->mp4s->esds sub-atom */
14048 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
14050 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
14051 if (esds == NULL) {
14053 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
14057 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
14058 stream->stream_tags);
14062 GST_INFO_OBJECT (qtdemux,
14063 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14066 GST_INFO_OBJECT (qtdemux,
14067 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14068 GST_FOURCC_ARGS (fourcc), entry->caps);
14069 } else if (stream->subtype == FOURCC_meta) {
14070 entry->sampled = TRUE;
14071 entry->sparse = TRUE;
14074 qtdemux_meta_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14077 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14078 GST_TAG_CODEC, codec, NULL);
14083 GST_INFO_OBJECT (qtdemux,
14084 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14085 GST_FOURCC_ARGS (fourcc), entry->caps);
14087 /* everything in 1 sample */
14088 entry->sampled = TRUE;
14091 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14094 if (entry->caps == NULL)
14095 goto unknown_stream;
14098 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14099 GST_TAG_SUBTITLE_CODEC, codec, NULL);
14105 /* promote to sampled format */
14106 if (entry->fourcc == FOURCC_samr) {
14107 /* force mono 8000 Hz for AMR */
14108 entry->sampled = TRUE;
14109 entry->n_channels = 1;
14110 entry->rate = 8000;
14111 } else if (entry->fourcc == FOURCC_sawb) {
14112 /* force mono 16000 Hz for AMR-WB */
14113 entry->sampled = TRUE;
14114 entry->n_channels = 1;
14115 entry->rate = 16000;
14116 } else if (entry->fourcc == FOURCC_mp4a) {
14117 entry->sampled = TRUE;
14121 stsd_entry_data += len;
14122 remaining_stsd_len -= len;
14126 /* collect sample information */
14127 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
14128 goto samples_failed;
14130 if (qtdemux->fragmented) {
14133 /* need all moov samples as basis; probably not many if any at all */
14134 /* prevent moof parsing taking of at this time */
14135 offset = qtdemux->moof_offset;
14136 qtdemux->moof_offset = 0;
14137 if (stream->n_samples &&
14138 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
14139 qtdemux->moof_offset = offset;
14140 goto samples_failed;
14142 qtdemux->moof_offset = offset;
14143 /* movie duration more reliable in this case (e.g. mehd) */
14144 if (qtdemux->segment.duration &&
14145 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
14147 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
14150 /* configure segments */
14151 if (!qtdemux_parse_segments (qtdemux, stream, trak))
14152 goto segments_failed;
14154 /* add some language tag, if useful */
14155 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
14156 strcmp (stream->lang_id, "und")) {
14157 const gchar *lang_code;
14159 /* convert ISO 639-2 code to ISO 639-1 */
14160 lang_code = gst_tag_get_language_code (stream->lang_id);
14161 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14162 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
14165 /* Check for UDTA tags */
14166 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
14167 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
14170 /* Insert and sort new stream in track-id order.
14171 * This will help in comparing old/new streams during stream update check */
14172 g_ptr_array_add (qtdemux->active_streams, stream);
14173 g_ptr_array_sort (qtdemux->active_streams,
14174 (GCompareFunc) qtdemux_track_id_compare_func);
14175 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
14176 QTDEMUX_N_STREAMS (qtdemux));
14183 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
14184 (_("This file is corrupt and cannot be played.")), (NULL));
14186 gst_qtdemux_stream_unref (stream);
14191 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
14192 gst_qtdemux_stream_unref (stream);
14198 /* we posted an error already */
14199 /* free stbl sub-atoms */
14200 gst_qtdemux_stbl_free (stream);
14201 gst_qtdemux_stream_unref (stream);
14206 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
14212 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
14213 GST_FOURCC_ARGS (stream->subtype));
14214 gst_qtdemux_stream_unref (stream);
14219 /* If we can estimate the overall bitrate, and don't have information about the
14220 * stream bitrate for exactly one stream, this guesses the stream bitrate as
14221 * the overall bitrate minus the sum of the bitrates of all other streams. This
14222 * should be useful for the common case where we have one audio and one video
14223 * stream and can estimate the bitrate of one, but not the other. */
14225 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
14227 QtDemuxStream *stream = NULL;
14228 gint64 size, sys_bitrate, sum_bitrate = 0;
14229 GstClockTime duration;
14233 if (qtdemux->fragmented)
14236 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
14238 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
14240 GST_DEBUG_OBJECT (qtdemux,
14241 "Size in bytes of the stream not known - bailing");
14245 /* Subtract the header size */
14246 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
14247 size, qtdemux->header_size);
14249 if (size < qtdemux->header_size)
14252 size = size - qtdemux->header_size;
14254 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
14255 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
14259 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14260 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
14261 switch (str->subtype) {
14264 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
14265 CUR_STREAM (str)->caps);
14266 /* retrieve bitrate, prefer avg then max */
14268 if (str->stream_tags) {
14269 if (gst_tag_list_get_uint (str->stream_tags,
14270 GST_TAG_MAXIMUM_BITRATE, &bitrate))
14271 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
14272 if (gst_tag_list_get_uint (str->stream_tags,
14273 GST_TAG_NOMINAL_BITRATE, &bitrate))
14274 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
14275 if (gst_tag_list_get_uint (str->stream_tags,
14276 GST_TAG_BITRATE, &bitrate))
14277 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
14280 sum_bitrate += bitrate;
14283 GST_DEBUG_OBJECT (qtdemux,
14284 ">1 stream with unknown bitrate - bailing");
14291 /* For other subtypes, we assume no significant impact on bitrate */
14297 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
14301 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
14303 if (sys_bitrate < sum_bitrate) {
14304 /* This can happen, since sum_bitrate might be derived from maximum
14305 * bitrates and not average bitrates */
14306 GST_DEBUG_OBJECT (qtdemux,
14307 "System bitrate less than sum bitrate - bailing");
14311 bitrate = sys_bitrate - sum_bitrate;
14312 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
14313 ", Stream bitrate = %u", sys_bitrate, bitrate);
14315 if (!stream->stream_tags)
14316 stream->stream_tags = gst_tag_list_new_empty ();
14318 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
14320 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14321 GST_TAG_BITRATE, bitrate, NULL);
14324 static GstFlowReturn
14325 qtdemux_prepare_streams (GstQTDemux * qtdemux)
14327 GstFlowReturn ret = GST_FLOW_OK;
14328 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14329 guint64 tkhd_max_duration = 0;
14333 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
14335 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14336 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14337 guint32 sample_num = 0;
14339 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
14340 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
14342 if (qtdemux->fragmented && qtdemux->pullbased) {
14343 /* need all moov samples first */
14344 GST_OBJECT_LOCK (qtdemux);
14345 while (stream->n_samples == 0)
14346 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
14348 GST_OBJECT_UNLOCK (qtdemux);
14350 /* discard any stray moof */
14351 qtdemux->moof_offset = 0;
14352 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14353 if (tkhd_max_duration < stream->tkhd_duration)
14354 tkhd_max_duration = stream->tkhd_duration;
14358 /* prepare braking */
14359 if (ret != GST_FLOW_ERROR)
14362 /* in pull mode, we should have parsed some sample info by now;
14363 * and quite some code will not handle no samples.
14364 * in push mode, we'll just have to deal with it */
14365 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
14366 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
14367 g_ptr_array_remove_index (qtdemux->active_streams, i);
14370 } else if (stream->track_id == qtdemux->chapters_track_id &&
14371 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
14372 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
14373 so that it doesn't look like a subtitle track */
14374 g_ptr_array_remove_index (qtdemux->active_streams, i);
14379 /* parse the initial sample for use in setting the frame rate cap */
14380 while (sample_num == 0 && sample_num < stream->n_samples) {
14381 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
14387 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14388 if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
14389 GST_INFO_OBJECT (qtdemux,
14390 "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
14391 qtdemux->duration, tkhd_max_duration);
14392 qtdemux->duration = tkhd_max_duration;
14400 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
14402 return g_strcmp0 (stream->stream_id, stream_id) == 0;
14406 qtdemux_is_streams_update (GstQTDemux * qtdemux)
14410 /* Different length, updated */
14411 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
14414 /* streams in list are sorted in track-id order */
14415 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14416 /* Different stream-id, updated */
14417 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
14418 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
14426 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
14427 QtDemuxStream * oldstream, QtDemuxStream * newstream)
14429 /* Connect old stream's srcpad to new stream */
14430 newstream->pad = oldstream->pad;
14431 oldstream->pad = NULL;
14433 /* unset new_stream to prevent stream-start event, unless we are EOS in which
14434 * case we need to force one through */
14435 newstream->new_stream = newstream->pad != NULL
14436 && GST_PAD_IS_EOS (newstream->pad);
14438 return gst_qtdemux_configure_stream (qtdemux, newstream);
14442 qtdemux_update_streams (GstQTDemux * qtdemux)
14445 g_assert (qtdemux->streams_aware);
14447 /* At below, figure out which stream in active_streams has identical stream-id
14448 * with that of in old_streams. If there is matching stream-id,
14449 * corresponding newstream will not be exposed again,
14450 * but demux will reuse srcpad of matched old stream
14452 * active_streams : newly created streams from the latest moov
14453 * old_streams : existing streams (belong to previous moov)
14456 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14457 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14458 QtDemuxStream *oldstream = NULL;
14461 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
14462 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
14464 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
14465 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
14466 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
14468 /* null pad stream cannot be reused */
14469 if (oldstream->pad == NULL)
14474 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
14476 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
14479 /* we don't need to preserve order of old streams */
14480 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
14484 /* now we have all info and can expose */
14485 list = stream->stream_tags;
14486 stream->stream_tags = NULL;
14487 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14495 /* Must be called with expose lock */
14496 static GstFlowReturn
14497 qtdemux_expose_streams (GstQTDemux * qtdemux)
14501 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
14503 if (!qtdemux_is_streams_update (qtdemux)) {
14504 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
14505 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14506 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14507 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14508 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
14509 return GST_FLOW_ERROR;
14512 g_ptr_array_set_size (qtdemux->old_streams, 0);
14513 qtdemux->need_segment = TRUE;
14515 return GST_FLOW_OK;
14518 if (qtdemux->streams_aware) {
14519 if (!qtdemux_update_streams (qtdemux))
14520 return GST_FLOW_ERROR;
14522 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14523 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14526 /* now we have all info and can expose */
14527 list = stream->stream_tags;
14528 stream->stream_tags = NULL;
14529 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14530 return GST_FLOW_ERROR;
14535 gst_qtdemux_guess_bitrate (qtdemux);
14537 /* If we have still old_streams, it's no more used stream */
14538 for (i = 0; i < qtdemux->old_streams->len; i++) {
14539 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14544 event = gst_event_new_eos ();
14545 if (qtdemux->segment_seqnum)
14546 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14548 gst_pad_push_event (stream->pad, event);
14552 g_ptr_array_set_size (qtdemux->old_streams, 0);
14554 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14556 /* check if we should post a redirect in case there is a single trak
14557 * and it is a redirecting trak */
14558 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14559 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
14562 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14563 "an external content");
14564 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14565 gst_structure_new ("redirect",
14566 "new-location", G_TYPE_STRING,
14567 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
14568 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14569 g_free (qtdemux->redirect_location);
14570 qtdemux->redirect_location =
14571 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
14574 g_ptr_array_foreach (qtdemux->active_streams,
14575 (GFunc) qtdemux_do_allocation, qtdemux);
14577 qtdemux->need_segment = TRUE;
14579 qtdemux->exposed = TRUE;
14580 return GST_FLOW_OK;
14585 GstStructure *structure; /* helper for sort function */
14587 guint min_req_bitrate;
14588 guint min_req_qt_version;
14592 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14594 GstQtReference *ref_a = (GstQtReference *) a;
14595 GstQtReference *ref_b = (GstQtReference *) b;
14597 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14598 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14600 /* known bitrates go before unknown; higher bitrates go first */
14601 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14604 /* sort the redirects and post a message for the application.
14607 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14609 GstQtReference *best;
14612 GValue list_val = { 0, };
14615 g_assert (references != NULL);
14617 references = g_list_sort (references, qtdemux_redirects_sort_func);
14619 best = (GstQtReference *) references->data;
14621 g_value_init (&list_val, GST_TYPE_LIST);
14623 for (l = references; l != NULL; l = l->next) {
14624 GstQtReference *ref = (GstQtReference *) l->data;
14625 GValue struct_val = { 0, };
14627 ref->structure = gst_structure_new ("redirect",
14628 "new-location", G_TYPE_STRING, ref->location, NULL);
14630 if (ref->min_req_bitrate > 0) {
14631 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14632 ref->min_req_bitrate, NULL);
14635 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14636 g_value_set_boxed (&struct_val, ref->structure);
14637 gst_value_list_append_value (&list_val, &struct_val);
14638 g_value_unset (&struct_val);
14639 /* don't free anything here yet, since we need best->structure below */
14642 g_assert (best != NULL);
14643 s = gst_structure_copy (best->structure);
14645 if (g_list_length (references) > 1) {
14646 gst_structure_set_value (s, "locations", &list_val);
14649 g_value_unset (&list_val);
14651 for (l = references; l != NULL; l = l->next) {
14652 GstQtReference *ref = (GstQtReference *) l->data;
14654 gst_structure_free (ref->structure);
14655 g_free (ref->location);
14658 g_list_free (references);
14660 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14661 g_free (qtdemux->redirect_location);
14662 qtdemux->redirect_location =
14663 g_strdup (gst_structure_get_string (s, "new-location"));
14664 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14665 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14668 /* look for redirect nodes, collect all redirect information and
14672 qtdemux_parse_redirects (GstQTDemux * qtdemux)
14674 GNode *rmra, *rmda, *rdrf;
14676 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14678 GList *redirects = NULL;
14680 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14682 GstQtReference ref = { NULL, NULL, 0, 0 };
14683 GNode *rmdr, *rmvc;
14685 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14686 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14687 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14688 ref.min_req_bitrate);
14691 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14692 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14693 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14695 #ifndef GST_DISABLE_GST_DEBUG
14696 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14698 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14700 GST_LOG_OBJECT (qtdemux,
14701 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14702 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14703 bitmask, check_type);
14704 if (package == FOURCC_qtim && check_type == 0) {
14705 ref.min_req_qt_version = version;
14709 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14715 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14716 if (ref_len > 20) {
14717 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14718 ref_data = (guint8 *) rdrf->data + 20;
14719 if (ref_type == FOURCC_alis) {
14720 guint record_len, record_version, fn_len;
14722 if (ref_len > 70) {
14723 /* MacOSX alias record, google for alias-layout.txt */
14724 record_len = QT_UINT16 (ref_data + 4);
14725 record_version = QT_UINT16 (ref_data + 4 + 2);
14726 fn_len = QT_UINT8 (ref_data + 50);
14727 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14728 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14731 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14734 } else if (ref_type == FOURCC_url_) {
14735 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14737 GST_DEBUG_OBJECT (qtdemux,
14738 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14739 GST_FOURCC_ARGS (ref_type));
14741 if (ref.location != NULL) {
14742 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14744 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14746 GST_WARNING_OBJECT (qtdemux,
14747 "Failed to extract redirect location from rdrf atom");
14750 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14754 /* look for others */
14755 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14758 if (redirects != NULL) {
14759 qtdemux_process_redirects (qtdemux, redirects);
14765 static GstTagList *
14766 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14770 if (tags == NULL) {
14771 tags = gst_tag_list_new_empty ();
14772 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14775 if (qtdemux->major_brand == FOURCC_mjp2)
14776 fmt = "Motion JPEG 2000";
14777 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14779 else if (qtdemux->major_brand == FOURCC_qt__)
14781 else if (qtdemux->fragmented)
14784 fmt = "ISO MP4/M4A";
14786 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14787 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14789 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14795 /* we have read the complete moov node now.
14796 * This function parses all of the relevant info, creates the traks and
14797 * prepares all data structures for playback
14800 qtdemux_parse_tree (GstQTDemux * qtdemux)
14807 guint64 creation_time;
14808 GstDateTime *datetime = NULL;
14811 /* make sure we have a usable taglist */
14812 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14814 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14815 if (mvhd == NULL) {
14816 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14817 return qtdemux_parse_redirects (qtdemux);
14820 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14821 if (version == 1) {
14822 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14823 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14824 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14825 } else if (version == 0) {
14826 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14827 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14828 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14830 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14834 /* Moving qt creation time (secs since 1904) to unix time */
14835 if (creation_time != 0) {
14836 /* Try to use epoch first as it should be faster and more commonly found */
14837 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14840 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14841 /* some data cleansing sanity */
14842 now_s = g_get_real_time () / G_USEC_PER_SEC;
14843 if (now_s + 24 * 3600 < creation_time) {
14844 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14846 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14849 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14850 GDateTime *dt, *dt_local;
14852 dt = g_date_time_add_seconds (base_dt, creation_time);
14853 dt_local = g_date_time_to_local (dt);
14854 datetime = gst_date_time_new_from_g_date_time (dt_local);
14856 g_date_time_unref (base_dt);
14857 g_date_time_unref (dt);
14861 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14862 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14864 gst_date_time_unref (datetime);
14867 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14868 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14870 /* check for fragmented file and get some (default) data */
14871 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14874 GstByteReader mehd_data;
14876 /* let track parsing or anyone know weird stuff might happen ... */
14877 qtdemux->fragmented = TRUE;
14879 /* compensate for total duration */
14880 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14882 qtdemux_parse_mehd (qtdemux, &mehd_data);
14885 /* Update the movie segment duration, unless it was directly given to us
14886 * by upstream. Otherwise let it as is, as we don't want to mangle the
14887 * duration provided by upstream that may come e.g. from a MPD file. */
14888 if (!qtdemux->upstream_format_is_time) {
14889 GstClockTime duration;
14890 /* set duration in the segment info */
14891 gst_qtdemux_get_duration (qtdemux, &duration);
14892 qtdemux->segment.duration = duration;
14893 /* also do not exceed duration; stop is set that way post seek anyway,
14894 * and segment activation falls back to duration,
14895 * whereas loop only checks stop, so let's align this here as well */
14896 qtdemux->segment.stop = duration;
14899 /* parse all traks */
14900 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14902 qtdemux_parse_trak (qtdemux, trak);
14903 /* iterate all siblings */
14904 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14907 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14910 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14912 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14914 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14917 /* maybe also some tags in meta box */
14918 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14920 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14921 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14923 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14926 /* parse any protection system info */
14927 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14929 /* Unref old protection events if we are going to receive new ones. */
14930 qtdemux_clear_protection_events_on_all_streams (qtdemux);
14933 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14934 qtdemux_parse_pssh (qtdemux, pssh);
14935 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14938 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14943 /* taken from ffmpeg */
14945 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14957 len = (len << 7) | (c & 0x7f);
14966 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14967 gsize codec_data_size)
14969 GList *list = NULL;
14970 guint8 *p = codec_data;
14971 gint i, offset, num_packets;
14972 guint *length, last;
14974 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14976 if (codec_data == NULL || codec_data_size == 0)
14979 /* start of the stream and vorbis audio or theora video, need to
14980 * send the codec_priv data as first three packets */
14981 num_packets = p[0] + 1;
14982 GST_DEBUG_OBJECT (qtdemux,
14983 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14984 (guint) num_packets, codec_data_size);
14986 /* Let's put some limits, Don't think there even is a xiph codec
14987 * with more than 3-4 headers */
14988 if (G_UNLIKELY (num_packets > 16)) {
14989 GST_WARNING_OBJECT (qtdemux,
14990 "Unlikely number of xiph headers, most likely not valid");
14994 length = g_alloca (num_packets * sizeof (guint));
14998 /* first packets, read length values */
14999 for (i = 0; i < num_packets - 1; i++) {
15001 while (offset < codec_data_size) {
15002 length[i] += p[offset];
15003 if (p[offset++] != 0xff)
15008 if (offset + last > codec_data_size)
15011 /* last packet is the remaining size */
15012 length[i] = codec_data_size - offset - last;
15014 for (i = 0; i < num_packets; i++) {
15017 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
15019 if (offset + length[i] > codec_data_size)
15022 hdr = gst_buffer_new_memdup (p + offset, length[i]);
15023 list = g_list_append (list, hdr);
15025 offset += length[i];
15034 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
15040 /* this can change the codec originally present in @list */
15042 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
15043 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
15045 int len = QT_UINT32 (esds->data);
15046 guint8 *ptr = esds->data;
15047 guint8 *end = ptr + len;
15049 guint8 *data_ptr = NULL;
15051 guint8 object_type_id = 0;
15052 guint8 stream_type = 0;
15053 const char *codec_name = NULL;
15054 GstCaps *caps = NULL;
15056 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
15058 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
15060 while (ptr + 1 < end) {
15061 tag = QT_UINT8 (ptr);
15062 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
15064 len = read_descr_size (ptr, end, &ptr);
15065 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
15067 /* Check the stated amount of data is available for reading */
15068 if (len < 0 || ptr + len > end)
15072 case ES_DESCRIPTOR_TAG:
15073 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
15074 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
15077 case DECODER_CONFIG_DESC_TAG:{
15078 guint max_bitrate, avg_bitrate;
15080 object_type_id = QT_UINT8 (ptr);
15081 stream_type = QT_UINT8 (ptr + 1) >> 2;
15082 max_bitrate = QT_UINT32 (ptr + 5);
15083 avg_bitrate = QT_UINT32 (ptr + 9);
15084 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
15085 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
15086 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
15087 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
15088 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
15089 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
15090 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15091 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
15093 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
15094 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
15095 avg_bitrate, NULL);
15100 case DECODER_SPECIFIC_INFO_TAG:
15101 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
15102 if (object_type_id == 0xe0 && len == 0x40) {
15108 GST_DEBUG_OBJECT (qtdemux,
15109 "Have VOBSUB palette. Creating palette event");
15110 /* move to decConfigDescr data and read palette */
15112 for (i = 0; i < 16; i++) {
15113 clut[i] = QT_UINT32 (data);
15117 s = gst_structure_new ("application/x-gst-dvd", "event",
15118 G_TYPE_STRING, "dvd-spu-clut-change",
15119 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
15120 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
15121 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
15122 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
15123 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
15124 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
15125 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
15126 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
15129 /* store event and trigger custom processing */
15130 stream->pending_event =
15131 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
15133 /* Generic codec_data handler puts it on the caps */
15140 case SL_CONFIG_DESC_TAG:
15141 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
15145 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
15147 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
15153 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
15154 * in use, and should also be used to override some other parameters for some
15156 switch (object_type_id) {
15157 case 0x20: /* MPEG-4 */
15158 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
15159 * profile_and_level_indication */
15160 if (data_ptr != NULL && data_len >= 5 &&
15161 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
15162 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
15163 data_ptr + 4, data_len - 4);
15165 break; /* Nothing special needed here */
15166 case 0x21: /* H.264 */
15167 codec_name = "H.264 / AVC";
15168 caps = gst_caps_new_simple ("video/x-h264",
15169 "stream-format", G_TYPE_STRING, "avc",
15170 "alignment", G_TYPE_STRING, "au", NULL);
15172 case 0x40: /* AAC (any) */
15173 case 0x66: /* AAC Main */
15174 case 0x67: /* AAC LC */
15175 case 0x68: /* AAC SSR */
15176 /* Override channels and rate based on the codec_data, as it's often
15178 /* Only do so for basic setup without HE-AAC extension */
15179 if (data_ptr && data_len == 2) {
15180 guint channels, rate;
15182 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
15184 entry->n_channels = channels;
15186 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15188 entry->rate = rate;
15191 /* Set level and profile if possible */
15192 if (data_ptr != NULL && data_len >= 2) {
15193 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15194 data_ptr, data_len);
15196 const gchar *profile_str = NULL;
15199 guint8 *codec_data;
15200 gint rate_idx, profile;
15202 /* No codec_data, let's invent something.
15203 * FIXME: This is wrong for SBR! */
15205 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15207 buffer = gst_buffer_new_and_alloc (2);
15208 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15209 codec_data = map.data;
15212 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
15215 switch (object_type_id) {
15217 profile_str = "main";
15221 profile_str = "lc";
15225 profile_str = "ssr";
15233 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15235 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
15237 gst_buffer_unmap (buffer, &map);
15238 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
15239 GST_TYPE_BUFFER, buffer, NULL);
15240 gst_buffer_unref (buffer);
15243 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
15244 G_TYPE_STRING, profile_str, NULL);
15248 case 0x60: /* MPEG-2, various profiles */
15254 codec_name = "MPEG-2 video";
15255 caps = gst_caps_new_simple ("video/mpeg",
15256 "mpegversion", G_TYPE_INT, 2,
15257 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15259 case 0x69: /* MPEG-2 BC audio */
15260 case 0x6B: /* MPEG-1 audio */
15261 caps = gst_caps_new_simple ("audio/mpeg",
15262 "mpegversion", G_TYPE_INT, 1, NULL);
15263 codec_name = "MPEG-1 audio";
15265 case 0x6A: /* MPEG-1 */
15266 codec_name = "MPEG-1 video";
15267 caps = gst_caps_new_simple ("video/mpeg",
15268 "mpegversion", G_TYPE_INT, 1,
15269 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15271 case 0x6C: /* MJPEG */
15273 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15275 codec_name = "Motion-JPEG";
15277 case 0x6D: /* PNG */
15279 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15281 codec_name = "PNG still images";
15283 case 0x6E: /* JPEG2000 */
15284 codec_name = "JPEG-2000";
15285 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15287 case 0xA4: /* Dirac */
15288 codec_name = "Dirac";
15289 caps = gst_caps_new_empty_simple ("video/x-dirac");
15291 case 0xA5: /* AC3 */
15292 codec_name = "AC-3 audio";
15293 caps = gst_caps_new_simple ("audio/x-ac3",
15294 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15296 case 0xA9: /* AC3 */
15297 codec_name = "DTS audio";
15298 caps = gst_caps_new_simple ("audio/x-dts",
15299 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15302 if (stream_type == 0x05 && data_ptr) {
15304 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
15307 GValue arr_val = G_VALUE_INIT;
15308 GValue buf_val = G_VALUE_INIT;
15311 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15312 codec_name = "Vorbis";
15313 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15314 g_value_init (&arr_val, GST_TYPE_ARRAY);
15315 g_value_init (&buf_val, GST_TYPE_BUFFER);
15316 for (tmp = headers; tmp; tmp = tmp->next) {
15317 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15318 gst_value_array_append_value (&arr_val, &buf_val);
15320 s = gst_caps_get_structure (caps, 0);
15321 gst_structure_take_value (s, "streamheader", &arr_val);
15322 g_value_unset (&buf_val);
15323 g_list_free (headers);
15330 case 0xE1: /* QCELP */
15331 /* QCELP, the codec_data is a riff tag (little endian) with
15332 * 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). */
15333 caps = gst_caps_new_empty_simple ("audio/qcelp");
15334 codec_name = "QCELP";
15340 /* If we have a replacement caps, then change our caps for this stream */
15342 gst_caps_unref (entry->caps);
15343 entry->caps = caps;
15346 if (codec_name && list)
15347 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15348 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15350 /* Add the codec_data attribute to caps, if we have it */
15354 buffer = gst_buffer_new_and_alloc (data_len);
15355 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15357 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15358 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15360 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15362 gst_buffer_unref (buffer);
15367 static inline GstCaps *
15368 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
15372 char *s, fourstr[5];
15374 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15375 for (i = 0; i < 4; i++) {
15376 if (!g_ascii_isalnum (fourstr[i]))
15379 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15380 caps = gst_caps_new_empty_simple (s);
15385 #define _codec(name) \
15387 if (codec_name) { \
15388 *codec_name = g_strdup (name); \
15393 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15394 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15395 const guint8 * stsd_entry_data, gchar ** codec_name)
15397 GstCaps *caps = NULL;
15398 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15402 _codec ("PNG still images");
15403 caps = gst_caps_new_empty_simple ("image/png");
15406 _codec ("JPEG still images");
15408 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15411 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15412 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15413 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15414 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
15415 _codec ("Motion-JPEG");
15417 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15420 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
15421 _codec ("Motion-JPEG format B");
15422 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15425 _codec ("JPEG-2000");
15426 /* override to what it should be according to spec, avoid palette_data */
15427 entry->bits_per_sample = 24;
15428 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15431 _codec ("Sorensen video v.3");
15432 caps = gst_caps_new_simple ("video/x-svq",
15433 "svqversion", G_TYPE_INT, 3, NULL);
15435 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15436 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
15437 _codec ("Sorensen video v.1");
15438 caps = gst_caps_new_simple ("video/x-svq",
15439 "svqversion", G_TYPE_INT, 1, NULL);
15441 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15442 caps = gst_caps_new_empty_simple ("video/x-raw");
15443 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
15444 _codec ("Windows Raw RGB");
15445 stream->alignment = 32;
15451 bps = QT_UINT16 (stsd_entry_data + 82);
15454 format = GST_VIDEO_FORMAT_RGB15;
15457 format = GST_VIDEO_FORMAT_RGB16;
15460 format = GST_VIDEO_FORMAT_RGB;
15463 format = GST_VIDEO_FORMAT_ARGB;
15471 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15472 format = GST_VIDEO_FORMAT_I420;
15474 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15475 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15476 format = GST_VIDEO_FORMAT_I420;
15479 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15480 format = GST_VIDEO_FORMAT_UYVY;
15482 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15483 format = GST_VIDEO_FORMAT_v308;
15485 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15486 format = GST_VIDEO_FORMAT_v216;
15489 format = GST_VIDEO_FORMAT_v210;
15491 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15492 format = GST_VIDEO_FORMAT_r210;
15494 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15495 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15496 format = GST_VIDEO_FORMAT_v410;
15499 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15500 * but different order than AYUV
15501 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15502 format = GST_VIDEO_FORMAT_v408;
15505 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15506 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
15507 _codec ("MPEG-1 video");
15508 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15509 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15511 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15512 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15513 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15514 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15515 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15516 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15517 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15518 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15519 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15520 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15521 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15522 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15523 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15524 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15525 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15526 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15527 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15528 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15529 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15530 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15531 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15532 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15533 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15534 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15535 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15536 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15537 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15538 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15539 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15540 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15541 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15542 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15543 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15544 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15545 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15546 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15547 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15548 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15549 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15550 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15551 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15552 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15553 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15554 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15555 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15556 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15557 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15558 _codec ("MPEG-2 video");
15559 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15560 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15562 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15563 _codec ("GIF still images");
15564 caps = gst_caps_new_empty_simple ("image/gif");
15567 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15569 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15571 /* ffmpeg uses the height/width props, don't know why */
15572 caps = gst_caps_new_simple ("video/x-h263",
15573 "variant", G_TYPE_STRING, "itu", NULL);
15577 _codec ("MPEG-4 video");
15578 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15579 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15581 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15582 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15583 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15584 caps = gst_caps_new_simple ("video/x-msmpeg",
15585 "msmpegversion", G_TYPE_INT, 43, NULL);
15587 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15589 caps = gst_caps_new_simple ("video/x-divx",
15590 "divxversion", G_TYPE_INT, 3, NULL);
15592 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15593 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15595 caps = gst_caps_new_simple ("video/x-divx",
15596 "divxversion", G_TYPE_INT, 4, NULL);
15598 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15600 caps = gst_caps_new_simple ("video/x-divx",
15601 "divxversion", G_TYPE_INT, 5, NULL);
15604 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15606 caps = gst_caps_new_simple ("video/x-ffv",
15607 "ffvversion", G_TYPE_INT, 1, NULL);
15610 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15611 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15616 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15617 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15618 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15622 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15623 _codec ("Cinepak");
15624 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15626 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15627 _codec ("Apple QuickDraw");
15628 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15630 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15631 _codec ("Apple video");
15632 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15637 _codec ("H.264 / AVC");
15638 caps = gst_caps_new_simple ("video/x-h264",
15639 "stream-format", G_TYPE_STRING, "avc",
15640 "alignment", G_TYPE_STRING, "au", NULL);
15644 _codec ("H.264 / AVC");
15645 caps = gst_caps_new_simple ("video/x-h264",
15646 "stream-format", G_TYPE_STRING, "avc3",
15647 "alignment", G_TYPE_STRING, "au", NULL);
15661 _codec ("H.264 / AVC");
15662 caps = gst_caps_new_simple ("video/x-h264",
15663 "stream-format", G_TYPE_STRING, "byte-stream",
15664 "alignment", G_TYPE_STRING, "au", NULL);
15669 _codec ("H.265 / HEVC");
15670 caps = gst_caps_new_simple ("video/x-h265",
15671 "stream-format", G_TYPE_STRING, "hvc1",
15672 "alignment", G_TYPE_STRING, "au", NULL);
15676 _codec ("H.265 / HEVC");
15677 caps = gst_caps_new_simple ("video/x-h265",
15678 "stream-format", G_TYPE_STRING, "hev1",
15679 "alignment", G_TYPE_STRING, "au", NULL);
15682 _codec ("Run-length encoding");
15683 caps = gst_caps_new_simple ("video/x-rle",
15684 "layout", G_TYPE_STRING, "quicktime", NULL);
15687 _codec ("Run-length encoding");
15688 caps = gst_caps_new_simple ("video/x-rle",
15689 "layout", G_TYPE_STRING, "microsoft", NULL);
15691 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15692 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15693 _codec ("Indeo Video 3");
15694 caps = gst_caps_new_simple ("video/x-indeo",
15695 "indeoversion", G_TYPE_INT, 3, NULL);
15697 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15698 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15699 _codec ("Intel Video 4");
15700 caps = gst_caps_new_simple ("video/x-indeo",
15701 "indeoversion", G_TYPE_INT, 4, NULL);
15705 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15706 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15707 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15708 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15709 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15710 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15711 _codec ("DV Video");
15712 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15713 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15715 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15716 case FOURCC_dv5p: /* DVCPRO50 PAL */
15717 _codec ("DVCPro50 Video");
15718 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15719 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15721 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15722 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15723 _codec ("DVCProHD Video");
15724 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15725 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15727 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15728 _codec ("Apple Graphics (SMC)");
15729 caps = gst_caps_new_empty_simple ("video/x-smc");
15731 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15733 caps = gst_caps_new_empty_simple ("video/x-vp3");
15735 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15736 _codec ("VP6 Flash");
15737 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15741 caps = gst_caps_new_empty_simple ("video/x-theora");
15742 /* theora uses one byte of padding in the data stream because it does not
15743 * allow 0 sized packets while theora does */
15744 entry->padding = 1;
15748 caps = gst_caps_new_empty_simple ("video/x-dirac");
15750 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15751 _codec ("TIFF still images");
15752 caps = gst_caps_new_empty_simple ("image/tiff");
15754 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15755 _codec ("Apple Intermediate Codec");
15756 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15758 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15759 _codec ("AVID DNxHD");
15760 caps = gst_caps_from_string ("video/x-dnxhd");
15764 _codec ("On2 VP8");
15765 caps = gst_caps_from_string ("video/x-vp8");
15768 _codec ("Google VP9");
15769 caps = gst_caps_from_string ("video/x-vp9");
15772 _codec ("Apple ProRes LT");
15774 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15778 _codec ("Apple ProRes HQ");
15780 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15784 _codec ("Apple ProRes");
15786 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15790 _codec ("Apple ProRes Proxy");
15792 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15796 _codec ("Apple ProRes 4444");
15798 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15801 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15802 if (entry->bits_per_sample > 0) {
15803 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15808 _codec ("Apple ProRes 4444 XQ");
15810 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15813 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15814 if (entry->bits_per_sample > 0) {
15815 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15820 _codec ("GoPro CineForm");
15821 caps = gst_caps_from_string ("video/x-cineform");
15826 caps = gst_caps_new_simple ("video/x-wmv",
15827 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15831 caps = gst_caps_new_simple ("video/x-av1",
15832 "stream-format", G_TYPE_STRING, "obu-stream",
15833 "alignment", G_TYPE_STRING, "tu", NULL);
15835 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15838 caps = _get_unknown_codec_name ("video", fourcc);
15843 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15846 gst_video_info_init (&info);
15847 gst_video_info_set_format (&info, format, entry->width, entry->height);
15849 caps = gst_video_info_to_caps (&info);
15850 *codec_name = gst_pb_utils_get_codec_description (caps);
15852 /* enable clipping for raw video streams */
15853 stream->need_clip = TRUE;
15854 stream->alignment = 32;
15861 round_up_pow2 (guint n)
15873 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15874 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15875 int len, gchar ** codec_name)
15878 const GstStructure *s;
15881 GstAudioFormat format = 0;
15884 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15886 depth = entry->bytes_per_packet * 8;
15889 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15891 /* 8-bit audio is unsigned */
15893 format = GST_AUDIO_FORMAT_U8;
15894 /* otherwise it's signed and big-endian just like 'twos' */
15896 endian = G_BIG_ENDIAN;
15903 endian = G_LITTLE_ENDIAN;
15906 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15908 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15912 caps = gst_caps_new_simple ("audio/x-raw",
15913 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15914 "layout", G_TYPE_STRING, "interleaved", NULL);
15915 stream->alignment = GST_ROUND_UP_8 (depth);
15916 stream->alignment = round_up_pow2 (stream->alignment);
15920 _codec ("Raw 64-bit floating-point audio");
15921 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15923 caps = gst_caps_new_simple ("audio/x-raw",
15924 "format", G_TYPE_STRING, "F64BE",
15925 "layout", G_TYPE_STRING, "interleaved", NULL);
15926 stream->alignment = 8;
15929 _codec ("Raw 32-bit floating-point audio");
15930 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15932 caps = gst_caps_new_simple ("audio/x-raw",
15933 "format", G_TYPE_STRING, "F32BE",
15934 "layout", G_TYPE_STRING, "interleaved", NULL);
15935 stream->alignment = 4;
15938 _codec ("Raw 24-bit PCM audio");
15939 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15941 caps = gst_caps_new_simple ("audio/x-raw",
15942 "format", G_TYPE_STRING, "S24BE",
15943 "layout", G_TYPE_STRING, "interleaved", NULL);
15944 stream->alignment = 4;
15947 _codec ("Raw 32-bit PCM audio");
15948 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15950 caps = gst_caps_new_simple ("audio/x-raw",
15951 "format", G_TYPE_STRING, "S32BE",
15952 "layout", G_TYPE_STRING, "interleaved", NULL);
15953 stream->alignment = 4;
15956 _codec ("Raw 16-bit PCM audio");
15957 caps = gst_caps_new_simple ("audio/x-raw",
15958 "format", G_TYPE_STRING, "S16LE",
15959 "layout", G_TYPE_STRING, "interleaved", NULL);
15960 stream->alignment = 2;
15963 _codec ("Mu-law audio");
15964 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15967 _codec ("A-law audio");
15968 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15972 _codec ("Microsoft ADPCM");
15973 /* Microsoft ADPCM-ACM code 2 */
15974 caps = gst_caps_new_simple ("audio/x-adpcm",
15975 "layout", G_TYPE_STRING, "microsoft", NULL);
15979 _codec ("DVI/IMA ADPCM");
15980 caps = gst_caps_new_simple ("audio/x-adpcm",
15981 "layout", G_TYPE_STRING, "dvi", NULL);
15985 _codec ("DVI/Intel IMA ADPCM");
15986 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15987 caps = gst_caps_new_simple ("audio/x-adpcm",
15988 "layout", G_TYPE_STRING, "quicktime", NULL);
15992 /* MPEG layer 3, CBR only (pre QT4.1) */
15995 _codec ("MPEG-1 layer 3");
15996 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15997 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15998 "mpegversion", G_TYPE_INT, 1, NULL);
16000 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
16001 _codec ("MPEG-1 layer 2");
16003 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
16004 "mpegversion", G_TYPE_INT, 1, NULL);
16007 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
16008 _codec ("EAC-3 audio");
16009 caps = gst_caps_new_simple ("audio/x-eac3",
16010 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16011 entry->sampled = TRUE;
16013 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
16015 _codec ("AC-3 audio");
16016 caps = gst_caps_new_simple ("audio/x-ac3",
16017 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16018 entry->sampled = TRUE;
16020 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
16021 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
16022 _codec ("DTS audio");
16023 caps = gst_caps_new_simple ("audio/x-dts",
16024 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16025 entry->sampled = TRUE;
16027 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
16028 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
16029 _codec ("DTS-HD audio");
16030 caps = gst_caps_new_simple ("audio/x-dts",
16031 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16032 entry->sampled = TRUE;
16036 caps = gst_caps_new_simple ("audio/x-mace",
16037 "maceversion", G_TYPE_INT, 3, NULL);
16041 caps = gst_caps_new_simple ("audio/x-mace",
16042 "maceversion", G_TYPE_INT, 6, NULL);
16044 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
16046 caps = gst_caps_new_empty_simple ("application/ogg");
16048 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
16049 _codec ("DV audio");
16050 caps = gst_caps_new_empty_simple ("audio/x-dv");
16053 _codec ("MPEG-4 AAC audio");
16054 caps = gst_caps_new_simple ("audio/mpeg",
16055 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
16056 "stream-format", G_TYPE_STRING, "raw", NULL);
16058 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
16059 _codec ("QDesign Music");
16060 caps = gst_caps_new_empty_simple ("audio/x-qdm");
16063 _codec ("QDesign Music v.2");
16064 /* FIXME: QDesign music version 2 (no constant) */
16065 if (FALSE && data) {
16066 caps = gst_caps_new_simple ("audio/x-qdm2",
16067 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
16068 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
16069 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
16071 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
16075 _codec ("GSM audio");
16076 caps = gst_caps_new_empty_simple ("audio/x-gsm");
16079 _codec ("AMR audio");
16080 caps = gst_caps_new_empty_simple ("audio/AMR");
16083 _codec ("AMR-WB audio");
16084 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
16087 _codec ("Quicktime IMA ADPCM");
16088 caps = gst_caps_new_simple ("audio/x-adpcm",
16089 "layout", G_TYPE_STRING, "quicktime", NULL);
16092 _codec ("Apple lossless audio");
16093 caps = gst_caps_new_empty_simple ("audio/x-alac");
16096 _codec ("Free Lossless Audio Codec");
16097 caps = gst_caps_new_simple ("audio/x-flac",
16098 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16100 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
16101 _codec ("QualComm PureVoice");
16102 caps = gst_caps_from_string ("audio/qcelp");
16107 caps = gst_caps_new_empty_simple ("audio/x-wma");
16111 caps = gst_caps_new_empty_simple ("audio/x-opus");
16118 GstAudioFormat format;
16121 FLAG_IS_FLOAT = 0x1,
16122 FLAG_IS_BIG_ENDIAN = 0x2,
16123 FLAG_IS_SIGNED = 0x4,
16124 FLAG_IS_PACKED = 0x8,
16125 FLAG_IS_ALIGNED_HIGH = 0x10,
16126 FLAG_IS_NON_INTERLEAVED = 0x20
16128 _codec ("Raw LPCM audio");
16130 if (data && len >= 36) {
16131 depth = QT_UINT32 (data + 24);
16132 flags = QT_UINT32 (data + 28);
16133 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
16135 if ((flags & FLAG_IS_FLOAT) == 0) {
16140 if ((flags & FLAG_IS_ALIGNED_HIGH))
16143 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
16144 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
16145 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
16146 caps = gst_caps_new_simple ("audio/x-raw",
16147 "format", G_TYPE_STRING,
16149 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
16150 "UNKNOWN", "layout", G_TYPE_STRING,
16151 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
16152 "interleaved", NULL);
16153 stream->alignment = GST_ROUND_UP_8 (depth);
16154 stream->alignment = round_up_pow2 (stream->alignment);
16159 if (flags & FLAG_IS_BIG_ENDIAN)
16160 format = GST_AUDIO_FORMAT_F64BE;
16162 format = GST_AUDIO_FORMAT_F64LE;
16164 if (flags & FLAG_IS_BIG_ENDIAN)
16165 format = GST_AUDIO_FORMAT_F32BE;
16167 format = GST_AUDIO_FORMAT_F32LE;
16169 caps = gst_caps_new_simple ("audio/x-raw",
16170 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
16171 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
16172 "non-interleaved" : "interleaved", NULL);
16173 stream->alignment = width / 8;
16177 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
16180 caps = gst_caps_new_empty_simple ("audio/x-ac4");
16183 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
16187 caps = _get_unknown_codec_name ("audio", fourcc);
16193 GstCaps *templ_caps =
16194 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
16195 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16196 gst_caps_unref (caps);
16197 gst_caps_unref (templ_caps);
16198 caps = intersection;
16201 /* enable clipping for raw audio streams */
16202 s = gst_caps_get_structure (caps, 0);
16203 name = gst_structure_get_name (s);
16204 if (g_str_has_prefix (name, "audio/x-raw")) {
16205 stream->need_clip = TRUE;
16206 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
16207 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
16208 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
16209 stream->max_buffer_size);
16215 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16216 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16217 const guint8 * stsd_entry_data, gchar ** codec_name)
16221 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16225 _codec ("DVD subtitle");
16226 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
16227 stream->process_func = gst_qtdemux_process_buffer_dvd;
16230 _codec ("Quicktime timed text");
16233 _codec ("3GPP timed text");
16235 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16237 /* actual text piece needs to be extracted */
16238 stream->process_func = gst_qtdemux_process_buffer_text;
16241 _codec ("XML subtitles");
16242 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16247 const gchar *buf = "WEBVTT\n\n";
16249 _codec ("WebVTT subtitles");
16250 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
16251 stream->process_func = gst_qtdemux_process_buffer_wvtt;
16253 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
16254 buffer = gst_buffer_new_and_alloc (8);
16255 gst_buffer_fill (buffer, 0, buf, 8);
16256 stream->buffers = g_slist_append (stream->buffers, buffer);
16261 _codec ("CEA 608 Closed Caption");
16263 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16264 G_TYPE_STRING, "s334-1a", NULL);
16265 stream->process_func = gst_qtdemux_process_buffer_clcp;
16266 stream->need_split = TRUE;
16269 _codec ("CEA 708 Closed Caption");
16271 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16272 G_TYPE_STRING, "cdp", NULL);
16273 stream->process_func = gst_qtdemux_process_buffer_clcp;
16278 caps = _get_unknown_codec_name ("text", fourcc);
16286 qtdemux_meta_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16287 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16288 const guint8 * stsd_entry_data, gchar ** codec_name)
16290 GstCaps *caps = NULL;
16292 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16296 gsize size = QT_UINT32 (stsd_entry_data);
16297 GstByteReader reader = GST_BYTE_READER_INIT (stsd_entry_data, size);
16298 const gchar *content_encoding;
16299 const gchar *namespaces;
16300 const gchar *schema_locations;
16302 if (!gst_byte_reader_skip (&reader, 8 + 6 + 2)) {
16303 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
16307 if (!gst_byte_reader_get_string (&reader, &content_encoding) ||
16308 !gst_byte_reader_get_string (&reader, &namespaces) ||
16309 !gst_byte_reader_get_string (&reader, &schema_locations)) {
16310 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
16314 if (strstr (namespaces, "http://www.onvif.org/ver10/schema") != 0) {
16315 if (content_encoding == NULL || *content_encoding == '\0'
16316 || g_ascii_strcasecmp (content_encoding, "xml") == 0) {
16317 _codec ("ONVIF Timed XML MetaData");
16319 gst_caps_new_simple ("application/x-onvif-metadata", "parsed",
16320 G_TYPE_BOOLEAN, TRUE, NULL);
16322 GST_DEBUG_OBJECT (qtdemux, "Unknown content encoding: %s",
16326 GST_DEBUG_OBJECT (qtdemux, "Unknown metadata namespaces: %s",
16337 caps = _get_unknown_codec_name ("meta", fourcc);
16343 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16344 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16345 const guint8 * stsd_entry_data, gchar ** codec_name)
16351 _codec ("MPEG 1 video");
16352 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16353 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16363 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
16364 const gchar * system_id)
16368 if (!qtdemux->protection_system_ids)
16369 qtdemux->protection_system_ids =
16370 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16371 /* Check whether we already have an entry for this system ID. */
16372 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16373 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16374 if (g_ascii_strcasecmp (system_id, id) == 0) {
16378 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16379 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,