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 /* raw audio streams can be ignored as we can seek anywhere within them */
1231 if (str->subtype == FOURCC_soun && str->need_clip)
1234 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1235 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1237 /* get segment and time in the segment */
1238 seg = &str->segments[seg_idx];
1239 seg_time = (desired_time - seg->time) * seg->rate;
1241 while (QTSEGMENT_IS_EMPTY (seg)) {
1243 empty_segment = TRUE;
1244 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1247 if (seg_idx == str->n_segments)
1249 seg = &str->segments[seg_idx];
1252 if (seg_idx == str->n_segments) {
1253 /* FIXME track shouldn't have the last segment as empty, but if it
1254 * happens we better handle it */
1258 /* get the media time in the segment */
1259 media_start = seg->media_start + seg_time;
1261 /* get the index of the sample with media time */
1262 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1263 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1264 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1265 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1268 /* shift to next frame if we are looking for next keyframe */
1269 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1270 && index < str->stbl_index)
1273 if (!empty_segment) {
1274 /* find previous or next keyframe */
1275 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1277 /* if looking for next one, we will settle for one before if none found after */
1278 if (next && kindex == -1)
1279 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1281 /* Update the requested time whenever a keyframe was found, to make it
1282 * accurate and avoid having the first buffer fall outside of the segment
1287 /* get timestamp of keyframe */
1288 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1289 GST_DEBUG_OBJECT (qtdemux,
1290 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1291 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1292 str->samples[kindex].offset);
1294 /* keyframes in the segment get a chance to change the
1295 * desired_offset. keyframes out of the segment are
1297 if (media_time >= seg->media_start) {
1298 GstClockTime seg_time;
1300 /* this keyframe is inside the segment, convert back to
1302 seg_time = (media_time - seg->media_start) + seg->time;
1304 /* Adjust the offset based on the earliest suitable keyframe found,
1305 * based on which GST_SEEK_FLAG_SNAP_* is present (indicated by 'next').
1306 * For SNAP_BEFORE we look for the earliest keyframe before desired_time,
1307 * and in case of SNAP_AFTER - for the closest one after it. */
1308 if (seg_time < min_offset)
1309 min_offset = seg_time;
1314 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1315 min_byte_offset = str->samples[index].offset;
1319 *key_time = min_offset;
1321 *key_offset = min_byte_offset;
1325 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1326 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1330 g_return_val_if_fail (format != NULL, FALSE);
1331 g_return_val_if_fail (cur != NULL, FALSE);
1332 g_return_val_if_fail (stop != NULL, FALSE);
1334 if (*format == GST_FORMAT_TIME)
1338 if (cur_type != GST_SEEK_TYPE_NONE)
1339 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1340 if (res && stop_type != GST_SEEK_TYPE_NONE)
1341 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1344 *format = GST_FORMAT_TIME;
1349 /* perform seek in push based mode:
1350 find BYTE position to move to based on time and delegate to upstream
1353 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1358 GstSeekType cur_type, stop_type;
1359 gint64 cur, stop, key_cur;
1362 gint64 original_stop;
1365 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1367 gst_event_parse_seek (event, &rate, &format, &flags,
1368 &cur_type, &cur, &stop_type, &stop);
1369 seqnum = gst_event_get_seqnum (event);
1371 /* Directly send the instant-rate-change event here before taking the
1372 * stream-lock so that it can be applied as soon as possible */
1373 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1376 /* instant rate change only supported if direction does not change. All
1377 * other requirements are already checked before creating the seek event
1378 * but let's double-check here to be sure */
1379 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1380 (qtdemux->segment.rate < 0 && rate > 0) ||
1381 cur_type != GST_SEEK_TYPE_NONE ||
1382 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1383 GST_ERROR_OBJECT (qtdemux,
1384 "Instant rate change seeks only supported in the "
1385 "same direction, without flushing and position change");
1389 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1390 (GstSegmentFlags) flags);
1391 gst_event_set_seqnum (ev, seqnum);
1392 gst_qtdemux_push_event (qtdemux, ev);
1396 /* only forward streaming and seeking is possible */
1398 goto unsupported_seek;
1400 /* convert to TIME if needed and possible */
1401 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1405 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1406 * the original stop position to use when upstream pushes the new segment
1408 original_stop = stop;
1411 /* find reasonable corresponding BYTE position,
1412 * also try to mind about keyframes, since we can not go back a bit for them
1414 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1415 * mostly just work, but let's not yet boldly go there ... */
1416 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1421 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1422 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1425 GST_OBJECT_LOCK (qtdemux);
1426 qtdemux->seek_offset = byte_cur;
1427 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1428 qtdemux->push_seek_start = cur;
1430 qtdemux->push_seek_start = key_cur;
1433 if (stop_type == GST_SEEK_TYPE_NONE) {
1434 qtdemux->push_seek_stop = qtdemux->segment.stop;
1436 qtdemux->push_seek_stop = original_stop;
1438 GST_OBJECT_UNLOCK (qtdemux);
1440 qtdemux->segment_seqnum = seqnum;
1441 /* BYTE seek event */
1442 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1444 gst_event_set_seqnum (event, seqnum);
1445 res = gst_pad_push_event (qtdemux->sinkpad, event);
1452 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1458 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1463 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1468 /* perform the seek.
1470 * We set all segment_indexes in the streams to unknown and
1471 * adjust the time_position to the desired position. this is enough
1472 * to trigger a segment switch in the streaming thread to start
1473 * streaming from the desired position.
1475 * Keyframe seeking is a little more complicated when dealing with
1476 * segments. Ideally we want to move to the previous keyframe in
1477 * the segment but there might not be a keyframe in the segment. In
1478 * fact, none of the segments could contain a keyframe. We take a
1479 * practical approach: seek to the previous keyframe in the segment,
1480 * if there is none, seek to the beginning of the segment.
1482 * Called with STREAM_LOCK
1485 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1486 guint32 seqnum, GstSeekFlags flags)
1488 gint64 desired_offset;
1491 desired_offset = segment->position;
1493 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1494 GST_TIME_ARGS (desired_offset));
1496 /* may not have enough fragmented info to do this adjustment,
1497 * and we can't scan (and probably should not) at this time with
1498 * possibly flushing upstream */
1499 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1501 gboolean next, before, after;
1503 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1504 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1505 next = after && !before;
1506 if (segment->rate < 0)
1509 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1511 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1512 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1513 desired_offset = min_offset;
1516 /* and set all streams to the final position */
1517 GST_OBJECT_LOCK (qtdemux);
1518 gst_flow_combiner_reset (qtdemux->flowcombiner);
1519 GST_OBJECT_UNLOCK (qtdemux);
1520 qtdemux->segment_seqnum = seqnum;
1521 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1522 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1524 stream->time_position = desired_offset;
1525 stream->accumulated_base = 0;
1526 stream->sample_index = -1;
1527 stream->offset_in_sample = 0;
1528 stream->segment_index = -1;
1529 stream->sent_eos = FALSE;
1530 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1532 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1533 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1535 segment->position = desired_offset;
1536 if (segment->rate >= 0) {
1537 segment->start = desired_offset;
1538 /* We need to update time as we update start in that direction */
1539 segment->time = desired_offset;
1541 /* we stop at the end */
1542 if (segment->stop == -1)
1543 segment->stop = segment->duration;
1545 segment->stop = desired_offset;
1548 if (qtdemux->fragmented)
1549 qtdemux->fragmented_seek_pending = TRUE;
1554 /* do a seek in pull based mode */
1556 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1561 GstSeekType cur_type, stop_type;
1563 gboolean flush, instant_rate_change;
1565 GstSegment seeksegment;
1566 guint32 seqnum = GST_SEQNUM_INVALID;
1567 GstEvent *flush_event;
1570 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1572 gst_event_parse_seek (event, &rate, &format, &flags,
1573 &cur_type, &cur, &stop_type, &stop);
1574 seqnum = gst_event_get_seqnum (event);
1576 /* we have to have a format as the segment format. Try to convert
1578 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1582 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1584 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1585 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1587 /* Directly send the instant-rate-change event here before taking the
1588 * stream-lock so that it can be applied as soon as possible */
1589 if (instant_rate_change) {
1592 /* instant rate change only supported if direction does not change. All
1593 * other requirements are already checked before creating the seek event
1594 * but let's double-check here to be sure */
1595 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1596 (qtdemux->segment.rate < 0 && rate > 0) ||
1597 cur_type != GST_SEEK_TYPE_NONE ||
1598 stop_type != GST_SEEK_TYPE_NONE || flush) {
1599 GST_ERROR_OBJECT (qtdemux,
1600 "Instant rate change seeks only supported in the "
1601 "same direction, without flushing and position change");
1605 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1606 (GstSegmentFlags) flags);
1607 gst_event_set_seqnum (ev, seqnum);
1608 gst_qtdemux_push_event (qtdemux, ev);
1612 /* stop streaming, either by flushing or by pausing the task */
1614 flush_event = gst_event_new_flush_start ();
1615 if (seqnum != GST_SEQNUM_INVALID)
1616 gst_event_set_seqnum (flush_event, seqnum);
1617 /* unlock upstream pull_range */
1618 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1619 /* make sure out loop function exits */
1620 gst_qtdemux_push_event (qtdemux, flush_event);
1622 /* non flushing seek, pause the task */
1623 gst_pad_pause_task (qtdemux->sinkpad);
1626 /* wait for streaming to finish */
1627 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1629 /* copy segment, we need this because we still need the old
1630 * segment when we close the current segment. */
1631 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1633 /* configure the segment with the seek variables */
1634 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1635 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1636 cur_type, cur, stop_type, stop, &update)) {
1638 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1640 /* now do the seek */
1641 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1644 /* prepare for streaming again */
1646 flush_event = gst_event_new_flush_stop (TRUE);
1647 if (seqnum != GST_SEQNUM_INVALID)
1648 gst_event_set_seqnum (flush_event, seqnum);
1650 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1651 gst_qtdemux_push_event (qtdemux, flush_event);
1654 /* commit the new segment */
1655 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1657 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1658 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1659 qtdemux->segment.format, qtdemux->segment.position);
1660 if (seqnum != GST_SEQNUM_INVALID)
1661 gst_message_set_seqnum (msg, seqnum);
1662 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1665 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1666 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1667 qtdemux->sinkpad, NULL);
1669 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1676 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1682 qtdemux_ensure_index (GstQTDemux * qtdemux)
1686 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1688 /* Build complete index */
1689 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1690 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1692 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1693 GST_LOG_OBJECT (qtdemux,
1694 "Building complete index of track-id %u for seeking failed!",
1704 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1707 gboolean res = TRUE;
1708 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1710 switch (GST_EVENT_TYPE (event)) {
1711 case GST_EVENT_RECONFIGURE:
1712 GST_OBJECT_LOCK (qtdemux);
1713 gst_flow_combiner_reset (qtdemux->flowcombiner);
1714 GST_OBJECT_UNLOCK (qtdemux);
1715 res = gst_pad_event_default (pad, parent, event);
1717 case GST_EVENT_SEEK:
1719 GstSeekFlags flags = 0;
1720 GstFormat seek_format;
1721 gboolean instant_rate_change;
1723 #ifndef GST_DISABLE_GST_DEBUG
1724 GstClockTime ts = gst_util_get_timestamp ();
1726 guint32 seqnum = gst_event_get_seqnum (event);
1728 qtdemux->received_seek = TRUE;
1730 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1732 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1734 if (seqnum == qtdemux->segment_seqnum) {
1735 GST_LOG_OBJECT (pad,
1736 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1737 gst_event_unref (event);
1741 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1742 /* seek should be handled by upstream, we might need to re-download fragments */
1743 GST_DEBUG_OBJECT (qtdemux,
1744 "let upstream handle seek for fragmented playback");
1748 if (seek_format == GST_FORMAT_BYTES) {
1749 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1750 gst_event_unref (event);
1754 gst_event_parse_seek_trickmode_interval (event,
1755 &qtdemux->trickmode_interval);
1757 /* Build complete index for seeking;
1758 * if not a fragmented file at least and we're really doing a seek,
1759 * not just an instant-rate-change */
1760 if (!qtdemux->fragmented && !instant_rate_change) {
1761 if (!qtdemux_ensure_index (qtdemux))
1764 #ifndef GST_DISABLE_GST_DEBUG
1765 ts = gst_util_get_timestamp () - ts;
1766 GST_INFO_OBJECT (qtdemux,
1767 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1769 if (qtdemux->pullbased) {
1770 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1771 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1772 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1774 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1775 && QTDEMUX_N_STREAMS (qtdemux)
1776 && !qtdemux->fragmented) {
1777 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1779 GST_DEBUG_OBJECT (qtdemux,
1780 "ignoring seek in push mode in current state");
1783 gst_event_unref (event);
1788 res = gst_pad_event_default (pad, parent, event);
1798 GST_ERROR_OBJECT (qtdemux, "Index failed");
1799 gst_event_unref (event);
1805 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1807 * If @fw is false, the coding order is explored backwards.
1809 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1810 * sample is found for that track.
1812 * The stream and sample index of the sample with the minimum offset in the direction explored
1813 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1815 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1816 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1817 * @_stream and @_index. */
1819 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1820 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1823 gint64 time, min_time;
1824 QtDemuxStream *stream;
1831 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1834 gboolean set_sample;
1836 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1843 i = str->n_samples - 1;
1847 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1848 if (str->samples[i].size == 0)
1851 if (fw && (str->samples[i].offset < byte_pos))
1854 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1857 /* move stream to first available sample */
1859 gst_qtdemux_move_stream (qtdemux, str, i);
1863 /* avoid index from sparse streams since they might be far away */
1864 if (!CUR_STREAM (str)->sparse) {
1865 /* determine min/max time */
1866 time = QTSAMPLE_PTS (str, &str->samples[i]);
1867 if (min_time == -1 || (!fw && time > min_time) ||
1868 (fw && time < min_time)) {
1872 /* determine stream with leading sample, to get its position */
1874 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1875 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1883 /* no sample for this stream, mark eos */
1885 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1896 /* Copied from mpegtsbase code */
1897 /* FIXME: replace this function when we add new util function for stream-id creation */
1899 _get_upstream_id (GstQTDemux * demux)
1901 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1904 /* Try to create one from the upstream URI, else use a randome number */
1908 /* Try to generate one from the URI query and
1909 * if it fails take a random number instead */
1910 query = gst_query_new_uri ();
1911 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1912 gst_query_parse_uri (query, &uri);
1918 /* And then generate an SHA256 sum of the URI */
1919 cs = g_checksum_new (G_CHECKSUM_SHA256);
1920 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1922 upstream_id = g_strdup (g_checksum_get_string (cs));
1923 g_checksum_free (cs);
1925 /* Just get some random number if the URI query fails */
1926 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1927 "implementing a deterministic way of creating a stream-id");
1929 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1930 g_random_int (), g_random_int ());
1933 gst_query_unref (query);
1938 static QtDemuxStream *
1939 _create_stream (GstQTDemux * demux, guint32 track_id)
1941 QtDemuxStream *stream;
1944 stream = g_new0 (QtDemuxStream, 1);
1945 stream->demux = demux;
1946 stream->track_id = track_id;
1947 upstream_id = _get_upstream_id (demux);
1948 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1949 g_free (upstream_id);
1950 /* new streams always need a discont */
1951 stream->discont = TRUE;
1952 /* we enable clipping for raw audio/video streams */
1953 stream->need_clip = FALSE;
1954 stream->process_func = NULL;
1955 stream->segment_index = -1;
1956 stream->time_position = 0;
1957 stream->sample_index = -1;
1958 stream->offset_in_sample = 0;
1959 stream->new_stream = TRUE;
1960 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1961 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1962 stream->protected = FALSE;
1963 stream->protection_scheme_type = 0;
1964 stream->protection_scheme_version = 0;
1965 stream->protection_scheme_info = NULL;
1966 stream->n_samples_moof = 0;
1967 stream->duration_moof = 0;
1968 stream->duration_last_moof = 0;
1969 stream->alignment = 1;
1970 stream->stream_tags = gst_tag_list_new_empty ();
1971 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1972 g_queue_init (&stream->protection_scheme_event_queue);
1973 stream->ref_count = 1;
1974 /* consistent default for push based mode */
1975 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1980 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1982 GstStructure *structure;
1983 const gchar *variant;
1984 const GstCaps *mediacaps = NULL;
1986 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1988 structure = gst_caps_get_structure (caps, 0);
1989 variant = gst_structure_get_string (structure, "variant");
1991 if (variant && strcmp (variant, "mse-bytestream") == 0) {
1992 demux->variant = VARIANT_MSE_BYTESTREAM;
1995 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1996 QtDemuxStream *stream;
1997 const GValue *value;
1999 demux->fragmented = TRUE;
2000 demux->variant = VARIANT_MSS_FRAGMENTED;
2002 if (QTDEMUX_N_STREAMS (demux) > 1) {
2003 /* can't do this, we can only renegotiate for another mss format */
2007 value = gst_structure_get_value (structure, "media-caps");
2010 const GValue *timescale_v;
2012 /* TODO update when stream changes during playback */
2014 if (QTDEMUX_N_STREAMS (demux) == 0) {
2015 stream = _create_stream (demux, 1);
2016 g_ptr_array_add (demux->active_streams, stream);
2017 /* mss has no stsd/stsd entry, use id 0 as default */
2018 stream->stsd_entries_length = 1;
2019 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2020 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2022 stream = QTDEMUX_NTH_STREAM (demux, 0);
2025 timescale_v = gst_structure_get_value (structure, "timescale");
2027 stream->timescale = g_value_get_uint64 (timescale_v);
2029 /* default mss timescale */
2030 stream->timescale = 10000000;
2032 demux->timescale = stream->timescale;
2034 mediacaps = gst_value_get_caps (value);
2035 if (!CUR_STREAM (stream)->caps
2036 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2037 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2039 stream->new_caps = TRUE;
2041 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2042 structure = gst_caps_get_structure (mediacaps, 0);
2043 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2044 stream->subtype = FOURCC_vide;
2046 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2047 gst_structure_get_int (structure, "height",
2048 &CUR_STREAM (stream)->height);
2049 gst_structure_get_fraction (structure, "framerate",
2050 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2051 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2053 stream->subtype = FOURCC_soun;
2054 gst_structure_get_int (structure, "channels",
2055 &CUR_STREAM (stream)->n_channels);
2056 gst_structure_get_int (structure, "rate", &rate);
2057 CUR_STREAM (stream)->rate = rate;
2058 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
2059 if (gst_structure_has_field (structure, "original-media-type")) {
2060 const gchar *media_type =
2061 gst_structure_get_string (structure, "original-media-type");
2062 if (g_str_has_prefix (media_type, "video")) {
2063 stream->subtype = FOURCC_vide;
2064 } else if (g_str_has_prefix (media_type, "audio")) {
2065 stream->subtype = FOURCC_soun;
2070 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2077 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2081 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2083 if (hard || qtdemux->upstream_format_is_time) {
2084 qtdemux->state = QTDEMUX_STATE_INITIAL;
2085 qtdemux->neededbytes = 16;
2086 qtdemux->todrop = 0;
2087 qtdemux->pullbased = FALSE;
2088 g_clear_pointer (&qtdemux->redirect_location, g_free);
2089 qtdemux->first_mdat = -1;
2090 qtdemux->header_size = 0;
2091 qtdemux->mdatoffset = -1;
2092 qtdemux->restoredata_offset = -1;
2093 if (qtdemux->mdatbuffer)
2094 gst_buffer_unref (qtdemux->mdatbuffer);
2095 if (qtdemux->restoredata_buffer)
2096 gst_buffer_unref (qtdemux->restoredata_buffer);
2097 qtdemux->mdatbuffer = NULL;
2098 qtdemux->restoredata_buffer = NULL;
2099 qtdemux->mdatleft = 0;
2100 qtdemux->mdatsize = 0;
2101 if (qtdemux->comp_brands)
2102 gst_buffer_unref (qtdemux->comp_brands);
2103 qtdemux->comp_brands = NULL;
2104 qtdemux->last_moov_offset = -1;
2105 if (qtdemux->moov_node_compressed) {
2106 g_node_destroy (qtdemux->moov_node_compressed);
2107 if (qtdemux->moov_node)
2108 g_free (qtdemux->moov_node->data);
2110 qtdemux->moov_node_compressed = NULL;
2111 if (qtdemux->moov_node)
2112 g_node_destroy (qtdemux->moov_node);
2113 qtdemux->moov_node = NULL;
2114 if (qtdemux->tag_list)
2115 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2116 qtdemux->tag_list = gst_tag_list_new_empty ();
2117 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2119 if (qtdemux->element_index)
2120 gst_object_unref (qtdemux->element_index);
2121 qtdemux->element_index = NULL;
2123 qtdemux->major_brand = 0;
2124 qtdemux->upstream_format_is_time = FALSE;
2125 qtdemux->upstream_seekable = FALSE;
2126 qtdemux->upstream_size = 0;
2128 qtdemux->fragment_start = -1;
2129 qtdemux->fragment_start_offset = -1;
2130 qtdemux->duration = 0;
2131 qtdemux->moof_offset = 0;
2132 qtdemux->chapters_track_id = 0;
2133 qtdemux->have_group_id = FALSE;
2134 qtdemux->group_id = G_MAXUINT;
2136 g_queue_clear_full (&qtdemux->protection_event_queue,
2137 (GDestroyNotify) gst_event_unref);
2139 qtdemux->received_seek = FALSE;
2140 qtdemux->first_moof_already_parsed = FALSE;
2142 qtdemux->offset = 0;
2143 gst_adapter_clear (qtdemux->adapter);
2144 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2145 qtdemux->need_segment = TRUE;
2148 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2149 qtdemux->trickmode_interval = 0;
2150 g_ptr_array_set_size (qtdemux->active_streams, 0);
2151 g_ptr_array_set_size (qtdemux->old_streams, 0);
2152 qtdemux->n_video_streams = 0;
2153 qtdemux->n_audio_streams = 0;
2154 qtdemux->n_sub_streams = 0;
2155 qtdemux->n_meta_streams = 0;
2156 qtdemux->exposed = FALSE;
2157 qtdemux->fragmented = FALSE;
2158 qtdemux->variant = VARIANT_NONE;
2159 gst_caps_replace (&qtdemux->media_caps, NULL);
2160 qtdemux->timescale = 0;
2161 qtdemux->got_moov = FALSE;
2162 qtdemux->start_utc_time = GST_CLOCK_TIME_NONE;
2163 qtdemux->cenc_aux_info_offset = 0;
2164 g_free (qtdemux->cenc_aux_info_sizes);
2165 qtdemux->cenc_aux_info_sizes = NULL;
2166 qtdemux->cenc_aux_sample_count = 0;
2167 if (qtdemux->protection_system_ids) {
2168 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2169 qtdemux->protection_system_ids = NULL;
2171 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2172 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2173 GST_BIN_FLAG_STREAMS_AWARE);
2175 if (qtdemux->preferred_protection_system_id) {
2176 g_free (qtdemux->preferred_protection_system_id);
2177 qtdemux->preferred_protection_system_id = NULL;
2179 } else if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
2180 gst_flow_combiner_reset (qtdemux->flowcombiner);
2181 g_ptr_array_foreach (qtdemux->active_streams,
2182 (GFunc) gst_qtdemux_stream_clear, NULL);
2184 gst_flow_combiner_reset (qtdemux->flowcombiner);
2185 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2186 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2187 stream->sent_eos = FALSE;
2188 stream->time_position = 0;
2189 stream->accumulated_base = 0;
2190 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2196 qtdemux_clear_protection_events_on_all_streams (GstQTDemux * qtdemux)
2198 for (unsigned i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2199 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2200 g_queue_clear_full (&stream->protection_scheme_event_queue,
2201 (GDestroyNotify) gst_event_unref);
2205 /* Maps the @segment to the qt edts internal segments and pushes
2206 * the corresponding segment event.
2208 * If it ends up being at a empty segment, a gap will be pushed and the next
2209 * edts segment will be activated in sequence.
2211 * To be used in push-mode only */
2213 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2217 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2218 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2220 stream->time_position = segment->start;
2222 /* in push mode we should be guaranteed that we will have empty segments
2223 * at the beginning and then one segment after, other scenarios are not
2224 * supported and are discarded when parsing the edts */
2225 for (i = 0; i < stream->n_segments; i++) {
2226 if (stream->segments[i].stop_time > segment->start) {
2227 /* push the empty segment and move to the next one */
2228 gst_qtdemux_activate_segment (qtdemux, stream, i,
2229 stream->time_position);
2230 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2231 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2232 stream->time_position);
2234 /* accumulate previous segments */
2235 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2236 stream->accumulated_base +=
2237 (stream->segment.stop -
2238 stream->segment.start) / ABS (stream->segment.rate);
2242 g_assert (i == stream->n_segments - 1);
2249 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2260 for (i = 0; i < len; i++) {
2261 QtDemuxStream *stream = g_ptr_array_index (src, i);
2263 #ifndef GST_DISABLE_GST_DEBUG
2264 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2265 stream, GST_STR_NULL (stream->stream_id), dest);
2267 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2270 g_ptr_array_set_size (src, 0);
2274 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2277 GstQTDemux *demux = GST_QTDEMUX (parent);
2278 gboolean res = TRUE;
2280 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2282 switch (GST_EVENT_TYPE (event)) {
2283 case GST_EVENT_SEGMENT:
2286 QtDemuxStream *stream;
2290 /* some debug output */
2291 gst_event_copy_segment (event, &segment);
2292 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2295 if (segment.format == GST_FORMAT_TIME) {
2296 demux->upstream_format_is_time = TRUE;
2297 demux->segment_seqnum = gst_event_get_seqnum (event);
2299 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2300 "not in time format");
2302 /* chain will send initial newsegment after pads have been added */
2303 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2304 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2309 /* check if this matches a time seek we received previously
2310 * FIXME for backwards compatibility reasons we use the
2311 * seek_offset here to compare. In the future we might want to
2312 * change this to use the seqnum as it uniquely should identify
2313 * the segment that corresponds to the seek. */
2314 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2315 ", received segment offset %" G_GINT64_FORMAT,
2316 demux->seek_offset, segment.start);
2317 if (segment.format == GST_FORMAT_BYTES
2318 && demux->seek_offset == segment.start) {
2319 GST_OBJECT_LOCK (demux);
2320 offset = segment.start;
2322 segment.format = GST_FORMAT_TIME;
2323 segment.start = demux->push_seek_start;
2324 segment.stop = demux->push_seek_stop;
2325 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2326 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2327 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2328 GST_OBJECT_UNLOCK (demux);
2331 /* we only expect a BYTE segment, e.g. following a seek */
2332 if (segment.format == GST_FORMAT_BYTES) {
2333 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2334 offset = segment.start;
2336 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2337 NULL, (gint64 *) & segment.start);
2338 if ((gint64) segment.start < 0)
2341 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2342 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2343 NULL, (gint64 *) & segment.stop);
2344 /* keyframe seeking should already arrange for start >= stop,
2345 * but make sure in other rare cases */
2346 segment.stop = MAX (segment.stop, segment.start);
2348 } else if (segment.format == GST_FORMAT_TIME) {
2349 /* push all data on the adapter before starting this
2351 gst_qtdemux_process_adapter (demux, TRUE);
2353 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2357 /* We shouldn't modify upstream driven TIME FORMAT segment */
2358 if (!demux->upstream_format_is_time) {
2359 /* accept upstream's notion of segment and distribute along */
2360 segment.format = GST_FORMAT_TIME;
2361 segment.position = segment.time = segment.start;
2362 segment.duration = demux->segment.duration;
2363 segment.base = gst_segment_to_running_time (&demux->segment,
2364 GST_FORMAT_TIME, demux->segment.position);
2367 gst_segment_copy_into (&segment, &demux->segment);
2368 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2370 /* map segment to internal qt segments and push on each stream */
2371 if (QTDEMUX_N_STREAMS (demux)) {
2372 demux->need_segment = TRUE;
2373 gst_qtdemux_check_send_pending_segment (demux);
2376 /* clear leftover in current segment, if any */
2377 gst_adapter_clear (demux->adapter);
2379 /* set up streaming thread */
2380 demux->offset = offset;
2381 if (demux->upstream_format_is_time) {
2382 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2383 "set values to restart reading from a new atom");
2384 demux->neededbytes = 16;
2387 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2390 demux->todrop = stream->samples[idx].offset - offset;
2391 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2393 /* set up for EOS */
2394 demux->neededbytes = -1;
2399 gst_event_unref (event);
2403 case GST_EVENT_FLUSH_START:
2405 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2406 gst_event_unref (event);
2409 QTDEMUX_EXPOSE_LOCK (demux);
2410 res = gst_pad_event_default (demux->sinkpad, parent, event);
2411 QTDEMUX_EXPOSE_UNLOCK (demux);
2414 case GST_EVENT_FLUSH_STOP:
2418 dur = demux->segment.duration;
2419 gst_qtdemux_reset (demux, FALSE);
2420 demux->segment.duration = dur;
2422 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2423 gst_event_unref (event);
2429 /* If we are in push mode, and get an EOS before we've seen any streams,
2430 * then error out - we have nowhere to send the EOS */
2431 if (!demux->pullbased) {
2433 gboolean has_valid_stream = FALSE;
2434 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2435 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2436 has_valid_stream = TRUE;
2440 if (!has_valid_stream)
2441 gst_qtdemux_post_no_playable_stream_error (demux);
2443 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2444 (guint) gst_adapter_available (demux->adapter));
2445 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2451 case GST_EVENT_CAPS:{
2452 GstCaps *caps = NULL;
2454 gst_event_parse_caps (event, &caps);
2455 gst_qtdemux_setcaps (demux, caps);
2457 gst_event_unref (event);
2460 case GST_EVENT_PROTECTION:
2462 const gchar *system_id = NULL;
2464 gst_event_parse_protection (event, &system_id, NULL, NULL);
2465 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2467 gst_qtdemux_append_protection_system_id (demux, system_id);
2468 /* save the event for later, for source pads that have not been created */
2469 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2470 /* send it to all pads that already exist */
2471 gst_qtdemux_push_event (demux, event);
2475 case GST_EVENT_STREAM_START:
2478 gst_event_unref (event);
2480 /* Drain all the buffers */
2481 gst_qtdemux_process_adapter (demux, TRUE);
2482 gst_qtdemux_reset (demux, FALSE);
2483 /* We expect new moov box after new stream-start event */
2484 if (demux->exposed) {
2485 gst_qtdemux_stream_concat (demux,
2486 demux->old_streams, demux->active_streams);
2495 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2502 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2505 GstQTDemux *demux = GST_QTDEMUX (parent);
2506 gboolean res = FALSE;
2508 switch (GST_QUERY_TYPE (query)) {
2509 case GST_QUERY_BITRATE:
2511 GstClockTime duration;
2513 /* populate demux->upstream_size if not done yet */
2514 gst_qtdemux_check_seekability (demux);
2516 if (demux->upstream_size != -1
2517 && gst_qtdemux_get_duration (demux, &duration)) {
2519 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2522 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2523 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2524 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2526 /* TODO: better results based on ranges/index tables */
2527 gst_query_set_bitrate (query, bitrate);
2533 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2543 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2545 GstQTDemux *demux = GST_QTDEMUX (element);
2547 GST_OBJECT_LOCK (demux);
2548 if (demux->element_index)
2549 gst_object_unref (demux->element_index);
2551 demux->element_index = gst_object_ref (index);
2553 demux->element_index = NULL;
2555 GST_OBJECT_UNLOCK (demux);
2556 /* object lock might be taken again */
2558 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2559 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2560 demux->element_index, demux->index_id);
2564 gst_qtdemux_get_index (GstElement * element)
2566 GstIndex *result = NULL;
2567 GstQTDemux *demux = GST_QTDEMUX (element);
2569 GST_OBJECT_LOCK (demux);
2570 if (demux->element_index)
2571 result = gst_object_ref (demux->element_index);
2572 GST_OBJECT_UNLOCK (demux);
2574 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2581 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2583 g_free ((gpointer) stream->stco.data);
2584 stream->stco.data = NULL;
2585 g_free ((gpointer) stream->stsz.data);
2586 stream->stsz.data = NULL;
2587 g_free ((gpointer) stream->stsc.data);
2588 stream->stsc.data = NULL;
2589 g_free ((gpointer) stream->stts.data);
2590 stream->stts.data = NULL;
2591 g_free ((gpointer) stream->stss.data);
2592 stream->stss.data = NULL;
2593 g_free ((gpointer) stream->stps.data);
2594 stream->stps.data = NULL;
2595 g_free ((gpointer) stream->ctts.data);
2596 stream->ctts.data = NULL;
2600 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2602 g_free (stream->segments);
2603 stream->segments = NULL;
2604 stream->segment_index = -1;
2605 stream->accumulated_base = 0;
2609 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2611 g_free (stream->samples);
2612 stream->samples = NULL;
2613 gst_qtdemux_stbl_free (stream);
2616 g_free (stream->ra_entries);
2617 stream->ra_entries = NULL;
2618 stream->n_ra_entries = 0;
2620 stream->sample_index = -1;
2621 stream->stbl_index = -1;
2622 stream->n_samples = 0;
2623 stream->time_position = 0;
2625 stream->n_samples_moof = 0;
2626 stream->duration_moof = 0;
2627 stream->duration_last_moof = 0;
2631 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2634 if (stream->allocator)
2635 gst_object_unref (stream->allocator);
2636 while (stream->buffers) {
2637 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2638 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2640 for (i = 0; i < stream->stsd_entries_length; i++) {
2641 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2642 if (entry->rgb8_palette) {
2643 gst_memory_unref (entry->rgb8_palette);
2644 entry->rgb8_palette = NULL;
2646 entry->sparse = FALSE;
2649 if (stream->stream_tags)
2650 gst_tag_list_unref (stream->stream_tags);
2652 stream->stream_tags = gst_tag_list_new_empty ();
2653 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2654 g_free (stream->redirect_uri);
2655 stream->redirect_uri = NULL;
2656 stream->sent_eos = FALSE;
2657 stream->protected = FALSE;
2658 if (stream->protection_scheme_info) {
2659 if (stream->protection_scheme_type == FOURCC_cenc
2660 || stream->protection_scheme_type == FOURCC_cbcs) {
2661 QtDemuxCencSampleSetInfo *info =
2662 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2663 if (info->default_properties)
2664 gst_structure_free (info->default_properties);
2665 if (info->crypto_info)
2666 g_ptr_array_free (info->crypto_info, TRUE);
2668 if (stream->protection_scheme_type == FOURCC_aavd) {
2669 QtDemuxAavdEncryptionInfo *info =
2670 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
2671 if (info->default_properties)
2672 gst_structure_free (info->default_properties);
2674 g_free (stream->protection_scheme_info);
2675 stream->protection_scheme_info = NULL;
2677 stream->protection_scheme_type = 0;
2678 stream->protection_scheme_version = 0;
2679 g_queue_clear_full (&stream->protection_scheme_event_queue,
2680 (GDestroyNotify) gst_event_unref);
2681 gst_qtdemux_stream_flush_segments_data (stream);
2682 gst_qtdemux_stream_flush_samples_data (stream);
2686 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2689 gst_qtdemux_stream_clear (stream);
2690 for (i = 0; i < stream->stsd_entries_length; i++) {
2691 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2693 gst_caps_unref (entry->caps);
2697 g_free (stream->stsd_entries);
2698 stream->stsd_entries = NULL;
2699 stream->stsd_entries_length = 0;
2702 static QtDemuxStream *
2703 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2705 g_atomic_int_add (&stream->ref_count, 1);
2711 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2713 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2714 gst_qtdemux_stream_reset (stream);
2715 gst_tag_list_unref (stream->stream_tags);
2717 GstQTDemux *demux = stream->demux;
2718 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2719 GST_OBJECT_LOCK (demux);
2720 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2721 GST_OBJECT_UNLOCK (demux);
2723 g_free (stream->stream_id);
2728 static GstStateChangeReturn
2729 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2731 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2732 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2734 switch (transition) {
2735 case GST_STATE_CHANGE_READY_TO_PAUSED:
2736 gst_qtdemux_reset (qtdemux, TRUE);
2742 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2744 switch (transition) {
2745 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2746 gst_qtdemux_reset (qtdemux, TRUE);
2757 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2759 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2761 g_return_if_fail (GST_IS_CONTEXT (context));
2763 if (gst_context_has_context_type (context,
2764 "drm-preferred-decryption-system-id")) {
2765 const GstStructure *s;
2767 s = gst_context_get_structure (context);
2768 g_free (qtdemux->preferred_protection_system_id);
2769 qtdemux->preferred_protection_system_id =
2770 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2771 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2772 qtdemux->preferred_protection_system_id);
2775 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2779 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2781 /* counts as header data */
2782 qtdemux->header_size += length;
2784 /* only consider at least a sufficiently complete ftyp atom */
2787 guint32 minor_version;
2790 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2791 GST_DEBUG_OBJECT (qtdemux, "ftyp major brand: %" GST_FOURCC_FORMAT,
2792 GST_FOURCC_ARGS (qtdemux->major_brand));
2793 minor_version = QT_UINT32 (buffer + 12);
2794 GST_DEBUG_OBJECT (qtdemux, "ftyp minor version: %u", minor_version);
2795 if (qtdemux->comp_brands)
2796 gst_buffer_unref (qtdemux->comp_brands);
2797 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2798 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2801 length = length - 16;
2802 while (length > 0) {
2803 GST_DEBUG_OBJECT (qtdemux, "ftyp compatible brand: %" GST_FOURCC_FORMAT,
2804 GST_FOURCC_ARGS (QT_FOURCC (p)));
2812 qtdemux_parse_styp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2814 /* only consider at least a sufficiently complete styp atom */
2817 guint32 major_brand;
2818 guint32 minor_version;
2821 major_brand = QT_FOURCC (buffer + 8);
2822 GST_DEBUG_OBJECT (qtdemux, "styp major brand: %" GST_FOURCC_FORMAT,
2823 GST_FOURCC_ARGS (major_brand));
2824 minor_version = QT_UINT32 (buffer + 12);
2825 GST_DEBUG_OBJECT (qtdemux, "styp minor version: %u", minor_version);
2826 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2827 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2830 length = length - 16;
2831 while (length > 0) {
2832 GST_DEBUG_OBJECT (qtdemux, "styp compatible brand: %" GST_FOURCC_FORMAT,
2833 GST_FOURCC_ARGS (QT_FOURCC (p)));
2840 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2842 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2843 const char *xml_str, const char *param_name, int *value)
2845 char *value_start, *value_end, *endptr;
2846 const short value_length_max = 12;
2847 char init_view_ret[12];
2848 int value_length = 0;
2851 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2854 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2859 value_start += strlen (param_name);
2860 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2863 value_end = strchr (value_start, '<');
2865 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2869 value_length = value_end - value_start;
2870 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2871 || (value_start[value_length - 1] == '\t')))
2874 if (value_start[i] == '+' || value_start[i] == '-')
2876 while (i < value_length) {
2877 if (value_start[i] < '0' || value_start[i] > '9') {
2878 GST_ERROR_OBJECT (qtdemux,
2879 "error: incorrect value, integer was expected\n");
2885 if (value_length >= value_length_max || value_length < 1) {
2886 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2890 strncpy (init_view_ret, value_start, value_length_max);
2891 init_view_ret[value_length] = '\0';
2893 *value = strtol (init_view_ret, &endptr, 10);
2894 if (endptr == init_view_ret) {
2895 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2903 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2904 const char *xml_str, const char *param_name, char **value)
2906 char *value_start, *value_end;
2907 const short value_length_max = 256;
2908 int value_length = 0;
2910 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2913 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2918 value_start += strlen (param_name);
2919 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2922 value_end = strchr (value_start, '<');
2924 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2928 value_length = value_end - value_start;
2929 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2930 || (value_start[value_length - 1] == '\t')))
2933 if (value_length >= value_length_max || value_length < 1) {
2934 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2938 *value = strndup(value_start, value_length);
2944 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2945 const char *xml_str, const char *param_name, gboolean * value)
2947 char *value_start, *value_end;
2948 int value_length = 0;
2950 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2953 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2958 value_start += strlen (param_name);
2959 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2962 value_end = strchr (value_start, '<');
2964 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2968 value_length = value_end - value_start;
2969 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2970 || (value_start[value_length - 1] == '\t')))
2973 if (value_length < 1) {
2974 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2978 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
2984 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
2986 const char is_spherical_str[] = "<GSpherical:Spherical>";
2987 const char is_stitched_str[] = "<GSpherical:Stitched>";
2988 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2989 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2990 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2991 const char source_count_str[] = "<GSpherical:SourceCount>";
2992 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2993 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2994 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2995 const char timestamp_str[] = "<GSpherical:Timestamp>";
2996 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2997 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2998 const char cropped_area_image_width_str[] =
2999 "<GSpherical:CroppedAreaImageWidthPixels>";
3000 const char cropped_area_image_height_str[] =
3001 "<GSpherical:CroppedAreaImageHeightPixels>";
3002 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
3003 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
3005 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
3007 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
3008 (gboolean *) & spherical_metadata->is_spherical);
3009 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
3010 (gboolean *) & spherical_metadata->is_stitched);
3012 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
3013 _get_string_value_from_xml_string (qtdemux, xmlStr,
3014 stitching_software_str, &spherical_metadata->stitching_software);
3015 _get_string_value_from_xml_string (qtdemux, xmlStr,
3016 projection_type_str, &spherical_metadata->projection_type);
3017 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
3018 &spherical_metadata->stereo_mode);
3019 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
3020 &spherical_metadata->source_count);
3021 _get_int_value_from_xml_string (qtdemux, xmlStr,
3022 init_view_heading_str, &spherical_metadata->init_view_heading);
3023 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
3024 &spherical_metadata->init_view_pitch);
3025 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
3026 &spherical_metadata->init_view_roll);
3027 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
3028 &spherical_metadata->timestamp);
3029 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
3030 &spherical_metadata->full_pano_width_pixels);
3031 _get_int_value_from_xml_string (qtdemux, xmlStr,
3032 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
3033 _get_int_value_from_xml_string (qtdemux, xmlStr,
3034 cropped_area_image_width_str,
3035 &spherical_metadata->cropped_area_image_width);
3036 _get_int_value_from_xml_string (qtdemux, xmlStr,
3037 cropped_area_image_height_str,
3038 &spherical_metadata->cropped_area_image_height);
3039 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
3040 &spherical_metadata->cropped_area_left);
3041 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
3042 &spherical_metadata->cropped_area_top);
3049 gst_tag_register_spherical_tags (void) {
3050 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
3053 _("Flag indicating if the video is a spherical video"),
3055 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
3058 _("Flag indicating if the video is stitched"),
3060 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
3062 _("tag-stitching-software"),
3063 _("Software used to stitch the spherical video"),
3065 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
3067 _("tag-projection-type"),
3068 _("Projection type used in the video frames"),
3070 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
3072 _("tag-stereo-mode"),
3073 _("Description of stereoscopic 3D layout"),
3075 gst_tag_register ("source_count", GST_TAG_FLAG_META,
3077 _("tag-source-count"),
3078 _("Number of cameras used to create the spherical video"),
3080 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
3082 _("tag-init-view-heading"),
3083 _("The heading angle of the initial view in degrees"),
3085 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
3087 _("tag-init-view-pitch"),
3088 _("The pitch angle of the initial view in degrees"),
3090 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
3092 _("tag-init-view-roll"),
3093 _("The roll angle of the initial view in degrees"),
3095 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
3098 _("Epoch timestamp of when the first frame in the video was recorded"),
3100 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
3102 _("tag-full-pano-width"),
3103 _("Width of the encoded video frame in pixels"),
3105 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
3107 _("tag-full-pano-height"),
3108 _("Height of the encoded video frame in pixels"),
3110 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
3112 _("tag-cropped-area-image-width"),
3113 _("Width of the video frame to display (e.g. cropping)"),
3115 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
3117 _("tag-cropped-area-image-height"),
3118 _("Height of the video frame to display (e.g. cropping)"),
3120 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3122 _("tag-cropped-area-left"),
3123 _("Column where the left edge of the image was cropped from the"
3124 " full sized panorama"),
3126 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3128 _("tag-cropped-area-top"),
3129 _("Row where the top edge of the image was cropped from the"
3130 " full sized panorama"),
3132 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3134 _("tag-ambisonic-type"),
3135 _("Specifies the type of ambisonic audio represented"),
3137 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3139 _("tag-ambisonic-format"),
3140 _("Specifies the ambisonic audio format"),
3142 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3144 _("tag-ambisonic-order"),
3145 _("Specifies the ambisonic audio channel order"),
3152 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3154 GstTagList *taglist;
3155 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3157 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3158 spherical_metadata->is_spherical);
3159 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3160 spherical_metadata->is_stitched);
3161 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3162 spherical_metadata->stitching_software);
3163 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3164 spherical_metadata->projection_type);
3165 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3166 spherical_metadata->stereo_mode);
3167 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3168 spherical_metadata->source_count);
3169 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3170 spherical_metadata->init_view_heading);
3171 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3172 spherical_metadata->init_view_pitch);
3173 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3174 spherical_metadata->init_view_roll);
3175 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3176 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3177 spherical_metadata->full_pano_width_pixels);
3178 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3179 spherical_metadata->full_pano_height_pixels);
3180 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3181 spherical_metadata->cropped_area_image_width);
3182 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3183 spherical_metadata->cropped_area_image_height);
3184 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3185 spherical_metadata->cropped_area_left);
3186 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3187 spherical_metadata->cropped_area_top);
3188 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3189 spherical_metadata->ambisonic_type);
3190 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3191 spherical_metadata->ambisonic_order);
3192 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3193 spherical_metadata->ambisonic_format);
3195 taglist = gst_tag_list_new_empty ();
3196 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3197 "is_spherical", spherical_metadata->is_spherical,
3198 "is_stitched", spherical_metadata->is_stitched,
3199 "source_count", spherical_metadata->source_count,
3200 "init_view_heading", spherical_metadata->init_view_heading,
3201 "init_view_pitch", spherical_metadata->init_view_pitch,
3202 "init_view_roll", spherical_metadata->init_view_roll,
3203 "timestamp", spherical_metadata->timestamp,
3204 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3205 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3206 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3207 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3208 "cropped_area_left", spherical_metadata->cropped_area_left,
3209 "cropped_area_top", spherical_metadata->cropped_area_top,
3210 "ambisonic_type", spherical_metadata->ambisonic_type,
3211 "ambisonic_format", spherical_metadata->ambisonic_format,
3212 "ambisonic_order", spherical_metadata->ambisonic_order,
3215 if (spherical_metadata->stitching_software)
3216 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3217 "stitching_software", spherical_metadata->stitching_software,
3219 if (spherical_metadata->projection_type)
3220 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3221 "projection_type", spherical_metadata->projection_type,
3223 if (spherical_metadata->stereo_mode)
3224 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3225 "stereo_mode", spherical_metadata->stereo_mode,
3228 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3229 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3230 gst_tag_list_copy (taglist)));
3232 gst_tag_list_unref(taglist);
3238 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3243 guint8 ambisonic_type = 0;
3244 guint32 ambisonic_order = 0;
3245 guint8 ambisonic_channel_ordering = 0;
3246 guint8 ambisonic_normalization = 0;
3247 guint32 num_channels = 0;
3248 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3252 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3254 qtdemux->header_size += length;
3255 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3257 if (length <= offset + 16) {
3258 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3262 version = QT_UINT8 (buffer + offset);
3263 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3264 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3265 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3266 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3267 num_channels = QT_UINT32 (buffer + offset + 8);
3268 for (i = 0; i < num_channels; ++i)
3269 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3271 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3272 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3273 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3274 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3275 ambisonic_channel_ordering);
3276 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3277 ambisonic_normalization);
3278 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3279 for (i = 0; i < num_channels; ++i)
3280 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3282 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3283 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3284 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3286 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3287 if (num_channels == 4) {
3288 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3290 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3291 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3292 && (channel_map[0] == 0) && (channel_map[1] == 1)
3293 && (channel_map[2] == 2) && (channel_map[3] == 3))
3294 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3296 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3297 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3298 && (channel_map[0] == 0) && (channel_map[1] == 3)
3299 && (channel_map[2] == 1) && (channel_map[3] == 2))
3300 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3307 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3310 qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
3311 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
3312 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
3313 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
3314 const guint8 * constant_iv)
3316 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3317 gst_buffer_fill (kid_buf, 0, kid, 16);
3318 if (info->default_properties)
3319 gst_structure_free (info->default_properties);
3320 info->default_properties =
3321 gst_structure_new ("application/x-cenc",
3322 "iv_size", G_TYPE_UINT, iv_size,
3323 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
3324 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3325 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3326 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3327 gst_buffer_unref (kid_buf);
3328 if (protection_scheme_type == FOURCC_cbcs) {
3329 if (crypt_byte_block != 0 || skip_byte_block != 0) {
3330 gst_structure_set (info->default_properties, "crypt_byte_block",
3331 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
3332 skip_byte_block, NULL);
3334 if (constant_iv != NULL) {
3335 GstBuffer *constant_iv_buf =
3336 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
3337 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
3338 gst_structure_set (info->default_properties, "constant_iv_size",
3339 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
3341 gst_buffer_unref (constant_iv_buf);
3343 gst_structure_set (info->default_properties, "cipher-mode",
3344 G_TYPE_STRING, "cbcs", NULL);
3346 gst_structure_set (info->default_properties, "cipher-mode",
3347 G_TYPE_STRING, "cenc", NULL);
3352 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
3353 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
3355 guint32 algorithm_id = 0;
3357 gboolean is_encrypted = TRUE;
3360 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
3361 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3366 if (algorithm_id == 0) {
3367 is_encrypted = FALSE;
3368 } else if (algorithm_id == 1) {
3369 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3370 } else if (algorithm_id == 2) {
3371 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3374 if (!gst_byte_reader_get_uint8 (br, &iv_size))
3377 if (!gst_byte_reader_get_data (br, 16, &kid))
3380 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
3381 is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
3382 gst_structure_set (info->default_properties, "piff_algorithm_id",
3383 G_TYPE_UINT, algorithm_id, NULL);
3389 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3397 QtDemuxStream *stream;
3398 GstStructure *structure;
3399 QtDemuxCencSampleSetInfo *ss_info = NULL;
3400 const gchar *system_id;
3401 gboolean uses_sub_sample_encryption = FALSE;
3402 guint32 sample_count;
3404 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
3407 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
3409 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3410 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3411 GST_WARNING_OBJECT (qtdemux,
3412 "Attempting PIFF box parsing on an unencrypted stream.");
3416 if (!gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3417 G_TYPE_STRING, &system_id, NULL)) {
3418 GST_WARNING_OBJECT (qtdemux, "%s field not present in caps",
3419 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD);
3423 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3425 stream->protected = TRUE;
3426 stream->protection_scheme_type = FOURCC_cenc;
3428 if (!stream->protection_scheme_info)
3429 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3431 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3432 if (!ss_info->default_properties) {
3433 ss_info->default_properties =
3434 gst_structure_new ("application/x-cenc",
3435 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
3440 if (ss_info->crypto_info) {
3441 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3442 g_ptr_array_free (ss_info->crypto_info, TRUE);
3443 ss_info->crypto_info = NULL;
3447 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3449 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3450 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3454 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3455 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3459 if ((flags & 0x000001)) {
3460 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
3463 } else if ((flags & 0x000002)) {
3464 uses_sub_sample_encryption = TRUE;
3467 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
3469 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
3473 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
3474 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3478 ss_info->crypto_info =
3479 g_ptr_array_new_full (sample_count,
3480 (GDestroyNotify) qtdemux_gst_structure_free);
3482 for (i = 0; i < sample_count; ++i) {
3483 GstStructure *properties;
3487 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3488 if (properties == NULL) {
3489 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3490 qtdemux->cenc_aux_sample_count = i;
3494 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3495 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3496 gst_structure_free (properties);
3497 qtdemux->cenc_aux_sample_count = i;
3500 buf = gst_buffer_new_wrapped (data, iv_size);
3501 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3502 gst_buffer_unref (buf);
3504 if (uses_sub_sample_encryption) {
3505 guint16 n_subsamples;
3506 const GValue *kid_buf_value;
3508 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3509 || n_subsamples == 0) {
3510 GST_ERROR_OBJECT (qtdemux,
3511 "failed to get subsample count for sample %u", i);
3512 gst_structure_free (properties);
3513 qtdemux->cenc_aux_sample_count = i;
3516 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3517 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3518 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3520 gst_structure_free (properties);
3521 qtdemux->cenc_aux_sample_count = i;
3524 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3527 gst_structure_get_value (ss_info->default_properties, "kid");
3529 gst_structure_set (properties,
3530 "subsample_count", G_TYPE_UINT, n_subsamples,
3531 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3532 gst_structure_set_value (properties, "kid", kid_buf_value);
3533 gst_buffer_unref (buf);
3535 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3538 g_ptr_array_add (ss_info->crypto_info, properties);
3541 qtdemux->cenc_aux_sample_count = sample_count;
3545 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3547 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3548 0x97, 0xA9, 0x42, 0xE8,
3549 0x9C, 0x71, 0x99, 0x94,
3550 0x91, 0xE3, 0xAF, 0xAC
3552 static const guint8 playready_uuid[] = {
3553 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3554 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3557 static const guint8 piff_sample_encryption_uuid[] = {
3558 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3559 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3562 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3563 static const guint8 spherical_uuid[] = {
3564 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3565 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3567 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3571 /* counts as header data */
3572 qtdemux->header_size += length;
3574 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3576 if (length <= offset + 16) {
3577 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3581 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3582 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3583 const char *contents;
3585 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3586 contents = (char *) (buffer + offset + 16);
3587 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3589 if (qtdemux->spherical_metadata)
3590 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3594 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3596 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3598 GstTagList *taglist;
3600 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3601 length - offset - 16, NULL);
3602 taglist = gst_tag_list_from_xmp_buffer (buf);
3603 gst_buffer_unref (buf);
3605 /* make sure we have a usable taglist */
3606 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3608 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3610 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3612 const gunichar2 *s_utf16;
3615 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3616 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3617 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3618 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3622 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3623 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3625 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3626 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3628 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3629 GST_READ_UINT32_LE (buffer + offset),
3630 GST_READ_UINT32_LE (buffer + offset + 4),
3631 GST_READ_UINT32_LE (buffer + offset + 8),
3632 GST_READ_UINT32_LE (buffer + offset + 12));
3637 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3639 GstSidxParser sidx_parser;
3640 GstIsoffParserResult res;
3643 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3646 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3648 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3649 if (res == GST_ISOFF_QT_PARSER_DONE) {
3650 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3652 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3656 qtdemux_parse_cstb (GstQTDemux * qtdemux, GstByteReader * data)
3659 guint32 entry_count;
3661 GST_DEBUG_OBJECT (qtdemux, "Parsing CorrectStartTime box");
3663 qtdemux->start_utc_time = GST_CLOCK_TIME_NONE;
3665 if (gst_byte_reader_get_remaining (data) < 4) {
3666 GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box");
3670 entry_count = gst_byte_reader_get_uint32_be_unchecked (data);
3671 if (entry_count == 0)
3674 /* XXX: We assume that all start times are the same as different start times
3675 * would violate the MP4 synchronization model, so we just take the first
3676 * one here and apply it to all tracks.
3679 if (gst_byte_reader_get_remaining (data) < entry_count * 12) {
3680 GST_WARNING_OBJECT (qtdemux, "Too small CorrectStartTime box");
3685 gst_byte_reader_skip_unchecked (data, 4);
3687 /* In 100ns intervals */
3688 start_time = gst_byte_reader_get_uint64_be_unchecked (data);
3690 /* Convert from Jan 1 1601 to Jan 1 1970 */
3691 if (start_time < 11644473600 * G_GUINT64_CONSTANT (10000000)) {
3692 GST_WARNING_OBJECT (qtdemux, "Start UTC time before UNIX epoch");
3695 start_time -= 11644473600 * G_GUINT64_CONSTANT (10000000);
3697 /* Convert to GstClockTime */
3700 GST_DEBUG_OBJECT (qtdemux, "Start UTC time: %" GST_TIME_FORMAT,
3701 GST_TIME_ARGS (start_time));
3703 qtdemux->start_utc_time = start_time;
3706 /* caller verifies at least 8 bytes in buf */
3708 extract_initial_length_and_fourcc (const guint8 * data, gsize size,
3709 guint64 * plength, guint32 * pfourcc)
3714 length = QT_UINT32 (data);
3715 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3716 fourcc = QT_FOURCC (data + 4);
3717 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3720 length = G_MAXUINT64;
3721 } else if (length == 1 && size >= 16) {
3722 /* this means we have an extended size, which is the 64 bit value of
3723 * the next 8 bytes */
3724 length = QT_UINT64 (data + 8);
3725 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3735 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3737 guint32 version = 0;
3738 GstClockTime duration = 0;
3740 if (!gst_byte_reader_get_uint32_be (br, &version))
3745 if (!gst_byte_reader_get_uint64_be (br, &duration))
3750 if (!gst_byte_reader_get_uint32_be (br, &dur))
3755 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3756 qtdemux->duration = duration;
3762 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3768 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3769 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3771 if (!stream->parsed_trex && qtdemux->moov_node) {
3773 GstByteReader trex_data;
3775 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3777 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3780 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3782 /* skip version/flags */
3783 if (!gst_byte_reader_skip (&trex_data, 4))
3785 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3787 if (id != stream->track_id)
3789 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3791 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3793 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3795 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3798 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3799 "duration %d, size %d, flags 0x%x", stream->track_id,
3802 stream->parsed_trex = TRUE;
3803 stream->def_sample_description_index = sdi;
3804 stream->def_sample_duration = dur;
3805 stream->def_sample_size = size;
3806 stream->def_sample_flags = flags;
3809 /* iterate all siblings */
3810 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3816 *ds_duration = stream->def_sample_duration;
3817 *ds_size = stream->def_sample_size;
3818 *ds_flags = stream->def_sample_flags;
3820 /* even then, above values are better than random ... */
3821 if (G_UNLIKELY (!stream->parsed_trex)) {
3822 GST_WARNING_OBJECT (qtdemux,
3823 "failed to find fragment defaults for stream %d", stream->track_id);
3830 /* This method should be called whenever a more accurate duration might
3831 * have been found. It will update all relevant variables if/where needed
3834 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3838 GstClockTime prevdur;
3840 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3842 if (movdur > qtdemux->duration) {
3843 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3844 GST_DEBUG_OBJECT (qtdemux,
3845 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3846 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3847 qtdemux->duration = movdur;
3848 GST_DEBUG_OBJECT (qtdemux,
3849 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3850 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3851 GST_TIME_ARGS (qtdemux->segment.stop));
3852 if (qtdemux->segment.duration == prevdur) {
3853 /* If the current segment has duration/stop identical to previous duration
3854 * update them also (because they were set at that point in time with
3855 * the wrong duration */
3856 /* We convert the value *from* the timescale version to avoid rounding errors */
3857 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3858 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3859 qtdemux->segment.duration = fixeddur;
3860 qtdemux->segment.stop = fixeddur;
3864 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3865 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3867 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3868 if (movdur > stream->duration) {
3869 GST_DEBUG_OBJECT (qtdemux,
3870 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3871 GST_TIME_ARGS (duration));
3872 stream->duration = movdur;
3873 /* internal duration tracking state has been updated above, so */
3874 /* preserve an open-ended dummy segment rather than repeatedly updating
3875 * it and spamming downstream accordingly with segment events */
3876 /* also mangle the edit list end time when fragmented with a single edit
3877 * list that may only cover any non-fragmented data */
3878 if ((stream->dummy_segment ||
3879 (qtdemux->fragmented && stream->n_segments == 1)) &&
3880 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3881 /* Update all dummy values to new duration */
3882 stream->segments[0].stop_time = duration;
3883 stream->segments[0].duration = duration;
3884 stream->segments[0].media_stop = duration;
3886 /* let downstream know we possibly have a new stop time */
3887 if (stream->segment_index != -1) {
3890 if (qtdemux->segment.rate >= 0) {
3891 pos = stream->segment.start;
3893 pos = stream->segment.stop;
3896 gst_qtdemux_stream_update_segment (qtdemux, stream,
3897 stream->segment_index, pos, NULL, NULL);
3905 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3906 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3907 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3908 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3911 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3913 gint32 data_offset = 0;
3915 guint32 flags = 0, first_flags = 0, samples_count = 0;
3918 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3919 QtDemuxSample *sample;
3920 gboolean ismv = FALSE;
3921 gint64 initial_offset;
3924 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3925 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3926 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3927 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3929 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3930 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3934 /* presence of stss or not can't really tell us much,
3935 * and flags and so on tend to be marginally reliable in these files */
3936 if (stream->subtype == FOURCC_soun) {
3937 GST_DEBUG_OBJECT (qtdemux,
3938 "sound track in fragmented file; marking all keyframes");
3939 stream->all_keyframe = TRUE;
3942 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3943 !gst_byte_reader_get_uint24_be (trun, &flags))
3946 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3949 if (flags & TR_DATA_OFFSET) {
3950 /* note this is really signed */
3951 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3953 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3954 /* default base offset = first byte of moof */
3955 if (*base_offset == -1) {
3956 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3957 *base_offset = moof_offset;
3959 *running_offset = *base_offset + data_offset;
3961 /* if no offset at all, that would mean data starts at moof start,
3962 * which is a bit wrong and is ismv crappy way, so compensate
3963 * assuming data is in mdat following moof */
3964 if (*base_offset == -1) {
3965 *base_offset = moof_offset + moof_length + 8;
3966 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3969 if (*running_offset == -1)
3970 *running_offset = *base_offset;
3973 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3975 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3976 data_offset, flags, samples_count);
3978 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3979 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3980 GST_DEBUG_OBJECT (qtdemux,
3981 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3982 flags ^= TR_FIRST_SAMPLE_FLAGS;
3984 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3986 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3990 /* FIXME ? spec says other bits should also be checked to determine
3991 * entry size (and prefix size for that matter) */
3993 dur_offset = size_offset = 0;
3994 if (flags & TR_SAMPLE_DURATION) {
3995 GST_LOG_OBJECT (qtdemux, "entry duration present");
3996 dur_offset = entry_size;
3999 if (flags & TR_SAMPLE_SIZE) {
4000 GST_LOG_OBJECT (qtdemux, "entry size present");
4001 size_offset = entry_size;
4004 if (flags & TR_SAMPLE_FLAGS) {
4005 GST_LOG_OBJECT (qtdemux, "entry flags present");
4006 flags_offset = entry_size;
4009 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4010 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
4011 ct_offset = entry_size;
4015 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
4017 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
4019 if (stream->n_samples + samples_count >=
4020 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
4023 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
4024 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
4025 (stream->n_samples + samples_count) *
4026 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
4028 /* create a new array of samples if it's the first sample parsed */
4029 if (stream->n_samples == 0) {
4030 g_assert (stream->samples == NULL);
4031 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
4032 /* or try to reallocate it with space enough to insert the new samples */
4034 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
4035 stream->n_samples + samples_count);
4036 if (stream->samples == NULL)
4039 if (qtdemux->fragment_start != -1) {
4040 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
4041 qtdemux->fragment_start = -1;
4043 if (stream->n_samples == 0) {
4044 if (decode_ts > 0) {
4045 timestamp = decode_ts;
4046 } else if (stream->pending_seek != NULL) {
4047 /* if we don't have a timestamp from a tfdt box, we'll use the one
4048 * from the mfra seek table */
4049 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
4050 GST_TIME_ARGS (stream->pending_seek->ts));
4052 /* FIXME: this is not fully correct, the timestamp refers to the random
4053 * access sample refered to in the tfra entry, which may not necessarily
4054 * be the first sample in the tfrag/trun (but hopefully/usually is) */
4055 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
4060 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4061 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
4062 GST_TIME_ARGS (gst_ts));
4064 /* If this is a GST_FORMAT_BYTES stream and we have a tfdt then use it
4065 * instead of the sum of sample durations */
4066 if (has_tfdt && !qtdemux->upstream_format_is_time) {
4067 timestamp = decode_ts;
4068 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4069 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4070 " (using tfdt)", GST_TIME_ARGS (gst_ts));
4072 /* subsequent fragments extend stream */
4074 stream->samples[stream->n_samples - 1].timestamp +
4075 stream->samples[stream->n_samples - 1].duration;
4076 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
4077 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
4078 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
4083 initial_offset = *running_offset;
4085 sample = stream->samples + stream->n_samples;
4086 for (i = 0; i < samples_count; i++) {
4087 guint32 dur, size, sflags;
4090 /* first read sample data */
4091 if (flags & TR_SAMPLE_DURATION) {
4092 dur = QT_UINT32 (data + dur_offset);
4094 dur = d_sample_duration;
4096 if (flags & TR_SAMPLE_SIZE) {
4097 size = QT_UINT32 (data + size_offset);
4099 size = d_sample_size;
4101 if (flags & TR_FIRST_SAMPLE_FLAGS) {
4103 sflags = first_flags;
4105 sflags = d_sample_flags;
4107 } else if (flags & TR_SAMPLE_FLAGS) {
4108 sflags = QT_UINT32 (data + flags_offset);
4110 sflags = d_sample_flags;
4113 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
4114 /* Read offsets as signed numbers regardless of trun version as very
4115 * high offsets are unlikely and there are files out there that use
4116 * version=0 truns with negative offsets */
4117 ct = QT_UINT32 (data + ct_offset);
4119 /* FIXME: Set offset to 0 for "no decode samples". This needs
4120 * to be handled in a codec specific manner ideally. */
4121 if (ct == G_MININT32)
4128 /* fill the sample information */
4129 sample->offset = *running_offset;
4130 sample->pts_offset = ct;
4131 sample->size = size;
4132 sample->timestamp = timestamp;
4133 sample->duration = dur;
4134 /* sample-is-difference-sample */
4135 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
4136 * now idea how it relates to bitfield other than massive LE/BE confusion */
4137 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
4138 *running_offset += size;
4140 stream->duration_moof += dur;
4147 /* Shift PTS/DTS to allow for negative composition offsets while keeping
4148 * A/V sync in place. This is similar to the code handling ctts/cslg in the
4149 * non-fragmented case.
4152 stream->cslg_shift = -min_ct;
4154 stream->cslg_shift = 0;
4156 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
4157 stream->cslg_shift);
4159 /* Update total duration if needed */
4160 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
4162 /* Pre-emptively figure out size of mdat based on trun information.
4163 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
4164 * size, else we will still be able to use this when dealing with gap'ed
4166 qtdemux->mdatleft = *running_offset - initial_offset;
4167 qtdemux->mdatoffset = initial_offset;
4168 qtdemux->mdatsize = qtdemux->mdatleft;
4170 stream->n_samples += samples_count;
4171 stream->n_samples_moof += samples_count;
4173 if (stream->pending_seek != NULL)
4174 stream->pending_seek = NULL;
4180 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
4185 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
4191 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
4192 "be larger than %uMB (broken file?)", stream->n_samples,
4193 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
4198 /* find stream with @id */
4199 static inline QtDemuxStream *
4200 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
4202 QtDemuxStream *stream;
4206 if (G_UNLIKELY (!id)) {
4207 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
4211 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4212 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4213 if (stream->track_id == id)
4216 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
4217 /* mss should have only 1 stream anyway */
4218 return QTDEMUX_NTH_STREAM (qtdemux, 0);
4225 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
4226 guint32 * fragment_number)
4228 if (!gst_byte_reader_skip (mfhd, 4))
4230 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
4235 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
4241 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
4242 QtDemuxStream ** stream, guint32 * default_sample_duration,
4243 guint32 * default_sample_size, guint32 * default_sample_flags,
4244 gint64 * base_offset)
4247 guint32 track_id = 0;
4249 if (!gst_byte_reader_skip (tfhd, 1) ||
4250 !gst_byte_reader_get_uint24_be (tfhd, &flags))
4253 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4256 *stream = qtdemux_find_stream (qtdemux, track_id);
4257 if (G_UNLIKELY (!*stream))
4258 goto unknown_stream;
4260 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4261 *base_offset = qtdemux->moof_offset;
4263 if (flags & TF_BASE_DATA_OFFSET)
4264 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4267 /* obtain stream defaults */
4268 if (qtdemux_parse_trex (qtdemux, *stream,
4269 default_sample_duration, default_sample_size, default_sample_flags)) {
4271 /* Default sample description index is only valid if trex parsing succeeded */
4272 (*stream)->stsd_sample_description_id =
4273 (*stream)->def_sample_description_index - 1;
4276 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4277 guint32 sample_description_index;
4278 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4280 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4283 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
4284 /* mss has no stsd entry */
4285 (*stream)->stsd_sample_description_id = 0;
4288 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4289 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4292 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4293 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4296 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4297 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4304 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4309 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
4315 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4316 guint64 * decode_time)
4318 guint32 version = 0;
4320 if (!gst_byte_reader_get_uint32_be (br, &version))
4325 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4328 guint32 dec_time = 0;
4329 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4331 *decode_time = dec_time;
4334 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4341 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4346 /* Returns a pointer to a GstStructure containing the properties of
4347 * the stream sample identified by @sample_index. The caller must unref
4348 * the returned object after use. Returns NULL if unsuccessful. */
4349 static GstStructure *
4350 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4351 QtDemuxStream * stream, guint sample_index)
4353 QtDemuxCencSampleSetInfo *info = NULL;
4355 g_return_val_if_fail (stream != NULL, NULL);
4356 g_return_val_if_fail (stream->protected, NULL);
4357 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4359 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4361 /* Currently, cenc properties for groups of samples are not supported, so
4362 * simply return a copy of the default sample properties */
4363 return gst_structure_copy (info->default_properties);
4366 /* Parses the sizes of sample auxiliary information contained within a stream,
4367 * as given in a saiz box. Returns array of sample_count guint8 size values,
4368 * or NULL on failure */
4370 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4371 GstByteReader * br, guint32 * sample_count)
4375 guint8 default_info_size;
4377 g_return_val_if_fail (qtdemux != NULL, NULL);
4378 g_return_val_if_fail (stream != NULL, NULL);
4379 g_return_val_if_fail (br != NULL, NULL);
4380 g_return_val_if_fail (sample_count != NULL, NULL);
4382 if (!gst_byte_reader_get_uint32_be (br, &flags))
4386 /* aux_info_type and aux_info_type_parameter are ignored */
4387 if (!gst_byte_reader_skip (br, 8))
4391 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4393 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4395 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4397 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4400 if (default_info_size == 0) {
4401 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4405 info_sizes = g_new (guint8, *sample_count);
4406 memset (info_sizes, default_info_size, *sample_count);
4412 /* Parses the offset of sample auxiliary information contained within a stream,
4413 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4415 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4416 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4421 guint32 aux_info_type = 0;
4422 guint32 aux_info_type_parameter = 0;
4423 guint32 entry_count;
4426 const guint8 *aux_info_type_data = NULL;
4428 g_return_val_if_fail (qtdemux != NULL, FALSE);
4429 g_return_val_if_fail (stream != NULL, FALSE);
4430 g_return_val_if_fail (br != NULL, FALSE);
4431 g_return_val_if_fail (offset != NULL, FALSE);
4433 if (!gst_byte_reader_get_uint8 (br, &version))
4436 if (!gst_byte_reader_get_uint24_be (br, &flags))
4441 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4443 aux_info_type = QT_FOURCC (aux_info_type_data);
4445 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4447 } else if (stream->protected) {
4448 aux_info_type = stream->protection_scheme_type;
4450 aux_info_type = CUR_STREAM (stream)->fourcc;
4454 *info_type = aux_info_type;
4455 if (info_type_parameter)
4456 *info_type_parameter = aux_info_type_parameter;
4458 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4459 "aux_info_type_parameter: %#06x",
4460 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4462 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4465 if (entry_count != 1) {
4466 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4471 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4473 *offset = (guint64) off_32;
4475 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4480 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4485 qtdemux_gst_structure_free (GstStructure * gststructure)
4488 gst_structure_free (gststructure);
4492 /* Parses auxiliary information relating to samples protected using
4493 * Common Encryption (cenc); the format of this information
4494 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
4497 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4498 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4500 QtDemuxCencSampleSetInfo *ss_info = NULL;
4503 GPtrArray *old_crypto_info = NULL;
4504 guint old_entries = 0;
4506 g_return_val_if_fail (qtdemux != NULL, FALSE);
4507 g_return_val_if_fail (stream != NULL, FALSE);
4508 g_return_val_if_fail (br != NULL, FALSE);
4509 g_return_val_if_fail (stream->protected, FALSE);
4510 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4512 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4514 if (ss_info->crypto_info) {
4515 old_crypto_info = ss_info->crypto_info;
4516 /* Count number of non-null entries remaining at the tail end */
4517 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4518 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4524 ss_info->crypto_info =
4525 g_ptr_array_new_full (sample_count + old_entries,
4526 (GDestroyNotify) qtdemux_gst_structure_free);
4528 /* We preserve old entries because we parse the next moof in advance
4529 * of consuming all samples from the previous moof, and otherwise
4530 * we'd discard the corresponding crypto info for the samples
4531 * from the previous fragment. */
4533 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4535 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4536 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4538 g_ptr_array_index (old_crypto_info, i) = NULL;
4542 if (old_crypto_info) {
4543 /* Everything now belongs to the new array */
4544 g_ptr_array_free (old_crypto_info, TRUE);
4547 for (i = 0; i < sample_count; ++i) {
4548 GstStructure *properties;
4549 guint16 n_subsamples = 0;
4553 gboolean could_read_iv;
4555 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4556 if (properties == NULL) {
4557 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4560 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4561 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4562 gst_structure_free (properties);
4566 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
4567 if (could_read_iv) {
4568 buf = gst_buffer_new_wrapped (data, iv_size);
4569 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4570 gst_buffer_unref (buf);
4571 } else if (stream->protection_scheme_type == FOURCC_cbcs) {
4572 const GValue *constant_iv_size_value =
4573 gst_structure_get_value (properties, "constant_iv_size");
4574 const GValue *constant_iv_value =
4575 gst_structure_get_value (properties, "iv");
4576 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
4577 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
4578 gst_structure_free (properties);
4581 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
4582 gst_structure_remove_field (properties, "constant_iv_size");
4583 } else if (stream->protection_scheme_type == FOURCC_cenc) {
4584 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4585 gst_structure_free (properties);
4588 size = info_sizes[i];
4589 if (size > iv_size) {
4590 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4591 || !(n_subsamples > 0)) {
4592 gst_structure_free (properties);
4593 GST_ERROR_OBJECT (qtdemux,
4594 "failed to get subsample count for sample %u", i);
4597 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4598 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4599 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4601 gst_structure_free (properties);
4604 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4606 gst_structure_free (properties);
4609 gst_structure_set (properties,
4610 "subsample_count", G_TYPE_UINT, n_subsamples,
4611 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4612 gst_buffer_unref (buf);
4614 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4616 g_ptr_array_add (ss_info->crypto_info, properties);
4621 /* Converts a UUID in raw byte form to a string representation, as defined in
4622 * RFC 4122. The caller takes ownership of the returned string and is
4623 * responsible for freeing it after use. */
4625 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4627 const guint8 *uuid = (const guint8 *) uuid_bytes;
4629 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4630 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4631 uuid[0], uuid[1], uuid[2], uuid[3],
4632 uuid[4], uuid[5], uuid[6], uuid[7],
4633 uuid[8], uuid[9], uuid[10], uuid[11],
4634 uuid[12], uuid[13], uuid[14], uuid[15]);
4637 /* Parses a Protection System Specific Header box (pssh), as defined in the
4638 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4639 * information needed by a specific content protection system in order to
4640 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4643 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4645 gchar *sysid_string;
4646 guint32 pssh_size = QT_UINT32 (node->data);
4647 GstBuffer *pssh = NULL;
4648 GstEvent *event = NULL;
4649 guint32 parent_box_type;
4652 if (G_UNLIKELY (pssh_size < 32U)) {
4653 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4658 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4660 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4662 pssh = gst_buffer_new_memdup (node->data, pssh_size);
4663 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4664 gst_buffer_get_size (pssh));
4666 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4668 /* Push an event containing the pssh box onto the queues of all streams. */
4669 event = gst_event_new_protection (sysid_string, pssh,
4670 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4671 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4672 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4673 GST_TRACE_OBJECT (qtdemux,
4674 "adding protection event for stream %s and system %s",
4675 stream->stream_id, sysid_string);
4676 g_queue_push_tail (&stream->protection_scheme_event_queue,
4677 gst_event_ref (event));
4679 g_free (sysid_string);
4680 gst_event_unref (event);
4681 gst_buffer_unref (pssh);
4686 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4687 guint64 moof_offset, QtDemuxStream * stream)
4689 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4691 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4692 GNode *saiz_node, *saio_node, *pssh_node;
4693 GstByteReader saiz_data, saio_data;
4694 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4695 gint64 base_offset, running_offset;
4697 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4699 /* NOTE @stream ignored */
4701 moof_node = g_node_new ((guint8 *) buffer);
4702 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4703 qtdemux_node_dump (qtdemux, moof_node);
4705 /* Get fragment number from mfhd and check it's valid */
4707 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4708 if (mfhd_node == NULL)
4710 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4712 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4714 /* unknown base_offset to start with */
4715 base_offset = running_offset = -1;
4716 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4718 guint64 decode_time = 0;
4720 /* Fragment Header node */
4722 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4726 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4727 &ds_size, &ds_flags, &base_offset))
4730 /* The following code assumes at most a single set of sample auxiliary
4731 * data in the fragment (consisting of a saiz box and a corresponding saio
4732 * box); in theory, however, there could be multiple sets of sample
4733 * auxiliary data in a fragment. */
4735 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4738 guint32 info_type = 0;
4740 guint32 info_type_parameter = 0;
4742 g_free (qtdemux->cenc_aux_info_sizes);
4744 qtdemux->cenc_aux_info_sizes =
4745 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4746 &qtdemux->cenc_aux_sample_count);
4747 if (qtdemux->cenc_aux_info_sizes == NULL) {
4748 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4752 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4755 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4756 g_free (qtdemux->cenc_aux_info_sizes);
4757 qtdemux->cenc_aux_info_sizes = NULL;
4761 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4762 &info_type, &info_type_parameter, &offset))) {
4763 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4764 g_free (qtdemux->cenc_aux_info_sizes);
4765 qtdemux->cenc_aux_info_sizes = NULL;
4768 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4769 offset += (guint64) (base_offset - qtdemux->moof_offset);
4770 if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs)
4771 && info_type_parameter == 0U) {
4773 if (offset > length) {
4774 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4775 qtdemux->cenc_aux_info_offset = offset;
4777 gst_byte_reader_init (&br, buffer + offset, length - offset);
4778 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4779 qtdemux->cenc_aux_info_sizes,
4780 qtdemux->cenc_aux_sample_count)) {
4781 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4782 g_free (qtdemux->cenc_aux_info_sizes);
4783 qtdemux->cenc_aux_info_sizes = NULL;
4791 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4794 /* We'll use decode_time to interpolate timestamps
4795 * in case the input timestamps are missing */
4796 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4798 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4799 " (%" GST_TIME_FORMAT ")", decode_time,
4800 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4801 decode_time) : GST_CLOCK_TIME_NONE));
4803 /* Discard the fragment buffer timestamp info to avoid using it.
4804 * Rely on tfdt instead as it is more accurate than the timestamp
4805 * that is fetched from a manifest/playlist and is usually
4807 qtdemux->fragment_start = -1;
4810 if (G_UNLIKELY (!stream)) {
4811 /* we lost track of offset, we'll need to regain it,
4812 * but can delay complaining until later or avoid doing so altogether */
4816 if (G_UNLIKELY (base_offset < -1))
4819 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4821 if (!qtdemux->pullbased) {
4822 /* Sample tables can grow enough to be problematic if the system memory
4823 * is very low (e.g. embedded devices) and the videos very long
4824 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4825 * Fortunately, we can easily discard them for each new fragment when
4826 * we know qtdemux will not receive seeks outside of the current fragment.
4827 * adaptivedemux honors this assumption.
4828 * This optimization is also useful for applications that use qtdemux as
4829 * a push-based simple demuxer, like Media Source Extensions. */
4830 gst_qtdemux_stream_flush_samples_data (stream);
4833 /* initialise moof sample data */
4834 stream->n_samples_moof = 0;
4835 stream->duration_last_moof = stream->duration_moof;
4836 stream->duration_moof = 0;
4838 /* Track Run node */
4840 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4843 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4844 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4845 &running_offset, decode_time, (tfdt_node != NULL));
4846 /* iterate all siblings */
4847 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4849 /* don't use tfdt for subsequent trun as it only refers to the first */
4853 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4855 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4856 guint32 box_length = QT_UINT32 (uuid_buffer);
4858 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4861 /* if no new base_offset provided for next traf,
4862 * base is end of current traf */
4863 base_offset = running_offset;
4864 running_offset = -1;
4866 if (stream->n_samples_moof && stream->duration_moof)
4867 stream->new_caps = TRUE;
4870 /* iterate all siblings */
4871 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4874 /* parse any protection system info */
4875 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4877 /* Unref old protection events if we are going to receive new ones. */
4878 qtdemux_clear_protection_events_on_all_streams (qtdemux);
4881 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4882 qtdemux_parse_pssh (qtdemux, pssh_node);
4883 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4886 if (!qtdemux->upstream_format_is_time
4887 && qtdemux->variant != VARIANT_MSE_BYTESTREAM
4888 && !qtdemux->first_moof_already_parsed
4889 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4891 /* Unless the user has explicitly requested another seek, perform an
4892 * internal seek to the time specified in the tfdt.
4894 * This way if the user opens a file where the first tfdt is 1 hour
4895 * into the presentation, they will not have to wait 1 hour for run
4896 * time to catch up and actual playback to start. */
4899 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4900 "performing an internal seek to %" GST_TIME_FORMAT,
4901 GST_TIME_ARGS (min_dts));
4903 qtdemux->segment.start = min_dts;
4904 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4906 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4907 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4908 stream->time_position = min_dts;
4911 /* Before this code was run a segment was already sent when the moov was
4912 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4913 * be emitted after a moov, and we can emit a second segment anyway for
4914 * special cases like this. */
4915 qtdemux->need_segment = TRUE;
4918 qtdemux->first_moof_already_parsed = TRUE;
4920 g_node_destroy (moof_node);
4925 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4930 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4935 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4940 g_node_destroy (moof_node);
4941 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4942 (_("This file is corrupt and cannot be played.")), (NULL));
4948 /* might be used if some day we actually use mfra & co
4949 * for random access to fragments,
4950 * but that will require quite some modifications and much less relying
4951 * on a sample array */
4955 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4957 QtDemuxStream *stream;
4958 guint32 ver_flags, track_id, len, num_entries, i;
4959 guint value_size, traf_size, trun_size, sample_size;
4960 guint64 time = 0, moof_offset = 0;
4962 GstBuffer *buf = NULL;
4967 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4969 if (!gst_byte_reader_skip (&tfra, 8))
4972 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4975 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4976 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4977 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4980 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4982 stream = qtdemux_find_stream (qtdemux, track_id);
4984 goto unknown_trackid;
4986 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4987 sample_size = (len & 3) + 1;
4988 trun_size = ((len & 12) >> 2) + 1;
4989 traf_size = ((len & 48) >> 4) + 1;
4991 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4992 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4994 if (num_entries == 0)
4997 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4998 value_size + value_size + traf_size + trun_size + sample_size))
5001 g_free (stream->ra_entries);
5002 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
5003 stream->n_ra_entries = num_entries;
5005 for (i = 0; i < num_entries; i++) {
5006 qt_atom_parser_get_offset (&tfra, value_size, &time);
5007 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
5008 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
5009 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
5010 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
5012 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
5014 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
5015 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
5017 stream->ra_entries[i].ts = time;
5018 stream->ra_entries[i].moof_offset = moof_offset;
5020 /* don't want to go through the entire file and read all moofs at startup */
5022 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
5023 if (ret != GST_FLOW_OK)
5025 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
5026 moof_offset, stream);
5027 gst_buffer_unref (buf);
5031 check_update_duration (qtdemux, time);
5038 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
5043 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
5048 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
5054 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
5056 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
5057 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
5058 GstBuffer *mfro = NULL, *mfra = NULL;
5060 gboolean ret = FALSE;
5061 GNode *mfra_node, *tfra_node;
5062 guint64 mfra_offset = 0;
5063 guint32 fourcc, mfra_size;
5066 /* query upstream size in bytes */
5067 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
5068 goto size_query_failed;
5070 /* mfro box should be at the very end of the file */
5071 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
5072 if (flow != GST_FLOW_OK)
5075 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
5077 fourcc = QT_FOURCC (mfro_map.data + 4);
5078 if (fourcc != FOURCC_mfro)
5081 GST_INFO_OBJECT (qtdemux, "Found mfro box");
5082 if (mfro_map.size < 16)
5083 goto invalid_mfro_size;
5085 mfra_size = QT_UINT32 (mfro_map.data + 12);
5086 if (mfra_size >= len)
5087 goto invalid_mfra_size;
5089 mfra_offset = len - mfra_size;
5091 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
5092 mfra_offset, mfra_size);
5094 /* now get and parse mfra box */
5095 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
5096 if (flow != GST_FLOW_OK)
5099 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
5101 mfra_node = g_node_new ((guint8 *) mfra_map.data);
5102 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
5104 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
5107 qtdemux_parse_tfra (qtdemux, tfra_node);
5108 /* iterate all siblings */
5109 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
5111 g_node_destroy (mfra_node);
5113 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
5119 if (mfro_map.memory != NULL)
5120 gst_buffer_unmap (mfro, &mfro_map);
5121 gst_buffer_unref (mfro);
5124 if (mfra_map.memory != NULL)
5125 gst_buffer_unmap (mfra, &mfra_map);
5126 gst_buffer_unref (mfra);
5133 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
5138 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
5143 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
5148 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
5154 add_offset (guint64 offset, guint64 advance)
5156 /* Avoid 64-bit overflow by clamping */
5157 if (offset > G_MAXUINT64 - advance)
5159 return offset + advance;
5162 static GstFlowReturn
5163 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
5167 GstBuffer *buf = NULL;
5168 GstFlowReturn ret = GST_FLOW_OK;
5169 guint64 cur_offset = qtdemux->offset;
5172 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
5173 if (G_UNLIKELY (ret != GST_FLOW_OK))
5175 gst_buffer_map (buf, &map, GST_MAP_READ);
5176 if (G_LIKELY (map.size >= 8))
5177 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
5178 gst_buffer_unmap (buf, &map);
5179 gst_buffer_unref (buf);
5181 /* maybe we already got most we needed, so only consider this eof */
5182 if (G_UNLIKELY (length == 0)) {
5183 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5184 (_("Invalid atom size.")),
5185 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
5186 GST_FOURCC_ARGS (fourcc)));
5193 /* record for later parsing when needed */
5194 if (!qtdemux->moof_offset) {
5195 qtdemux->moof_offset = qtdemux->offset;
5197 if (qtdemux_pull_mfro_mfra (qtdemux)) {
5200 qtdemux->offset += length; /* skip moof and keep going */
5202 if (qtdemux->got_moov) {
5203 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
5215 GST_LOG_OBJECT (qtdemux,
5216 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5217 GST_FOURCC_ARGS (fourcc), cur_offset);
5218 qtdemux->offset = add_offset (qtdemux->offset, length);
5223 GstBuffer *moov = NULL;
5225 if (qtdemux->got_moov) {
5226 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
5227 qtdemux->offset = add_offset (qtdemux->offset, length);
5231 if (length == G_MAXUINT64) {
5232 /* Read until the end */
5234 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES,
5236 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5237 (_("Cannot query file size")),
5238 ("Duration query on sink pad failed"));
5239 ret = GST_FLOW_ERROR;
5242 if (G_UNLIKELY (cur_offset > duration)) {
5243 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5244 (_("Cannot query file size")),
5245 ("Duration %" G_GINT64_FORMAT " < current offset %"
5246 G_GUINT64_FORMAT, duration, cur_offset));
5247 ret = GST_FLOW_ERROR;
5250 length = duration - cur_offset;
5251 if (length > QTDEMUX_MAX_ATOM_SIZE) {
5252 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5253 (_("Cannot demux file")),
5254 ("Moov atom size %" G_GINT64_FORMAT " > maximum %d", length,
5255 QTDEMUX_MAX_ATOM_SIZE));
5256 ret = GST_FLOW_ERROR;
5261 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
5262 if (ret != GST_FLOW_OK)
5264 gst_buffer_map (moov, &map, GST_MAP_READ);
5266 if (length != map.size) {
5267 /* Some files have a 'moov' atom at the end of the file which contains
5268 * a terminal 'free' atom where the body of the atom is missing.
5269 * Check for, and permit, this special case.
5271 if (map.size >= 8) {
5272 guint8 *final_data = map.data + (map.size - 8);
5273 guint32 final_length = QT_UINT32 (final_data);
5274 guint32 final_fourcc = QT_FOURCC (final_data + 4);
5276 if (final_fourcc == FOURCC_free
5277 && map.size + final_length - 8 == length) {
5278 /* Ok, we've found that special case. Allocate a new buffer with
5279 * that free atom actually present. */
5280 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
5281 gst_buffer_fill (newmoov, 0, map.data, map.size);
5282 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
5283 gst_buffer_unmap (moov, &map);
5284 gst_buffer_unref (moov);
5286 gst_buffer_map (moov, &map, GST_MAP_READ);
5291 if (length != map.size) {
5292 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5293 (_("This file is incomplete and cannot be played.")),
5294 ("We got less than expected (received %" G_GSIZE_FORMAT
5295 ", wanted %" G_GUINT64_FORMAT ", offset %" G_GUINT64_FORMAT ")",
5296 map.size, length, cur_offset));
5297 gst_buffer_unmap (moov, &map);
5298 gst_buffer_unref (moov);
5299 ret = GST_FLOW_ERROR;
5302 qtdemux->offset += length;
5304 qtdemux_parse_moov (qtdemux, map.data, length);
5305 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
5307 qtdemux_parse_tree (qtdemux);
5308 if (qtdemux->moov_node_compressed) {
5309 g_node_destroy (qtdemux->moov_node_compressed);
5310 g_free (qtdemux->moov_node->data);
5312 qtdemux->moov_node_compressed = NULL;
5313 g_node_destroy (qtdemux->moov_node);
5314 qtdemux->moov_node = NULL;
5315 gst_buffer_unmap (moov, &map);
5316 gst_buffer_unref (moov);
5317 qtdemux->got_moov = TRUE;
5323 GstBuffer *ftyp = NULL;
5325 /* extract major brand; might come in handy for ISO vs QT issues */
5326 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
5327 if (ret != GST_FLOW_OK)
5329 qtdemux->offset += length;
5330 gst_buffer_map (ftyp, &map, GST_MAP_READ);
5331 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
5332 gst_buffer_unmap (ftyp, &map);
5333 gst_buffer_unref (ftyp);
5338 GstBuffer *styp = NULL;
5340 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &styp);
5341 if (ret != GST_FLOW_OK)
5343 qtdemux->offset += length;
5344 gst_buffer_map (styp, &map, GST_MAP_READ);
5345 qtdemux_parse_styp (qtdemux, map.data, map.size);
5346 gst_buffer_unmap (styp, &map);
5347 gst_buffer_unref (styp);
5352 GstBuffer *uuid = NULL;
5354 /* uuid are extension atoms */
5355 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
5356 if (ret != GST_FLOW_OK)
5358 qtdemux->offset += length;
5359 gst_buffer_map (uuid, &map, GST_MAP_READ);
5360 qtdemux_parse_uuid (qtdemux, map.data, map.size);
5361 gst_buffer_unmap (uuid, &map);
5362 gst_buffer_unref (uuid);
5367 GstBuffer *sidx = NULL;
5368 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
5369 if (ret != GST_FLOW_OK)
5371 qtdemux->offset += length;
5372 gst_buffer_map (sidx, &map, GST_MAP_READ);
5373 qtdemux_parse_sidx (qtdemux, map.data, map.size);
5374 gst_buffer_unmap (sidx, &map);
5375 gst_buffer_unref (sidx);
5380 GstBuffer *meta = NULL;
5381 GNode *node, *child;
5382 GstByteReader child_data;
5383 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &meta);
5384 if (ret != GST_FLOW_OK)
5386 qtdemux->offset += length;
5387 gst_buffer_map (meta, &map, GST_MAP_READ);
5389 node = g_node_new (map.data);
5391 qtdemux_parse_node (qtdemux, node, map.data, map.size);
5393 /* Parse ONVIF Export File Format CorrectStartTime box if available */
5395 qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb,
5397 qtdemux_parse_cstb (qtdemux, &child_data);
5400 g_node_destroy (node);
5402 gst_buffer_unmap (meta, &map);
5403 gst_buffer_unref (meta);
5408 GstBuffer *unknown = NULL;
5410 GST_LOG_OBJECT (qtdemux,
5411 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5412 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5414 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5415 if (ret != GST_FLOW_OK)
5417 gst_buffer_map (unknown, &map, GST_MAP_READ);
5418 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5419 gst_buffer_unmap (unknown, &map);
5420 gst_buffer_unref (unknown);
5421 qtdemux->offset += length;
5427 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5428 /* digested all data, show what we have */
5429 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5430 if (qtdemux->spherical_metadata)
5431 _send_spherical_metadata_msg_to_bus (qtdemux);
5432 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5433 qtdemux_prepare_streams (qtdemux);
5434 QTDEMUX_EXPOSE_LOCK (qtdemux);
5435 ret = qtdemux_expose_streams (qtdemux);
5436 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
5438 qtdemux->state = QTDEMUX_STATE_MOVIE;
5439 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5446 /* Seeks to the previous keyframe of the indexed stream and
5447 * aligns other streams with respect to the keyframe timestamp
5448 * of indexed stream. Only called in case of Reverse Playback
5450 static GstFlowReturn
5451 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5453 guint32 seg_idx = 0, k_index = 0;
5454 guint32 ref_seg_idx, ref_k_index;
5455 GstClockTime k_pos = 0, last_stop = 0;
5456 QtDemuxSegment *seg = NULL;
5457 QtDemuxStream *ref_str = NULL;
5458 guint64 seg_media_start_mov; /* segment media start time in mov format */
5462 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5463 * and finally align all the other streams on that timestamp with their
5464 * respective keyframes */
5465 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5466 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5468 /* No candidate yet, take the first stream */
5474 /* So that stream has a segment, we prefer video streams */
5475 if (str->subtype == FOURCC_vide) {
5481 if (G_UNLIKELY (!ref_str)) {
5482 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5486 if (G_UNLIKELY (!ref_str->from_sample)) {
5487 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5491 /* So that stream has been playing from from_sample to to_sample. We will
5492 * get the timestamp of the previous sample and search for a keyframe before
5493 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5494 if (ref_str->subtype == FOURCC_vide) {
5495 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5496 ref_str->from_sample - 1, FALSE);
5498 if (ref_str->from_sample >= 10)
5499 k_index = ref_str->from_sample - 10;
5505 ref_str->samples[k_index].timestamp +
5506 ref_str->samples[k_index].pts_offset;
5508 /* get current segment for that stream */
5509 seg = &ref_str->segments[ref_str->segment_index];
5510 /* Use segment start in original timescale for comparisons */
5511 seg_media_start_mov = seg->trak_media_start;
5513 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5514 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
5515 k_index, target_ts, seg_media_start_mov,
5516 GST_TIME_ARGS (seg->media_start));
5518 /* Crawl back through segments to find the one containing this I frame */
5519 while (target_ts < seg_media_start_mov) {
5520 GST_DEBUG_OBJECT (qtdemux,
5521 "keyframe position (sample %u) is out of segment %u " " target %"
5522 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5523 ref_str->segment_index, target_ts, seg_media_start_mov);
5525 if (G_UNLIKELY (!ref_str->segment_index)) {
5526 /* Reached first segment, let's consider it's EOS */
5529 ref_str->segment_index--;
5530 seg = &ref_str->segments[ref_str->segment_index];
5531 /* Use segment start in original timescale for comparisons */
5532 seg_media_start_mov = seg->trak_media_start;
5534 /* Calculate time position of the keyframe and where we should stop */
5536 QTSTREAMTIME_TO_GSTTIME (ref_str,
5537 target_ts - seg->trak_media_start) + seg->time;
5539 QTSTREAMTIME_TO_GSTTIME (ref_str,
5540 ref_str->samples[ref_str->from_sample].timestamp -
5541 seg->trak_media_start) + seg->time;
5543 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5544 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5545 k_index, GST_TIME_ARGS (k_pos));
5547 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5548 qtdemux->segment.position = last_stop;
5549 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5550 GST_TIME_ARGS (last_stop));
5552 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5553 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5557 ref_seg_idx = ref_str->segment_index;
5558 ref_k_index = k_index;
5560 /* Align them all on this */
5561 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5563 GstClockTime seg_time = 0;
5564 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5566 /* aligning reference stream again might lead to backing up to yet another
5567 * keyframe (due to timestamp rounding issues),
5568 * potentially putting more load on downstream; so let's try to avoid */
5569 if (str == ref_str) {
5570 seg_idx = ref_seg_idx;
5571 seg = &str->segments[seg_idx];
5572 k_index = ref_k_index;
5573 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5574 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5576 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5577 GST_DEBUG_OBJECT (qtdemux,
5578 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5579 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5581 /* get segment and time in the segment */
5582 seg = &str->segments[seg_idx];
5583 seg_time = k_pos - seg->time;
5585 /* get the media time in the segment.
5586 * No adjustment for empty "filler" segments */
5587 if (seg->media_start != GST_CLOCK_TIME_NONE)
5588 seg_time += seg->media_start;
5590 /* get the index of the sample with media time */
5591 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5592 GST_DEBUG_OBJECT (qtdemux,
5593 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5594 GST_TIME_ARGS (seg_time), index);
5596 /* find previous keyframe */
5597 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5600 /* Remember until where we want to go */
5601 if (str->from_sample == 0) {
5602 GST_LOG_OBJECT (qtdemux, "already at sample 0");
5605 str->to_sample = str->from_sample - 1;
5607 /* Define our time position */
5609 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5610 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5611 if (seg->media_start != GST_CLOCK_TIME_NONE)
5612 str->time_position -= seg->media_start;
5614 /* Now seek back in time */
5615 gst_qtdemux_move_stream (qtdemux, str, k_index);
5616 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5617 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5618 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5624 return GST_FLOW_EOS;
5628 * Gets the current qt segment start, stop and position for the
5629 * given time offset. This is used in update_segment()
5632 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5633 QtDemuxStream * stream, GstClockTime offset,
5634 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5636 GstClockTime seg_time;
5637 GstClockTime start, stop, time;
5638 QtDemuxSegment *segment;
5640 segment = &stream->segments[stream->segment_index];
5642 /* get time in this segment */
5643 seg_time = (offset - segment->time) * segment->rate;
5645 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5646 GST_TIME_ARGS (seg_time));
5648 if (G_UNLIKELY (seg_time > segment->duration)) {
5649 GST_LOG_OBJECT (stream->pad,
5650 "seg_time > segment->duration %" GST_TIME_FORMAT,
5651 GST_TIME_ARGS (segment->duration));
5652 seg_time = segment->duration;
5655 /* qtdemux->segment.stop is in outside-time-realm, whereas
5656 * segment->media_stop is in track-time-realm.
5658 * In order to compare the two, we need to bring segment.stop
5659 * into the track-time-realm
5661 * FIXME - does this comment still hold? Don't see any conversion here */
5663 stop = qtdemux->segment.stop;
5664 if (stop == GST_CLOCK_TIME_NONE)
5665 stop = qtdemux->segment.duration;
5666 if (stop == GST_CLOCK_TIME_NONE)
5667 stop = segment->media_stop;
5670 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5672 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5673 start = segment->time + seg_time;
5675 stop = start - seg_time + segment->duration;
5676 } else if (qtdemux->segment.rate >= 0) {
5677 start = MIN (segment->media_start + seg_time, stop);
5680 if (segment->media_start >= qtdemux->segment.start) {
5681 time = segment->time;
5683 time = segment->time + (qtdemux->segment.start - segment->media_start);
5686 start = MAX (segment->media_start, qtdemux->segment.start);
5687 stop = MIN (segment->media_start + seg_time, stop);
5696 * Updates the qt segment used for the stream and pushes a new segment event
5697 * downstream on this stream's pad.
5700 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5701 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5702 GstClockTime * _stop)
5704 QtDemuxSegment *segment;
5705 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5709 /* update the current segment */
5710 stream->segment_index = seg_idx;
5712 /* get the segment */
5713 segment = &stream->segments[seg_idx];
5715 if (G_UNLIKELY (offset < segment->time)) {
5716 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5717 GST_TIME_ARGS (segment->time));
5721 /* segment lies beyond total indicated duration */
5722 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5723 segment->time > qtdemux->segment.duration)) {
5724 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5725 " < segment->time %" GST_TIME_FORMAT,
5726 GST_TIME_ARGS (qtdemux->segment.duration),
5727 GST_TIME_ARGS (segment->time));
5731 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5732 &start, &stop, &time);
5734 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5735 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5736 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5738 /* combine global rate with that of the segment */
5739 rate = segment->rate * qtdemux->segment.rate;
5741 /* Copy flags from main segment */
5742 stream->segment.flags = qtdemux->segment.flags;
5744 /* update the segment values used for clipping */
5745 stream->segment.offset = qtdemux->segment.offset;
5746 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5747 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5748 stream->segment.rate = rate;
5749 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5750 stream->cslg_shift);
5752 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5753 stream->cslg_shift);
5755 stream->segment.stop = stop;
5756 stream->segment.time = time;
5757 stream->segment.position = stream->segment.start;
5759 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5762 /* now prepare and send the segment */
5764 event = gst_event_new_segment (&stream->segment);
5765 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5766 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5768 gst_pad_push_event (stream->pad, event);
5769 /* assume we can send more data now */
5770 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5771 /* clear to send tags on this pad now */
5772 gst_qtdemux_push_tags (qtdemux, stream);
5783 /* activate the given segment number @seg_idx of @stream at time @offset.
5784 * @offset is an absolute global position over all the segments.
5786 * This will push out a NEWSEGMENT event with the right values and
5787 * position the stream index to the first decodable sample before
5791 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5792 guint32 seg_idx, GstClockTime offset)
5794 QtDemuxSegment *segment;
5795 guint32 index, kf_index;
5796 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5798 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5799 seg_idx, GST_TIME_ARGS (offset));
5801 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5805 segment = &stream->segments[stream->segment_index];
5807 /* in the fragmented case, we pick a fragment that starts before our
5808 * desired position and rely on downstream to wait for a keyframe
5809 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5810 * tfra entries tells us which trun/sample the key unit is in, but we don't
5811 * make use of this additional information at the moment) */
5812 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5813 stream->to_sample = G_MAXUINT32;
5816 /* well, it will be taken care of below */
5817 qtdemux->fragmented_seek_pending = FALSE;
5818 /* FIXME ideally the do_fragmented_seek can be done right here,
5819 * rather than at loop level
5820 * (which might even allow handling edit lists in a fragmented file) */
5823 /* We don't need to look for a sample in push-based */
5824 if (!qtdemux->pullbased)
5827 /* and move to the keyframe before the indicated media time of the
5829 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5830 if (qtdemux->segment.rate >= 0) {
5831 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5832 stream->to_sample = G_MAXUINT32;
5833 GST_DEBUG_OBJECT (stream->pad,
5834 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5835 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5836 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5838 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5839 stream->to_sample = index;
5840 GST_DEBUG_OBJECT (stream->pad,
5841 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5842 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5843 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5846 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5847 "this is an empty segment");
5851 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5852 * encountered an error and printed a message so we return appropriately */
5856 /* we're at the right spot */
5857 if (index == stream->sample_index) {
5858 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5862 /* find keyframe of the target index */
5863 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5865 /* go back two frames to provide lead-in for non-raw audio decoders */
5866 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
5867 guint32 lead_in = 2;
5868 guint32 old_index = kf_index;
5869 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
5871 if (gst_structure_has_name (s, "audio/mpeg")) {
5873 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5874 && mpegversion == 1) {
5875 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5880 kf_index = MAX (kf_index, lead_in) - lead_in;
5881 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
5882 GST_DEBUG_OBJECT (stream->pad,
5883 "Moving backwards %u frames to ensure sufficient sound lead-in",
5884 old_index - kf_index);
5886 kf_index = old_index;
5890 /* if we move forwards, we don't have to go back to the previous
5891 * keyframe since we already sent that. We can also just jump to
5892 * the keyframe right before the target index if there is one. */
5893 if (index > stream->sample_index) {
5894 /* moving forwards check if we move past a keyframe */
5895 if (kf_index > stream->sample_index) {
5896 GST_DEBUG_OBJECT (stream->pad,
5897 "moving forwards to keyframe at %u "
5898 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5900 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5901 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5902 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5904 GST_DEBUG_OBJECT (stream->pad,
5905 "moving forwards, keyframe at %u "
5906 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5908 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5909 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5912 GST_DEBUG_OBJECT (stream->pad,
5913 "moving backwards to %sframe at %u "
5914 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5915 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5916 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5917 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5918 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5924 /* prepare to get the current sample of @stream, getting essential values.
5926 * This function will also prepare and send the segment when needed.
5928 * Return FALSE if the stream is EOS.
5933 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5934 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5935 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5936 gboolean * keyframe)
5938 QtDemuxSample *sample;
5939 GstClockTime time_position;
5942 g_return_val_if_fail (stream != NULL, FALSE);
5944 time_position = stream->time_position;
5945 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5948 seg_idx = stream->segment_index;
5949 if (G_UNLIKELY (seg_idx == -1)) {
5950 /* find segment corresponding to time_position if we are looking
5952 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5955 /* different segment, activate it, sample_index will be set. */
5956 if (G_UNLIKELY (stream->segment_index != seg_idx))
5957 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5959 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5960 segments[stream->segment_index]))) {
5961 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5963 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5964 " prepare empty sample");
5967 *pts = *dts = time_position;
5968 *duration = seg->duration - (time_position - seg->time);
5975 if (stream->sample_index == -1)
5976 stream->sample_index = 0;
5978 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5979 stream->sample_index, stream->n_samples);
5981 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5982 if (!qtdemux->fragmented)
5985 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5989 GST_OBJECT_LOCK (qtdemux);
5990 flow = qtdemux_add_fragmented_samples (qtdemux);
5991 GST_OBJECT_UNLOCK (qtdemux);
5993 if (flow != GST_FLOW_OK)
5996 while (stream->sample_index >= stream->n_samples);
5999 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6000 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
6001 stream->sample_index);
6005 /* now get the info for the sample we're at */
6006 sample = &stream->samples[stream->sample_index];
6008 *dts = QTSAMPLE_DTS (stream, sample);
6009 *pts = QTSAMPLE_PTS (stream, sample);
6010 *offset = sample->offset;
6011 *size = sample->size;
6012 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
6013 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
6020 stream->time_position = GST_CLOCK_TIME_NONE;
6025 /* move to the next sample in @stream.
6027 * Moves to the next segment when needed.
6030 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
6032 QtDemuxSample *sample;
6033 QtDemuxSegment *segment;
6035 /* get current segment */
6036 segment = &stream->segments[stream->segment_index];
6038 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
6039 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
6043 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
6044 /* Mark the stream as EOS */
6045 GST_DEBUG_OBJECT (qtdemux,
6046 "reached max allowed sample %u, mark EOS", stream->to_sample);
6047 stream->time_position = GST_CLOCK_TIME_NONE;
6051 /* move to next sample */
6052 stream->sample_index++;
6053 stream->offset_in_sample = 0;
6055 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
6058 /* reached the last sample, we need the next segment */
6059 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
6062 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6063 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
6064 stream->sample_index);
6068 /* get next sample */
6069 sample = &stream->samples[stream->sample_index];
6071 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
6072 GST_TIME_FORMAT, GST_TIME_ARGS (QTSAMPLE_DTS (stream, sample)),
6073 GST_TIME_ARGS (segment->media_stop));
6075 /* see if we are past the segment */
6076 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
6079 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
6080 /* inside the segment, update time_position, looks very familiar to
6081 * GStreamer segments, doesn't it? */
6082 stream->time_position =
6083 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
6085 /* not yet in segment, time does not yet increment. This means
6086 * that we are still prerolling keyframes to the decoder so it can
6087 * decode the first sample of the segment. */
6088 stream->time_position = segment->time;
6092 /* move to the next segment */
6095 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
6097 if (stream->segment_index == stream->n_segments - 1) {
6098 /* are we at the end of the last segment, we're EOS */
6099 stream->time_position = GST_CLOCK_TIME_NONE;
6101 /* else we're only at the end of the current segment */
6102 stream->time_position = segment->stop_time;
6104 /* make sure we select a new segment */
6106 /* accumulate previous segments */
6107 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
6108 stream->accumulated_base +=
6109 (stream->segment.stop -
6110 stream->segment.start) / ABS (stream->segment.rate);
6112 stream->segment_index = -1;
6117 gst_qtdemux_sync_streams (GstQTDemux * demux)
6121 if (QTDEMUX_N_STREAMS (demux) <= 1)
6124 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6125 QtDemuxStream *stream;
6126 GstClockTime end_time;
6128 stream = QTDEMUX_NTH_STREAM (demux, i);
6133 /* TODO advance time on subtitle streams here, if any some day */
6135 /* some clips/trailers may have unbalanced streams at the end,
6136 * so send EOS on shorter stream to prevent stalling others */
6138 /* do not mess with EOS if SEGMENT seeking */
6139 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
6142 if (demux->pullbased) {
6143 /* loop mode is sample time based */
6144 if (!STREAM_IS_EOS (stream))
6147 /* push mode is byte position based */
6148 if (stream->n_samples &&
6149 stream->samples[stream->n_samples - 1].offset >= demux->offset)
6153 if (stream->sent_eos)
6156 /* only act if some gap */
6157 end_time = stream->segments[stream->n_segments - 1].stop_time;
6158 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
6159 ", stream end: %" GST_TIME_FORMAT,
6160 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
6161 if (GST_CLOCK_TIME_IS_VALID (end_time)
6162 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
6165 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
6166 GST_PAD_NAME (stream->pad));
6167 stream->sent_eos = TRUE;
6168 event = gst_event_new_eos ();
6169 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
6170 gst_event_set_seqnum (event, demux->segment_seqnum);
6171 gst_pad_push_event (stream->pad, event);
6176 /* EOS and NOT_LINKED need to be combined. This means that we return:
6178 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
6179 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
6181 static GstFlowReturn
6182 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
6185 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
6188 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
6191 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
6193 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
6197 /* the input buffer metadata must be writable. Returns NULL when the buffer is
6198 * completely clipped
6200 * Should be used only with raw buffers */
6202 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6205 guint64 start, stop, cstart, cstop, diff;
6206 GstClockTime pts, duration;
6208 gint num_rate, denom_rate;
6213 osize = size = gst_buffer_get_size (buf);
6216 /* depending on the type, setup the clip parameters */
6217 if (stream->subtype == FOURCC_soun) {
6218 frame_size = CUR_STREAM (stream)->bytes_per_frame;
6219 num_rate = GST_SECOND;
6220 denom_rate = (gint) CUR_STREAM (stream)->rate;
6222 } else if (stream->subtype == FOURCC_vide) {
6224 num_rate = CUR_STREAM (stream)->fps_n;
6225 denom_rate = CUR_STREAM (stream)->fps_d;
6230 if (frame_size <= 0)
6231 goto bad_frame_size;
6233 /* we can only clip if we have a valid pts */
6234 pts = GST_BUFFER_PTS (buf);
6235 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
6238 duration = GST_BUFFER_DURATION (buf);
6240 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
6242 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
6246 stop = start + duration;
6248 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
6249 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
6252 /* see if some clipping happened */
6253 diff = cstart - start;
6259 /* bring clipped time to samples and to bytes */
6260 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6263 GST_DEBUG_OBJECT (qtdemux,
6264 "clipping start to %" GST_TIME_FORMAT " %"
6265 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
6271 diff = stop - cstop;
6276 /* bring clipped time to samples and then to bytes */
6277 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6279 GST_DEBUG_OBJECT (qtdemux,
6280 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
6281 " bytes", GST_TIME_ARGS (cstop), diff);
6286 if (offset != 0 || size != osize)
6287 gst_buffer_resize (buf, offset, size);
6289 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
6290 GST_BUFFER_PTS (buf) = pts;
6291 GST_BUFFER_DURATION (buf) = duration;
6295 /* dropped buffer */
6298 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
6303 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
6308 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
6313 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
6314 gst_buffer_unref (buf);
6320 gst_qtdemux_align_buffer (GstQTDemux * demux,
6321 GstBuffer * buffer, gsize alignment)
6325 gst_buffer_map (buffer, &map, GST_MAP_READ);
6327 if (map.size < sizeof (guintptr)) {
6328 gst_buffer_unmap (buffer, &map);
6332 if (((guintptr) map.data) & (alignment - 1)) {
6333 GstBuffer *new_buffer;
6334 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
6336 new_buffer = gst_buffer_new_allocate (NULL,
6337 gst_buffer_get_size (buffer), ¶ms);
6339 /* Copy data "by hand", so ensure alignment is kept: */
6340 gst_buffer_fill (new_buffer, 0, map.data, map.size);
6342 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
6343 GST_DEBUG_OBJECT (demux,
6344 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
6347 gst_buffer_unmap (buffer, &map);
6348 gst_buffer_unref (buffer);
6353 gst_buffer_unmap (buffer, &map);
6358 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
6364 /* We are converting from pairs to triplets */
6365 *res = ccpair_size / 2 * 3;
6366 storage = g_malloc (*res);
6367 for (i = 0; i * 2 < ccpair_size; i += 1) {
6368 /* FIXME: Use line offset 0 as we simply can't know here */
6370 storage[i * 3] = 0x80 | 0x00;
6372 storage[i * 3] = 0x00 | 0x00;
6373 storage[i * 3 + 1] = ccpair[i * 2];
6374 storage[i * 3 + 2] = ccpair[i * 2 + 1];
6381 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
6385 guint32 atom_length, fourcc;
6386 QtDemuxStreamStsdEntry *stsd_entry;
6388 GST_MEMDUMP ("caption atom", data, size);
6390 /* There might be multiple atoms */
6395 atom_length = QT_UINT32 (data);
6396 fourcc = QT_FOURCC (data + 4);
6397 if (G_UNLIKELY (atom_length > size || atom_length == 8))
6400 GST_DEBUG_OBJECT (stream->pad, "here");
6402 /* Check if we have something compatible */
6403 stsd_entry = CUR_STREAM (stream);
6404 switch (stsd_entry->fourcc) {
6406 guint8 *cdat = NULL, *cdt2 = NULL;
6407 gsize cdat_size = 0, cdt2_size = 0;
6408 /* Should be cdat or cdt2 */
6409 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
6410 GST_WARNING_OBJECT (stream->pad,
6411 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
6412 GST_FOURCC_ARGS (fourcc));
6416 /* Convert to S334-1 Annex A byte triplet */
6417 if (fourcc == FOURCC_cdat)
6418 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
6420 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
6421 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
6424 /* Check for another atom ? */
6425 if (size > atom_length + 8) {
6426 guint32 new_atom_length = QT_UINT32 (data + atom_length);
6427 if (size >= atom_length + new_atom_length) {
6428 fourcc = QT_FOURCC (data + atom_length + 4);
6429 if (fourcc == FOURCC_cdat) {
6432 convert_to_s334_1a (data + atom_length + 8,
6433 new_atom_length - 8, 1, &cdat_size);
6435 GST_WARNING_OBJECT (stream->pad,
6436 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6440 convert_to_s334_1a (data + atom_length + 8,
6441 new_atom_length - 8, 2, &cdt2_size);
6443 GST_WARNING_OBJECT (stream->pad,
6444 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6449 *cclen = cdat_size + cdt2_size;
6450 res = g_malloc (*cclen);
6452 memcpy (res, cdat, cdat_size);
6454 memcpy (res + cdat_size, cdt2, cdt2_size);
6460 if (fourcc != FOURCC_ccdp) {
6461 GST_WARNING_OBJECT (stream->pad,
6462 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
6463 GST_FOURCC_ARGS (fourcc));
6466 *cclen = atom_length - 8;
6467 res = g_memdup2 (data + 8, *cclen);
6470 /* Keep this here in case other closed caption formats are added */
6471 g_assert_not_reached ();
6475 GST_MEMDUMP ("Output", res, *cclen);
6480 GST_WARNING ("[cdat] atom is too small or invalid");
6484 /* Handle Closed Caption sample buffers.
6485 * The input buffer metadata must be writable,
6486 * but time/duration etc not yet set and need not be preserved */
6488 gst_qtdemux_process_buffer_clcp (GstQTDemux * qtdemux, QtDemuxStream * stream,
6491 GstBuffer *outbuf = NULL;
6496 gst_buffer_map (buf, &map, GST_MAP_READ);
6498 /* empty buffer is sent to terminate previous subtitle */
6499 if (map.size <= 2) {
6500 gst_buffer_unmap (buf, &map);
6501 gst_buffer_unref (buf);
6505 /* For closed caption, we need to extract the information from the
6506 * [cdat],[cdt2] or [ccdp] atom */
6507 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
6508 gst_buffer_unmap (buf, &map);
6510 outbuf = _gst_buffer_new_wrapped (cc, cclen, g_free);
6511 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6513 /* Conversion failed or there's nothing */
6515 gst_buffer_unref (buf);
6520 /* DVD subpicture specific sample handling.
6521 * the input buffer metadata must be writable,
6522 * but time/duration etc not yet set and need not be preserved */
6524 gst_qtdemux_process_buffer_dvd (GstQTDemux * qtdemux, QtDemuxStream * stream,
6527 /* send a one time dvd clut event */
6528 if (stream->pending_event && stream->pad)
6529 gst_pad_push_event (stream->pad, stream->pending_event);
6530 stream->pending_event = NULL;
6532 /* empty buffer is sent to terminate previous subtitle */
6533 if (gst_buffer_get_size (buf) <= 2) {
6534 gst_buffer_unref (buf);
6538 /* That's all the processing needed for subpictures */
6542 /* Timed text formats
6543 * the input buffer metadata must be writable,
6544 * but time/duration etc not yet set and need not be preserved */
6546 gst_qtdemux_process_buffer_text (GstQTDemux * qtdemux, QtDemuxStream * stream,
6549 GstBuffer *outbuf = NULL;
6554 /* not many cases for now */
6555 if (G_UNLIKELY (stream->subtype != FOURCC_text &&
6556 stream->subtype != FOURCC_sbtl)) {
6560 gst_buffer_map (buf, &map, GST_MAP_READ);
6562 /* empty buffer is sent to terminate previous subtitle */
6563 if (map.size <= 2) {
6564 gst_buffer_unmap (buf, &map);
6565 gst_buffer_unref (buf);
6569 nsize = GST_READ_UINT16_BE (map.data);
6570 nsize = MIN (nsize, map.size - 2);
6572 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6575 /* takes care of UTF-8 validation or UTF-16 recognition,
6576 * no other encoding expected */
6577 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6578 gst_buffer_unmap (buf, &map);
6581 outbuf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
6582 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6584 /* this should not really happen unless the subtitle is corrupted */
6586 gst_buffer_unref (buf);
6588 /* FIXME ? convert optional subsequent style info to markup */
6593 /* WebVTT sample handling according to 14496-30 */
6595 gst_qtdemux_process_buffer_wvtt (GstQTDemux * qtdemux, QtDemuxStream * stream,
6598 GstBuffer *outbuf = NULL;
6601 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
6602 g_assert_not_reached (); /* The buffer must be mappable */
6605 if (qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
6606 GstEvent *gap = NULL;
6607 /* Push a gap event */
6608 stream->segment.position = GST_BUFFER_PTS (buf);
6610 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
6611 gst_pad_push_event (stream->pad, gap);
6613 if (GST_BUFFER_DURATION_IS_VALID (buf))
6614 stream->segment.position += GST_BUFFER_DURATION (buf);
6617 qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
6618 GST_BUFFER_DURATION (buf), map.data, map.size);
6619 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6622 gst_buffer_unmap (buf, &map);
6623 gst_buffer_unref (buf);
6628 static GstFlowReturn
6629 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6632 GstFlowReturn ret = GST_FLOW_OK;
6633 GstClockTime pts, duration;
6635 if (stream->need_clip)
6636 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6638 if (G_UNLIKELY (buf == NULL))
6641 if (G_UNLIKELY (stream->discont)) {
6642 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6643 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6644 stream->discont = FALSE;
6646 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6649 GST_LOG_OBJECT (qtdemux,
6650 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6651 ", duration %" GST_TIME_FORMAT " on pad %s",
6652 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6653 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6654 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
6656 if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
6657 GstStructure *crypto_info;
6658 QtDemuxAavdEncryptionInfo *info =
6659 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
6661 crypto_info = gst_structure_copy (info->default_properties);
6662 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6663 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
6666 if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
6667 || stream->protection_scheme_type == FOURCC_cbcs)) {
6668 GstStructure *crypto_info;
6669 QtDemuxCencSampleSetInfo *info =
6670 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6674 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6675 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6676 GST_PTR_FORMAT, event);
6677 gst_pad_push_event (stream->pad, event);
6680 if (info->crypto_info == NULL) {
6681 if (stream->protection_scheme_type == FOURCC_cbcs) {
6682 if (CUR_STREAM (stream)->fourcc == FOURCC_enca ||
6683 CUR_STREAM (stream)->fourcc == FOURCC_encs ||
6684 CUR_STREAM (stream)->fourcc == FOURCC_enct ||
6685 CUR_STREAM (stream)->fourcc == FOURCC_encv) {
6686 crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
6688 || !gst_buffer_add_protection_meta (buf, crypto_info)) {
6689 GST_ERROR_OBJECT (qtdemux,
6690 "failed to attach cbcs metadata to buffer");
6691 qtdemux_gst_structure_free (crypto_info);
6693 GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
6696 GST_TRACE_OBJECT (qtdemux,
6697 "cbcs stream is not encrypted yet, not adding protection metadata");
6700 GST_DEBUG_OBJECT (qtdemux,
6701 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
6704 /* The end of the crypto_info array matches our n_samples position,
6705 * so count backward from there */
6706 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6707 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6708 /* steal structure from array */
6709 crypto_info = g_ptr_array_index (info->crypto_info, index);
6710 g_ptr_array_index (info->crypto_info, index) = NULL;
6711 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6712 info->crypto_info->len);
6713 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6714 GST_ERROR_OBJECT (qtdemux,
6715 "failed to attach cenc metadata to buffer");
6717 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6718 index, stream->sample_index);
6723 if (stream->alignment > 1)
6724 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6726 pts = GST_BUFFER_PTS (buf);
6727 duration = GST_BUFFER_DURATION (buf);
6729 ret = gst_pad_push (stream->pad, buf);
6731 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6732 /* mark position in stream, we'll need this to know when to send GAP event */
6733 stream->segment.position = pts + duration;
6741 static GstFlowReturn
6742 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6745 GstFlowReturn ret = GST_FLOW_OK;
6747 if (stream->subtype == FOURCC_clcp
6748 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6750 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6751 guint n_triplets, i;
6752 guint field1_off = 0, field2_off = 0;
6754 /* We have to split CEA608 buffers so that each outgoing buffer contains
6755 * one byte pair per field according to the framerate of the video track.
6757 * If there is only a single byte pair per field we don't have to do
6761 gst_buffer_map (buf, &map, GST_MAP_READ);
6763 n_triplets = map.size / 3;
6764 for (i = 0; i < n_triplets; i++) {
6765 if (map.data[3 * i] & 0x80)
6771 g_assert (n_field1 || n_field2);
6773 /* If there's more than 1 frame we have to split, otherwise we can just
6775 if (n_field1 > 1 || n_field2 > 1) {
6777 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6778 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6780 for (i = 0; i < n_output_buffers; i++) {
6782 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6786 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6787 outptr = outmap.data;
6790 gboolean found = FALSE;
6792 while (map.data + field1_off < map.data + map.size) {
6793 if (map.data[field1_off] & 0x80) {
6794 memcpy (outptr, &map.data[field1_off], 3);
6803 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6805 memcpy (outptr, empty, 3);
6812 gboolean found = FALSE;
6814 while (map.data + field2_off < map.data + map.size) {
6815 if ((map.data[field2_off] & 0x80) == 0) {
6816 memcpy (outptr, &map.data[field2_off], 3);
6825 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6827 memcpy (outptr, empty, 3);
6833 gst_buffer_unmap (outbuf, &outmap);
6835 GST_BUFFER_PTS (outbuf) =
6836 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6837 GST_SECOND * CUR_STREAM (stream)->fps_d,
6838 CUR_STREAM (stream)->fps_n);
6839 GST_BUFFER_DURATION (outbuf) =
6840 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6841 CUR_STREAM (stream)->fps_n);
6842 GST_BUFFER_OFFSET (outbuf) = -1;
6843 GST_BUFFER_OFFSET_END (outbuf) = -1;
6845 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6847 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6850 gst_buffer_unmap (buf, &map);
6851 gst_buffer_unref (buf);
6853 gst_buffer_unmap (buf, &map);
6854 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6857 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6863 /* Sets a buffer's attributes properly and pushes it downstream.
6864 * Also checks for additional actions and custom processing that may
6865 * need to be done first.
6867 static GstFlowReturn
6868 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6869 QtDemuxStream * stream, GstBuffer * buf,
6870 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6871 gboolean keyframe, GstClockTime position, guint64 byte_position)
6873 GstFlowReturn ret = GST_FLOW_OK;
6875 /* offset the timestamps according to the edit list */
6877 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6881 gst_buffer_map (buf, &map, GST_MAP_READ);
6882 url = g_strndup ((gchar *) map.data, map.size);
6883 gst_buffer_unmap (buf, &map);
6884 if (url != NULL && strlen (url) != 0) {
6885 /* we have RTSP redirect now */
6886 g_free (qtdemux->redirect_location);
6887 qtdemux->redirect_location = g_strdup (url);
6888 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6889 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6890 gst_structure_new ("redirect",
6891 "new-location", G_TYPE_STRING, url, NULL)));
6893 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6899 /* position reporting */
6900 if (qtdemux->segment.rate >= 0) {
6901 qtdemux->segment.position = position;
6902 gst_qtdemux_sync_streams (qtdemux);
6905 if (G_UNLIKELY (!stream->pad)) {
6906 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6907 gst_buffer_unref (buf);
6911 /* send out pending buffers */
6912 while (stream->buffers) {
6913 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6915 if (G_UNLIKELY (stream->discont)) {
6916 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6917 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6918 stream->discont = FALSE;
6920 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6923 if (stream->alignment > 1)
6924 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6925 gst_pad_push (stream->pad, buffer);
6927 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6930 /* we're going to modify the metadata */
6931 buf = gst_buffer_make_writable (buf);
6933 if (qtdemux->start_utc_time != GST_CLOCK_TIME_NONE) {
6934 static GstStaticCaps unix_caps = GST_STATIC_CAPS ("timestamp/x-unix");
6935 GstCaps *caps = gst_static_caps_get (&unix_caps);
6936 gst_buffer_add_reference_timestamp_meta (buf, caps,
6937 pts + qtdemux->start_utc_time - stream->cslg_shift,
6938 GST_CLOCK_TIME_NONE);
6939 gst_caps_unref (caps);
6942 GST_BUFFER_DTS (buf) = dts;
6943 GST_BUFFER_PTS (buf) = pts;
6944 GST_BUFFER_DURATION (buf) = duration;
6945 GST_BUFFER_OFFSET (buf) = -1;
6946 GST_BUFFER_OFFSET_END (buf) = -1;
6948 if (G_UNLIKELY (stream->process_func))
6949 buf = stream->process_func (qtdemux, stream, buf);
6956 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6957 stream->on_keyframe = FALSE;
6959 stream->on_keyframe = TRUE;
6962 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6963 gst_buffer_append_memory (buf,
6964 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6966 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6967 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6970 if (G_UNLIKELY (qtdemux->element_index)) {
6971 GstClockTime stream_time;
6974 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6976 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6977 GST_LOG_OBJECT (qtdemux,
6978 "adding association %" GST_TIME_FORMAT "-> %"
6979 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6980 gst_index_add_association (qtdemux->element_index,
6982 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6983 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6984 GST_FORMAT_BYTES, byte_position, NULL);
6989 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6995 static const QtDemuxRandomAccessEntry *
6996 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6997 GstClockTime pos, gboolean after)
6999 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
7000 guint n_entries = stream->n_ra_entries;
7003 /* we assume the table is sorted */
7004 for (i = 0; i < n_entries; ++i) {
7005 if (entries[i].ts > pos)
7009 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
7010 * probably okay to assume that the index lists the very first fragment */
7017 return &entries[i - 1];
7021 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
7023 const QtDemuxRandomAccessEntry *best_entry = NULL;
7026 GST_OBJECT_LOCK (qtdemux);
7028 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
7030 /* first see if we can determine where to go to using mfra,
7031 * before we start clearing things */
7032 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7033 const QtDemuxRandomAccessEntry *entry;
7034 QtDemuxStream *stream;
7035 gboolean is_audio_or_video;
7037 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7039 if (stream->ra_entries == NULL)
7042 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
7043 is_audio_or_video = TRUE;
7045 is_audio_or_video = FALSE;
7048 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
7049 stream->time_position, !is_audio_or_video);
7051 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
7052 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
7054 stream->pending_seek = entry;
7056 /* decide position to jump to just based on audio/video tracks, not subs */
7057 if (!is_audio_or_video)
7060 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
7064 /* no luck, will handle seek otherwise */
7065 if (best_entry == NULL) {
7066 GST_OBJECT_UNLOCK (qtdemux);
7070 /* ok, now we can prepare for processing as of located moof */
7071 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7072 QtDemuxStream *stream;
7074 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7076 g_free (stream->samples);
7077 stream->samples = NULL;
7078 stream->n_samples = 0;
7079 stream->stbl_index = -1; /* no samples have yet been parsed */
7080 stream->sample_index = -1;
7082 if (stream->protection_scheme_info) {
7083 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
7084 if (stream->protection_scheme_type == FOURCC_cenc
7085 || stream->protection_scheme_type == FOURCC_cbcs) {
7086 QtDemuxCencSampleSetInfo *info =
7087 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
7088 if (info->crypto_info) {
7089 g_ptr_array_free (info->crypto_info, TRUE);
7090 info->crypto_info = NULL;
7096 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
7097 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
7098 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
7099 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
7101 qtdemux->moof_offset = best_entry->moof_offset;
7103 qtdemux_add_fragmented_samples (qtdemux);
7105 GST_OBJECT_UNLOCK (qtdemux);
7109 static GstFlowReturn
7110 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
7112 GstFlowReturn ret = GST_FLOW_OK;
7113 GstBuffer *buf = NULL;
7114 QtDemuxStream *stream, *target_stream = NULL;
7115 GstClockTime min_time;
7117 GstClockTime dts = GST_CLOCK_TIME_NONE;
7118 GstClockTime pts = GST_CLOCK_TIME_NONE;
7119 GstClockTime duration = 0;
7120 gboolean keyframe = FALSE;
7121 guint sample_size = 0;
7122 guint num_samples = 1;
7127 if (qtdemux->fragmented_seek_pending) {
7128 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
7129 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
7130 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
7131 qtdemux->fragmented_seek_pending = FALSE;
7133 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
7137 /* Figure out the next stream sample to output, min_time is expressed in
7138 * global time and runs over the edit list segments. */
7139 min_time = G_MAXUINT64;
7140 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
7141 GstClockTime position;
7143 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7144 position = stream->time_position;
7146 /* position of -1 is EOS */
7147 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
7148 min_time = position;
7149 target_stream = stream;
7153 if (G_UNLIKELY (target_stream == NULL)) {
7154 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
7158 /* check for segment end */
7159 if (G_UNLIKELY (qtdemux->segment.stop != -1
7160 && qtdemux->segment.rate >= 0
7161 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
7162 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
7163 target_stream->time_position = GST_CLOCK_TIME_NONE;
7167 /* fetch info for the current sample of this stream */
7168 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, target_stream,
7169 &empty, &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
7172 /* Send catche-up GAP event for each other stream if required.
7173 * This logic will be applied only for positive rate */
7174 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux) &&
7175 qtdemux->segment.rate >= 0; i++) {
7176 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
7178 if (stream == target_stream ||
7179 !GST_CLOCK_TIME_IS_VALID (stream->segment.stop) ||
7180 !GST_CLOCK_TIME_IS_VALID (stream->segment.position))
7184 GstClockTime gap_threshold;
7185 /* kind of running time with offset segment.base and segment.start */
7186 GstClockTime pseudo_target_time = target_stream->segment.base;
7187 GstClockTime pseudo_cur_time = stream->segment.base;
7189 /* make sure positive offset, segment.position can be smallr than
7190 * segment.start for some reasons */
7191 if (target_stream->segment.position >= target_stream->segment.start) {
7192 pseudo_target_time +=
7193 (target_stream->segment.position - target_stream->segment.start);
7196 if (stream->segment.position >= stream->segment.start)
7197 pseudo_cur_time += (stream->segment.position - stream->segment.start);
7199 /* Only send gap events on non-subtitle streams if lagging way behind. */
7200 if (stream->subtype == FOURCC_subp
7201 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl ||
7202 stream->subtype == FOURCC_wvtt)
7203 gap_threshold = 1 * GST_SECOND;
7205 gap_threshold = 3 * GST_SECOND;
7207 /* send gap events until the stream catches up */
7208 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
7209 while (GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
7210 pseudo_cur_time < (G_MAXUINT64 - gap_threshold) &&
7211 pseudo_cur_time + gap_threshold < pseudo_target_time) {
7213 gst_event_new_gap (stream->segment.position, gap_threshold);
7214 GST_LOG_OBJECT (stream->pad, "Sending %" GST_PTR_FORMAT, gap);
7216 gst_pad_push_event (stream->pad, gap);
7217 stream->segment.position += gap_threshold;
7218 pseudo_cur_time += gap_threshold;
7223 stream = target_stream;
7225 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
7226 if (stream->new_caps) {
7227 gst_qtdemux_configure_stream (qtdemux, stream);
7228 qtdemux_do_allocation (stream, qtdemux);
7231 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
7232 if (G_UNLIKELY (qtdemux->segment.
7233 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
7234 if (stream->subtype == FOURCC_vide) {
7236 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
7239 } else if (qtdemux->trickmode_interval > 0) {
7240 GstClockTimeDiff interval;
7242 if (qtdemux->segment.rate > 0)
7243 interval = stream->time_position - stream->last_keyframe_dts;
7245 interval = stream->last_keyframe_dts - stream->time_position;
7247 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
7248 && interval < qtdemux->trickmode_interval) {
7249 GST_LOG_OBJECT (qtdemux,
7250 "Skipping keyframe within interval on track-id %u",
7254 stream->last_keyframe_dts = stream->time_position;
7260 GST_DEBUG_OBJECT (qtdemux,
7261 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
7262 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
7263 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
7264 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
7265 GST_TIME_ARGS (duration));
7267 if (G_UNLIKELY (empty)) {
7268 /* empty segment, push a gap if there's a second or more
7269 * difference and move to the next one */
7270 if ((pts + duration - stream->segment.position) >= GST_SECOND)
7271 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
7272 stream->segment.position = pts + duration;
7276 /* hmm, empty sample, skip and move to next sample */
7277 if (G_UNLIKELY (sample_size <= 0))
7280 /* last pushed sample was out of boundary, goto next sample */
7281 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
7284 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
7285 GST_DEBUG_OBJECT (qtdemux,
7286 "size %d larger than stream max_buffer_size %d, trimming",
7287 sample_size, stream->max_buffer_size);
7289 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
7290 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
7291 && sample_size < stream->min_buffer_size) {
7292 guint start_sample_index = stream->sample_index;
7293 guint accumulated_size = sample_size;
7294 guint64 expected_next_offset = offset + sample_size;
7296 GST_DEBUG_OBJECT (qtdemux,
7297 "size %d smaller than stream min_buffer_size %d, combining with the next",
7298 sample_size, stream->min_buffer_size);
7300 while (stream->sample_index < stream->to_sample
7301 && stream->sample_index + 1 < stream->n_samples) {
7302 const QtDemuxSample *next_sample;
7304 /* Increment temporarily */
7305 stream->sample_index++;
7307 /* Failed to parse sample so let's go back to the previous one that was
7308 * still successful */
7309 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
7310 stream->sample_index--;
7314 next_sample = &stream->samples[stream->sample_index];
7316 /* Not contiguous with the previous sample so let's go back to the
7317 * previous one that was still successful */
7318 if (next_sample->offset != expected_next_offset) {
7319 stream->sample_index--;
7323 accumulated_size += next_sample->size;
7324 expected_next_offset += next_sample->size;
7325 if (accumulated_size >= stream->min_buffer_size)
7329 num_samples = stream->sample_index + 1 - start_sample_index;
7330 stream->sample_index = start_sample_index;
7331 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
7332 num_samples, accumulated_size);
7333 size = accumulated_size;
7338 if (qtdemux->cenc_aux_info_offset > 0) {
7341 GstBuffer *aux_info = NULL;
7343 /* pull the data stored before the sample */
7345 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
7346 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
7347 if (G_UNLIKELY (ret != GST_FLOW_OK))
7349 gst_buffer_map (aux_info, &map, GST_MAP_READ);
7350 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
7351 gst_byte_reader_init (&br, map.data + 8, map.size);
7352 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
7353 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
7354 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
7355 gst_buffer_unmap (aux_info, &map);
7356 gst_buffer_unref (aux_info);
7357 ret = GST_FLOW_ERROR;
7360 gst_buffer_unmap (aux_info, &map);
7361 gst_buffer_unref (aux_info);
7364 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
7367 if (stream->use_allocator) {
7368 /* if we have a per-stream allocator, use it */
7369 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
7372 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
7374 if (G_UNLIKELY (ret != GST_FLOW_OK))
7377 /* Update for both splitting and combining of samples */
7378 if (size != sample_size) {
7379 pts += gst_util_uint64_scale_int (GST_SECOND,
7380 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7383 gst_util_uint64_scale_int (GST_SECOND,
7384 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7387 gst_util_uint64_scale_int (GST_SECOND,
7388 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
7391 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
7392 dts, pts, duration, keyframe, min_time, offset);
7394 if (size < sample_size) {
7395 QtDemuxSample *sample = &stream->samples[stream->sample_index];
7396 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
7398 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
7400 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
7401 if (time_position >= segment->media_start) {
7402 /* inside the segment, update time_position, looks very familiar to
7403 * GStreamer segments, doesn't it? */
7404 stream->time_position = (time_position - segment->media_start) +
7407 /* not yet in segment, time does not yet increment. This means
7408 * that we are still prerolling keyframes to the decoder so it can
7409 * decode the first sample of the segment. */
7410 stream->time_position = segment->time;
7412 } else if (size > sample_size) {
7413 /* Increase to the last sample we already pulled so that advancing
7414 * below brings us to the next sample we need to pull */
7415 stream->sample_index += num_samples - 1;
7419 GST_OBJECT_LOCK (qtdemux);
7420 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
7421 GST_OBJECT_UNLOCK (qtdemux);
7422 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
7423 * we have no more data for the pad to push */
7424 if (ret == GST_FLOW_EOS)
7427 stream->offset_in_sample += size;
7428 if (stream->offset_in_sample >= sample_size) {
7429 gst_qtdemux_advance_sample (qtdemux, stream);
7434 gst_qtdemux_advance_sample (qtdemux, stream);
7442 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
7448 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
7449 /* EOS will be raised if all are EOS */
7456 gst_qtdemux_loop (GstPad * pad)
7458 GstQTDemux *qtdemux;
7462 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
7464 cur_offset = qtdemux->offset;
7465 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
7466 cur_offset, qt_demux_state_string (qtdemux->state));
7468 switch (qtdemux->state) {
7469 case QTDEMUX_STATE_INITIAL:
7470 case QTDEMUX_STATE_HEADER:
7471 ret = gst_qtdemux_loop_state_header (qtdemux);
7473 case QTDEMUX_STATE_MOVIE:
7474 ret = gst_qtdemux_loop_state_movie (qtdemux);
7475 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
7476 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
7484 /* if something went wrong, pause */
7485 if (ret != GST_FLOW_OK)
7489 gst_object_unref (qtdemux);
7495 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7496 (NULL), ("streaming stopped, invalid state"));
7497 gst_pad_pause_task (pad);
7498 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7503 const gchar *reason = gst_flow_get_name (ret);
7505 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7507 gst_pad_pause_task (pad);
7509 /* fatal errors need special actions */
7511 if (ret == GST_FLOW_EOS) {
7512 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
7513 /* we have no streams, post an error */
7514 gst_qtdemux_post_no_playable_stream_error (qtdemux);
7516 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7519 if ((stop = qtdemux->segment.stop) == -1)
7520 stop = qtdemux->segment.duration;
7522 if (qtdemux->segment.rate >= 0) {
7523 GstMessage *message;
7526 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7527 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7528 GST_FORMAT_TIME, stop);
7529 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7530 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7531 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7532 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7534 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7535 gst_qtdemux_push_event (qtdemux, event);
7537 GstMessage *message;
7540 /* For Reverse Playback */
7541 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7542 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7543 GST_FORMAT_TIME, qtdemux->segment.start);
7544 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7545 qtdemux->segment.start);
7546 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7547 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7548 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7550 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7551 gst_qtdemux_push_event (qtdemux, event);
7556 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7557 event = gst_event_new_eos ();
7558 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7559 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7560 gst_qtdemux_push_event (qtdemux, event);
7562 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7563 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
7564 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7573 * Returns if there are samples to be played.
7576 has_next_entry (GstQTDemux * demux)
7578 QtDemuxStream *stream;
7581 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7583 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7584 stream = QTDEMUX_NTH_STREAM (demux, i);
7586 if (stream->sample_index == -1) {
7587 stream->sample_index = 0;
7588 stream->offset_in_sample = 0;
7591 if (stream->sample_index >= stream->n_samples) {
7592 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7595 GST_DEBUG_OBJECT (demux, "Found a sample");
7599 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7606 * Returns the size of the first entry at the current offset.
7607 * If -1, there are none (which means EOS or empty file).
7610 next_entry_size (GstQTDemux * demux)
7612 QtDemuxStream *stream, *target_stream = NULL;
7613 guint64 smalloffs = (guint64) - 1;
7614 QtDemuxSample *sample;
7617 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7620 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7621 stream = QTDEMUX_NTH_STREAM (demux, i);
7623 if (stream->sample_index == -1) {
7624 stream->sample_index = 0;
7625 stream->offset_in_sample = 0;
7628 if (stream->sample_index >= stream->n_samples) {
7629 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7633 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
7634 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7635 stream->sample_index);
7639 sample = &stream->samples[stream->sample_index];
7641 GST_LOG_OBJECT (demux,
7642 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7643 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7644 stream->sample_index, sample->offset, sample->size);
7646 if (((smalloffs == -1)
7647 || (sample->offset < smalloffs)) && (sample->size)) {
7648 smalloffs = sample->offset;
7649 target_stream = stream;
7656 GST_LOG_OBJECT (demux,
7657 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7658 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7660 stream = target_stream;
7661 sample = &stream->samples[stream->sample_index];
7663 if (sample->offset >= demux->offset) {
7664 demux->todrop = sample->offset - demux->offset;
7665 return sample->size + demux->todrop;
7668 GST_DEBUG_OBJECT (demux,
7669 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7674 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
7676 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7678 gst_element_post_message (GST_ELEMENT_CAST (demux),
7679 gst_message_new_element (GST_OBJECT_CAST (demux),
7680 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7684 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
7689 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7692 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7693 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7694 GST_SEEK_TYPE_NONE, -1);
7696 /* store seqnum to drop flush events, they don't need to reach downstream */
7697 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7698 res = gst_pad_push_event (demux->sinkpad, event);
7699 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7704 /* check for seekable upstream, above and beyond a mere query */
7706 gst_qtdemux_check_seekability (GstQTDemux * demux)
7709 gboolean seekable = FALSE;
7710 gint64 start = -1, stop = -1;
7712 if (demux->upstream_size)
7715 if (demux->upstream_format_is_time)
7718 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7719 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7720 GST_DEBUG_OBJECT (demux, "seeking query failed");
7724 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7726 /* try harder to query upstream size if we didn't get it the first time */
7727 if (seekable && stop == -1) {
7728 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7729 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7732 /* if upstream doesn't know the size, it's likely that it's not seekable in
7733 * practice even if it technically may be seekable */
7734 if (seekable && (start != 0 || stop <= start)) {
7735 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7740 gst_query_unref (query);
7742 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7743 G_GUINT64_FORMAT ")", seekable, start, stop);
7744 demux->upstream_seekable = seekable;
7745 demux->upstream_size = seekable ? stop : -1;
7749 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7751 g_return_if_fail (bytes <= demux->todrop);
7753 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7754 gst_adapter_flush (demux->adapter, bytes);
7755 demux->neededbytes -= bytes;
7756 demux->offset += bytes;
7757 demux->todrop -= bytes;
7760 /* PUSH-MODE only: Send a segment, if not done already. */
7762 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7764 if (G_UNLIKELY (demux->need_segment)) {
7767 if (!demux->upstream_format_is_time) {
7768 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7770 GstEvent *segment_event;
7771 segment_event = gst_event_new_segment (&demux->segment);
7772 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7773 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7774 gst_qtdemux_push_event (demux, segment_event);
7777 demux->need_segment = FALSE;
7779 /* clear to send tags on all streams */
7780 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7781 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7782 gst_qtdemux_push_tags (demux, stream);
7783 if (CUR_STREAM (stream)->sparse) {
7784 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7785 gst_pad_push_event (stream->pad,
7786 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7792 /* Used for push mode only. */
7794 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7795 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7797 GstClockTime ts, dur;
7801 stream->segments[segment_index].duration - (pos -
7802 stream->segments[segment_index].time);
7803 stream->time_position += dur;
7805 /* Only gaps with a duration of at least one second are propagated.
7806 * Same workaround as in pull mode.
7807 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7808 if (dur >= GST_SECOND) {
7810 gap = gst_event_new_gap (ts, dur);
7812 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7813 "segment: %" GST_PTR_FORMAT, gap);
7814 gst_pad_push_event (stream->pad, gap);
7818 static GstFlowReturn
7819 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7823 demux = GST_QTDEMUX (parent);
7825 GST_DEBUG_OBJECT (demux,
7826 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7827 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7828 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7829 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7830 gst_buffer_get_size (inbuf), demux->offset);
7832 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7833 gboolean is_gap_input = FALSE;
7836 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7838 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7839 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7842 /* Check if we can land back on our feet in the case where upstream is
7843 * handling the seeking/pushing of samples with gaps in between (like
7844 * in the case of trick-mode DASH for example) */
7845 if (demux->upstream_format_is_time
7846 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7847 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7849 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7850 GST_LOG_OBJECT (demux,
7851 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7852 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7854 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7855 stream, GST_BUFFER_OFFSET (inbuf));
7857 QtDemuxSample *sample = &stream->samples[res];
7858 GST_LOG_OBJECT (demux,
7859 "Checking if sample %d from track-id %u is valid (offset:%"
7860 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7861 stream->track_id, sample->offset, sample->size);
7862 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7863 GST_LOG_OBJECT (demux,
7864 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7866 is_gap_input = TRUE;
7867 /* We can go back to standard playback mode */
7868 demux->state = QTDEMUX_STATE_MOVIE;
7869 /* Remember which sample this stream is at */
7870 stream->sample_index = res;
7871 /* Finally update all push-based values to the expected values */
7872 demux->neededbytes = stream->samples[res].size;
7873 demux->offset = GST_BUFFER_OFFSET (inbuf);
7875 demux->mdatsize - demux->offset + demux->mdatoffset;
7880 if (!is_gap_input) {
7881 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7882 /* Reset state if it's a real discont */
7883 demux->neededbytes = 16;
7884 demux->state = QTDEMUX_STATE_INITIAL;
7885 demux->offset = GST_BUFFER_OFFSET (inbuf);
7886 gst_adapter_clear (demux->adapter);
7889 /* Reverse fragmented playback, need to flush all we have before
7890 * consuming a new fragment.
7891 * The samples array have the timestamps calculated by accumulating the
7892 * durations but this won't work for reverse playback of fragments as
7893 * the timestamps of a subsequent fragment should be smaller than the
7894 * previously received one. */
7895 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7896 gst_qtdemux_process_adapter (demux, TRUE);
7897 g_ptr_array_foreach (demux->active_streams,
7898 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7902 gst_adapter_push (demux->adapter, inbuf);
7904 GST_DEBUG_OBJECT (demux,
7905 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7906 demux->neededbytes, gst_adapter_available (demux->adapter));
7908 return gst_qtdemux_process_adapter (demux, FALSE);
7912 gst_segment_to_stream_time_clamped (const GstSegment * segment,
7915 guint64 segment_stream_time_start;
7916 guint64 segment_stream_time_stop = GST_CLOCK_TIME_NONE;
7917 guint64 stream_pts_unsigned;
7920 g_return_val_if_fail (segment != NULL, GST_CLOCK_TIME_NONE);
7921 g_return_val_if_fail (segment->format == GST_FORMAT_TIME,
7922 GST_CLOCK_TIME_NONE);
7924 segment_stream_time_start = segment->time;
7925 if (segment->stop != GST_CLOCK_TIME_NONE)
7926 segment_stream_time_stop =
7927 gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
7930 gst_segment_to_stream_time_full (segment, GST_FORMAT_TIME, position,
7931 &stream_pts_unsigned);
7932 /* ret == 0 if the segment is invalid (either position, segment->time or the segment start are -1). */
7933 g_return_val_if_fail (ret != 0, GST_CLOCK_TIME_NONE);
7935 if (ret == -1 || stream_pts_unsigned < segment_stream_time_start) {
7936 /* Negative or prior to segment start stream time, clamp to segment start. */
7937 return segment_stream_time_start;
7938 } else if (segment_stream_time_stop != GST_CLOCK_TIME_NONE
7939 && stream_pts_unsigned > segment_stream_time_stop) {
7940 /* Clamp to segment end. */
7941 return segment_stream_time_stop;
7943 return stream_pts_unsigned;
7947 static GstFlowReturn
7948 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7950 GstFlowReturn ret = GST_FLOW_OK;
7952 /* we never really mean to buffer that much */
7953 if (demux->neededbytes == -1) {
7957 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7958 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7960 #ifndef GST_DISABLE_GST_DEBUG
7962 guint64 discont_offset, distance_from_discont;
7964 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7965 distance_from_discont =
7966 gst_adapter_distance_from_discont (demux->adapter);
7968 GST_DEBUG_OBJECT (demux,
7969 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7970 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7971 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7972 demux->offset, discont_offset, distance_from_discont);
7976 switch (demux->state) {
7977 case QTDEMUX_STATE_INITIAL:{
7982 gst_qtdemux_check_seekability (demux);
7984 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7986 /* get fourcc/length, set neededbytes */
7987 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7989 gst_adapter_unmap (demux->adapter);
7991 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7992 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7994 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7995 (_("This file is invalid and cannot be played.")),
7996 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7997 GST_FOURCC_ARGS (fourcc)));
7998 ret = GST_FLOW_ERROR;
8001 if (fourcc == FOURCC_mdat) {
8002 gint next_entry = next_entry_size (demux);
8003 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
8004 || !demux->fragmented)) {
8005 /* we have the headers, start playback */
8006 demux->state = QTDEMUX_STATE_MOVIE;
8007 demux->neededbytes = next_entry;
8008 demux->mdatleft = size;
8009 demux->mdatsize = demux->mdatleft;
8011 /* no headers yet, try to get them */
8014 guint64 old, target;
8017 old = demux->offset;
8018 target = old + size;
8020 /* try to jump over the atom with a seek */
8021 /* only bother if it seems worth doing so,
8022 * and avoids possible upstream/server problems */
8023 if (demux->upstream_seekable &&
8024 demux->upstream_size > 4 * (1 << 20)) {
8025 res = qtdemux_seek_offset (demux, target);
8027 GST_DEBUG_OBJECT (demux, "skipping seek");
8032 GST_DEBUG_OBJECT (demux, "seek success");
8033 /* remember the offset fo the first mdat so we can seek back to it
8034 * after we have the headers */
8035 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
8036 demux->first_mdat = old;
8037 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
8040 /* seek worked, continue reading */
8041 demux->offset = target;
8042 demux->neededbytes = 16;
8043 demux->state = QTDEMUX_STATE_INITIAL;
8045 /* seek failed, need to buffer */
8046 demux->offset = old;
8047 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
8048 /* there may be multiple mdat (or alike) buffers */
8050 if (demux->mdatbuffer)
8051 bs = gst_buffer_get_size (demux->mdatbuffer);
8054 if (size + bs > 10 * (1 << 20))
8056 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
8057 demux->neededbytes = size;
8058 if (!demux->mdatbuffer)
8059 demux->mdatoffset = demux->offset;
8062 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
8063 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8064 (_("This file is invalid and cannot be played.")),
8065 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
8066 GST_FOURCC_ARGS (fourcc), size));
8067 ret = GST_FLOW_ERROR;
8070 /* this means we already started buffering and still no moov header,
8071 * let's continue buffering everything till we get moov */
8072 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
8073 || fourcc == FOURCC_moof))
8075 demux->neededbytes = size;
8076 demux->state = QTDEMUX_STATE_HEADER;
8080 case QTDEMUX_STATE_HEADER:{
8084 GST_DEBUG_OBJECT (demux, "In header");
8086 data = gst_adapter_map (demux->adapter, demux->neededbytes);
8088 /* parse the header */
8089 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
8091 if (fourcc == FOURCC_moov) {
8092 /* in usual fragmented setup we could try to scan for more
8093 * and end up at the the moov (after mdat) again */
8094 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
8096 || demux->last_moov_offset == demux->offset)) {
8097 GST_DEBUG_OBJECT (demux,
8098 "Skipping moov atom as we have (this) one already");
8100 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
8102 if (demux->got_moov && demux->fragmented) {
8103 GST_DEBUG_OBJECT (demux,
8104 "Got a second moov, clean up data from old one");
8105 if (demux->moov_node_compressed) {
8106 g_node_destroy (demux->moov_node_compressed);
8107 if (demux->moov_node)
8108 g_free (demux->moov_node->data);
8110 demux->moov_node_compressed = NULL;
8111 if (demux->moov_node)
8112 g_node_destroy (demux->moov_node);
8113 demux->moov_node = NULL;
8114 demux->start_utc_time = GST_CLOCK_TIME_NONE;
8117 demux->last_moov_offset = demux->offset;
8119 /* Update streams with new moov */
8120 gst_qtdemux_stream_concat (demux,
8121 demux->old_streams, demux->active_streams);
8123 qtdemux_parse_moov (demux, data, demux->neededbytes);
8124 qtdemux_node_dump (demux, demux->moov_node);
8125 qtdemux_parse_tree (demux);
8126 qtdemux_prepare_streams (demux);
8127 QTDEMUX_EXPOSE_LOCK (demux);
8128 qtdemux_expose_streams (demux);
8129 QTDEMUX_EXPOSE_UNLOCK (demux);
8131 demux->got_moov = TRUE;
8133 gst_qtdemux_check_send_pending_segment (demux);
8135 if (demux->moov_node_compressed) {
8136 g_node_destroy (demux->moov_node_compressed);
8137 g_free (demux->moov_node->data);
8139 demux->moov_node_compressed = NULL;
8140 g_node_destroy (demux->moov_node);
8141 demux->moov_node = NULL;
8142 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
8144 } else if (fourcc == FOURCC_moof) {
8145 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
8147 GstClockTime prev_pts;
8148 guint64 prev_offset;
8149 guint64 adapter_discont_offset, adapter_discont_dist;
8151 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
8154 * The timestamp of the moof buffer is relevant as some scenarios
8155 * won't have the initial timestamp in the atoms. Whenever a new
8156 * buffer has started, we get that buffer's PTS and use it as a base
8157 * timestamp for the trun entries.
8159 * To keep track of the current buffer timestamp and starting point
8160 * we use gst_adapter_prev_pts that gives us the PTS and the distance
8161 * from the beginning of the buffer, with the distance and demux->offset
8162 * we know if it is still the same buffer or not.
8164 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
8165 prev_offset = demux->offset - dist;
8166 if (demux->fragment_start_offset == -1
8167 || prev_offset > demux->fragment_start_offset) {
8168 demux->fragment_start_offset = prev_offset;
8169 demux->fragment_start = prev_pts;
8170 GST_DEBUG_OBJECT (demux,
8171 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
8172 GST_TIME_FORMAT, demux->fragment_start_offset,
8173 GST_TIME_ARGS (demux->fragment_start));
8176 /* We can't use prev_offset() here because this would require
8177 * upstream to set consistent and correct offsets on all buffers
8178 * since the discont. Nothing ever did that in the past and we
8179 * would break backwards compatibility here then.
8180 * Instead take the offset we had at the last discont and count
8181 * the bytes from there. This works with old code as there would
8182 * be no discont between moov and moof, and also works with
8183 * adaptivedemux which correctly sets offset and will set the
8184 * DISCONT flag accordingly when needed.
8186 * We also only do this for upstream TIME segments as otherwise
8187 * there are potential backwards compatibility problems with
8188 * seeking in PUSH mode and upstream providing inconsistent
8190 adapter_discont_offset =
8191 gst_adapter_offset_at_discont (demux->adapter);
8192 adapter_discont_dist =
8193 gst_adapter_distance_from_discont (demux->adapter);
8195 GST_DEBUG_OBJECT (demux,
8196 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
8197 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
8198 demux->offset, adapter_discont_offset, adapter_discont_dist);
8200 if (demux->upstream_format_is_time) {
8201 demux->moof_offset = adapter_discont_offset;
8202 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
8203 demux->moof_offset += adapter_discont_dist;
8204 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
8205 demux->moof_offset = demux->offset;
8207 demux->moof_offset = demux->offset;
8210 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
8211 demux->moof_offset, NULL)) {
8212 gst_adapter_unmap (demux->adapter);
8213 ret = GST_FLOW_ERROR;
8217 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
8218 if (demux->variant == VARIANT_MSS_FRAGMENTED && !demux->exposed) {
8219 QTDEMUX_EXPOSE_LOCK (demux);
8220 qtdemux_expose_streams (demux);
8221 QTDEMUX_EXPOSE_UNLOCK (demux);
8224 gst_qtdemux_check_send_pending_segment (demux);
8226 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
8228 } else if (fourcc == FOURCC_ftyp) {
8229 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
8230 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
8231 } else if (fourcc == FOURCC_uuid) {
8232 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
8233 qtdemux_parse_uuid (demux, data, demux->neededbytes);
8234 } else if (fourcc == FOURCC_sidx) {
8235 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
8236 qtdemux_parse_sidx (demux, data, demux->neededbytes);
8237 } else if (fourcc == FOURCC_meta) {
8238 GNode *node, *child;
8239 GstByteReader child_data;
8241 node = g_node_new ((gpointer) data);
8242 qtdemux_parse_node (demux, node, data, demux->neededbytes);
8244 /* Parse ONVIF Export File Format CorrectStartTime box if available */
8246 qtdemux_tree_get_child_by_type_full (node, FOURCC_cstb,
8248 qtdemux_parse_cstb (demux, &child_data);
8251 g_node_destroy (node);
8255 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
8259 /* [free] and [skip] are padding atoms */
8260 GST_DEBUG_OBJECT (demux,
8261 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
8262 GST_FOURCC_ARGS (fourcc));
8265 GST_WARNING_OBJECT (demux,
8266 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
8267 GST_FOURCC_ARGS (fourcc));
8268 /* Let's jump that one and go back to initial state */
8272 gst_adapter_unmap (demux->adapter);
8275 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
8276 gsize remaining_data_size = 0;
8278 /* the mdat was before the header */
8279 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
8280 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
8281 /* restore our adapter/offset view of things with upstream;
8282 * put preceding buffered data ahead of current moov data.
8283 * This should also handle evil mdat, moov, mdat cases and alike */
8284 gst_adapter_flush (demux->adapter, demux->neededbytes);
8286 /* Store any remaining data after the mdat for later usage */
8287 remaining_data_size = gst_adapter_available (demux->adapter);
8288 if (remaining_data_size > 0) {
8289 g_assert (demux->restoredata_buffer == NULL);
8290 demux->restoredata_buffer =
8291 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
8292 demux->restoredata_offset = demux->offset + demux->neededbytes;
8293 GST_DEBUG_OBJECT (demux,
8294 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
8295 G_GUINT64_FORMAT, remaining_data_size,
8296 demux->restoredata_offset);
8299 gst_adapter_push (demux->adapter, demux->mdatbuffer);
8300 demux->mdatbuffer = NULL;
8301 demux->offset = demux->mdatoffset;
8302 demux->neededbytes = next_entry_size (demux);
8303 demux->state = QTDEMUX_STATE_MOVIE;
8304 demux->mdatleft = gst_adapter_available (demux->adapter);
8305 demux->mdatsize = demux->mdatleft;
8307 GST_DEBUG_OBJECT (demux, "Carrying on normally");
8308 gst_adapter_flush (demux->adapter, demux->neededbytes);
8310 /* only go back to the mdat if there are samples to play */
8311 if (demux->got_moov && demux->first_mdat != -1
8312 && has_next_entry (demux)) {
8315 /* we need to seek back */
8316 res = qtdemux_seek_offset (demux, demux->first_mdat);
8318 demux->offset = demux->first_mdat;
8320 GST_DEBUG_OBJECT (demux, "Seek back failed");
8323 demux->offset += demux->neededbytes;
8325 demux->neededbytes = 16;
8326 demux->state = QTDEMUX_STATE_INITIAL;
8331 case QTDEMUX_STATE_BUFFER_MDAT:{
8335 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
8337 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8338 gst_buffer_extract (buf, 0, fourcc, 4);
8339 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
8340 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
8341 if (demux->mdatbuffer)
8342 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
8344 demux->mdatbuffer = buf;
8345 demux->offset += demux->neededbytes;
8346 demux->neededbytes = 16;
8347 demux->state = QTDEMUX_STATE_INITIAL;
8348 gst_qtdemux_post_progress (demux, 1, 1);
8352 case QTDEMUX_STATE_MOVIE:{
8353 QtDemuxStream *stream = NULL;
8354 QtDemuxSample *sample;
8355 GstClockTime dts, pts, stream_pts, duration;
8359 GST_DEBUG_OBJECT (demux,
8360 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
8362 if (demux->fragmented) {
8363 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
8365 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
8366 /* if needed data starts within this atom,
8367 * then it should not exceed this atom */
8368 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
8369 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8370 (_("This file is invalid and cannot be played.")),
8371 ("sample data crosses atom boundary"));
8372 ret = GST_FLOW_ERROR;
8375 demux->mdatleft -= demux->neededbytes;
8377 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
8378 /* so we are dropping more than left in this atom */
8379 gst_qtdemux_drop_data (demux, demux->mdatleft);
8380 demux->mdatleft = 0;
8382 /* need to resume atom parsing so we do not miss any other pieces */
8383 demux->state = QTDEMUX_STATE_INITIAL;
8384 demux->neededbytes = 16;
8386 /* check if there was any stored post mdat data from previous buffers */
8387 if (demux->restoredata_buffer) {
8388 g_assert (gst_adapter_available (demux->adapter) == 0);
8390 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
8391 demux->restoredata_buffer = NULL;
8392 demux->offset = demux->restoredata_offset;
8399 if (demux->todrop) {
8400 if (demux->cenc_aux_info_offset > 0) {
8404 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
8405 data = gst_adapter_map (demux->adapter, demux->todrop);
8406 gst_byte_reader_init (&br, data + 8, demux->todrop);
8407 if (!qtdemux_parse_cenc_aux_info (demux,
8408 QTDEMUX_NTH_STREAM (demux, 0), &br,
8409 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
8410 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
8411 ret = GST_FLOW_ERROR;
8412 gst_adapter_unmap (demux->adapter);
8413 g_free (demux->cenc_aux_info_sizes);
8414 demux->cenc_aux_info_sizes = NULL;
8417 demux->cenc_aux_info_offset = 0;
8418 g_free (demux->cenc_aux_info_sizes);
8419 demux->cenc_aux_info_sizes = NULL;
8420 gst_adapter_unmap (demux->adapter);
8422 gst_qtdemux_drop_data (demux, demux->todrop);
8426 /* initial newsegment sent here after having added pads,
8427 * possible others in sink_event */
8428 gst_qtdemux_check_send_pending_segment (demux);
8430 /* Figure out which stream this packet belongs to */
8431 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8432 stream = QTDEMUX_NTH_STREAM (demux, i);
8433 if (stream->sample_index >= stream->n_samples) {
8434 /* reset to be checked below G_UNLIKELY (stream == NULL) */
8438 GST_LOG_OBJECT (demux,
8439 "Checking track-id %u (sample_index:%d / offset:%"
8440 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
8441 stream->sample_index,
8442 stream->samples[stream->sample_index].offset,
8443 stream->samples[stream->sample_index].size);
8445 if (stream->samples[stream->sample_index].offset == demux->offset)
8449 if (G_UNLIKELY (stream == NULL))
8450 goto unknown_stream;
8452 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
8454 if (stream->new_caps) {
8455 gst_qtdemux_configure_stream (demux, stream);
8458 /* Put data in a buffer, set timestamps, caps, ... */
8459 sample = &stream->samples[stream->sample_index];
8461 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
8462 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
8463 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
8465 dts = QTSAMPLE_DTS (stream, sample);
8466 pts = QTSAMPLE_PTS (stream, sample);
8468 gst_segment_to_stream_time_clamped (&stream->segment, pts);
8469 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
8470 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
8472 /* check for segment end */
8473 if (G_UNLIKELY (demux->segment.stop != -1
8474 && demux->segment.stop <= stream_pts && keyframe)
8475 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
8476 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
8477 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
8479 /* skip this data, stream is EOS */
8480 gst_adapter_flush (demux->adapter, demux->neededbytes);
8481 demux->offset += demux->neededbytes;
8483 /* check if all streams are eos */
8485 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8486 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
8495 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8497 /* FIXME: should either be an assert or a plain check */
8498 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8500 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
8501 dts, pts, duration, keyframe, dts, demux->offset);
8505 GST_OBJECT_LOCK (demux);
8506 ret = gst_qtdemux_combine_flows (demux, stream, ret);
8507 GST_OBJECT_UNLOCK (demux);
8509 /* skip this data, stream is EOS */
8510 gst_adapter_flush (demux->adapter, demux->neededbytes);
8513 stream->sample_index++;
8514 stream->offset_in_sample = 0;
8516 /* update current offset and figure out size of next buffer */
8517 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8518 demux->offset, demux->neededbytes);
8519 demux->offset += demux->neededbytes;
8520 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8524 if (ret == GST_FLOW_EOS) {
8525 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8526 demux->neededbytes = -1;
8530 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
8531 if (demux->fragmented) {
8532 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8533 /* there may be more to follow, only finish this atom */
8534 demux->todrop = demux->mdatleft;
8535 demux->neededbytes = demux->todrop;
8540 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8541 goto non_ok_unlinked_flow;
8550 /* when buffering movie data, at least show user something is happening */
8551 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
8552 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
8553 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
8554 demux->neededbytes);
8561 non_ok_unlinked_flow:
8563 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8564 gst_flow_get_name (ret));
8569 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8570 ret = GST_FLOW_ERROR;
8575 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8581 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8582 (NULL), ("qtdemuxer invalid state %d", demux->state));
8583 ret = GST_FLOW_ERROR;
8588 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8589 (NULL), ("no 'moov' atom within the first 10 MB"));
8590 ret = GST_FLOW_ERROR;
8596 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
8601 query = gst_query_new_scheduling ();
8603 if (!gst_pad_peer_query (sinkpad, query)) {
8604 gst_query_unref (query);
8608 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8609 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8610 gst_query_unref (query);
8615 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8616 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8620 GST_DEBUG_OBJECT (sinkpad, "activating push");
8621 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8626 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
8627 GstPadMode mode, gboolean active)
8630 GstQTDemux *demux = GST_QTDEMUX (parent);
8633 case GST_PAD_MODE_PUSH:
8634 demux->pullbased = FALSE;
8637 case GST_PAD_MODE_PULL:
8639 demux->pullbased = TRUE;
8640 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
8643 res = gst_pad_stop_task (sinkpad);
8655 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8661 memset (&z, 0, sizeof (z));
8666 if ((ret = inflateInit (&z)) != Z_OK) {
8667 GST_ERROR ("inflateInit() returned %d", ret);
8671 z.next_in = z_buffer;
8672 z.avail_in = z_length;
8674 buffer = (guint8 *) g_malloc (*length);
8675 z.avail_out = *length;
8676 z.next_out = (Bytef *) buffer;
8678 ret = inflate (&z, Z_NO_FLUSH);
8679 if (ret == Z_STREAM_END) {
8681 } else if (ret != Z_OK) {
8682 GST_WARNING ("inflate() returned %d", ret);
8686 if (*length > G_MAXUINT - 4096 || *length > QTDEMUX_MAX_SAMPLE_INDEX_SIZE) {
8687 GST_WARNING ("too big decompressed data");
8693 buffer = (guint8 *) g_realloc (buffer, *length);
8694 z.next_out = (Bytef *) (buffer + z.total_out);
8695 z.avail_out += *length - z.total_out;
8696 } while (z.avail_in > 0);
8698 if (ret != Z_STREAM_END) {
8703 *length = z.total_out;
8710 #endif /* HAVE_ZLIB */
8713 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
8717 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8719 /* counts as header data */
8720 qtdemux->header_size += length;
8722 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
8723 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
8725 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
8732 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
8733 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
8734 if (dcom == NULL || cmvd == NULL)
8735 goto invalid_compression;
8737 dcom_len = QT_UINT32 (dcom->data);
8739 goto invalid_compression;
8741 method = QT_FOURCC ((guint8 *) dcom->data + 8);
8745 guint uncompressed_length;
8746 guint compressed_length;
8750 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
8752 goto invalid_compression;
8754 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8755 compressed_length = cmvd_len - 12;
8756 GST_LOG ("length = %u", uncompressed_length);
8759 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8760 compressed_length, &uncompressed_length);
8763 qtdemux->moov_node_compressed = qtdemux->moov_node;
8764 qtdemux->moov_node = g_node_new (buf);
8766 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8767 uncompressed_length);
8771 #endif /* HAVE_ZLIB */
8773 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8774 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8781 invalid_compression:
8783 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8789 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8792 while (G_UNLIKELY (buf < end)) {
8796 if (G_UNLIKELY (buf + 4 > end)) {
8797 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8800 len = QT_UINT32 (buf);
8801 if (G_UNLIKELY (len == 0)) {
8802 GST_LOG_OBJECT (qtdemux, "empty container");
8805 if (G_UNLIKELY (len < 8)) {
8806 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8809 if (G_UNLIKELY (len > (end - buf))) {
8810 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8811 (gint) (end - buf));
8815 child = g_node_new ((guint8 *) buf);
8816 g_node_append (node, child);
8817 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8818 qtdemux_parse_node (qtdemux, child, buf, len);
8826 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8829 int len = QT_UINT32 (xdxt->data);
8830 guint8 *buf = xdxt->data;
8831 guint8 *end = buf + len;
8834 /* skip size and type */
8842 size = QT_UINT32 (buf);
8843 type = QT_FOURCC (buf + 4);
8845 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8847 if (buf + size > end || size <= 0)
8853 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8854 GST_FOURCC_ARGS (type));
8858 buffer = gst_buffer_new_and_alloc (size);
8859 gst_buffer_fill (buffer, 0, buf, size);
8860 stream->buffers = g_slist_append (stream->buffers, buffer);
8861 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8864 buffer = gst_buffer_new_and_alloc (size);
8865 gst_buffer_fill (buffer, 0, buf, size);
8866 stream->buffers = g_slist_append (stream->buffers, buffer);
8867 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8870 buffer = gst_buffer_new_and_alloc (size);
8871 gst_buffer_fill (buffer, 0, buf, size);
8872 stream->buffers = g_slist_append (stream->buffers, buffer);
8873 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8876 GST_WARNING_OBJECT (qtdemux,
8877 "unknown theora cookie %" GST_FOURCC_FORMAT,
8878 GST_FOURCC_ARGS (type));
8887 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8891 guint32 node_length = 0;
8892 const QtNodeType *type;
8895 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8897 if (G_UNLIKELY (length < 8))
8898 goto not_enough_data;
8900 node_length = QT_UINT32 (buffer);
8901 fourcc = QT_FOURCC (buffer + 4);
8903 /* ignore empty nodes */
8904 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8907 type = qtdemux_type_get (fourcc);
8909 end = buffer + length;
8911 GST_LOG_OBJECT (qtdemux,
8912 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8913 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8915 if (node_length > length)
8916 goto broken_atom_size;
8918 if (type->flags & QT_FLAG_CONTAINER) {
8919 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8924 if (node_length < 20) {
8925 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8928 GST_DEBUG_OBJECT (qtdemux,
8929 "parsing stsd (sample table, sample description) atom");
8930 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8931 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8944 /* also read alac (or whatever) in stead of mp4a in the following,
8945 * since a similar layout is used in other cases as well */
8946 if (fourcc == FOURCC_mp4a)
8948 else if (fourcc == FOURCC_fLaC)
8950 else if (fourcc == FOURCC_opus)
8955 /* There are two things we might encounter here: a true mp4a atom, and
8956 an mp4a entry in an stsd atom. The latter is what we're interested
8957 in, and it looks like an atom, but isn't really one. The true mp4a
8958 atom is short, so we detect it based on length here. */
8959 if (length < min_size) {
8960 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8961 GST_FOURCC_ARGS (fourcc));
8965 /* 'version' here is the sound sample description version. Types 0 and
8966 1 are documented in the QTFF reference, but type 2 is not: it's
8967 described in Apple header files instead (struct SoundDescriptionV2
8969 version = QT_UINT16 (buffer + 16);
8971 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8972 GST_FOURCC_ARGS (fourcc), version);
8974 /* parse any esds descriptors */
8986 GST_WARNING_OBJECT (qtdemux,
8987 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8988 GST_FOURCC_ARGS (fourcc), version);
8993 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
9021 /* codec_data is contained inside these atoms, which all have
9022 * the same format. */
9023 /* video sample description size is 86 bytes without extension.
9024 * node_length have to be bigger than 86 bytes because video sample
9025 * description can include extensions such as esds, fiel, glbl, etc. */
9026 if (node_length < 86) {
9027 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
9028 " sample description length too short (%u < 86)",
9029 GST_FOURCC_ARGS (fourcc), node_length);
9033 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
9034 GST_FOURCC_ARGS (fourcc));
9036 /* version (2 bytes) : this is set to 0, unless a compressor has changed
9038 * revision level (2 bytes) : must be set to 0. */
9039 version = QT_UINT32 (buffer + 16);
9040 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
9042 /* compressor name : PASCAL string and informative purposes
9043 * first byte : the number of bytes to be displayed.
9044 * it has to be less than 32 because it is reserved
9045 * space of 32 bytes total including itself. */
9046 str_len = QT_UINT8 (buffer + 50);
9048 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
9049 (char *) buffer + 51);
9051 GST_WARNING_OBJECT (qtdemux,
9052 "compressorname length too big (%u > 31)", str_len);
9054 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
9056 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
9061 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
9063 /* You are reading this correctly. QTFF specifies that the
9064 * metadata atom is a short atom, whereas ISO BMFF specifies
9065 * it's a full atom. But since so many people are doing things
9066 * differently, we actually peek into the atom to see which
9069 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
9070 GST_FOURCC_ARGS (fourcc));
9073 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
9074 /* Variant 1: What QTFF specifies. 'meta' is a short header which
9075 * starts with a 'hdlr' atom */
9076 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
9077 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
9078 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
9079 * with version/flags both set to zero */
9080 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
9082 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
9087 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
9088 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
9089 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
9098 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
9099 GST_FOURCC_ARGS (fourcc));
9103 version = QT_UINT32 (buffer + 12);
9104 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
9111 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
9116 if (length < offset) {
9117 GST_WARNING_OBJECT (qtdemux,
9118 "skipping too small %" GST_FOURCC_FORMAT " box",
9119 GST_FOURCC_ARGS (fourcc));
9122 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
9128 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
9133 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
9138 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
9141 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9144 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
9147 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
9149 if (!strcmp (type->name, "unknown"))
9150 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
9154 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
9155 GST_FOURCC_ARGS (fourcc));
9161 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9162 (_("This file is corrupt and cannot be played.")),
9163 ("Not enough data for an atom header, got only %u bytes", length));
9168 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9169 (_("This file is corrupt and cannot be played.")),
9170 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
9171 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
9178 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
9180 /* FIXME: This can only reliably work if demuxers have a
9181 * separate streaming thread per srcpad. This should be
9182 * done in a demuxer base class, which integrates parts
9185 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
9190 query = gst_query_new_allocation (stream->caps, FALSE);
9192 if (!gst_pad_peer_query (stream->pad, query)) {
9193 /* not a problem, just debug a little */
9194 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
9197 if (stream->allocator)
9198 gst_object_unref (stream->allocator);
9200 if (gst_query_get_n_allocation_params (query) > 0) {
9201 /* try the allocator */
9202 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
9204 stream->use_allocator = TRUE;
9206 stream->allocator = NULL;
9207 gst_allocation_params_init (&stream->params);
9208 stream->use_allocator = FALSE;
9210 gst_query_unref (query);
9215 pad_query (const GValue * item, GValue * value, gpointer user_data)
9217 GstPad *pad = g_value_get_object (item);
9218 GstQuery *query = user_data;
9221 res = gst_pad_peer_query (pad, query);
9224 g_value_set_boolean (value, TRUE);
9228 GST_INFO_OBJECT (pad, "pad peer query failed");
9233 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
9234 GstPadDirection direction)
9237 GstIteratorFoldFunction func = pad_query;
9238 GValue res = { 0, };
9240 g_value_init (&res, G_TYPE_BOOLEAN);
9241 g_value_set_boolean (&res, FALSE);
9244 if (direction == GST_PAD_SRC)
9245 it = gst_element_iterate_src_pads (element);
9247 it = gst_element_iterate_sink_pads (element);
9249 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
9250 gst_iterator_resync (it);
9252 gst_iterator_free (it);
9254 return g_value_get_boolean (&res);
9258 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
9259 QtDemuxStream * stream)
9263 GstElement *element = GST_ELEMENT (qtdemux);
9265 gchar **filtered_sys_ids;
9266 GValue event_list = G_VALUE_INIT;
9269 /* 1. Check if we already have the context. */
9270 if (qtdemux->preferred_protection_system_id != NULL) {
9271 GST_LOG_OBJECT (element,
9272 "already have the protection context, no need to request it again");
9276 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9277 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
9278 (const gchar **) qtdemux->protection_system_ids->pdata);
9280 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9281 qtdemux->protection_system_ids->len - 1);
9282 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
9283 "decryptors for %u of them, running context request",
9284 qtdemux->protection_system_ids->len,
9285 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
9288 if (stream->protection_scheme_event_queue.length) {
9289 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
9290 stream->protection_scheme_event_queue.length);
9291 walk = stream->protection_scheme_event_queue.tail;
9293 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
9294 qtdemux->protection_event_queue.length);
9295 walk = qtdemux->protection_event_queue.tail;
9298 g_value_init (&event_list, GST_TYPE_LIST);
9299 for (; walk; walk = g_list_previous (walk)) {
9300 GValue event_value = G_VALUE_INIT;
9301 g_value_init (&event_value, GST_TYPE_EVENT);
9302 g_value_set_boxed (&event_value, walk->data);
9303 gst_value_list_append_and_take_value (&event_list, &event_value);
9306 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
9307 * check if downstream already has a context of the specific type
9308 * 2b) Query upstream as above.
9310 query = gst_query_new_context ("drm-preferred-decryption-system-id");
9311 st = gst_query_writable_structure (query);
9312 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9313 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9315 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9316 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
9317 gst_query_parse_context (query, &ctxt);
9318 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
9319 gst_element_set_context (element, ctxt);
9320 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
9321 gst_query_parse_context (query, &ctxt);
9322 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
9323 gst_element_set_context (element, ctxt);
9325 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
9326 * the required context type and afterwards check if a
9327 * usable context was set now as in 1). The message could
9328 * be handled by the parent bins of the element and the
9333 GST_INFO_OBJECT (element, "posting need context message");
9334 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
9335 "drm-preferred-decryption-system-id");
9336 st = (GstStructure *) gst_message_get_structure (msg);
9337 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9338 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9341 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9342 gst_element_post_message (element, msg);
9345 g_strfreev (filtered_sys_ids);
9346 g_value_unset (&event_list);
9347 gst_query_unref (query);
9351 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
9352 QtDemuxStream * stream)
9355 const gchar *selected_system = NULL;
9357 g_return_val_if_fail (qtdemux != NULL, FALSE);
9358 g_return_val_if_fail (stream != NULL, FALSE);
9359 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
9362 if (stream->protection_scheme_type == FOURCC_aavd) {
9363 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9364 if (!gst_structure_has_name (s, "application/x-aavd")) {
9365 gst_structure_set (s,
9366 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
9368 gst_structure_set_name (s, "application/x-aavd");
9373 if (stream->protection_scheme_type != FOURCC_cenc
9374 && stream->protection_scheme_type != FOURCC_cbcs) {
9375 GST_ERROR_OBJECT (qtdemux,
9376 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
9377 GST_FOURCC_ARGS (stream->protection_scheme_type));
9381 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9382 if (!gst_structure_has_name (s, "application/x-cenc")) {
9383 gst_structure_set (s,
9384 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
9385 gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
9386 (stream->protection_scheme_type == FOURCC_cbcs) ? "cbcs" : "cenc",
9388 gst_structure_set_name (s, "application/x-cenc");
9391 if (qtdemux->protection_system_ids == NULL) {
9392 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
9393 "cenc protection system information has been found, not setting a "
9394 "protection system UUID");
9398 gst_qtdemux_request_protection_context (qtdemux, stream);
9399 if (qtdemux->preferred_protection_system_id != NULL) {
9400 const gchar *preferred_system_array[] =
9401 { qtdemux->preferred_protection_system_id, NULL };
9403 selected_system = gst_protection_select_system (preferred_system_array);
9405 if (selected_system) {
9406 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
9407 qtdemux->preferred_protection_system_id);
9409 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
9410 "because there is no available decryptor",
9411 qtdemux->preferred_protection_system_id);
9415 if (!selected_system) {
9416 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9417 selected_system = gst_protection_select_system ((const gchar **)
9418 qtdemux->protection_system_ids->pdata);
9419 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9420 qtdemux->protection_system_ids->len - 1);
9423 if (!selected_system) {
9424 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
9425 "suitable decryptor element has been found");
9429 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
9432 gst_structure_set (s,
9433 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
9440 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
9442 /* fps is calculated base on the duration of the average framerate since
9443 * qt does not have a fixed framerate. */
9444 gboolean fps_available = TRUE;
9445 guint32 first_duration = 0;
9447 if (stream->n_samples > 0)
9448 first_duration = stream->samples[0].duration;
9450 if ((stream->n_samples == 1 && first_duration == 0)
9451 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
9453 CUR_STREAM (stream)->fps_n = 0;
9454 CUR_STREAM (stream)->fps_d = 1;
9456 if (stream->duration == 0 || stream->n_samples < 2) {
9457 CUR_STREAM (stream)->fps_n = stream->timescale;
9458 CUR_STREAM (stream)->fps_d = 1;
9459 fps_available = FALSE;
9461 GstClockTime avg_duration;
9465 /* duration and n_samples can be updated for fragmented format
9466 * so, framerate of fragmented format is calculated using data in a moof */
9467 if (qtdemux->fragmented && stream->n_samples_moof > 0
9468 && stream->duration_moof > 0) {
9469 n_samples = stream->n_samples_moof;
9470 duration = stream->duration_moof;
9472 n_samples = stream->n_samples;
9473 duration = stream->duration;
9476 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
9477 /* stream->duration is guint64, timescale, n_samples are guint32 */
9479 gst_util_uint64_scale_round (duration -
9480 first_duration, GST_SECOND,
9481 (guint64) (stream->timescale) * (n_samples - 1));
9483 GST_LOG_OBJECT (qtdemux,
9484 "Calculating avg sample duration based on stream (or moof) duration %"
9486 " minus first sample %u, leaving %d samples gives %"
9487 GST_TIME_FORMAT, duration, first_duration,
9488 n_samples - 1, GST_TIME_ARGS (avg_duration));
9490 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9491 gst_video_guess_framerate (avg_duration,
9492 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9493 if (CUR_STREAM (stream)->fps_d == 0)
9494 fps_available = FALSE;
9497 gst_video_guess_framerate (avg_duration,
9498 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9501 GST_DEBUG_OBJECT (qtdemux,
9502 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
9503 stream->timescale, CUR_STREAM (stream)->fps_n,
9504 CUR_STREAM (stream)->fps_d);
9508 return fps_available;
9512 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
9514 if (stream->subtype == FOURCC_vide) {
9515 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9517 if (CUR_STREAM (stream)->caps) {
9518 CUR_STREAM (stream)->caps =
9519 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9521 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
9522 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9523 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
9524 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
9526 /* set framerate if calculated framerate is reliable */
9527 if (fps_available) {
9528 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9529 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9530 CUR_STREAM (stream)->fps_d, NULL);
9533 /* calculate pixel-aspect-ratio using display width and height */
9534 GST_DEBUG_OBJECT (qtdemux,
9535 "video size %dx%d, target display size %dx%d",
9536 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
9537 stream->display_width, stream->display_height);
9538 /* qt file might have pasp atom */
9539 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9540 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
9541 CUR_STREAM (stream)->par_h);
9542 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9543 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9544 CUR_STREAM (stream)->par_h, NULL);
9545 } else if (stream->display_width > 0 && stream->display_height > 0
9546 && CUR_STREAM (stream)->width > 0
9547 && CUR_STREAM (stream)->height > 0) {
9550 /* calculate the pixel aspect ratio using the display and pixel w/h */
9551 n = stream->display_width * CUR_STREAM (stream)->height;
9552 d = stream->display_height * CUR_STREAM (stream)->width;
9555 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
9556 CUR_STREAM (stream)->par_w = n;
9557 CUR_STREAM (stream)->par_h = d;
9558 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9559 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9560 CUR_STREAM (stream)->par_h, NULL);
9563 if (CUR_STREAM (stream)->interlace_mode > 0) {
9564 if (CUR_STREAM (stream)->interlace_mode == 1) {
9565 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9566 G_TYPE_STRING, "progressive", NULL);
9567 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
9568 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9569 G_TYPE_STRING, "interleaved", NULL);
9570 if (CUR_STREAM (stream)->field_order == 9) {
9571 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9572 G_TYPE_STRING, "top-field-first", NULL);
9573 } else if (CUR_STREAM (stream)->field_order == 14) {
9574 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9575 G_TYPE_STRING, "bottom-field-first", NULL);
9580 /* Create incomplete colorimetry here if needed */
9581 if (CUR_STREAM (stream)->colorimetry.range ||
9582 CUR_STREAM (stream)->colorimetry.matrix ||
9583 CUR_STREAM (stream)->colorimetry.transfer
9584 || CUR_STREAM (stream)->colorimetry.primaries) {
9585 gchar *colorimetry =
9586 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
9587 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
9588 G_TYPE_STRING, colorimetry, NULL);
9589 g_free (colorimetry);
9592 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9593 guint par_w = 1, par_h = 1;
9595 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9596 par_w = CUR_STREAM (stream)->par_w;
9597 par_h = CUR_STREAM (stream)->par_h;
9600 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
9601 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
9603 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9606 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9607 "multiview-mode", G_TYPE_STRING,
9608 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9609 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9610 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9615 else if (stream->subtype == FOURCC_soun) {
9616 if (CUR_STREAM (stream)->caps) {
9617 CUR_STREAM (stream)->caps =
9618 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9619 if (CUR_STREAM (stream)->rate > 0)
9620 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9621 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
9622 if (CUR_STREAM (stream)->n_channels > 0)
9623 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9624 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
9625 if (CUR_STREAM (stream)->n_channels > 2) {
9626 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9627 * correctly; this is just the minimum we can do - assume
9628 * we don't actually have any channel positions. */
9629 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9630 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9635 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
9636 const GstStructure *s;
9637 QtDemuxStream *fps_stream = NULL;
9638 gboolean fps_available = FALSE;
9640 /* CEA608 closed caption tracks are a bit special in that each sample
9641 * can contain CCs for multiple frames, and CCs can be omitted and have to
9642 * be inferred from the duration of the sample then.
9644 * As such we take the framerate from the (first) video track here for
9645 * CEA608 as there must be one CC byte pair for every video frame
9646 * according to the spec.
9648 * For CEA708 all is fine and there is one sample per frame.
9651 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9652 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9655 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
9656 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
9658 if (tmp->subtype == FOURCC_vide) {
9665 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
9666 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
9667 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
9670 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9671 fps_stream = stream;
9674 CUR_STREAM (stream)->caps =
9675 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9677 /* set framerate if calculated framerate is reliable */
9678 if (fps_available) {
9679 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9680 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9681 CUR_STREAM (stream)->fps_d, NULL);
9686 gboolean forward_collection = FALSE;
9687 GstCaps *prev_caps = NULL;
9689 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
9690 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
9691 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
9692 gst_pad_set_active (stream->pad, TRUE);
9694 gst_pad_use_fixed_caps (stream->pad);
9696 if (stream->protected) {
9697 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
9698 GST_ERROR_OBJECT (qtdemux,
9699 "Failed to configure protected stream caps.");
9704 if (stream->new_stream) {
9706 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9709 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9712 gst_event_parse_stream_flags (event, &stream_flags);
9713 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9714 qtdemux->have_group_id = TRUE;
9716 qtdemux->have_group_id = FALSE;
9717 gst_event_unref (event);
9718 } else if (!qtdemux->have_group_id) {
9719 qtdemux->have_group_id = TRUE;
9720 qtdemux->group_id = gst_util_group_id_next ();
9723 stream->new_stream = FALSE;
9724 event = gst_event_new_stream_start (stream->stream_id);
9725 if (qtdemux->have_group_id)
9726 gst_event_set_group_id (event, qtdemux->group_id);
9727 if (stream->disabled)
9728 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9729 if (CUR_STREAM (stream)->sparse) {
9730 stream_flags |= GST_STREAM_FLAG_SPARSE;
9732 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9734 gst_event_set_stream_flags (event, stream_flags);
9735 gst_pad_push_event (stream->pad, event);
9737 forward_collection = TRUE;
9740 prev_caps = gst_pad_get_current_caps (stream->pad);
9742 if (CUR_STREAM (stream)->caps) {
9744 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9745 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9746 CUR_STREAM (stream)->caps);
9747 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9749 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9752 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9756 gst_caps_unref (prev_caps);
9757 stream->new_caps = FALSE;
9759 if (forward_collection) {
9760 /* Forward upstream collection and selection if any */
9761 GstEvent *upstream_event = gst_pad_get_sticky_event (qtdemux->sinkpad,
9762 GST_EVENT_STREAM_COLLECTION, 0);
9764 gst_pad_push_event (stream->pad, upstream_event);
9771 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9772 QtDemuxStream * stream)
9774 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9777 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9778 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9779 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9780 stream->stsd_entries_length)) {
9781 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9782 (_("This file is invalid and cannot be played.")),
9783 ("New sample description id is out of bounds (%d >= %d)",
9784 stream->stsd_sample_description_id, stream->stsd_entries_length));
9786 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9787 stream->new_caps = TRUE;
9792 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9793 QtDemuxStream * stream, GstTagList * list)
9795 gboolean ret = TRUE;
9797 if (stream->subtype == FOURCC_vide) {
9798 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9801 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9804 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9805 gst_object_unref (stream->pad);
9811 qtdemux->n_video_streams++;
9812 } else if (stream->subtype == FOURCC_soun) {
9813 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9816 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9818 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9819 gst_object_unref (stream->pad);
9824 qtdemux->n_audio_streams++;
9825 } else if (stream->subtype == FOURCC_strm) {
9826 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9827 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9828 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9829 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
9830 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9833 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9835 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9836 gst_object_unref (stream->pad);
9841 qtdemux->n_sub_streams++;
9842 } else if (stream->subtype == FOURCC_meta) {
9843 gchar *name = g_strdup_printf ("meta_%u", qtdemux->n_meta_streams);
9846 gst_pad_new_from_static_template (&gst_qtdemux_metasrc_template, name);
9848 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9849 gst_object_unref (stream->pad);
9854 qtdemux->n_meta_streams++;
9855 } else if (CUR_STREAM (stream)->caps) {
9856 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9859 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9861 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9862 gst_object_unref (stream->pad);
9867 qtdemux->n_video_streams++;
9869 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9876 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9877 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9878 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9879 GST_OBJECT_LOCK (qtdemux);
9880 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9881 GST_OBJECT_UNLOCK (qtdemux);
9883 if (stream->stream_tags)
9884 gst_tag_list_unref (stream->stream_tags);
9885 stream->stream_tags = list;
9887 /* global tags go on each pad anyway */
9888 stream->send_global_tags = TRUE;
9889 /* send upstream GST_EVENT_PROTECTION events that were received before
9890 this source pad was created */
9891 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9892 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9896 gst_tag_list_unref (list);
9900 /* find next atom with @fourcc starting at @offset */
9901 static GstFlowReturn
9902 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9903 guint64 * length, guint32 fourcc)
9909 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9910 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9916 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9917 if (G_UNLIKELY (ret != GST_FLOW_OK))
9919 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9922 gst_buffer_unref (buf);
9925 gst_buffer_map (buf, &map, GST_MAP_READ);
9926 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9927 gst_buffer_unmap (buf, &map);
9928 gst_buffer_unref (buf);
9930 if (G_UNLIKELY (*length == 0)) {
9931 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9932 ret = GST_FLOW_ERROR;
9936 if (lfourcc == fourcc) {
9937 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9938 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9941 GST_LOG_OBJECT (qtdemux,
9942 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9943 GST_FOURCC_ARGS (lfourcc), *offset);
9944 if (*offset == G_MAXUINT64)
9954 /* might simply have had last one */
9955 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9960 /* should only do something in pull mode */
9961 /* call with OBJECT lock */
9962 static GstFlowReturn
9963 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9965 guint64 length, offset;
9966 GstBuffer *buf = NULL;
9967 GstFlowReturn ret = GST_FLOW_OK;
9968 GstFlowReturn res = GST_FLOW_OK;
9971 offset = qtdemux->moof_offset;
9972 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9975 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9976 return GST_FLOW_EOS;
9979 /* best not do pull etc with lock held */
9980 GST_OBJECT_UNLOCK (qtdemux);
9982 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9983 if (ret != GST_FLOW_OK)
9986 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9987 if (G_UNLIKELY (ret != GST_FLOW_OK))
9989 gst_buffer_map (buf, &map, GST_MAP_READ);
9990 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9991 gst_buffer_unmap (buf, &map);
9992 gst_buffer_unref (buf);
9997 gst_buffer_unmap (buf, &map);
9998 gst_buffer_unref (buf);
10002 /* look for next moof */
10003 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
10004 if (G_UNLIKELY (ret != GST_FLOW_OK))
10008 GST_OBJECT_LOCK (qtdemux);
10010 qtdemux->moof_offset = offset;
10016 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
10018 res = GST_FLOW_ERROR;
10023 /* maybe upstream temporarily flushing */
10024 if (ret != GST_FLOW_FLUSHING) {
10025 GST_DEBUG_OBJECT (qtdemux, "no next moof");
10028 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
10029 /* resume at current position next time */
10037 qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
10040 guint32 num_chunks;
10041 gint32 stts_duration;
10042 GstByteWriter stsc, stts, stsz;
10044 /* Each sample has a different size, which we don't support for merging */
10045 if (stream->sample_size == 0) {
10046 GST_DEBUG_OBJECT (qtdemux,
10047 "Not all samples have the same size, not merging");
10051 /* The stream has a ctts table, we don't support that */
10052 if (stream->ctts_present) {
10053 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
10057 /* If there's a sync sample table also ignore this stream */
10058 if (stream->stps_present || stream->stss_present) {
10059 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
10063 /* If chunks are considered samples already ignore this stream */
10064 if (stream->chunks_are_samples) {
10065 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
10069 /* Require that all samples have the same duration */
10070 if (stream->n_sample_times > 1) {
10071 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
10075 /* Parse the stts to get the sample duration and number of samples */
10076 gst_byte_reader_skip_unchecked (&stream->stts, 4);
10077 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10079 /* Parse the number of chunks from the stco manually because the
10080 * reader is already behind that */
10081 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
10083 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
10086 /* Now parse stsc, convert chunks into single samples and generate a
10087 * new stsc, stts and stsz from this information */
10088 gst_byte_writer_init (&stsc);
10089 gst_byte_writer_init (&stts);
10090 gst_byte_writer_init (&stsz);
10092 /* Note: we skip fourccs, size, version, flags and other fields of the new
10093 * atoms as the byte readers with them are already behind that position
10094 * anyway and only update the values of those inside the stream directly.
10096 stream->n_sample_times = 0;
10097 stream->n_samples = 0;
10098 for (i = 0; i < stream->n_samples_per_chunk; i++) {
10100 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
10102 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10103 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10104 sample_description_id =
10105 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10107 if (i == stream->n_samples_per_chunk - 1) {
10108 /* +1 because first_chunk is 1-based */
10109 last_chunk = num_chunks + 1;
10111 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10114 GST_DEBUG_OBJECT (qtdemux,
10115 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
10116 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
10118 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
10119 /* One sample in this chunk */
10120 gst_byte_writer_put_uint32_be (&stsc, 1);
10121 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
10123 /* For each chunk write a stts and stsz entry now */
10124 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
10125 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
10126 for (j = first_chunk; j < last_chunk; j++) {
10127 gst_byte_writer_put_uint32_be (&stsz,
10128 stream->sample_size * samples_per_chunk);
10131 stream->n_sample_times += 1;
10132 stream->n_samples += last_chunk - first_chunk;
10135 g_assert_cmpint (stream->n_samples, ==, num_chunks);
10137 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
10138 stream->n_samples, stream->n_sample_times);
10140 /* We don't have a fixed sample size anymore */
10141 stream->sample_size = 0;
10143 /* Free old data for the atoms */
10144 g_free ((gpointer) stream->stsz.data);
10145 stream->stsz.data = NULL;
10146 g_free ((gpointer) stream->stsc.data);
10147 stream->stsc.data = NULL;
10148 g_free ((gpointer) stream->stts.data);
10149 stream->stts.data = NULL;
10151 /* Store new data and replace byte readers */
10152 stream->stsz.size = gst_byte_writer_get_size (&stsz);
10153 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
10154 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
10155 stream->stts.size = gst_byte_writer_get_size (&stts);
10156 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
10157 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
10158 stream->stsc.size = gst_byte_writer_get_size (&stsc);
10159 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
10160 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
10163 /* initialise bytereaders for stbl sub-atoms */
10165 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
10167 stream->stbl_index = -1; /* no samples have yet been parsed */
10168 stream->sample_index = -1;
10170 /* time-to-sample atom */
10171 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
10174 /* copy atom data into a new buffer for later use */
10175 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
10177 /* skip version + flags */
10178 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
10179 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
10181 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
10183 /* make sure there's enough data */
10184 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
10185 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
10186 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
10187 stream->n_sample_times);
10188 if (!stream->n_sample_times)
10192 /* sync sample atom */
10193 stream->stps_present = FALSE;
10194 if ((stream->stss_present =
10195 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
10196 &stream->stss) ? TRUE : FALSE) == TRUE) {
10197 /* copy atom data into a new buffer for later use */
10198 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
10200 /* skip version + flags */
10201 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
10202 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
10205 if (stream->n_sample_syncs) {
10206 /* make sure there's enough data */
10207 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
10211 /* partial sync sample atom */
10212 if ((stream->stps_present =
10213 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
10214 &stream->stps) ? TRUE : FALSE) == TRUE) {
10215 /* copy atom data into a new buffer for later use */
10216 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
10218 /* skip version + flags */
10219 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
10220 !gst_byte_reader_get_uint32_be (&stream->stps,
10221 &stream->n_sample_partial_syncs))
10224 /* if there are no entries, the stss table contains the real
10226 if (stream->n_sample_partial_syncs) {
10227 /* make sure there's enough data */
10228 if (!qt_atom_parser_has_chunks (&stream->stps,
10229 stream->n_sample_partial_syncs, 4))
10236 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
10239 /* copy atom data into a new buffer for later use */
10240 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
10242 /* skip version + flags */
10243 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
10244 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
10247 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
10250 if (!stream->n_samples)
10253 /* sample-to-chunk atom */
10254 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
10257 /* copy atom data into a new buffer for later use */
10258 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
10260 /* skip version + flags */
10261 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
10262 !gst_byte_reader_get_uint32_be (&stream->stsc,
10263 &stream->n_samples_per_chunk))
10266 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
10267 stream->n_samples_per_chunk);
10269 /* make sure there's enough data */
10270 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
10276 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
10277 stream->co_size = sizeof (guint32);
10278 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
10280 stream->co_size = sizeof (guint64);
10284 /* copy atom data into a new buffer for later use */
10285 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
10287 /* skip version + flags */
10288 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
10291 /* chunks_are_samples == TRUE means treat chunks as samples */
10292 stream->chunks_are_samples = stream->sample_size
10293 && !CUR_STREAM (stream)->sampled;
10294 if (stream->chunks_are_samples) {
10295 /* treat chunks as samples */
10296 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
10299 /* skip number of entries */
10300 if (!gst_byte_reader_skip (&stream->stco, 4))
10303 /* make sure there are enough data in the stsz atom */
10304 if (!stream->sample_size) {
10305 /* different sizes for each sample */
10306 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
10311 /* composition time-to-sample */
10312 if ((stream->ctts_present =
10313 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
10314 &stream->ctts) ? TRUE : FALSE) == TRUE) {
10315 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
10316 guint8 ctts_version;
10317 gboolean checked_ctts = FALSE;
10319 /* copy atom data into a new buffer for later use */
10320 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
10322 /* version 1 has signed offsets */
10323 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
10327 if (!gst_byte_reader_skip (&stream->ctts, 3)
10328 || !gst_byte_reader_get_uint32_be (&stream->ctts,
10329 &stream->n_composition_times))
10332 /* make sure there's enough data */
10333 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
10337 /* This is optional, if missing we iterate the ctts */
10338 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
10339 guint8 cslg_version;
10341 /* cslg version 1 has 64 bit fields */
10342 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
10346 if (!gst_byte_reader_skip (&cslg, 3))
10349 if (cslg_version == 0) {
10350 gint32 composition_to_dts_shift;
10352 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
10355 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10357 gint64 composition_to_dts_shift;
10359 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
10362 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10365 gint32 cslg_least = 0;
10366 guint num_entries, pos;
10369 pos = gst_byte_reader_get_pos (&stream->ctts);
10370 num_entries = stream->n_composition_times;
10372 checked_ctts = TRUE;
10374 stream->cslg_shift = 0;
10376 for (i = 0; i < num_entries; i++) {
10379 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10380 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10381 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10382 * slightly inaccurate PTS could be more usable than corrupted one */
10383 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10384 && ABS (offset) / 2 > stream->duration)) {
10385 GST_WARNING_OBJECT (qtdemux,
10386 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10387 " larger than duration %" G_GUINT64_FORMAT, offset,
10390 stream->cslg_shift = 0;
10391 stream->ctts_present = FALSE;
10395 /* Don't consider "no decode samples" with offset G_MININT32
10396 * for the DTS/PTS shift */
10397 if (offset != G_MININT32 && offset < cslg_least)
10398 cslg_least = offset;
10401 if (cslg_least < 0)
10402 stream->cslg_shift = -cslg_least;
10404 stream->cslg_shift = 0;
10406 /* reset the reader so we can generate sample table */
10407 gst_byte_reader_set_pos (&stream->ctts, pos);
10410 /* Check if ctts values are looking reasonable if that didn't happen above */
10411 if (!checked_ctts) {
10412 guint num_entries, pos;
10415 pos = gst_byte_reader_get_pos (&stream->ctts);
10416 num_entries = stream->n_composition_times;
10418 for (i = 0; i < num_entries; i++) {
10421 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10422 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10423 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10424 * slightly inaccurate PTS could be more usable than corrupted one */
10425 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10426 && ABS (offset) / 2 > stream->duration)) {
10427 GST_WARNING_OBJECT (qtdemux,
10428 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10429 " larger than duration %" G_GUINT64_FORMAT, offset,
10432 stream->cslg_shift = 0;
10433 stream->ctts_present = FALSE;
10438 /* reset the reader so we can generate sample table */
10439 gst_byte_reader_set_pos (&stream->ctts, pos);
10442 /* Ensure the cslg_shift value is consistent so we can use it
10443 * unconditionally to produce TS and Segment */
10444 stream->cslg_shift = 0;
10447 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
10448 stream->cslg_shift);
10450 /* For raw audio streams especially we might want to merge the samples
10451 * to not output one audio sample per buffer. We're doing this here
10452 * before allocating the sample tables so that from this point onwards
10453 * the number of container samples are static */
10454 if (stream->min_buffer_size > 0) {
10455 qtdemux_merge_sample_table (qtdemux, stream);
10459 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
10460 stream->n_samples, (guint) sizeof (QtDemuxSample),
10461 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
10463 if (stream->n_samples >=
10464 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
10465 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
10466 "be larger than %uMB (broken file?)", stream->n_samples,
10467 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
10471 g_assert (stream->samples == NULL);
10472 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
10473 if (!stream->samples) {
10474 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
10475 stream->n_samples);
10483 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10484 (_("This file is corrupt and cannot be played.")), (NULL));
10489 gst_qtdemux_stbl_free (stream);
10490 if (!qtdemux->fragmented) {
10491 /* not quite good */
10492 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
10495 /* may pick up samples elsewhere */
10501 /* collect samples from the next sample to be parsed up to sample @n for @stream
10502 * by reading the info from @stbl
10504 * This code can be executed from both the streaming thread and the seeking
10505 * thread so it takes the object lock to protect itself
10508 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
10511 QtDemuxSample *samples, *first, *cur, *last;
10512 guint32 n_samples_per_chunk;
10515 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
10516 GST_FOURCC_FORMAT ", pad %s",
10517 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
10518 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
10520 n_samples = stream->n_samples;
10522 if (n >= n_samples)
10523 goto out_of_samples;
10525 GST_OBJECT_LOCK (qtdemux);
10526 if (n <= stream->stbl_index)
10527 goto already_parsed;
10529 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
10531 if (!stream->stsz.data) {
10532 /* so we already parsed and passed all the moov samples;
10533 * onto fragmented ones */
10534 g_assert (qtdemux->fragmented);
10538 /* pointer to the sample table */
10539 samples = stream->samples;
10541 /* starts from -1, moves to the next sample index to parse */
10542 stream->stbl_index++;
10544 /* keep track of the first and last sample to fill */
10545 first = &samples[stream->stbl_index];
10546 last = &samples[n];
10548 if (!stream->chunks_are_samples) {
10549 /* set the sample sizes */
10550 if (stream->sample_size == 0) {
10551 /* different sizes for each sample */
10552 for (cur = first; cur <= last; cur++) {
10553 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
10554 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
10555 (guint) (cur - samples), cur->size);
10558 /* samples have the same size */
10559 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
10560 for (cur = first; cur <= last; cur++)
10561 cur->size = stream->sample_size;
10565 n_samples_per_chunk = stream->n_samples_per_chunk;
10568 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
10569 guint32 last_chunk;
10571 if (stream->stsc_chunk_index >= stream->last_chunk
10572 || stream->stsc_chunk_index < stream->first_chunk) {
10573 stream->first_chunk =
10574 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10575 stream->samples_per_chunk =
10576 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10577 /* starts from 1 */
10578 stream->stsd_sample_description_id =
10579 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
10581 /* chunk numbers are counted from 1 it seems */
10582 if (G_UNLIKELY (stream->first_chunk == 0))
10585 --stream->first_chunk;
10587 /* the last chunk of each entry is calculated by taking the first chunk
10588 * of the next entry; except if there is no next, where we fake it with
10590 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
10591 stream->last_chunk = G_MAXUINT32;
10593 stream->last_chunk =
10594 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10595 if (G_UNLIKELY (stream->last_chunk == 0))
10598 --stream->last_chunk;
10601 GST_LOG_OBJECT (qtdemux,
10602 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
10603 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
10604 stream->samples_per_chunk, stream->stsd_sample_description_id);
10606 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
10609 if (stream->last_chunk != G_MAXUINT32) {
10610 if (!qt_atom_parser_peek_sub (&stream->stco,
10611 stream->first_chunk * stream->co_size,
10612 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10613 &stream->co_chunk))
10617 stream->co_chunk = stream->stco;
10618 if (!gst_byte_reader_skip (&stream->co_chunk,
10619 stream->first_chunk * stream->co_size))
10623 stream->stsc_chunk_index = stream->first_chunk;
10626 last_chunk = stream->last_chunk;
10628 if (stream->chunks_are_samples) {
10629 cur = &samples[stream->stsc_chunk_index];
10631 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10634 stream->stsc_chunk_index = j;
10639 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
10642 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10643 "%" G_GUINT64_FORMAT, j, cur->offset);
10645 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
10646 CUR_STREAM (stream)->bytes_per_frame > 0) {
10648 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
10649 CUR_STREAM (stream)->samples_per_frame *
10650 CUR_STREAM (stream)->bytes_per_frame;
10652 cur->size = stream->samples_per_chunk;
10655 GST_DEBUG_OBJECT (qtdemux,
10656 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
10657 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
10658 stream->stco_sample_index)), cur->size);
10660 cur->timestamp = stream->stco_sample_index;
10661 cur->duration = stream->samples_per_chunk;
10662 cur->keyframe = TRUE;
10665 stream->stco_sample_index += stream->samples_per_chunk;
10667 stream->stsc_chunk_index = j;
10669 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10670 guint32 samples_per_chunk;
10671 guint64 chunk_offset;
10673 if (!stream->stsc_sample_index
10674 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
10675 &stream->chunk_offset))
10678 samples_per_chunk = stream->samples_per_chunk;
10679 chunk_offset = stream->chunk_offset;
10681 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10682 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10683 G_GUINT64_FORMAT " and size %d",
10684 (guint) (cur - samples), chunk_offset, cur->size);
10686 cur->offset = chunk_offset;
10687 chunk_offset += cur->size;
10690 if (G_UNLIKELY (cur > last)) {
10692 stream->stsc_sample_index = k + 1;
10693 stream->chunk_offset = chunk_offset;
10694 stream->stsc_chunk_index = j;
10698 stream->stsc_sample_index = 0;
10700 stream->stsc_chunk_index = j;
10702 stream->stsc_index++;
10705 if (stream->chunks_are_samples)
10709 guint32 n_sample_times;
10711 n_sample_times = stream->n_sample_times;
10714 for (i = stream->stts_index; i < n_sample_times; i++) {
10715 guint32 stts_samples;
10716 gint32 stts_duration;
10719 if (stream->stts_sample_index >= stream->stts_samples
10720 || !stream->stts_sample_index) {
10722 stream->stts_samples =
10723 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10724 stream->stts_duration =
10725 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10727 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10728 i, stream->stts_samples, stream->stts_duration);
10730 stream->stts_sample_index = 0;
10733 stts_samples = stream->stts_samples;
10734 stts_duration = stream->stts_duration;
10735 stts_time = stream->stts_time;
10737 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10738 GST_DEBUG_OBJECT (qtdemux,
10739 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10740 (guint) (cur - samples), j,
10741 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10743 cur->timestamp = stts_time;
10744 cur->duration = stts_duration;
10746 /* avoid 32-bit wrap-around,
10747 * but still mind possible 'negative' duration */
10748 stts_time += (gint64) stts_duration;
10751 if (G_UNLIKELY (cur > last)) {
10753 stream->stts_time = stts_time;
10754 stream->stts_sample_index = j + 1;
10755 if (stream->stts_sample_index >= stream->stts_samples)
10756 stream->stts_index++;
10760 stream->stts_sample_index = 0;
10761 stream->stts_time = stts_time;
10762 stream->stts_index++;
10764 /* fill up empty timestamps with the last timestamp, this can happen when
10765 * the last samples do not decode and so we don't have timestamps for them.
10766 * We however look at the last timestamp to estimate the track length so we
10767 * need something in here. */
10768 for (; cur < last; cur++) {
10769 GST_DEBUG_OBJECT (qtdemux,
10770 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10771 (guint) (cur - samples),
10772 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10773 cur->timestamp = stream->stts_time;
10774 cur->duration = -1;
10779 /* sample sync, can be NULL */
10780 if (stream->stss_present == TRUE) {
10781 guint32 n_sample_syncs;
10783 n_sample_syncs = stream->n_sample_syncs;
10785 if (!n_sample_syncs) {
10786 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10787 stream->all_keyframe = TRUE;
10789 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10790 /* note that the first sample is index 1, not 0 */
10793 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10795 if (G_LIKELY (index > 0 && index <= n_samples)) {
10797 samples[index].keyframe = TRUE;
10798 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10799 /* and exit if we have enough samples */
10800 if (G_UNLIKELY (index >= n)) {
10807 stream->stss_index = i;
10810 /* stps marks partial sync frames like open GOP I-Frames */
10811 if (stream->stps_present == TRUE) {
10812 guint32 n_sample_partial_syncs;
10814 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10816 /* if there are no entries, the stss table contains the real
10818 if (n_sample_partial_syncs) {
10819 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10820 /* note that the first sample is index 1, not 0 */
10823 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10825 if (G_LIKELY (index > 0 && index <= n_samples)) {
10827 samples[index].keyframe = TRUE;
10828 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10829 /* and exit if we have enough samples */
10830 if (G_UNLIKELY (index >= n)) {
10837 stream->stps_index = i;
10841 /* no stss, all samples are keyframes */
10842 stream->all_keyframe = TRUE;
10843 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10848 /* composition time to sample */
10849 if (stream->ctts_present == TRUE) {
10850 guint32 n_composition_times;
10851 guint32 ctts_count;
10852 gint32 ctts_soffset;
10854 /* Fill in the pts_offsets */
10856 n_composition_times = stream->n_composition_times;
10858 for (i = stream->ctts_index; i < n_composition_times; i++) {
10859 if (stream->ctts_sample_index >= stream->ctts_count
10860 || !stream->ctts_sample_index) {
10861 stream->ctts_count =
10862 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10863 stream->ctts_soffset =
10864 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10865 stream->ctts_sample_index = 0;
10868 ctts_count = stream->ctts_count;
10869 ctts_soffset = stream->ctts_soffset;
10871 /* FIXME: Set offset to 0 for "no decode samples". This needs
10872 * to be handled in a codec specific manner ideally. */
10873 if (ctts_soffset == G_MININT32)
10876 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10877 cur->pts_offset = ctts_soffset;
10880 if (G_UNLIKELY (cur > last)) {
10882 stream->ctts_sample_index = j + 1;
10886 stream->ctts_sample_index = 0;
10887 stream->ctts_index++;
10891 stream->stbl_index = n;
10892 /* if index has been completely parsed, free data that is no-longer needed */
10893 if (n + 1 == stream->n_samples) {
10894 gst_qtdemux_stbl_free (stream);
10895 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10896 if (qtdemux->pullbased) {
10897 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10898 while (n + 1 == stream->n_samples)
10899 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10903 GST_OBJECT_UNLOCK (qtdemux);
10910 GST_LOG_OBJECT (qtdemux,
10911 "Tried to parse up to sample %u but this sample has already been parsed",
10913 /* if fragmented, there may be more */
10914 if (qtdemux->fragmented && n == stream->stbl_index)
10916 GST_OBJECT_UNLOCK (qtdemux);
10922 GST_LOG_OBJECT (qtdemux,
10923 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10924 stream->n_samples);
10925 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10926 (_("This file is corrupt and cannot be played.")), (NULL));
10931 GST_OBJECT_UNLOCK (qtdemux);
10932 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10933 (_("This file is corrupt and cannot be played.")), (NULL));
10938 /* collect all segment info for @stream.
10941 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10945 /* accept edts if they contain gaps at start and there is only
10946 * one media segment */
10947 gboolean allow_pushbased_edts = TRUE;
10948 gint media_segments_count = 0;
10950 /* parse and prepare segment info from the edit list */
10951 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10952 stream->n_segments = 0;
10953 stream->segments = NULL;
10954 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10957 guint segment_number, entry_size;
10959 GstClockTime stime;
10960 const guint8 *buffer;
10964 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10965 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10968 buffer = elst->data;
10970 size = QT_UINT32 (buffer);
10971 /* version, flags, n_segments */
10973 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10976 version = QT_UINT8 (buffer + 8);
10977 entry_size = (version == 1) ? 20 : 12;
10979 n_segments = QT_UINT32 (buffer + 12);
10981 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10982 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10986 /* we might allocate a bit too much, at least allocate 1 segment */
10987 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10989 /* segments always start from 0 */
10993 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10995 guint64 media_time;
10996 gboolean empty_edit = FALSE;
10997 QtDemuxSegment *segment;
10999 GstClockTime media_start = GST_CLOCK_TIME_NONE;
11001 if (version == 1) {
11002 media_time = QT_UINT64 (buffer + 8);
11003 duration = QT_UINT64 (buffer);
11004 if (media_time == G_MAXUINT64)
11007 media_time = QT_UINT32 (buffer + 4);
11008 duration = QT_UINT32 (buffer);
11009 if (media_time == G_MAXUINT32)
11014 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
11016 segment = &stream->segments[segment_number];
11018 /* time and duration expressed in global timescale */
11019 segment->time = stime;
11020 if (duration != 0 || empty_edit) {
11021 /* edge case: empty edits with duration=zero are treated here.
11022 * (files should not have these anyway). */
11024 /* add non scaled values so we don't cause roundoff errors */
11026 stime = QTTIME_TO_GSTTIME (qtdemux, time);
11027 segment->duration = stime - segment->time;
11029 /* zero duration does not imply media_start == media_stop
11030 * but, only specify media_start. The edit ends with the track. */
11031 stime = segment->duration = GST_CLOCK_TIME_NONE;
11032 /* Don't allow more edits after this one. */
11033 n_segments = segment_number + 1;
11035 segment->stop_time = stime;
11037 segment->trak_media_start = media_time;
11038 /* media_time expressed in stream timescale */
11040 segment->media_start = media_start;
11041 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
11042 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
11043 media_segments_count++;
11045 segment->media_start = GST_CLOCK_TIME_NONE;
11046 segment->media_stop = GST_CLOCK_TIME_NONE;
11048 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
11050 if (rate_int <= 1) {
11051 /* 0 is not allowed, some programs write 1 instead of the floating point
11053 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
11057 segment->rate = rate_int / 65536.0;
11060 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
11061 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
11062 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
11063 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
11064 segment_number, GST_TIME_ARGS (segment->time),
11065 GST_TIME_ARGS (segment->duration),
11066 GST_TIME_ARGS (segment->media_start), media_time,
11067 GST_TIME_ARGS (segment->media_stop),
11068 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
11069 stream->timescale);
11070 if (segment->stop_time > qtdemux->segment.stop &&
11071 !qtdemux->upstream_format_is_time) {
11072 GST_WARNING_OBJECT (qtdemux, "Segment %d "
11073 " extends to %" GST_TIME_FORMAT
11074 " past the end of the declared movie duration %" GST_TIME_FORMAT
11075 " movie segment will be extended", segment_number,
11076 GST_TIME_ARGS (segment->stop_time),
11077 GST_TIME_ARGS (qtdemux->segment.stop));
11078 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
11081 buffer += entry_size;
11083 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
11084 stream->n_segments = n_segments;
11085 if (media_segments_count != 1)
11086 allow_pushbased_edts = FALSE;
11090 /* push based does not handle segments, so act accordingly here,
11091 * and warn if applicable */
11092 if (!qtdemux->pullbased && !allow_pushbased_edts) {
11093 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
11094 /* remove and use default one below, we stream like it anyway */
11095 g_free (stream->segments);
11096 stream->segments = NULL;
11097 stream->n_segments = 0;
11100 /* no segments, create one to play the complete trak */
11101 if (stream->n_segments == 0) {
11102 GstClockTime stream_duration =
11103 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
11105 if (stream->segments == NULL)
11106 stream->segments = g_new (QtDemuxSegment, 1);
11108 /* represent unknown our way */
11109 if (stream_duration == 0)
11110 stream_duration = GST_CLOCK_TIME_NONE;
11112 stream->segments[0].time = 0;
11113 stream->segments[0].stop_time = stream_duration;
11114 stream->segments[0].duration = stream_duration;
11115 stream->segments[0].media_start = 0;
11116 stream->segments[0].media_stop = stream_duration;
11117 stream->segments[0].rate = 1.0;
11118 stream->segments[0].trak_media_start = 0;
11120 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
11121 GST_TIME_ARGS (stream_duration));
11122 stream->n_segments = 1;
11123 stream->dummy_segment = TRUE;
11125 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
11131 * Parses the stsd atom of a svq3 trak looking for
11132 * the SMI and gama atoms.
11135 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
11136 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
11138 const guint8 *_gamma = NULL;
11139 GstBuffer *_seqh = NULL;
11140 const guint8 *stsd_data = stsd_entry_data;
11141 guint32 length = QT_UINT32 (stsd_data);
11145 GST_WARNING_OBJECT (qtdemux, "stsd too short");
11151 version = QT_UINT16 (stsd_data);
11152 if (version == 3) {
11153 if (length >= 70) {
11156 while (length > 8) {
11157 guint32 fourcc, size;
11158 const guint8 *data;
11159 size = QT_UINT32 (stsd_data);
11160 fourcc = QT_FOURCC (stsd_data + 4);
11161 data = stsd_data + 8;
11164 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
11165 "svq3 atom parsing");
11174 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
11175 " for gama atom, expected 12", size);
11180 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
11182 if (_seqh != NULL) {
11183 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
11184 " found, ignoring");
11186 seqh_size = QT_UINT32 (data + 4);
11187 if (seqh_size > 0) {
11188 _seqh = gst_buffer_new_and_alloc (seqh_size);
11189 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
11196 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
11197 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
11201 if (size <= length) {
11207 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
11210 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
11211 G_GUINT16_FORMAT, version);
11221 } else if (_seqh) {
11222 gst_buffer_unref (_seqh);
11227 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
11230 GstByteReader dref;
11234 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
11235 * atom that might contain a 'data' atom with the rtsp uri.
11236 * This case was reported in bug #597497, some info about
11237 * the hndl atom can be found in TN1195
11239 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
11240 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
11243 guint32 dref_num_entries = 0;
11244 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
11245 gst_byte_reader_skip (&dref, 4) &&
11246 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
11249 /* search dref entries for hndl atom */
11250 for (i = 0; i < dref_num_entries; i++) {
11251 guint32 size = 0, type;
11252 guint8 string_len = 0;
11253 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
11254 qt_atom_parser_get_fourcc (&dref, &type)) {
11255 if (type == FOURCC_hndl) {
11256 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
11258 /* skip data reference handle bytes and the
11259 * following pascal string and some extra 4
11260 * bytes I have no idea what are */
11261 if (!gst_byte_reader_skip (&dref, 4) ||
11262 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
11263 !gst_byte_reader_skip (&dref, string_len + 4)) {
11264 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
11268 /* iterate over the atoms to find the data atom */
11269 while (gst_byte_reader_get_remaining (&dref) >= 8) {
11273 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
11274 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
11275 if (atom_type == FOURCC_data) {
11276 const guint8 *uri_aux = NULL;
11278 /* found the data atom that might contain the rtsp uri */
11279 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
11280 "hndl atom, interpreting it as an URI");
11281 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
11283 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
11284 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
11286 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
11287 "didn't contain a rtsp address");
11289 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
11294 /* skipping to the next entry */
11295 if (!gst_byte_reader_skip (&dref, atom_size - 8))
11298 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
11305 /* skip to the next entry */
11306 if (!gst_byte_reader_skip (&dref, size - 8))
11309 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
11312 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
11318 #define AMR_NB_ALL_MODES 0x81ff
11319 #define AMR_WB_ALL_MODES 0x83ff
11321 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
11323 /* The 'damr' atom is of the form:
11325 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
11326 * 32 b 8 b 16 b 8 b 8 b
11328 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
11329 * represents the highest mode used in the stream (and thus the maximum
11330 * bitrate), with a couple of special cases as seen below.
11333 /* Map of frame type ID -> bitrate */
11334 static const guint nb_bitrates[] = {
11335 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
11337 static const guint wb_bitrates[] = {
11338 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
11344 gst_buffer_map (buf, &map, GST_MAP_READ);
11346 if (map.size != 0x11) {
11347 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
11351 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
11352 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
11353 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
11357 mode_set = QT_UINT16 (map.data + 13);
11359 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
11360 max_mode = 7 + (wb ? 1 : 0);
11362 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
11363 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
11365 if (max_mode == -1) {
11366 GST_DEBUG ("No mode indication was found (mode set) = %x",
11371 gst_buffer_unmap (buf, &map);
11372 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
11375 gst_buffer_unmap (buf, &map);
11380 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
11381 GstByteReader * reader, guint32 * matrix, const gchar * atom)
11384 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
11390 if (gst_byte_reader_get_remaining (reader) < 36)
11393 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
11394 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
11395 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
11396 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
11397 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
11398 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
11399 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
11400 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
11401 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
11403 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
11404 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
11405 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
11407 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
11408 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
11410 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
11411 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
11418 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
11419 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
11426 * This macro will only compare value abdegh, it expects cfi to have already
11429 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
11430 (m)[3] == (d << 16) && (m)[4] == (e << 16))
11432 /* only handle the cases where the last column has standard values */
11433 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
11434 const gchar *rotation_tag = NULL;
11436 /* no rotation needed */
11437 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
11439 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
11440 rotation_tag = "rotate-90";
11441 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
11442 rotation_tag = "rotate-180";
11443 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
11444 rotation_tag = "rotate-270";
11446 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11449 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
11450 GST_STR_NULL (rotation_tag));
11451 if (rotation_tag != NULL) {
11452 if (*taglist == NULL)
11453 *taglist = gst_tag_list_new_empty ();
11454 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
11455 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
11458 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11463 qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
11464 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11468 GstBuffer *adrm_buf = NULL;
11469 QtDemuxAavdEncryptionInfo *info;
11471 adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
11472 if (G_UNLIKELY (!adrm)) {
11473 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
11476 adrm_size = QT_UINT32 (adrm->data);
11477 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
11479 stream->protection_scheme_type = FOURCC_aavd;
11481 if (!stream->protection_scheme_info)
11482 stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
11484 info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
11486 if (info->default_properties)
11487 gst_structure_free (info->default_properties);
11488 info->default_properties = gst_structure_new ("application/x-aavd",
11489 "encrypted", G_TYPE_BOOLEAN, TRUE,
11490 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
11491 gst_buffer_unref (adrm_buf);
11493 *original_fmt = FOURCC_mp4a;
11497 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
11498 * protected streams (sinf, frma, schm and schi); if the protection scheme is
11499 * Common Encryption (cenc), the function will also parse the tenc box (defined
11500 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
11501 * (typically an enc[v|a|t|s] sample entry); the function will set
11502 * @original_fmt to the fourcc of the original unencrypted stream format.
11503 * Returns TRUE if successful; FALSE otherwise. */
11505 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
11506 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11512 QtDemuxCencSampleSetInfo *info;
11514 const guint8 *tenc_data;
11516 g_return_val_if_fail (qtdemux != NULL, FALSE);
11517 g_return_val_if_fail (stream != NULL, FALSE);
11518 g_return_val_if_fail (container != NULL, FALSE);
11519 g_return_val_if_fail (original_fmt != NULL, FALSE);
11521 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
11522 if (G_UNLIKELY (!sinf)) {
11523 if (stream->protection_scheme_type == FOURCC_cenc
11524 || stream->protection_scheme_type == FOURCC_cbcs) {
11525 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
11526 "mandatory for Common Encryption");
11532 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
11533 if (G_UNLIKELY (!frma)) {
11534 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
11538 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
11539 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
11540 GST_FOURCC_ARGS (*original_fmt));
11542 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
11544 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
11547 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
11548 stream->protection_scheme_version =
11549 QT_UINT32 ((const guint8 *) schm->data + 16);
11551 GST_DEBUG_OBJECT (qtdemux,
11552 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
11553 "protection_scheme_version: %#010x",
11554 GST_FOURCC_ARGS (stream->protection_scheme_type),
11555 stream->protection_scheme_version);
11557 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
11559 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
11562 if (stream->protection_scheme_type != FOURCC_cenc &&
11563 stream->protection_scheme_type != FOURCC_piff &&
11564 stream->protection_scheme_type != FOURCC_cbcs) {
11565 GST_ERROR_OBJECT (qtdemux,
11566 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
11567 GST_FOURCC_ARGS (stream->protection_scheme_type));
11571 if (G_UNLIKELY (!stream->protection_scheme_info))
11572 stream->protection_scheme_info =
11573 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
11575 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
11577 if (stream->protection_scheme_type == FOURCC_cenc
11578 || stream->protection_scheme_type == FOURCC_cbcs) {
11579 guint8 is_encrypted;
11581 guint8 constant_iv_size = 0;
11582 const guint8 *default_kid;
11583 guint8 crypt_byte_block = 0;
11584 guint8 skip_byte_block = 0;
11585 const guint8 *constant_iv = NULL;
11587 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
11589 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11590 "which is mandatory for Common Encryption");
11593 tenc_data = (const guint8 *) tenc->data + 12;
11594 is_encrypted = QT_UINT8 (tenc_data + 2);
11595 iv_size = QT_UINT8 (tenc_data + 3);
11596 default_kid = (tenc_data + 4);
11597 if (stream->protection_scheme_type == FOURCC_cbcs) {
11598 guint8 possible_pattern_info;
11599 if (iv_size == 0) {
11600 constant_iv_size = QT_UINT8 (tenc_data + 20);
11601 if (constant_iv_size != 8 && constant_iv_size != 16) {
11602 GST_ERROR_OBJECT (qtdemux,
11603 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
11606 constant_iv = (tenc_data + 21);
11608 possible_pattern_info = QT_UINT8 (tenc_data + 1);
11609 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
11610 skip_byte_block = possible_pattern_info & 0x0f;
11612 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
11613 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
11614 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
11615 } else if (stream->protection_scheme_type == FOURCC_piff) {
11617 static const guint8 piff_track_encryption_uuid[] = {
11618 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
11619 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
11622 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
11624 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11625 "which is mandatory for Common Encryption");
11629 tenc_data = (const guint8 *) tenc->data + 8;
11630 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
11631 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
11632 GST_ERROR_OBJECT (qtdemux,
11633 "Unsupported track encryption box with uuid: %s", box_uuid);
11637 tenc_data = (const guint8 *) tenc->data + 16 + 12;
11638 gst_byte_reader_init (&br, tenc_data, 20);
11639 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
11640 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
11643 stream->protection_scheme_type = FOURCC_cenc;
11650 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
11651 QtDemuxStream ** stream2)
11653 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
11657 qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
11662 /*parse svmi header if existing */
11663 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11665 guint32 len = QT_UINT32 ((guint8 *) svmi->data);
11666 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11668 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11669 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11670 guint8 frame_type, frame_layout;
11671 guint32 stereo_mono_change_count;
11676 /* MPEG-A stereo video */
11677 if (qtdemux->major_brand == FOURCC_ss02)
11678 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11680 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11681 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11682 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
11684 switch (frame_type) {
11686 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11689 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11692 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11695 /* mode 3 is primary/secondary view sequence, ie
11696 * left/right views in separate tracks. See section 7.2
11697 * of ISO/IEC 23000-11:2009 */
11698 /* In the future this might be supported using related
11699 * streams, like an enhancement track - if files like this
11701 GST_FIXME_OBJECT (qtdemux,
11702 "Implement stereo video in separate streams");
11705 if ((frame_layout & 0x1) == 0)
11706 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11708 GST_LOG_OBJECT (qtdemux,
11709 "StereoVideo: composition type: %u, is_left_first: %u",
11710 frame_type, frame_layout);
11712 if (stereo_mono_change_count > 1) {
11713 GST_FIXME_OBJECT (qtdemux,
11714 "Mixed-mono flags are not yet supported in qtdemux.");
11717 stream->multiview_mode = mode;
11718 stream->multiview_flags = flags;
11725 /* parse the traks.
11726 * With each track we associate a new QtDemuxStream that contains all the info
11728 * traks that do not decode to something (like strm traks) will not have a pad.
11731 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
11733 GstByteReader tkhd;
11747 QtDemuxStream *stream = NULL;
11748 const guint8 *stsd_data;
11749 const guint8 *stsd_entry_data;
11750 guint remaining_stsd_len;
11751 guint stsd_entry_count;
11753 guint16 lang_code; /* quicktime lang code or packed iso code */
11755 guint32 tkhd_flags = 0;
11756 guint8 tkhd_version = 0;
11757 guint32 w = 0, h = 0;
11758 guint value_size, stsd_len, len;
11762 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11764 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11765 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11766 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11769 /* pick between 64 or 32 bits */
11770 value_size = tkhd_version == 1 ? 8 : 4;
11771 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11772 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11775 /* Check if current moov has duplicated track_id */
11776 if (qtdemux_find_stream (qtdemux, track_id))
11777 goto existing_stream;
11779 stream = _create_stream (qtdemux, track_id);
11780 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11782 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11783 if (!gst_byte_reader_skip (&tkhd, 4))
11786 if (tkhd_version == 1) {
11787 if (!gst_byte_reader_get_uint64_be (&tkhd, &stream->tkhd_duration))
11791 if (!gst_byte_reader_get_uint32_be (&tkhd, &dur))
11793 stream->tkhd_duration = dur;
11795 GST_INFO_OBJECT (qtdemux, "tkhd duration: %" G_GUINT64_FORMAT,
11796 stream->tkhd_duration);
11798 /* need defaults for fragments */
11799 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11801 if ((tkhd_flags & 1) == 0)
11802 stream->disabled = TRUE;
11804 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11805 tkhd_version, tkhd_flags, stream->track_id);
11807 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11810 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11811 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11812 if (qtdemux->major_brand != FOURCC_mjp2 ||
11813 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11817 len = QT_UINT32 ((guint8 *) mdhd->data);
11818 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11819 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11820 if (version == 0x01000000) {
11823 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11824 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11825 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11829 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11830 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11831 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11834 if (lang_code < 0x400) {
11835 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11836 } else if (lang_code == 0x7fff) {
11837 stream->lang_id[0] = 0; /* unspecified */
11839 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11840 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11841 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11842 stream->lang_id[3] = 0;
11845 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11846 stream->timescale);
11847 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11849 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11850 lang_code, stream->lang_id);
11852 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11855 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11856 /* chapters track reference */
11857 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11859 gsize length = GST_READ_UINT32_BE (chap->data);
11860 if (qtdemux->chapters_track_id)
11861 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11863 if (length >= 12) {
11864 qtdemux->chapters_track_id =
11865 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11870 /* fragmented files may have bogus duration in moov */
11871 if (!qtdemux->fragmented &&
11872 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11873 guint64 tdur1, tdur2;
11875 /* don't overflow */
11876 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11877 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11880 * some of those trailers, nowadays, have prologue images that are
11881 * themselves video tracks as well. I haven't really found a way to
11882 * identify those yet, except for just looking at their duration. */
11883 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11884 GST_WARNING_OBJECT (qtdemux,
11885 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11886 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11887 "found, assuming preview image or something; skipping track",
11888 stream->duration, stream->timescale, qtdemux->duration,
11889 qtdemux->timescale);
11890 gst_qtdemux_stream_unref (stream);
11895 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11898 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11899 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11901 len = QT_UINT32 ((guint8 *) hdlr->data);
11903 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11904 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11905 GST_FOURCC_ARGS (stream->subtype));
11907 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11910 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11913 /* Parse out svmi (and later st3d/sv3d) atoms */
11914 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
11917 /* parse rest of tkhd */
11918 if (stream->subtype == FOURCC_vide) {
11921 /* version 1 uses some 64-bit ints */
11922 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11923 if (!gst_byte_reader_skip (&tkhd, 16))
11925 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11929 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11932 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11933 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11936 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11937 &stream->stream_tags);
11941 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11943 stsd_data = (const guint8 *) stsd->data;
11945 /* stsd should at least have one entry */
11946 stsd_len = QT_UINT32 (stsd_data);
11947 if (stsd_len < 24) {
11948 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11949 if (stream->subtype == FOURCC_vivo) {
11950 gst_qtdemux_stream_unref (stream);
11957 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11958 /* each stsd entry must contain at least 8 bytes */
11959 if (stream->stsd_entries_length == 0
11960 || stream->stsd_entries_length > stsd_len / 8) {
11961 stream->stsd_entries_length = 0;
11964 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11965 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11966 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11968 stsd_entry_data = stsd_data + 16;
11969 remaining_stsd_len = stsd_len - 16;
11970 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11972 gchar *codec = NULL;
11973 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11975 /* and that entry should fit within stsd */
11976 len = QT_UINT32 (stsd_entry_data);
11977 if (len > remaining_stsd_len)
11980 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11981 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11982 GST_FOURCC_ARGS (entry->fourcc));
11983 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11985 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11986 goto error_encrypted;
11988 if (fourcc == FOURCC_aavd) {
11989 if (stream->subtype != FOURCC_soun) {
11990 GST_ERROR_OBJECT (qtdemux,
11991 "Unexpeced stsd type 'aavd' outside 'soun' track");
11993 /* encrypted audio with sound sample description v0 */
11994 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11995 stream->protected = TRUE;
11996 if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
11997 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
12001 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
12002 /* FIXME this looks wrong, there might be multiple children
12003 * with the same type */
12004 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
12005 stream->protected = TRUE;
12006 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
12007 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
12010 if (stream->subtype == FOURCC_vide) {
12015 gint depth, palette_size, palette_count;
12016 guint32 *palette_data = NULL;
12018 entry->sampled = TRUE;
12020 stream->display_width = w >> 16;
12021 stream->display_height = h >> 16;
12024 if (len < 86) /* TODO verify */
12027 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
12028 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
12029 entry->fps_n = 0; /* this is filled in later */
12030 entry->fps_d = 0; /* this is filled in later */
12031 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
12032 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
12034 /* if color_table_id is 0, ctab atom must follow; however some files
12035 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
12036 * if color table is not present we'll correct the value */
12037 if (entry->color_table_id == 0 &&
12039 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
12040 entry->color_table_id = -1;
12043 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
12044 entry->width, entry->height, entry->bits_per_sample,
12045 entry->color_table_id);
12047 depth = entry->bits_per_sample;
12049 /* more than 32 bits means grayscale */
12050 gray = (depth > 32);
12051 /* low 32 bits specify the depth */
12054 /* different number of palette entries is determined by depth. */
12056 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
12057 palette_count = (1 << depth);
12058 palette_size = palette_count * 4;
12060 if (entry->color_table_id) {
12061 switch (palette_count) {
12065 palette_data = g_memdup2 (ff_qt_default_palette_2, palette_size);
12068 palette_data = g_memdup2 (ff_qt_default_palette_4, palette_size);
12073 g_memdup2 (ff_qt_grayscale_palette_16, palette_size);
12075 palette_data = g_memdup2 (ff_qt_default_palette_16, palette_size);
12080 g_memdup2 (ff_qt_grayscale_palette_256, palette_size);
12083 g_memdup2 (ff_qt_default_palette_256, palette_size);
12086 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
12087 (_("The video in this file might not play correctly.")),
12088 ("unsupported palette depth %d", depth));
12092 guint i, j, start, end;
12098 start = QT_UINT32 (stsd_entry_data + offset + 70);
12099 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
12100 end = QT_UINT16 (stsd_entry_data + offset + 76);
12102 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
12103 start, end, palette_count);
12110 if (len < 94 + (end - start) * 8)
12113 /* palette is always the same size */
12114 palette_data = g_malloc0 (256 * 4);
12115 palette_size = 256 * 4;
12117 for (j = 0, i = start; i <= end; j++, i++) {
12118 guint32 a, r, g, b;
12120 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
12121 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
12122 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
12123 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
12125 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
12126 (g & 0xff00) | (b >> 8);
12131 gst_caps_unref (entry->caps);
12134 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12136 if (G_UNLIKELY (!entry->caps)) {
12137 g_free (palette_data);
12138 goto unknown_stream;
12142 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12143 GST_TAG_VIDEO_CODEC, codec, NULL);
12148 if (palette_data) {
12151 if (entry->rgb8_palette)
12152 gst_memory_unref (entry->rgb8_palette);
12153 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
12154 palette_data, palette_size, 0, palette_size, palette_data, g_free);
12156 s = gst_caps_get_structure (entry->caps, 0);
12158 /* non-raw video has a palette_data property. raw video has the palette as
12159 * an extra plane that we append to the output buffers before we push
12161 if (!gst_structure_has_name (s, "video/x-raw")) {
12162 GstBuffer *palette;
12164 palette = gst_buffer_new ();
12165 gst_buffer_append_memory (palette, entry->rgb8_palette);
12166 entry->rgb8_palette = NULL;
12168 gst_caps_set_simple (entry->caps, "palette_data",
12169 GST_TYPE_BUFFER, palette, NULL);
12170 gst_buffer_unref (palette);
12172 } else if (palette_count != 0) {
12173 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
12174 (NULL), ("Unsupported palette depth %d", depth));
12177 GST_LOG_OBJECT (qtdemux, "frame count: %u",
12178 QT_UINT16 (stsd_entry_data + offset + 32));
12184 /* pick 'the' stsd child */
12185 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12186 // We should skip parsing the stsd for non-protected streams if
12187 // the entry doesn't match the fourcc, since they don't change
12188 // format. However, for protected streams we can have partial
12189 // encryption, where parts of the stream are encrypted and parts
12190 // not. For both parts of such streams, we should ensure the
12191 // esds overrides are parsed for both from the stsd.
12192 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
12193 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
12195 else if (!stream->protected)
12200 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
12201 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
12202 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
12203 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
12207 const guint8 *pasp_data = (const guint8 *) pasp->data;
12208 guint len = QT_UINT32 (pasp_data);
12211 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
12212 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
12214 CUR_STREAM (stream)->par_w = 0;
12215 CUR_STREAM (stream)->par_h = 0;
12218 CUR_STREAM (stream)->par_w = 0;
12219 CUR_STREAM (stream)->par_h = 0;
12223 const guint8 *fiel_data = (const guint8 *) fiel->data;
12224 guint len = QT_UINT32 (fiel_data);
12227 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
12228 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
12233 const guint8 *colr_data = (const guint8 *) colr->data;
12234 guint len = QT_UINT32 (colr_data);
12236 if (len == 19 || len == 18) {
12237 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
12239 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
12240 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
12241 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
12242 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
12243 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
12245 CUR_STREAM (stream)->colorimetry.primaries =
12246 gst_video_color_primaries_from_iso (primaries);
12247 CUR_STREAM (stream)->colorimetry.transfer =
12248 gst_video_transfer_function_from_iso (transfer_function);
12249 CUR_STREAM (stream)->colorimetry.matrix =
12250 gst_video_color_matrix_from_iso (matrix);
12251 CUR_STREAM (stream)->colorimetry.range =
12252 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
12253 GST_VIDEO_COLOR_RANGE_16_235;
12255 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
12258 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
12263 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12264 stream->stream_tags);
12271 guint len = QT_UINT32 (stsd_entry_data);
12272 len = len <= 0x56 ? 0 : len - 0x56;
12273 const guint8 *avc_data = stsd_entry_data + 0x56;
12276 while (len >= 0x8) {
12279 if (QT_UINT32 (avc_data) <= 0x8)
12281 else if (QT_UINT32 (avc_data) <= len)
12282 size = QT_UINT32 (avc_data) - 0x8;
12287 /* No real data, so break out */
12290 switch (QT_FOURCC (avc_data + 0x4)) {
12293 /* parse, if found */
12296 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
12298 /* First 4 bytes are the length of the atom, the next 4 bytes
12299 * are the fourcc, the next 1 byte is the version, and the
12300 * subsequent bytes are profile_tier_level structure like data. */
12301 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
12302 avc_data + 8 + 1, size - 1);
12303 buf = gst_buffer_new_and_alloc (size);
12304 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
12305 gst_caps_set_simple (entry->caps,
12306 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12307 gst_buffer_unref (buf);
12315 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
12317 /* First 4 bytes are the length of the atom, the next 4 bytes
12318 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
12319 * next 1 byte is the version, and the
12320 * subsequent bytes are sequence parameter set like data. */
12322 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
12324 gst_codec_utils_h264_caps_set_level_and_profile
12325 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
12327 buf = gst_buffer_new_and_alloc (size);
12328 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
12329 gst_caps_set_simple (entry->caps,
12330 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12331 gst_buffer_unref (buf);
12337 guint avg_bitrate, max_bitrate;
12339 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
12343 max_bitrate = QT_UINT32 (avc_data + 0xc);
12344 avg_bitrate = QT_UINT32 (avc_data + 0x10);
12346 if (!max_bitrate && !avg_bitrate)
12349 /* Some muxers seem to swap the average and maximum bitrates
12350 * (I'm looking at you, YouTube), so we swap for sanity. */
12351 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
12352 guint temp = avg_bitrate;
12354 avg_bitrate = max_bitrate;
12355 max_bitrate = temp;
12358 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12359 gst_tag_list_add (stream->stream_tags,
12360 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
12361 max_bitrate, NULL);
12363 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12364 gst_tag_list_add (stream->stream_tags,
12365 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
12377 avc_data += size + 8;
12388 guint len = QT_UINT32 (stsd_entry_data);
12389 len = len <= 0x56 ? 0 : len - 0x56;
12390 const guint8 *hevc_data = stsd_entry_data + 0x56;
12393 while (len >= 0x8) {
12396 if (QT_UINT32 (hevc_data) <= 0x8)
12398 else if (QT_UINT32 (hevc_data) <= len)
12399 size = QT_UINT32 (hevc_data) - 0x8;
12404 /* No real data, so break out */
12407 switch (QT_FOURCC (hevc_data + 0x4)) {
12410 /* parse, if found */
12413 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
12415 /* First 4 bytes are the length of the atom, the next 4 bytes
12416 * are the fourcc, the next 1 byte is the version, and the
12417 * subsequent bytes are sequence parameter set like data. */
12418 gst_codec_utils_h265_caps_set_level_tier_and_profile
12419 (entry->caps, hevc_data + 8 + 1, size - 1);
12421 buf = gst_buffer_new_and_alloc (size);
12422 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
12423 gst_caps_set_simple (entry->caps,
12424 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12425 gst_buffer_unref (buf);
12432 hevc_data += size + 8;
12445 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
12446 GST_FOURCC_ARGS (fourcc));
12448 /* codec data might be in glbl extension atom */
12450 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
12456 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
12458 len = QT_UINT32 (data);
12461 buf = gst_buffer_new_and_alloc (len);
12462 gst_buffer_fill (buf, 0, data + 8, len);
12463 gst_caps_set_simple (entry->caps,
12464 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12465 gst_buffer_unref (buf);
12472 /* see annex I of the jpeg2000 spec */
12473 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
12474 const guint8 *data;
12475 const gchar *colorspace = NULL;
12477 guint32 ncomp_map = 0;
12478 gint32 *comp_map = NULL;
12479 guint32 nchan_def = 0;
12480 gint32 *chan_def = NULL;
12482 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12483 /* some required atoms */
12484 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12487 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
12491 /* number of components; redundant with info in codestream, but useful
12493 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
12494 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
12496 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
12498 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
12501 GST_DEBUG_OBJECT (qtdemux, "found colr");
12502 /* extract colour space info */
12503 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12504 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
12506 colorspace = "sRGB";
12509 colorspace = "GRAY";
12512 colorspace = "sYUV";
12520 /* colr is required, and only values 16, 17, and 18 are specified,
12521 so error if we have no colorspace */
12524 /* extract component mapping */
12525 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
12527 guint32 cmap_len = 0;
12529 cmap_len = QT_UINT32 (cmap->data);
12530 if (cmap_len >= 8) {
12531 /* normal box, subtract off header */
12533 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12534 if (cmap_len % 4 == 0) {
12535 ncomp_map = (cmap_len / 4);
12536 comp_map = g_new0 (gint32, ncomp_map);
12537 for (i = 0; i < ncomp_map; i++) {
12540 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12541 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12542 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
12543 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12548 /* extract channel definitions */
12549 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
12551 guint32 cdef_len = 0;
12553 cdef_len = QT_UINT32 (cdef->data);
12554 if (cdef_len >= 10) {
12555 /* normal box, subtract off header and len */
12557 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12558 if (cdef_len % 6 == 0) {
12559 nchan_def = (cdef_len / 6);
12560 chan_def = g_new0 (gint32, nchan_def);
12561 for (i = 0; i < nchan_def; i++)
12563 for (i = 0; i < nchan_def; i++) {
12564 guint16 cn, typ, asoc;
12565 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12566 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12567 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
12568 if (cn < nchan_def) {
12571 chan_def[cn] = asoc;
12574 chan_def[cn] = 0; /* alpha */
12577 chan_def[cn] = -typ;
12585 gst_caps_set_simple (entry->caps,
12586 "num-components", G_TYPE_INT, ncomp, NULL);
12587 gst_caps_set_simple (entry->caps,
12588 "colorspace", G_TYPE_STRING, colorspace, NULL);
12591 GValue arr = { 0, };
12592 GValue elt = { 0, };
12594 g_value_init (&arr, GST_TYPE_ARRAY);
12595 g_value_init (&elt, G_TYPE_INT);
12596 for (i = 0; i < ncomp_map; i++) {
12597 g_value_set_int (&elt, comp_map[i]);
12598 gst_value_array_append_value (&arr, &elt);
12600 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12601 "component-map", &arr);
12602 g_value_unset (&elt);
12603 g_value_unset (&arr);
12608 GValue arr = { 0, };
12609 GValue elt = { 0, };
12611 g_value_init (&arr, GST_TYPE_ARRAY);
12612 g_value_init (&elt, G_TYPE_INT);
12613 for (i = 0; i < nchan_def; i++) {
12614 g_value_set_int (&elt, chan_def[i]);
12615 gst_value_array_append_value (&arr, &elt);
12617 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12618 "channel-definitions", &arr);
12619 g_value_unset (&elt);
12620 g_value_unset (&arr);
12624 /* some optional atoms */
12625 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
12626 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
12628 /* indicate possible fields in caps */
12630 data = (guint8 *) field->data + 8;
12632 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12633 (gint) * data, NULL);
12635 /* add codec_data if provided */
12640 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12641 data = prefix->data;
12642 len = QT_UINT32 (data);
12645 buf = gst_buffer_new_and_alloc (len);
12646 gst_buffer_fill (buf, 0, data + 8, len);
12647 gst_caps_set_simple (entry->caps,
12648 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12649 gst_buffer_unref (buf);
12658 GstBuffer *seqh = NULL;
12659 const guint8 *gamma_data = NULL;
12660 guint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
12662 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
12665 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
12666 QT_FP32 (gamma_data), NULL);
12669 /* sorry for the bad name, but we don't know what this is, other
12670 * than its own fourcc */
12671 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12673 gst_buffer_unref (seqh);
12676 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12677 buf = gst_buffer_new_and_alloc (len);
12678 gst_buffer_fill (buf, 0, stsd_data, len);
12679 gst_caps_set_simple (entry->caps,
12680 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12681 gst_buffer_unref (buf);
12686 /* https://developer.apple.com/standards/qtff-2001.pdf,
12687 * page 92, "Video Sample Description", under table 3.1 */
12690 const gint compressor_offset =
12691 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12692 const gint min_size = compressor_offset + 32 + 2 + 2;
12695 guint16 color_table_id = 0;
12698 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12700 /* recover information on interlaced/progressive */
12701 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
12705 len = QT_UINT32 (jpeg->data);
12706 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12708 if (len >= min_size) {
12709 gst_byte_reader_init (&br, jpeg->data, len);
12711 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12712 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12713 if (color_table_id != 0) {
12714 /* the spec says there can be concatenated chunks in the data, and we want
12715 * to find one called field. Walk through them. */
12716 gint offset = min_size;
12717 while (offset + 8 < len) {
12718 guint32 size = 0, tag;
12719 ok = gst_byte_reader_get_uint32_le (&br, &size);
12720 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12721 if (!ok || size < 8) {
12722 GST_WARNING_OBJECT (qtdemux,
12723 "Failed to walk optional chunk list");
12726 GST_DEBUG_OBJECT (qtdemux,
12727 "Found optional %4.4s chunk, size %u",
12728 (const char *) &tag, size);
12729 if (tag == FOURCC_fiel) {
12730 guint8 n_fields = 0, ordering = 0;
12731 gst_byte_reader_get_uint8 (&br, &n_fields);
12732 gst_byte_reader_get_uint8 (&br, &ordering);
12733 if (n_fields == 1 || n_fields == 2) {
12734 GST_DEBUG_OBJECT (qtdemux,
12735 "Found fiel tag with %u fields, ordering %u",
12736 n_fields, ordering);
12738 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12739 "interlace-mode", G_TYPE_STRING, "interleaved",
12742 GST_WARNING_OBJECT (qtdemux,
12743 "Found fiel tag with invalid fields (%u)", n_fields);
12749 GST_DEBUG_OBJECT (qtdemux,
12750 "Color table ID is 0, not trying to get interlacedness");
12753 GST_WARNING_OBJECT (qtdemux,
12754 "Length of jpeg chunk is too small, not trying to get interlacedness");
12762 gst_caps_set_simple (entry->caps,
12763 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12769 GNode *xith, *xdxt;
12771 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12772 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12776 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12780 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12781 /* collect the headers and store them in a stream list so that we can
12782 * send them out first */
12783 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12793 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12794 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12797 ovc1_data = ovc1->data;
12798 ovc1_len = QT_UINT32 (ovc1_data);
12799 if (ovc1_len <= 198) {
12800 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12803 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12804 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12805 gst_caps_set_simple (entry->caps,
12806 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12807 gst_buffer_unref (buf);
12812 guint len = QT_UINT32 (stsd_entry_data);
12813 len = len <= 0x56 ? 0 : len - 0x56;
12814 const guint8 *vc1_data = stsd_entry_data + 0x56;
12820 if (QT_UINT32 (vc1_data) <= 8)
12822 else if (QT_UINT32 (vc1_data) <= len)
12823 size = QT_UINT32 (vc1_data) - 8;
12828 /* No real data, so break out */
12831 switch (QT_FOURCC (vc1_data + 0x4)) {
12832 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12836 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12837 buf = gst_buffer_new_and_alloc (size);
12838 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12839 gst_caps_set_simple (entry->caps,
12840 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12841 gst_buffer_unref (buf);
12848 vc1_data += size + 8;
12854 guint len = QT_UINT32 (stsd_entry_data);
12855 len = len <= 0x56 ? 0 : len - 0x56;
12856 const guint8 *av1_data = stsd_entry_data + 0x56;
12859 while (len >= 0x8) {
12862 if (QT_UINT32 (av1_data) <= 0x8)
12864 else if (QT_UINT32 (av1_data) <= len)
12865 size = QT_UINT32 (av1_data) - 0x8;
12870 /* No real data, so break out */
12873 switch (QT_FOURCC (av1_data + 0x4)) {
12876 /* parse, if found */
12879 GST_DEBUG_OBJECT (qtdemux,
12880 "found av1C codec_data in stsd of size %d", size);
12882 /* not enough data, just ignore and hope for the best */
12887 * 4 bytes: atom length
12890 * version 1 (marker=1):
12892 * unsigned int (1) marker = 1;
12893 * unsigned int (7) version = 1;
12894 * unsigned int (3) seq_profile;
12895 * unsigned int (5) seq_level_idx_0;
12896 * unsigned int (1) seq_tier_0;
12897 * unsigned int (1) high_bitdepth;
12898 * unsigned int (1) twelve_bit;
12899 * unsigned int (1) monochrome;
12900 * unsigned int (1) chroma_subsampling_x;
12901 * unsigned int (1) chroma_subsampling_y;
12902 * unsigned int (2) chroma_sample_position;
12903 * unsigned int (3) reserved = 0;
12905 * unsigned int (1) initial_presentation_delay_present;
12906 * if (initial_presentation_delay_present) {
12907 * unsigned int (4) initial_presentation_delay_minus_one;
12909 * unsigned int (4) reserved = 0;
12912 * unsigned int (8) configOBUs[];
12917 switch (av1_data[8]) {
12919 guint8 pres_delay_field;
12921 /* We let profile and the other parts be figured out by
12922 * av1parse and only include the presentation delay here
12924 /* We skip initial_presentation_delay* for now */
12925 pres_delay_field = *(av1_data + 11);
12926 if (pres_delay_field & (1 << 5)) {
12927 gst_caps_set_simple (entry->caps,
12928 "presentation-delay", G_TYPE_INT,
12929 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12932 buf = gst_buffer_new_and_alloc (size);
12933 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12934 gst_buffer_fill (buf, 0, av1_data + 8, size);
12935 gst_caps_set_simple (entry->caps,
12936 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12937 gst_buffer_unref (buf);
12941 GST_WARNING ("Unknown version 0x%02x of av1C box",
12953 av1_data += size + 8;
12959 /* TODO: Need to parse vpcC for VP8 codec too.
12960 * Note that VPCodecConfigurationBox (vpcC) is defined for
12961 * vp08, vp09, and vp10 fourcc. */
12964 guint len = QT_UINT32 (stsd_entry_data);
12965 len = len <= 0x56 ? 0 : len - 0x56;
12966 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12969 while (len >= 0x8) {
12972 if (QT_UINT32 (vpcc_data) <= 0x8)
12974 else if (QT_UINT32 (vpcc_data) <= len)
12975 size = QT_UINT32 (vpcc_data) - 0x8;
12980 /* No real data, so break out */
12983 switch (QT_FOURCC (vpcc_data + 0x4)) {
12986 const gchar *profile_str = NULL;
12987 const gchar *chroma_format_str = NULL;
12990 guint8 chroma_format;
12991 GstVideoColorimetry cinfo;
12993 /* parse, if found */
12994 GST_DEBUG_OBJECT (qtdemux,
12995 "found vp codec_data in stsd of size %d", size);
12997 /* the meaning of "size" is length of the atom body, excluding
12998 * atom length and fourcc fields */
13003 * 4 bytes: atom length
13010 * 3 bits: chromaSubsampling
13011 * 1 bit: videoFullRangeFlag
13012 * 1 byte: colourPrimaries
13013 * 1 byte: transferCharacteristics
13014 * 1 byte: matrixCoefficients
13015 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
13016 * rest: codecIntializationData (not used for vp8 and vp9)
13019 if (vpcc_data[8] != 1) {
13020 GST_WARNING_OBJECT (qtdemux,
13021 "unknown vpcC version %d", vpcc_data[8]);
13025 profile = vpcc_data[12];
13044 gst_caps_set_simple (entry->caps,
13045 "profile", G_TYPE_STRING, profile_str, NULL);
13048 /* skip level, the VP9 spec v0.6 defines only one level atm,
13049 * but webm spec define various ones. Add level to caps
13050 * if we really need it then */
13052 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
13053 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
13054 gst_caps_set_simple (entry->caps,
13055 "bit-depth-luma", G_TYPE_UINT, bitdepth,
13056 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
13059 chroma_format = (vpcc_data[14] & 0xe) >> 1;
13060 switch (chroma_format) {
13063 chroma_format_str = "4:2:0";
13066 chroma_format_str = "4:2:2";
13069 chroma_format_str = "4:4:4";
13075 if (chroma_format_str) {
13076 gst_caps_set_simple (entry->caps,
13077 "chroma-format", G_TYPE_STRING, chroma_format_str,
13081 if ((vpcc_data[14] & 0x1) != 0)
13082 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
13084 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
13086 gst_video_color_primaries_from_iso (vpcc_data[15]);
13088 gst_video_transfer_function_from_iso (vpcc_data[16]);
13090 gst_video_color_matrix_from_iso (vpcc_data[17]);
13092 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
13093 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
13094 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
13095 /* set this only if all values are known, otherwise this
13096 * might overwrite valid ones parsed from other color box */
13097 CUR_STREAM (stream)->colorimetry = cinfo;
13106 vpcc_data += size + 8;
13116 GST_INFO_OBJECT (qtdemux,
13117 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13118 GST_FOURCC_ARGS (fourcc), entry->caps);
13120 } else if (stream->subtype == FOURCC_soun) {
13122 guint version, samplesize;
13123 guint16 compression_id;
13124 gboolean amrwb = FALSE;
13127 /* sample description entry (16) + sound sample description v0 (20) */
13131 version = QT_UINT32 (stsd_entry_data + offset);
13132 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
13133 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
13134 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
13135 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
13137 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
13138 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
13139 QT_UINT32 (stsd_entry_data + offset + 4));
13140 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13141 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
13142 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
13143 GST_LOG_OBJECT (qtdemux, "packet size: %d",
13144 QT_UINT16 (stsd_entry_data + offset + 14));
13145 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13147 if (compression_id == 0xfffe)
13148 entry->sampled = TRUE;
13150 /* first assume uncompressed audio */
13151 entry->bytes_per_sample = samplesize / 8;
13152 entry->samples_per_frame = entry->n_channels;
13153 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13154 entry->samples_per_packet = entry->samples_per_frame;
13155 entry->bytes_per_packet = entry->bytes_per_sample;
13159 if (version == 0x00010000) {
13160 /* sample description entry (16) + sound sample description v1 (20+16) */
13164 /* take information from here over the normal sample description */
13165 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
13166 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
13167 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
13168 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
13170 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
13171 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
13172 entry->samples_per_packet);
13173 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13174 entry->bytes_per_packet);
13175 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
13176 entry->bytes_per_frame);
13177 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
13178 entry->bytes_per_sample);
13180 if (!entry->sampled && entry->bytes_per_packet) {
13181 entry->samples_per_frame = (entry->bytes_per_frame /
13182 entry->bytes_per_packet) * entry->samples_per_packet;
13183 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
13184 entry->samples_per_frame);
13186 } else if (version == 0x00020000) {
13187 /* sample description entry (16) + sound sample description v2 (56) */
13191 /* take information from here over the normal sample description */
13192 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
13193 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
13194 entry->samples_per_frame = entry->n_channels;
13195 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
13196 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
13197 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
13198 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
13200 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
13201 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13202 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13203 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
13204 entry->bytes_per_sample * 8);
13205 GST_LOG_OBJECT (qtdemux, "format flags: %X",
13206 QT_UINT32 (stsd_entry_data + offset + 24));
13207 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13208 entry->bytes_per_packet);
13209 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
13210 entry->samples_per_packet);
13211 } else if (version != 0x00000) {
13212 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
13217 /* Yes, these have to be hard-coded */
13220 entry->samples_per_packet = 6;
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 = 6 * entry->n_channels;
13229 entry->samples_per_packet = 3;
13230 entry->bytes_per_packet = 1;
13231 entry->bytes_per_frame = 1 * entry->n_channels;
13232 entry->bytes_per_sample = 1;
13233 entry->samples_per_frame = 3 * entry->n_channels;
13238 entry->samples_per_packet = 64;
13239 entry->bytes_per_packet = 34;
13240 entry->bytes_per_frame = 34 * entry->n_channels;
13241 entry->bytes_per_sample = 2;
13242 entry->samples_per_frame = 64 * entry->n_channels;
13248 entry->samples_per_packet = 1;
13249 entry->bytes_per_packet = 1;
13250 entry->bytes_per_frame = 1 * entry->n_channels;
13251 entry->bytes_per_sample = 1;
13252 entry->samples_per_frame = 1 * entry->n_channels;
13257 entry->samples_per_packet = 160;
13258 entry->bytes_per_packet = 33;
13259 entry->bytes_per_frame = 33 * entry->n_channels;
13260 entry->bytes_per_sample = 2;
13261 entry->samples_per_frame = 160 * entry->n_channels;
13264 /* fix up any invalid header information from above */
13269 /* Sometimes these are set to 0 in the sound sample descriptions so
13270 * let's try to infer useful values from the other information we
13271 * have available */
13272 if (entry->bytes_per_sample == 0)
13273 entry->bytes_per_sample =
13274 entry->bytes_per_frame / entry->n_channels;
13275 if (entry->bytes_per_sample == 0)
13276 entry->bytes_per_sample = samplesize / 8;
13278 if (entry->bytes_per_frame == 0)
13279 entry->bytes_per_frame =
13280 entry->bytes_per_sample * entry->n_channels;
13282 if (entry->bytes_per_packet == 0)
13283 entry->bytes_per_packet = entry->bytes_per_sample;
13285 if (entry->samples_per_frame == 0)
13286 entry->samples_per_frame = entry->n_channels;
13288 if (entry->samples_per_packet == 0)
13289 entry->samples_per_packet = entry->samples_per_frame;
13299 entry->bytes_per_sample = 3;
13303 entry->bytes_per_sample = 4;
13306 entry->bytes_per_sample = 8;
13309 entry->bytes_per_sample = 2;
13312 g_assert_not_reached ();
13315 entry->samples_per_frame = entry->n_channels;
13316 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13317 entry->samples_per_packet = entry->samples_per_frame;
13318 entry->bytes_per_packet = entry->bytes_per_sample;
13322 /* According to TS 102 366, the channel count in
13323 * a (E)AC3SampleEntry box is to be ignored */
13325 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
13326 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
13328 entry->n_channels = 0;
13336 gst_caps_unref (entry->caps);
13338 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
13339 stsd_entry_data + 32, len - 16, &codec);
13350 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
13352 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
13354 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
13356 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
13359 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
13360 const gchar *format_str;
13364 format_str = (enda_value) ? "S24LE" : "S24BE";
13367 format_str = (enda_value) ? "S32LE" : "S32BE";
13370 format_str = (enda_value) ? "F32LE" : "F32BE";
13373 format_str = (enda_value) ? "F64LE" : "F64BE";
13376 g_assert_not_reached ();
13379 gst_caps_set_simple (entry->caps,
13380 "format", G_TYPE_STRING, format_str, NULL);
13386 const guint8 *owma_data;
13387 const gchar *codec_name = NULL;
13391 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13392 /* FIXME this should also be gst_riff_strf_auds,
13393 * but the latter one is actually missing bits-per-sample :( */
13398 gint32 nSamplesPerSec;
13399 gint32 nAvgBytesPerSec;
13400 gint16 nBlockAlign;
13401 gint16 wBitsPerSample;
13404 WAVEFORMATEX *wfex;
13406 GST_DEBUG_OBJECT (qtdemux, "parse owma");
13407 owma_data = stsd_entry_data;
13408 owma_len = QT_UINT32 (owma_data);
13409 if (owma_len <= 54) {
13410 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
13413 wfex = (WAVEFORMATEX *) (owma_data + 36);
13414 buf = gst_buffer_new_and_alloc (owma_len - 54);
13415 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
13416 if (wfex->wFormatTag == 0x0161) {
13417 codec_name = "Windows Media Audio";
13419 } else if (wfex->wFormatTag == 0x0162) {
13420 codec_name = "Windows Media Audio 9 Pro";
13422 } else if (wfex->wFormatTag == 0x0163) {
13423 codec_name = "Windows Media Audio 9 Lossless";
13424 /* is that correct? gstffmpegcodecmap.c is missing it, but
13425 * fluendo codec seems to support it */
13429 gst_caps_set_simple (entry->caps,
13430 "codec_data", GST_TYPE_BUFFER, buf,
13431 "wmaversion", G_TYPE_INT, version,
13432 "block_align", G_TYPE_INT,
13433 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
13434 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13435 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13436 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13437 gst_buffer_unref (buf);
13441 codec = g_strdup (codec_name);
13447 guint len = QT_UINT32 (stsd_entry_data);
13448 len = len <= offset ? 0 : len - offset;
13449 const guint8 *wfex_data = stsd_entry_data + offset;
13450 const gchar *codec_name = NULL;
13452 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13453 /* FIXME this should also be gst_riff_strf_auds,
13454 * but the latter one is actually missing bits-per-sample :( */
13459 gint32 nSamplesPerSec;
13460 gint32 nAvgBytesPerSec;
13461 gint16 nBlockAlign;
13462 gint16 wBitsPerSample;
13467 /* FIXME: unify with similar wavformatex parsing code above */
13468 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13474 if (QT_UINT32 (wfex_data) <= 0x8)
13476 else if (QT_UINT32 (wfex_data) <= len)
13477 size = QT_UINT32 (wfex_data) - 8;
13482 /* No real data, so break out */
13485 switch (QT_FOURCC (wfex_data + 4)) {
13486 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13488 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13493 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13494 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13495 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13496 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13497 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13498 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13499 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13501 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13502 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13503 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13504 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13505 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13506 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13508 if (wfex.wFormatTag == 0x0161) {
13509 codec_name = "Windows Media Audio";
13511 } else if (wfex.wFormatTag == 0x0162) {
13512 codec_name = "Windows Media Audio 9 Pro";
13514 } else if (wfex.wFormatTag == 0x0163) {
13515 codec_name = "Windows Media Audio 9 Lossless";
13516 /* is that correct? gstffmpegcodecmap.c is missing it, but
13517 * fluendo codec seems to support it */
13521 gst_caps_set_simple (entry->caps,
13522 "wmaversion", G_TYPE_INT, version,
13523 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13524 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13525 "width", G_TYPE_INT, wfex.wBitsPerSample,
13526 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13528 if (size > wfex.cbSize) {
13531 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13532 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13533 size - wfex.cbSize);
13534 gst_caps_set_simple (entry->caps,
13535 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13536 gst_buffer_unref (buf);
13538 GST_WARNING_OBJECT (qtdemux, "no codec data");
13543 codec = g_strdup (codec_name);
13551 wfex_data += size + 8;
13557 guint8 *channel_mapping = NULL;
13558 guint32 dops_len, rate;
13560 guint8 channel_mapping_family;
13561 guint8 stream_count;
13562 guint8 coupled_count;
13568 opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
13569 if (opus == NULL) {
13570 GST_WARNING_OBJECT (qtdemux, "Opus Sample Entry not found");
13574 dops = qtdemux_tree_get_child_by_type (opus, FOURCC_dops);
13575 if (dops == NULL) {
13576 GST_WARNING_OBJECT (qtdemux, "Opus Specific Box not found");
13580 /* Opus Specific Box content:
13584 * 1 byte: OutputChannelCount;
13585 * 2 bytes: PreSkip (big-endians);
13586 * 4 bytes: InputSampleRate (big-endians);
13587 * 2 bytes: OutputGain (big-endians);
13588 * 1 byte: ChannelMappingFamily;
13589 * if (ChannelMappingFamily != 0) {
13590 * 1 byte: StreamCount;
13591 * 1 byte: CoupledCount;
13592 * for (OutputChannel in 0..OutputChannelCount) {
13593 * 1 byte: ChannelMapping;
13598 dops_len = QT_UINT32 ((guint8 *) dops->data);
13599 if (len < offset + dops_len) {
13600 GST_WARNING_OBJECT (qtdemux,
13601 "Opus Sample Entry has bogus size %" G_GUINT32_FORMAT, len);
13604 if (dops_len < 19) {
13605 GST_WARNING_OBJECT (qtdemux,
13606 "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
13611 n_channels = GST_READ_UINT8 ((guint8 *) dops->data + 9);
13612 rate = GST_READ_UINT32_BE ((guint8 *) dops->data + 12);
13613 channel_mapping_family = GST_READ_UINT8 ((guint8 *) dops->data + 18);
13615 if (channel_mapping_family != 0) {
13616 if (dops_len < 21 + n_channels) {
13617 GST_WARNING_OBJECT (qtdemux,
13618 "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
13623 stream_count = GST_READ_UINT8 ((guint8 *) dops->data + 19);
13624 coupled_count = GST_READ_UINT8 ((guint8 *) dops->data + 20);
13626 if (n_channels > 0) {
13627 channel_mapping = g_malloc (n_channels * sizeof (guint8));
13628 for (i = 0; i < n_channels; i++)
13629 channel_mapping[i] =
13630 GST_READ_UINT8 ((guint8 *) dops->data + i + 21);
13632 } else if (n_channels == 1) {
13635 } else if (n_channels == 2) {
13639 GST_WARNING_OBJECT (qtdemux,
13640 "Opus unexpected nb of channels %d without channel mapping",
13645 entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
13646 channel_mapping_family, stream_count, coupled_count,
13648 g_free (channel_mapping);
13650 entry->sampled = TRUE;
13662 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13663 GST_TAG_AUDIO_CODEC, codec, NULL);
13667 /* some bitrate info may have ended up in caps */
13668 s = gst_caps_get_structure (entry->caps, 0);
13669 gst_structure_get_int (s, "bitrate", &bitrate);
13671 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13672 GST_TAG_BITRATE, bitrate, NULL);
13676 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
13677 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
13678 if (stream->protected) {
13679 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
13680 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13682 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
13692 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
13694 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
13696 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13700 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13701 16 bits is a byte-swapped wave-style codec identifier,
13702 and we can find a WAVE header internally to a 'wave' atom here.
13703 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13704 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13707 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13708 if (len < offset + 20) {
13709 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13711 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
13712 const guint8 *data = stsd_entry_data + offset + 16;
13714 GNode *waveheadernode;
13716 wavenode = g_node_new ((guint8 *) data);
13717 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
13718 const guint8 *waveheader;
13721 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
13722 if (waveheadernode) {
13723 waveheader = (const guint8 *) waveheadernode->data;
13724 headerlen = QT_UINT32 (waveheader);
13726 if (headerlen > 8) {
13727 gst_riff_strf_auds *header = NULL;
13728 GstBuffer *headerbuf;
13734 headerbuf = gst_buffer_new_and_alloc (headerlen);
13735 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13737 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13738 headerbuf, &header, &extra)) {
13739 gst_caps_unref (entry->caps);
13740 /* FIXME: Need to do something with the channel reorder map */
13742 gst_riff_create_audio_caps (header->format, NULL, header,
13743 extra, NULL, NULL, NULL);
13746 gst_buffer_unref (extra);
13751 GST_DEBUG ("Didn't find waveheadernode for this codec");
13753 g_node_destroy (wavenode);
13756 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13757 stream->stream_tags);
13761 /* FIXME: what is in the chunk? */
13764 gint len = QT_UINT32 (stsd_data);
13766 /* seems to be always = 116 = 0x74 */
13772 gint len = QT_UINT32 (stsd_entry_data);
13775 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13777 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13778 gst_caps_set_simple (entry->caps,
13779 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13780 gst_buffer_unref (buf);
13782 gst_caps_set_simple (entry->caps,
13783 "samplesize", G_TYPE_INT, samplesize, NULL);
13788 GNode *alac, *wave = NULL;
13790 /* apparently, m4a has this atom appended directly in the stsd entry,
13791 * while mov has it in a wave atom */
13792 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
13794 /* alac now refers to stsd entry atom */
13795 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
13797 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
13799 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
13802 const guint8 *alac_data = alac->data;
13803 gint len = QT_UINT32 (alac->data);
13807 GST_DEBUG_OBJECT (qtdemux,
13808 "discarding alac atom with unexpected len %d", len);
13810 /* codec-data contains alac atom size and prefix,
13811 * ffmpeg likes it that way, not quite gst-ish though ...*/
13812 buf = gst_buffer_new_and_alloc (len);
13813 gst_buffer_fill (buf, 0, alac->data, len);
13814 gst_caps_set_simple (entry->caps,
13815 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13816 gst_buffer_unref (buf);
13818 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
13819 entry->n_channels = QT_UINT8 (alac_data + 21);
13820 entry->rate = QT_UINT32 (alac_data + 32);
13821 samplesize = QT_UINT8 (alac_data + 16 + 1);
13824 gst_caps_set_simple (entry->caps,
13825 "samplesize", G_TYPE_INT, samplesize, NULL);
13830 /* The codingname of the sample entry is 'fLaC' */
13831 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
13834 /* The 'dfLa' box is added to the sample entry to convey
13835 initializing information for the decoder. */
13836 const GNode *dfla =
13837 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
13840 const guint32 len = QT_UINT32 (dfla->data);
13842 /* Must contain at least dfLa box header (12),
13843 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13845 GST_DEBUG_OBJECT (qtdemux,
13846 "discarding dfla atom with unexpected len %d", len);
13848 /* skip dfLa header to get the METADATA_BLOCKs */
13849 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13850 const guint32 metadata_blocks_len = len - 12;
13852 gchar *stream_marker = g_strdup ("fLaC");
13853 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13854 strlen (stream_marker));
13857 guint32 remainder = 0;
13858 guint32 block_size = 0;
13859 gboolean is_last = FALSE;
13861 GValue array = G_VALUE_INIT;
13862 GValue value = G_VALUE_INIT;
13864 g_value_init (&array, GST_TYPE_ARRAY);
13865 g_value_init (&value, GST_TYPE_BUFFER);
13867 gst_value_set_buffer (&value, block);
13868 gst_value_array_append_value (&array, &value);
13869 g_value_reset (&value);
13871 gst_buffer_unref (block);
13873 /* check there's at least one METADATA_BLOCK_HEADER's worth
13874 * of data, and we haven't already finished parsing */
13875 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13876 remainder = metadata_blocks_len - index;
13878 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13880 (metadata_blocks[index + 1] << 16) +
13881 (metadata_blocks[index + 2] << 8) +
13882 metadata_blocks[index + 3];
13884 /* be careful not to read off end of box */
13885 if (block_size > remainder) {
13889 is_last = metadata_blocks[index] >> 7;
13891 block = gst_buffer_new_and_alloc (block_size);
13893 gst_buffer_fill (block, 0, &metadata_blocks[index],
13896 gst_value_set_buffer (&value, block);
13897 gst_value_array_append_value (&array, &value);
13898 g_value_reset (&value);
13900 gst_buffer_unref (block);
13902 index += block_size;
13905 /* only append the metadata if we successfully read all of it */
13907 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
13908 (stream)->caps, 0), "streamheader", &array);
13910 GST_WARNING_OBJECT (qtdemux,
13911 "discarding all METADATA_BLOCKs due to invalid "
13912 "block_size %d at idx %d, rem %d", block_size, index,
13916 g_value_unset (&value);
13917 g_value_unset (&array);
13919 /* The sample rate obtained from the stsd may not be accurate
13920 * since it cannot represent rates greater than 65535Hz, so
13921 * override that value with the sample rate from the
13922 * METADATA_BLOCK_STREAMINFO block */
13923 CUR_STREAM (stream)->rate =
13924 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
13935 gint len = QT_UINT32 (stsd_entry_data);
13938 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13941 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13943 /* If we have enough data, let's try to get the 'damr' atom. See
13944 * the 3GPP container spec (26.244) for more details. */
13945 if ((len - 0x34) > 8 &&
13946 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
13947 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13948 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13951 gst_caps_set_simple (entry->caps,
13952 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13953 gst_buffer_unref (buf);
13959 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
13960 gint len = QT_UINT32 (stsd_entry_data);
13961 guint16 sound_version = 0;
13962 /* FIXME: Can this be determined somehow? There doesn't seem to be
13963 * anything in mp4a atom that specifis compression */
13965 guint16 channels = entry->n_channels;
13966 guint32 time_scale = (guint32) entry->rate;
13967 gint sample_rate_index = -1;
13970 sound_version = QT_UINT16 (stsd_entry_data + 16);
13972 if (sound_version == 1) {
13973 channels = QT_UINT16 (stsd_entry_data + 24);
13974 time_scale = QT_UINT32 (stsd_entry_data + 30);
13976 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13980 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13984 sample_rate_index =
13985 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13986 if (sample_rate_index >= 0 && channels > 0) {
13987 guint8 codec_data[2];
13990 /* build AAC codec data */
13991 codec_data[0] = profile << 3;
13992 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13993 codec_data[1] = (sample_rate_index & 0x01) << 7;
13994 codec_data[1] |= (channels & 0xF) << 3;
13996 buf = gst_buffer_new_and_alloc (2);
13997 gst_buffer_fill (buf, 0, codec_data, 2);
13998 gst_caps_set_simple (entry->caps,
13999 "codec_data", GST_TYPE_BUFFER, buf, NULL);
14000 gst_buffer_unref (buf);
14011 /* Fully handled elsewhere */
14014 GST_INFO_OBJECT (qtdemux,
14015 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14019 GST_INFO_OBJECT (qtdemux,
14020 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14021 GST_FOURCC_ARGS (fourcc), entry->caps);
14023 } else if (stream->subtype == FOURCC_strm) {
14024 if (fourcc == FOURCC_rtsp) {
14025 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
14027 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
14028 GST_FOURCC_ARGS (fourcc));
14029 goto unknown_stream;
14031 entry->sampled = TRUE;
14032 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
14033 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
14034 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
14036 entry->sampled = TRUE;
14037 entry->sparse = TRUE;
14040 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14043 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14044 GST_TAG_SUBTITLE_CODEC, codec, NULL);
14049 /* hunt for sort-of codec data */
14053 GNode *mp4s = NULL;
14054 GNode *esds = NULL;
14056 /* look for palette in a stsd->mp4s->esds sub-atom */
14057 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
14059 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
14060 if (esds == NULL) {
14062 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
14066 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
14067 stream->stream_tags);
14071 GST_INFO_OBJECT (qtdemux,
14072 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14075 GST_INFO_OBJECT (qtdemux,
14076 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14077 GST_FOURCC_ARGS (fourcc), entry->caps);
14078 } else if (stream->subtype == FOURCC_meta) {
14079 entry->sampled = TRUE;
14080 entry->sparse = TRUE;
14083 qtdemux_meta_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14086 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14087 GST_TAG_CODEC, codec, NULL);
14092 GST_INFO_OBJECT (qtdemux,
14093 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
14094 GST_FOURCC_ARGS (fourcc), entry->caps);
14096 /* everything in 1 sample */
14097 entry->sampled = TRUE;
14100 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
14103 if (entry->caps == NULL)
14104 goto unknown_stream;
14107 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14108 GST_TAG_SUBTITLE_CODEC, codec, NULL);
14114 /* promote to sampled format */
14115 if (entry->fourcc == FOURCC_samr) {
14116 /* force mono 8000 Hz for AMR */
14117 entry->sampled = TRUE;
14118 entry->n_channels = 1;
14119 entry->rate = 8000;
14120 } else if (entry->fourcc == FOURCC_sawb) {
14121 /* force mono 16000 Hz for AMR-WB */
14122 entry->sampled = TRUE;
14123 entry->n_channels = 1;
14124 entry->rate = 16000;
14125 } else if (entry->fourcc == FOURCC_mp4a) {
14126 entry->sampled = TRUE;
14130 stsd_entry_data += len;
14131 remaining_stsd_len -= len;
14135 /* collect sample information */
14136 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
14137 goto samples_failed;
14139 if (qtdemux->fragmented) {
14142 /* need all moov samples as basis; probably not many if any at all */
14143 /* prevent moof parsing taking of at this time */
14144 offset = qtdemux->moof_offset;
14145 qtdemux->moof_offset = 0;
14146 if (stream->n_samples &&
14147 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
14148 qtdemux->moof_offset = offset;
14149 goto samples_failed;
14151 qtdemux->moof_offset = offset;
14152 /* movie duration more reliable in this case (e.g. mehd) */
14153 if (qtdemux->segment.duration &&
14154 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
14156 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
14159 /* configure segments */
14160 if (!qtdemux_parse_segments (qtdemux, stream, trak))
14161 goto segments_failed;
14163 /* add some language tag, if useful */
14164 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
14165 strcmp (stream->lang_id, "und")) {
14166 const gchar *lang_code;
14168 /* convert ISO 639-2 code to ISO 639-1 */
14169 lang_code = gst_tag_get_language_code (stream->lang_id);
14170 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14171 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
14174 /* Check for UDTA tags */
14175 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
14176 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
14179 /* Insert and sort new stream in track-id order.
14180 * This will help in comparing old/new streams during stream update check */
14181 g_ptr_array_add (qtdemux->active_streams, stream);
14182 g_ptr_array_sort (qtdemux->active_streams,
14183 (GCompareFunc) qtdemux_track_id_compare_func);
14184 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
14185 QTDEMUX_N_STREAMS (qtdemux));
14192 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
14193 (_("This file is corrupt and cannot be played.")), (NULL));
14195 gst_qtdemux_stream_unref (stream);
14200 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
14201 gst_qtdemux_stream_unref (stream);
14207 /* we posted an error already */
14208 /* free stbl sub-atoms */
14209 gst_qtdemux_stbl_free (stream);
14210 gst_qtdemux_stream_unref (stream);
14215 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
14221 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
14222 GST_FOURCC_ARGS (stream->subtype));
14223 gst_qtdemux_stream_unref (stream);
14228 /* If we can estimate the overall bitrate, and don't have information about the
14229 * stream bitrate for exactly one stream, this guesses the stream bitrate as
14230 * the overall bitrate minus the sum of the bitrates of all other streams. This
14231 * should be useful for the common case where we have one audio and one video
14232 * stream and can estimate the bitrate of one, but not the other. */
14234 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
14236 QtDemuxStream *stream = NULL;
14237 gint64 size, sys_bitrate, sum_bitrate = 0;
14238 GstClockTime duration;
14242 if (qtdemux->fragmented)
14245 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
14247 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
14249 GST_DEBUG_OBJECT (qtdemux,
14250 "Size in bytes of the stream not known - bailing");
14254 /* Subtract the header size */
14255 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
14256 size, qtdemux->header_size);
14258 if (size < qtdemux->header_size)
14261 size = size - qtdemux->header_size;
14263 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
14264 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
14268 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14269 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
14270 switch (str->subtype) {
14273 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
14274 CUR_STREAM (str)->caps);
14275 /* retrieve bitrate, prefer avg then max */
14277 if (str->stream_tags) {
14278 if (gst_tag_list_get_uint (str->stream_tags,
14279 GST_TAG_MAXIMUM_BITRATE, &bitrate))
14280 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
14281 if (gst_tag_list_get_uint (str->stream_tags,
14282 GST_TAG_NOMINAL_BITRATE, &bitrate))
14283 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
14284 if (gst_tag_list_get_uint (str->stream_tags,
14285 GST_TAG_BITRATE, &bitrate))
14286 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
14289 sum_bitrate += bitrate;
14292 GST_DEBUG_OBJECT (qtdemux,
14293 ">1 stream with unknown bitrate - bailing");
14300 /* For other subtypes, we assume no significant impact on bitrate */
14306 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
14310 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
14312 if (sys_bitrate < sum_bitrate) {
14313 /* This can happen, since sum_bitrate might be derived from maximum
14314 * bitrates and not average bitrates */
14315 GST_DEBUG_OBJECT (qtdemux,
14316 "System bitrate less than sum bitrate - bailing");
14320 bitrate = sys_bitrate - sum_bitrate;
14321 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
14322 ", Stream bitrate = %u", sys_bitrate, bitrate);
14324 if (!stream->stream_tags)
14325 stream->stream_tags = gst_tag_list_new_empty ();
14327 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
14329 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14330 GST_TAG_BITRATE, bitrate, NULL);
14333 static GstFlowReturn
14334 qtdemux_prepare_streams (GstQTDemux * qtdemux)
14336 GstFlowReturn ret = GST_FLOW_OK;
14337 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14338 guint64 tkhd_max_duration = 0;
14342 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
14344 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14345 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14346 guint32 sample_num = 0;
14348 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
14349 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
14351 if (qtdemux->fragmented && qtdemux->pullbased) {
14352 /* need all moov samples first */
14353 GST_OBJECT_LOCK (qtdemux);
14354 while (stream->n_samples == 0)
14355 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
14357 GST_OBJECT_UNLOCK (qtdemux);
14359 /* discard any stray moof */
14360 qtdemux->moof_offset = 0;
14361 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14362 if (tkhd_max_duration < stream->tkhd_duration)
14363 tkhd_max_duration = stream->tkhd_duration;
14367 /* prepare braking */
14368 if (ret != GST_FLOW_ERROR)
14371 /* in pull mode, we should have parsed some sample info by now;
14372 * and quite some code will not handle no samples.
14373 * in push mode, we'll just have to deal with it */
14374 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
14375 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
14376 g_ptr_array_remove_index (qtdemux->active_streams, i);
14379 } else if (stream->track_id == qtdemux->chapters_track_id &&
14380 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
14381 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
14382 so that it doesn't look like a subtitle track */
14383 g_ptr_array_remove_index (qtdemux->active_streams, i);
14388 /* parse the initial sample for use in setting the frame rate cap */
14389 while (sample_num == 0 && sample_num < stream->n_samples) {
14390 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
14396 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
14397 if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
14398 GST_INFO_OBJECT (qtdemux,
14399 "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
14400 qtdemux->duration, tkhd_max_duration);
14401 qtdemux->duration = tkhd_max_duration;
14409 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
14411 return g_strcmp0 (stream->stream_id, stream_id) == 0;
14415 qtdemux_is_streams_update (GstQTDemux * qtdemux)
14419 /* Different length, updated */
14420 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
14423 /* streams in list are sorted in track-id order */
14424 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14425 /* Different stream-id, updated */
14426 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
14427 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
14435 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
14436 QtDemuxStream * oldstream, QtDemuxStream * newstream)
14438 /* Connect old stream's srcpad to new stream */
14439 newstream->pad = oldstream->pad;
14440 oldstream->pad = NULL;
14442 /* unset new_stream to prevent stream-start event, unless we are EOS in which
14443 * case we need to force one through */
14444 newstream->new_stream = newstream->pad != NULL
14445 && GST_PAD_IS_EOS (newstream->pad);
14447 return gst_qtdemux_configure_stream (qtdemux, newstream);
14451 qtdemux_update_streams (GstQTDemux * qtdemux)
14454 g_assert (qtdemux->streams_aware);
14456 /* At below, figure out which stream in active_streams has identical stream-id
14457 * with that of in old_streams. If there is matching stream-id,
14458 * corresponding newstream will not be exposed again,
14459 * but demux will reuse srcpad of matched old stream
14461 * active_streams : newly created streams from the latest moov
14462 * old_streams : existing streams (belong to previous moov)
14465 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14466 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14467 QtDemuxStream *oldstream = NULL;
14470 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
14471 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
14473 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
14474 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
14475 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
14477 /* null pad stream cannot be reused */
14478 if (oldstream->pad == NULL)
14483 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
14485 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
14488 /* we don't need to preserve order of old streams */
14489 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
14493 /* now we have all info and can expose */
14494 list = stream->stream_tags;
14495 stream->stream_tags = NULL;
14496 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14504 /* Must be called with expose lock */
14505 static GstFlowReturn
14506 qtdemux_expose_streams (GstQTDemux * qtdemux)
14510 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
14512 if (!qtdemux_is_streams_update (qtdemux)) {
14513 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
14514 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14515 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14516 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14517 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
14518 return GST_FLOW_ERROR;
14521 g_ptr_array_set_size (qtdemux->old_streams, 0);
14522 qtdemux->need_segment = TRUE;
14524 return GST_FLOW_OK;
14527 if (qtdemux->streams_aware) {
14528 if (!qtdemux_update_streams (qtdemux))
14529 return GST_FLOW_ERROR;
14531 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14532 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14535 /* now we have all info and can expose */
14536 list = stream->stream_tags;
14537 stream->stream_tags = NULL;
14538 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14539 return GST_FLOW_ERROR;
14544 gst_qtdemux_guess_bitrate (qtdemux);
14546 /* If we have still old_streams, it's no more used stream */
14547 for (i = 0; i < qtdemux->old_streams->len; i++) {
14548 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14553 event = gst_event_new_eos ();
14554 if (qtdemux->segment_seqnum)
14555 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14557 gst_pad_push_event (stream->pad, event);
14561 g_ptr_array_set_size (qtdemux->old_streams, 0);
14563 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14565 /* check if we should post a redirect in case there is a single trak
14566 * and it is a redirecting trak */
14567 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14568 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
14571 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14572 "an external content");
14573 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14574 gst_structure_new ("redirect",
14575 "new-location", G_TYPE_STRING,
14576 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
14577 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14578 g_free (qtdemux->redirect_location);
14579 qtdemux->redirect_location =
14580 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
14583 g_ptr_array_foreach (qtdemux->active_streams,
14584 (GFunc) qtdemux_do_allocation, qtdemux);
14586 qtdemux->need_segment = TRUE;
14588 qtdemux->exposed = TRUE;
14589 return GST_FLOW_OK;
14594 GstStructure *structure; /* helper for sort function */
14596 guint min_req_bitrate;
14597 guint min_req_qt_version;
14601 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14603 GstQtReference *ref_a = (GstQtReference *) a;
14604 GstQtReference *ref_b = (GstQtReference *) b;
14606 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14607 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14609 /* known bitrates go before unknown; higher bitrates go first */
14610 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14613 /* sort the redirects and post a message for the application.
14616 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14618 GstQtReference *best;
14621 GValue list_val = { 0, };
14624 g_assert (references != NULL);
14626 references = g_list_sort (references, qtdemux_redirects_sort_func);
14628 best = (GstQtReference *) references->data;
14630 g_value_init (&list_val, GST_TYPE_LIST);
14632 for (l = references; l != NULL; l = l->next) {
14633 GstQtReference *ref = (GstQtReference *) l->data;
14634 GValue struct_val = { 0, };
14636 ref->structure = gst_structure_new ("redirect",
14637 "new-location", G_TYPE_STRING, ref->location, NULL);
14639 if (ref->min_req_bitrate > 0) {
14640 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14641 ref->min_req_bitrate, NULL);
14644 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14645 g_value_set_boxed (&struct_val, ref->structure);
14646 gst_value_list_append_value (&list_val, &struct_val);
14647 g_value_unset (&struct_val);
14648 /* don't free anything here yet, since we need best->structure below */
14651 g_assert (best != NULL);
14652 s = gst_structure_copy (best->structure);
14654 if (g_list_length (references) > 1) {
14655 gst_structure_set_value (s, "locations", &list_val);
14658 g_value_unset (&list_val);
14660 for (l = references; l != NULL; l = l->next) {
14661 GstQtReference *ref = (GstQtReference *) l->data;
14663 gst_structure_free (ref->structure);
14664 g_free (ref->location);
14667 g_list_free (references);
14669 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14670 g_free (qtdemux->redirect_location);
14671 qtdemux->redirect_location =
14672 g_strdup (gst_structure_get_string (s, "new-location"));
14673 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14674 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14677 /* look for redirect nodes, collect all redirect information and
14681 qtdemux_parse_redirects (GstQTDemux * qtdemux)
14683 GNode *rmra, *rmda, *rdrf;
14685 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14687 GList *redirects = NULL;
14689 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14691 GstQtReference ref = { NULL, NULL, 0, 0 };
14692 GNode *rmdr, *rmvc;
14694 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14695 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14696 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14697 ref.min_req_bitrate);
14700 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14701 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14702 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14704 #ifndef GST_DISABLE_GST_DEBUG
14705 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14707 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14709 GST_LOG_OBJECT (qtdemux,
14710 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14711 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14712 bitmask, check_type);
14713 if (package == FOURCC_qtim && check_type == 0) {
14714 ref.min_req_qt_version = version;
14718 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14724 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14725 if (ref_len > 20) {
14726 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14727 ref_data = (guint8 *) rdrf->data + 20;
14728 if (ref_type == FOURCC_alis) {
14729 guint record_len, record_version, fn_len;
14731 if (ref_len > 70) {
14732 /* MacOSX alias record, google for alias-layout.txt */
14733 record_len = QT_UINT16 (ref_data + 4);
14734 record_version = QT_UINT16 (ref_data + 4 + 2);
14735 fn_len = QT_UINT8 (ref_data + 50);
14736 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14737 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14740 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14743 } else if (ref_type == FOURCC_url_) {
14744 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14746 GST_DEBUG_OBJECT (qtdemux,
14747 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14748 GST_FOURCC_ARGS (ref_type));
14750 if (ref.location != NULL) {
14751 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14753 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14755 GST_WARNING_OBJECT (qtdemux,
14756 "Failed to extract redirect location from rdrf atom");
14759 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14763 /* look for others */
14764 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14767 if (redirects != NULL) {
14768 qtdemux_process_redirects (qtdemux, redirects);
14774 static GstTagList *
14775 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14779 if (tags == NULL) {
14780 tags = gst_tag_list_new_empty ();
14781 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14784 if (qtdemux->major_brand == FOURCC_mjp2)
14785 fmt = "Motion JPEG 2000";
14786 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14788 else if (qtdemux->major_brand == FOURCC_qt__)
14790 else if (qtdemux->fragmented)
14793 fmt = "ISO MP4/M4A";
14795 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14796 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14798 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14804 /* we have read the complete moov node now.
14805 * This function parses all of the relevant info, creates the traks and
14806 * prepares all data structures for playback
14809 qtdemux_parse_tree (GstQTDemux * qtdemux)
14816 guint64 creation_time;
14817 GstDateTime *datetime = NULL;
14820 /* make sure we have a usable taglist */
14821 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14823 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14824 if (mvhd == NULL) {
14825 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14826 return qtdemux_parse_redirects (qtdemux);
14829 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14830 if (version == 1) {
14831 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14832 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14833 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14834 } else if (version == 0) {
14835 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14836 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14837 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14839 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14843 /* Moving qt creation time (secs since 1904) to unix time */
14844 if (creation_time != 0) {
14845 /* Try to use epoch first as it should be faster and more commonly found */
14846 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14849 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14850 /* some data cleansing sanity */
14851 now_s = g_get_real_time () / G_USEC_PER_SEC;
14852 if (now_s + 24 * 3600 < creation_time) {
14853 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14855 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14858 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14859 GDateTime *dt, *dt_local;
14861 dt = g_date_time_add_seconds (base_dt, creation_time);
14862 dt_local = g_date_time_to_local (dt);
14863 datetime = gst_date_time_new_from_g_date_time (dt_local);
14865 g_date_time_unref (base_dt);
14866 g_date_time_unref (dt);
14870 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14871 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14873 gst_date_time_unref (datetime);
14876 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14877 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14879 /* check for fragmented file and get some (default) data */
14880 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14883 GstByteReader mehd_data;
14885 /* let track parsing or anyone know weird stuff might happen ... */
14886 qtdemux->fragmented = TRUE;
14888 /* compensate for total duration */
14889 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14891 qtdemux_parse_mehd (qtdemux, &mehd_data);
14894 /* Update the movie segment duration, unless it was directly given to us
14895 * by upstream. Otherwise let it as is, as we don't want to mangle the
14896 * duration provided by upstream that may come e.g. from a MPD file. */
14897 if (!qtdemux->upstream_format_is_time) {
14898 GstClockTime duration;
14899 /* set duration in the segment info */
14900 gst_qtdemux_get_duration (qtdemux, &duration);
14901 qtdemux->segment.duration = duration;
14902 /* also do not exceed duration; stop is set that way post seek anyway,
14903 * and segment activation falls back to duration,
14904 * whereas loop only checks stop, so let's align this here as well */
14905 qtdemux->segment.stop = duration;
14908 /* parse all traks */
14909 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14911 qtdemux_parse_trak (qtdemux, trak);
14912 /* iterate all siblings */
14913 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14916 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14919 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14921 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14923 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14926 /* maybe also some tags in meta box */
14927 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14929 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14930 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14932 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14935 /* parse any protection system info */
14936 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14938 /* Unref old protection events if we are going to receive new ones. */
14939 qtdemux_clear_protection_events_on_all_streams (qtdemux);
14942 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14943 qtdemux_parse_pssh (qtdemux, pssh);
14944 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14947 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14952 /* taken from ffmpeg */
14954 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14966 len = (len << 7) | (c & 0x7f);
14975 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14976 gsize codec_data_size)
14978 GList *list = NULL;
14979 guint8 *p = codec_data;
14980 gint i, offset, num_packets;
14981 guint *length, last;
14983 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14985 if (codec_data == NULL || codec_data_size == 0)
14988 /* start of the stream and vorbis audio or theora video, need to
14989 * send the codec_priv data as first three packets */
14990 num_packets = p[0] + 1;
14991 GST_DEBUG_OBJECT (qtdemux,
14992 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14993 (guint) num_packets, codec_data_size);
14995 /* Let's put some limits, Don't think there even is a xiph codec
14996 * with more than 3-4 headers */
14997 if (G_UNLIKELY (num_packets > 16)) {
14998 GST_WARNING_OBJECT (qtdemux,
14999 "Unlikely number of xiph headers, most likely not valid");
15003 length = g_alloca (num_packets * sizeof (guint));
15007 /* first packets, read length values */
15008 for (i = 0; i < num_packets - 1; i++) {
15010 while (offset < codec_data_size) {
15011 length[i] += p[offset];
15012 if (p[offset++] != 0xff)
15017 if (offset + last > codec_data_size)
15020 /* last packet is the remaining size */
15021 length[i] = codec_data_size - offset - last;
15023 for (i = 0; i < num_packets; i++) {
15026 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
15028 if (offset + length[i] > codec_data_size)
15031 hdr = gst_buffer_new_memdup (p + offset, length[i]);
15032 list = g_list_append (list, hdr);
15034 offset += length[i];
15043 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
15049 /* this can change the codec originally present in @list */
15051 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
15052 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
15054 int len = QT_UINT32 (esds->data);
15055 guint8 *ptr = esds->data;
15056 guint8 *end = ptr + len;
15058 guint8 *data_ptr = NULL;
15060 guint8 object_type_id = 0;
15061 guint8 stream_type = 0;
15062 const char *codec_name = NULL;
15063 GstCaps *caps = NULL;
15065 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
15067 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
15069 while (ptr + 1 < end) {
15070 tag = QT_UINT8 (ptr);
15071 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
15073 len = read_descr_size (ptr, end, &ptr);
15074 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
15076 /* Check the stated amount of data is available for reading */
15077 if (len < 0 || ptr + len > end)
15081 case ES_DESCRIPTOR_TAG:
15082 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
15083 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
15086 case DECODER_CONFIG_DESC_TAG:{
15087 guint max_bitrate, avg_bitrate;
15089 object_type_id = QT_UINT8 (ptr);
15090 stream_type = QT_UINT8 (ptr + 1) >> 2;
15091 max_bitrate = QT_UINT32 (ptr + 5);
15092 avg_bitrate = QT_UINT32 (ptr + 9);
15093 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
15094 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
15095 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
15096 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
15097 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
15098 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
15099 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15100 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
15102 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
15103 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
15104 avg_bitrate, NULL);
15109 case DECODER_SPECIFIC_INFO_TAG:
15110 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
15111 if (object_type_id == 0xe0 && len == 0x40) {
15117 GST_DEBUG_OBJECT (qtdemux,
15118 "Have VOBSUB palette. Creating palette event");
15119 /* move to decConfigDescr data and read palette */
15121 for (i = 0; i < 16; i++) {
15122 clut[i] = QT_UINT32 (data);
15126 s = gst_structure_new ("application/x-gst-dvd", "event",
15127 G_TYPE_STRING, "dvd-spu-clut-change",
15128 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
15129 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
15130 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
15131 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
15132 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
15133 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
15134 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
15135 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
15138 /* store event and trigger custom processing */
15139 stream->pending_event =
15140 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
15142 /* Generic codec_data handler puts it on the caps */
15149 case SL_CONFIG_DESC_TAG:
15150 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
15154 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
15156 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
15162 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
15163 * in use, and should also be used to override some other parameters for some
15165 switch (object_type_id) {
15166 case 0x20: /* MPEG-4 */
15167 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
15168 * profile_and_level_indication */
15169 if (data_ptr != NULL && data_len >= 5 &&
15170 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
15171 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
15172 data_ptr + 4, data_len - 4);
15174 break; /* Nothing special needed here */
15175 case 0x21: /* H.264 */
15176 codec_name = "H.264 / AVC";
15177 caps = gst_caps_new_simple ("video/x-h264",
15178 "stream-format", G_TYPE_STRING, "avc",
15179 "alignment", G_TYPE_STRING, "au", NULL);
15181 case 0x40: /* AAC (any) */
15182 case 0x66: /* AAC Main */
15183 case 0x67: /* AAC LC */
15184 case 0x68: /* AAC SSR */
15185 /* Override channels and rate based on the codec_data, as it's often
15187 /* Only do so for basic setup without HE-AAC extension */
15188 if (data_ptr && data_len == 2) {
15189 guint channels, rate;
15191 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
15193 entry->n_channels = channels;
15195 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15197 entry->rate = rate;
15200 /* Set level and profile if possible */
15201 if (data_ptr != NULL && data_len >= 2) {
15202 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15203 data_ptr, data_len);
15205 const gchar *profile_str = NULL;
15208 guint8 *codec_data;
15209 gint rate_idx, profile;
15211 /* No codec_data, let's invent something.
15212 * FIXME: This is wrong for SBR! */
15214 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15216 buffer = gst_buffer_new_and_alloc (2);
15217 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15218 codec_data = map.data;
15221 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
15224 switch (object_type_id) {
15226 profile_str = "main";
15230 profile_str = "lc";
15234 profile_str = "ssr";
15242 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15244 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
15246 gst_buffer_unmap (buffer, &map);
15247 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
15248 GST_TYPE_BUFFER, buffer, NULL);
15249 gst_buffer_unref (buffer);
15252 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
15253 G_TYPE_STRING, profile_str, NULL);
15257 case 0x60: /* MPEG-2, various profiles */
15263 codec_name = "MPEG-2 video";
15264 caps = gst_caps_new_simple ("video/mpeg",
15265 "mpegversion", G_TYPE_INT, 2,
15266 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15268 case 0x69: /* MPEG-2 BC audio */
15269 case 0x6B: /* MPEG-1 audio */
15270 caps = gst_caps_new_simple ("audio/mpeg",
15271 "mpegversion", G_TYPE_INT, 1, NULL);
15272 codec_name = "MPEG-1 audio";
15274 case 0x6A: /* MPEG-1 */
15275 codec_name = "MPEG-1 video";
15276 caps = gst_caps_new_simple ("video/mpeg",
15277 "mpegversion", G_TYPE_INT, 1,
15278 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15280 case 0x6C: /* MJPEG */
15282 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15284 codec_name = "Motion-JPEG";
15286 case 0x6D: /* PNG */
15288 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15290 codec_name = "PNG still images";
15292 case 0x6E: /* JPEG2000 */
15293 codec_name = "JPEG-2000";
15294 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15296 case 0xA4: /* Dirac */
15297 codec_name = "Dirac";
15298 caps = gst_caps_new_empty_simple ("video/x-dirac");
15300 case 0xA5: /* AC3 */
15301 codec_name = "AC-3 audio";
15302 caps = gst_caps_new_simple ("audio/x-ac3",
15303 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15305 case 0xA9: /* AC3 */
15306 codec_name = "DTS audio";
15307 caps = gst_caps_new_simple ("audio/x-dts",
15308 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15311 if (stream_type == 0x05 && data_ptr) {
15313 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
15316 GValue arr_val = G_VALUE_INIT;
15317 GValue buf_val = G_VALUE_INIT;
15320 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15321 codec_name = "Vorbis";
15322 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15323 g_value_init (&arr_val, GST_TYPE_ARRAY);
15324 g_value_init (&buf_val, GST_TYPE_BUFFER);
15325 for (tmp = headers; tmp; tmp = tmp->next) {
15326 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15327 gst_value_array_append_value (&arr_val, &buf_val);
15329 s = gst_caps_get_structure (caps, 0);
15330 gst_structure_take_value (s, "streamheader", &arr_val);
15331 g_value_unset (&buf_val);
15332 g_list_free (headers);
15339 case 0xE1: /* QCELP */
15340 /* QCELP, the codec_data is a riff tag (little endian) with
15341 * 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). */
15342 caps = gst_caps_new_empty_simple ("audio/qcelp");
15343 codec_name = "QCELP";
15349 /* If we have a replacement caps, then change our caps for this stream */
15351 gst_caps_unref (entry->caps);
15352 entry->caps = caps;
15355 if (codec_name && list)
15356 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15357 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15359 /* Add the codec_data attribute to caps, if we have it */
15363 buffer = gst_buffer_new_and_alloc (data_len);
15364 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15366 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15367 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15369 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15371 gst_buffer_unref (buffer);
15376 static inline GstCaps *
15377 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
15381 char *s, fourstr[5];
15383 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15384 for (i = 0; i < 4; i++) {
15385 if (!g_ascii_isalnum (fourstr[i]))
15388 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15389 caps = gst_caps_new_empty_simple (s);
15394 #define _codec(name) \
15396 if (codec_name) { \
15397 *codec_name = g_strdup (name); \
15402 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15403 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15404 const guint8 * stsd_entry_data, gchar ** codec_name)
15406 GstCaps *caps = NULL;
15407 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15411 _codec ("PNG still images");
15412 caps = gst_caps_new_empty_simple ("image/png");
15415 _codec ("JPEG still images");
15417 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15420 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15421 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15422 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15423 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
15424 _codec ("Motion-JPEG");
15426 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15429 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
15430 _codec ("Motion-JPEG format B");
15431 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15434 _codec ("JPEG-2000");
15435 /* override to what it should be according to spec, avoid palette_data */
15436 entry->bits_per_sample = 24;
15437 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15440 _codec ("Sorensen video v.3");
15441 caps = gst_caps_new_simple ("video/x-svq",
15442 "svqversion", G_TYPE_INT, 3, NULL);
15444 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15445 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
15446 _codec ("Sorensen video v.1");
15447 caps = gst_caps_new_simple ("video/x-svq",
15448 "svqversion", G_TYPE_INT, 1, NULL);
15450 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15451 caps = gst_caps_new_empty_simple ("video/x-raw");
15452 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
15453 _codec ("Windows Raw RGB");
15454 stream->alignment = 32;
15460 bps = QT_UINT16 (stsd_entry_data + 82);
15463 format = GST_VIDEO_FORMAT_RGB15;
15466 format = GST_VIDEO_FORMAT_RGB16;
15469 format = GST_VIDEO_FORMAT_RGB;
15472 format = GST_VIDEO_FORMAT_ARGB;
15480 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15481 format = GST_VIDEO_FORMAT_I420;
15483 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15484 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15485 format = GST_VIDEO_FORMAT_I420;
15488 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15489 format = GST_VIDEO_FORMAT_UYVY;
15491 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15492 format = GST_VIDEO_FORMAT_v308;
15494 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15495 format = GST_VIDEO_FORMAT_v216;
15498 format = GST_VIDEO_FORMAT_v210;
15500 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15501 format = GST_VIDEO_FORMAT_r210;
15503 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15504 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15505 format = GST_VIDEO_FORMAT_v410;
15508 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15509 * but different order than AYUV
15510 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15511 format = GST_VIDEO_FORMAT_v408;
15514 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15515 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
15516 _codec ("MPEG-1 video");
15517 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15518 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15520 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15521 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15522 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15523 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15524 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15525 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15526 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15527 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15528 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15529 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15530 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15531 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15532 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15533 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15534 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15535 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15536 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15537 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15538 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15539 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15540 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15541 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15542 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15543 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15544 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15545 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15546 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15547 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15548 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15549 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15550 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15551 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15552 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15553 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15554 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15555 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15556 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15557 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15558 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15559 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15560 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15561 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15562 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15563 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15564 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15565 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15566 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15567 _codec ("MPEG-2 video");
15568 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15569 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15571 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15572 _codec ("GIF still images");
15573 caps = gst_caps_new_empty_simple ("image/gif");
15576 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15578 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15580 /* ffmpeg uses the height/width props, don't know why */
15581 caps = gst_caps_new_simple ("video/x-h263",
15582 "variant", G_TYPE_STRING, "itu", NULL);
15586 _codec ("MPEG-4 video");
15587 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15588 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15590 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15591 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15592 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15593 caps = gst_caps_new_simple ("video/x-msmpeg",
15594 "msmpegversion", G_TYPE_INT, 43, NULL);
15596 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15598 caps = gst_caps_new_simple ("video/x-divx",
15599 "divxversion", G_TYPE_INT, 3, NULL);
15601 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15602 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15604 caps = gst_caps_new_simple ("video/x-divx",
15605 "divxversion", G_TYPE_INT, 4, NULL);
15607 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15609 caps = gst_caps_new_simple ("video/x-divx",
15610 "divxversion", G_TYPE_INT, 5, NULL);
15613 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15615 caps = gst_caps_new_simple ("video/x-ffv",
15616 "ffvversion", G_TYPE_INT, 1, NULL);
15619 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15620 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15625 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15626 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15627 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15631 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15632 _codec ("Cinepak");
15633 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15635 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15636 _codec ("Apple QuickDraw");
15637 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15639 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15640 _codec ("Apple video");
15641 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15646 _codec ("H.264 / AVC");
15647 caps = gst_caps_new_simple ("video/x-h264",
15648 "stream-format", G_TYPE_STRING, "avc",
15649 "alignment", G_TYPE_STRING, "au", NULL);
15653 _codec ("H.264 / AVC");
15654 caps = gst_caps_new_simple ("video/x-h264",
15655 "stream-format", G_TYPE_STRING, "avc3",
15656 "alignment", G_TYPE_STRING, "au", NULL);
15670 _codec ("H.264 / AVC");
15671 caps = gst_caps_new_simple ("video/x-h264",
15672 "stream-format", G_TYPE_STRING, "byte-stream",
15673 "alignment", G_TYPE_STRING, "au", NULL);
15678 _codec ("H.265 / HEVC");
15679 caps = gst_caps_new_simple ("video/x-h265",
15680 "stream-format", G_TYPE_STRING, "hvc1",
15681 "alignment", G_TYPE_STRING, "au", NULL);
15685 _codec ("H.265 / HEVC");
15686 caps = gst_caps_new_simple ("video/x-h265",
15687 "stream-format", G_TYPE_STRING, "hev1",
15688 "alignment", G_TYPE_STRING, "au", NULL);
15691 _codec ("Run-length encoding");
15692 caps = gst_caps_new_simple ("video/x-rle",
15693 "layout", G_TYPE_STRING, "quicktime", NULL);
15696 _codec ("Run-length encoding");
15697 caps = gst_caps_new_simple ("video/x-rle",
15698 "layout", G_TYPE_STRING, "microsoft", NULL);
15700 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15701 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15702 _codec ("Indeo Video 3");
15703 caps = gst_caps_new_simple ("video/x-indeo",
15704 "indeoversion", G_TYPE_INT, 3, NULL);
15706 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15707 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15708 _codec ("Intel Video 4");
15709 caps = gst_caps_new_simple ("video/x-indeo",
15710 "indeoversion", G_TYPE_INT, 4, NULL);
15714 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15715 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15716 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15717 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15718 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15719 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15720 _codec ("DV Video");
15721 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15722 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15724 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15725 case FOURCC_dv5p: /* DVCPRO50 PAL */
15726 _codec ("DVCPro50 Video");
15727 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15728 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15730 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15731 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15732 _codec ("DVCProHD Video");
15733 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15734 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15736 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15737 _codec ("Apple Graphics (SMC)");
15738 caps = gst_caps_new_empty_simple ("video/x-smc");
15740 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15742 caps = gst_caps_new_empty_simple ("video/x-vp3");
15744 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15745 _codec ("VP6 Flash");
15746 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15750 caps = gst_caps_new_empty_simple ("video/x-theora");
15751 /* theora uses one byte of padding in the data stream because it does not
15752 * allow 0 sized packets while theora does */
15753 entry->padding = 1;
15757 caps = gst_caps_new_empty_simple ("video/x-dirac");
15759 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15760 _codec ("TIFF still images");
15761 caps = gst_caps_new_empty_simple ("image/tiff");
15763 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15764 _codec ("Apple Intermediate Codec");
15765 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15767 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15768 _codec ("AVID DNxHD");
15769 caps = gst_caps_from_string ("video/x-dnxhd");
15773 _codec ("On2 VP8");
15774 caps = gst_caps_from_string ("video/x-vp8");
15777 _codec ("Google VP9");
15778 caps = gst_caps_from_string ("video/x-vp9");
15781 _codec ("Apple ProRes LT");
15783 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15787 _codec ("Apple ProRes HQ");
15789 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15793 _codec ("Apple ProRes");
15795 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15799 _codec ("Apple ProRes Proxy");
15801 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15805 _codec ("Apple ProRes 4444");
15807 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15810 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15811 if (entry->bits_per_sample > 0) {
15812 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15817 _codec ("Apple ProRes 4444 XQ");
15819 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15822 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15823 if (entry->bits_per_sample > 0) {
15824 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15829 _codec ("GoPro CineForm");
15830 caps = gst_caps_from_string ("video/x-cineform");
15835 caps = gst_caps_new_simple ("video/x-wmv",
15836 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15840 caps = gst_caps_new_simple ("video/x-av1",
15841 "stream-format", G_TYPE_STRING, "obu-stream",
15842 "alignment", G_TYPE_STRING, "tu", NULL);
15844 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15847 caps = _get_unknown_codec_name ("video", fourcc);
15852 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15855 gst_video_info_init (&info);
15856 gst_video_info_set_format (&info, format, entry->width, entry->height);
15858 caps = gst_video_info_to_caps (&info);
15859 *codec_name = gst_pb_utils_get_codec_description (caps);
15861 /* enable clipping for raw video streams */
15862 stream->need_clip = TRUE;
15863 stream->alignment = 32;
15870 round_up_pow2 (guint n)
15882 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15883 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15884 int len, gchar ** codec_name)
15887 const GstStructure *s;
15890 GstAudioFormat format = 0;
15893 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15895 depth = entry->bytes_per_packet * 8;
15898 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15900 /* 8-bit audio is unsigned */
15902 format = GST_AUDIO_FORMAT_U8;
15903 /* otherwise it's signed and big-endian just like 'twos' */
15905 endian = G_BIG_ENDIAN;
15912 endian = G_LITTLE_ENDIAN;
15915 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15917 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15921 caps = gst_caps_new_simple ("audio/x-raw",
15922 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15923 "layout", G_TYPE_STRING, "interleaved", NULL);
15924 stream->alignment = GST_ROUND_UP_8 (depth);
15925 stream->alignment = round_up_pow2 (stream->alignment);
15929 _codec ("Raw 64-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, "F64BE",
15934 "layout", G_TYPE_STRING, "interleaved", NULL);
15935 stream->alignment = 8;
15938 _codec ("Raw 32-bit floating-point 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, "F32BE",
15943 "layout", G_TYPE_STRING, "interleaved", NULL);
15944 stream->alignment = 4;
15947 _codec ("Raw 24-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, "S24BE",
15952 "layout", G_TYPE_STRING, "interleaved", NULL);
15953 stream->alignment = 4;
15956 _codec ("Raw 32-bit PCM audio");
15957 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15959 caps = gst_caps_new_simple ("audio/x-raw",
15960 "format", G_TYPE_STRING, "S32BE",
15961 "layout", G_TYPE_STRING, "interleaved", NULL);
15962 stream->alignment = 4;
15965 _codec ("Raw 16-bit PCM audio");
15966 caps = gst_caps_new_simple ("audio/x-raw",
15967 "format", G_TYPE_STRING, "S16LE",
15968 "layout", G_TYPE_STRING, "interleaved", NULL);
15969 stream->alignment = 2;
15972 _codec ("Mu-law audio");
15973 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15976 _codec ("A-law audio");
15977 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15981 _codec ("Microsoft ADPCM");
15982 /* Microsoft ADPCM-ACM code 2 */
15983 caps = gst_caps_new_simple ("audio/x-adpcm",
15984 "layout", G_TYPE_STRING, "microsoft", NULL);
15988 _codec ("DVI/IMA ADPCM");
15989 caps = gst_caps_new_simple ("audio/x-adpcm",
15990 "layout", G_TYPE_STRING, "dvi", NULL);
15994 _codec ("DVI/Intel IMA ADPCM");
15995 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15996 caps = gst_caps_new_simple ("audio/x-adpcm",
15997 "layout", G_TYPE_STRING, "quicktime", NULL);
16001 /* MPEG layer 3, CBR only (pre QT4.1) */
16004 _codec ("MPEG-1 layer 3");
16005 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
16006 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
16007 "mpegversion", G_TYPE_INT, 1, NULL);
16009 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
16010 _codec ("MPEG-1 layer 2");
16012 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
16013 "mpegversion", G_TYPE_INT, 1, NULL);
16016 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
16017 _codec ("EAC-3 audio");
16018 caps = gst_caps_new_simple ("audio/x-eac3",
16019 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16020 entry->sampled = TRUE;
16022 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
16024 _codec ("AC-3 audio");
16025 caps = gst_caps_new_simple ("audio/x-ac3",
16026 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16027 entry->sampled = TRUE;
16029 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
16030 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
16031 _codec ("DTS audio");
16032 caps = gst_caps_new_simple ("audio/x-dts",
16033 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16034 entry->sampled = TRUE;
16036 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
16037 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
16038 _codec ("DTS-HD audio");
16039 caps = gst_caps_new_simple ("audio/x-dts",
16040 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16041 entry->sampled = TRUE;
16045 caps = gst_caps_new_simple ("audio/x-mace",
16046 "maceversion", G_TYPE_INT, 3, NULL);
16050 caps = gst_caps_new_simple ("audio/x-mace",
16051 "maceversion", G_TYPE_INT, 6, NULL);
16053 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
16055 caps = gst_caps_new_empty_simple ("application/ogg");
16057 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
16058 _codec ("DV audio");
16059 caps = gst_caps_new_empty_simple ("audio/x-dv");
16062 _codec ("MPEG-4 AAC audio");
16063 caps = gst_caps_new_simple ("audio/mpeg",
16064 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
16065 "stream-format", G_TYPE_STRING, "raw", NULL);
16067 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
16068 _codec ("QDesign Music");
16069 caps = gst_caps_new_empty_simple ("audio/x-qdm");
16072 _codec ("QDesign Music v.2");
16073 /* FIXME: QDesign music version 2 (no constant) */
16074 if (FALSE && data) {
16075 caps = gst_caps_new_simple ("audio/x-qdm2",
16076 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
16077 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
16078 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
16080 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
16084 _codec ("GSM audio");
16085 caps = gst_caps_new_empty_simple ("audio/x-gsm");
16088 _codec ("AMR audio");
16089 caps = gst_caps_new_empty_simple ("audio/AMR");
16092 _codec ("AMR-WB audio");
16093 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
16096 _codec ("Quicktime IMA ADPCM");
16097 caps = gst_caps_new_simple ("audio/x-adpcm",
16098 "layout", G_TYPE_STRING, "quicktime", NULL);
16101 _codec ("Apple lossless audio");
16102 caps = gst_caps_new_empty_simple ("audio/x-alac");
16105 _codec ("Free Lossless Audio Codec");
16106 caps = gst_caps_new_simple ("audio/x-flac",
16107 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
16109 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
16110 _codec ("QualComm PureVoice");
16111 caps = gst_caps_from_string ("audio/qcelp");
16116 caps = gst_caps_new_empty_simple ("audio/x-wma");
16120 caps = gst_caps_new_empty_simple ("audio/x-opus");
16127 GstAudioFormat format;
16130 FLAG_IS_FLOAT = 0x1,
16131 FLAG_IS_BIG_ENDIAN = 0x2,
16132 FLAG_IS_SIGNED = 0x4,
16133 FLAG_IS_PACKED = 0x8,
16134 FLAG_IS_ALIGNED_HIGH = 0x10,
16135 FLAG_IS_NON_INTERLEAVED = 0x20
16137 _codec ("Raw LPCM audio");
16139 if (data && len >= 36) {
16140 depth = QT_UINT32 (data + 24);
16141 flags = QT_UINT32 (data + 28);
16142 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
16144 if ((flags & FLAG_IS_FLOAT) == 0) {
16149 if ((flags & FLAG_IS_ALIGNED_HIGH))
16152 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
16153 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
16154 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
16155 caps = gst_caps_new_simple ("audio/x-raw",
16156 "format", G_TYPE_STRING,
16158 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
16159 "UNKNOWN", "layout", G_TYPE_STRING,
16160 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
16161 "interleaved", NULL);
16162 stream->alignment = GST_ROUND_UP_8 (depth);
16163 stream->alignment = round_up_pow2 (stream->alignment);
16168 if (flags & FLAG_IS_BIG_ENDIAN)
16169 format = GST_AUDIO_FORMAT_F64BE;
16171 format = GST_AUDIO_FORMAT_F64LE;
16173 if (flags & FLAG_IS_BIG_ENDIAN)
16174 format = GST_AUDIO_FORMAT_F32BE;
16176 format = GST_AUDIO_FORMAT_F32LE;
16178 caps = gst_caps_new_simple ("audio/x-raw",
16179 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
16180 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
16181 "non-interleaved" : "interleaved", NULL);
16182 stream->alignment = width / 8;
16186 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
16189 caps = gst_caps_new_empty_simple ("audio/x-ac4");
16192 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
16196 caps = _get_unknown_codec_name ("audio", fourcc);
16202 GstCaps *templ_caps =
16203 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
16204 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16205 gst_caps_unref (caps);
16206 gst_caps_unref (templ_caps);
16207 caps = intersection;
16210 /* enable clipping for raw audio streams */
16211 s = gst_caps_get_structure (caps, 0);
16212 name = gst_structure_get_name (s);
16213 if (g_str_has_prefix (name, "audio/x-raw")) {
16214 stream->need_clip = TRUE;
16215 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
16216 stream->max_buffer_size = entry->rate * entry->bytes_per_frame;
16217 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
16218 stream->max_buffer_size);
16224 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16225 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16226 const guint8 * stsd_entry_data, gchar ** codec_name)
16230 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16234 _codec ("DVD subtitle");
16235 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
16236 stream->process_func = gst_qtdemux_process_buffer_dvd;
16239 _codec ("Quicktime timed text");
16242 _codec ("3GPP timed text");
16244 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16246 /* actual text piece needs to be extracted */
16247 stream->process_func = gst_qtdemux_process_buffer_text;
16250 _codec ("XML subtitles");
16251 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16256 const gchar *buf = "WEBVTT\n\n";
16258 _codec ("WebVTT subtitles");
16259 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
16260 stream->process_func = gst_qtdemux_process_buffer_wvtt;
16262 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
16263 buffer = gst_buffer_new_and_alloc (8);
16264 gst_buffer_fill (buffer, 0, buf, 8);
16265 stream->buffers = g_slist_append (stream->buffers, buffer);
16270 _codec ("CEA 608 Closed Caption");
16272 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16273 G_TYPE_STRING, "s334-1a", NULL);
16274 stream->process_func = gst_qtdemux_process_buffer_clcp;
16275 stream->need_split = TRUE;
16278 _codec ("CEA 708 Closed Caption");
16280 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16281 G_TYPE_STRING, "cdp", NULL);
16282 stream->process_func = gst_qtdemux_process_buffer_clcp;
16287 caps = _get_unknown_codec_name ("text", fourcc);
16295 qtdemux_meta_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16296 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16297 const guint8 * stsd_entry_data, gchar ** codec_name)
16299 GstCaps *caps = NULL;
16301 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16305 gsize size = QT_UINT32 (stsd_entry_data);
16306 GstByteReader reader = GST_BYTE_READER_INIT (stsd_entry_data, size);
16307 const gchar *content_encoding;
16308 const gchar *namespaces;
16309 const gchar *schema_locations;
16311 if (!gst_byte_reader_skip (&reader, 8 + 6 + 2)) {
16312 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
16316 if (!gst_byte_reader_get_string (&reader, &content_encoding) ||
16317 !gst_byte_reader_get_string (&reader, &namespaces) ||
16318 !gst_byte_reader_get_string (&reader, &schema_locations)) {
16319 GST_WARNING_OBJECT (qtdemux, "Too short metx sample entry");
16323 if (strstr (namespaces, "http://www.onvif.org/ver10/schema") != 0) {
16324 if (content_encoding == NULL || *content_encoding == '\0'
16325 || g_ascii_strcasecmp (content_encoding, "xml") == 0) {
16326 _codec ("ONVIF Timed XML MetaData");
16328 gst_caps_new_simple ("application/x-onvif-metadata", "parsed",
16329 G_TYPE_BOOLEAN, TRUE, NULL);
16331 GST_DEBUG_OBJECT (qtdemux, "Unknown content encoding: %s",
16335 GST_DEBUG_OBJECT (qtdemux, "Unknown metadata namespaces: %s",
16346 caps = _get_unknown_codec_name ("meta", fourcc);
16352 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
16353 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
16354 const guint8 * stsd_entry_data, gchar ** codec_name)
16360 _codec ("MPEG 1 video");
16361 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16362 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16372 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
16373 const gchar * system_id)
16377 if (!qtdemux->protection_system_ids)
16378 qtdemux->protection_system_ids =
16379 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16380 /* Check whether we already have an entry for this system ID. */
16381 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16382 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16383 if (g_ascii_strcasecmp (system_id, id) == 0) {
16387 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16388 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,