2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
4 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
5 * Copyright (C) <2007> Julien Moutte <julien@fluendo.com>
6 * Copyright (C) <2009> Tim-Philipp Müller <tim centricular net>
7 * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.com>
8 * Copyright (C) <2013> Sreerenj Balachandran <sreerenj.balachandran@intel.com>
9 * Copyright (C) <2013> Intel Corporation
10 * Copyright (C) <2014> Centricular Ltd
11 * Copyright (C) <2015> YouView TV Ltd.
12 * Copyright (C) <2016> British Broadcasting Corporation
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with this library; if not, write to the
26 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
31 * SECTION:element-qtdemux
34 * Demuxes a .mov file into raw or compressed audio and/or video streams.
36 * This element supports both push and pull-based scheduling, depending on the
37 * capabilities of the upstream elements.
39 * ## Example launch line
41 * gst-launch-1.0 filesrc location=test.mov ! qtdemux name=demux demux.audio_0 ! queue ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
42 * ]| Play (parse and decode) a .mov file and try to output it to
43 * an automatically detected soundcard and videosink. If the MOV file contains
44 * compressed audio or video data, this will only work if you have the
45 * right decoder elements/plugins installed.
53 #include "gst/gst-i18n-plugin.h"
55 #include <glib/gprintf.h>
56 #include <gst/base/base.h>
57 #include <gst/tag/tag.h>
58 #include <gst/audio/audio.h>
59 #include <gst/riff/riff.h>
60 #include <gst/pbutils/pbutils.h>
62 #include "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 #define gst_qtdemux_parent_class parent_class
321 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
322 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qtdemux, "qtdemux",
323 GST_RANK_PRIMARY, GST_TYPE_QTDEMUX, isomp4_element_init (plugin));
325 static void gst_qtdemux_dispose (GObject * object);
326 static void gst_qtdemux_finalize (GObject * object);
329 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
330 GstClockTime media_time);
332 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
333 QtDemuxStream * str, gint64 media_offset);
336 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
337 static GstIndex *gst_qtdemux_get_index (GstElement * element);
339 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
340 GstStateChange transition);
341 static void gst_qtdemux_set_context (GstElement * element,
342 GstContext * context);
343 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
344 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
345 GstObject * parent, GstPadMode mode, gboolean active);
347 static void gst_qtdemux_loop (GstPad * pad);
348 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
350 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
352 static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
354 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
355 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
356 QtDemuxStream * stream);
357 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
358 QtDemuxStream * stream);
359 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
362 static void gst_qtdemux_check_seekability (GstQTDemux * demux);
364 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
365 const guint8 * buffer, guint length);
366 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
367 const guint8 * buffer, guint length);
368 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
370 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
371 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
373 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
374 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
375 const guint8 * stsd_entry_data, gchar ** codec_name);
376 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
377 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
378 const guint8 * data, int len, gchar ** codec_name);
379 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
380 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
381 gchar ** codec_name);
382 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
383 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
384 const guint8 * stsd_entry_data, gchar ** codec_name);
386 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
387 QtDemuxStream * stream, guint32 n);
388 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
389 static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
390 static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
391 static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
392 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
393 static void qtdemux_do_allocation (QtDemuxStream * stream,
394 GstQTDemux * qtdemux);
395 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
396 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
397 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
398 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
399 GstClockTime * _start, GstClockTime * _stop);
400 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
401 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
403 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
404 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
406 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
408 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
409 QtDemuxStream * stream, guint sample_index);
410 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
412 static void qtdemux_gst_structure_free (GstStructure * gststructure);
413 static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
415 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
416 static void gst_tag_register_spherical_tags (void);
417 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
420 gst_qtdemux_class_init (GstQTDemuxClass * klass)
422 GObjectClass *gobject_class;
423 GstElementClass *gstelement_class;
425 gobject_class = (GObjectClass *) klass;
426 gstelement_class = (GstElementClass *) klass;
428 parent_class = g_type_class_peek_parent (klass);
430 gobject_class->dispose = gst_qtdemux_dispose;
431 gobject_class->finalize = gst_qtdemux_finalize;
433 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
435 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
436 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
438 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
440 gst_tag_register_musicbrainz_tags ();
442 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
443 gst_tag_register_spherical_tags ();
444 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
446 gst_element_class_add_static_pad_template (gstelement_class,
447 &gst_qtdemux_sink_template);
448 gst_element_class_add_static_pad_template (gstelement_class,
449 &gst_qtdemux_videosrc_template);
450 gst_element_class_add_static_pad_template (gstelement_class,
451 &gst_qtdemux_audiosrc_template);
452 gst_element_class_add_static_pad_template (gstelement_class,
453 &gst_qtdemux_subsrc_template);
454 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
456 "Demultiplex a QuickTime file into audio and video streams",
457 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
459 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
464 gst_qtdemux_init (GstQTDemux * qtdemux)
467 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
468 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
469 gst_pad_set_activatemode_function (qtdemux->sinkpad,
470 qtdemux_sink_activate_mode);
471 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
472 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
473 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
474 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
476 qtdemux->adapter = gst_adapter_new ();
477 g_queue_init (&qtdemux->protection_event_queue);
478 qtdemux->flowcombiner = gst_flow_combiner_new ();
479 g_mutex_init (&qtdemux->expose_lock);
481 qtdemux->active_streams = g_ptr_array_new_with_free_func
482 ((GDestroyNotify) gst_qtdemux_stream_unref);
483 qtdemux->old_streams = g_ptr_array_new_with_free_func
484 ((GDestroyNotify) gst_qtdemux_stream_unref);
486 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
487 qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
488 malloc (sizeof (QtDemuxSphericalMetadata));
490 if (qtdemux->spherical_metadata) {
491 qtdemux->spherical_metadata->is_spherical = FALSE;
492 qtdemux->spherical_metadata->is_stitched = FALSE;
493 qtdemux->spherical_metadata->stitching_software = NULL;
494 qtdemux->spherical_metadata->projection_type = NULL;
495 qtdemux->spherical_metadata->stereo_mode = NULL;
496 qtdemux->spherical_metadata->source_count = 0;
497 qtdemux->spherical_metadata->init_view_heading = 0;
498 qtdemux->spherical_metadata->init_view_pitch = 0;
499 qtdemux->spherical_metadata->init_view_roll = 0;
500 qtdemux->spherical_metadata->timestamp = 0;
501 qtdemux->spherical_metadata->full_pano_width_pixels = 0;
502 qtdemux->spherical_metadata->full_pano_height_pixels = 0;
503 qtdemux->spherical_metadata->cropped_area_image_width = 0;
504 qtdemux->spherical_metadata->cropped_area_image_height = 0;
505 qtdemux->spherical_metadata->cropped_area_left = 0;
506 qtdemux->spherical_metadata->cropped_area_top = 0;
507 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
508 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
509 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
511 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
513 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
515 gst_qtdemux_reset (qtdemux, TRUE);
519 gst_qtdemux_finalize (GObject * object)
521 GstQTDemux *qtdemux = GST_QTDEMUX (object);
523 g_free (qtdemux->redirect_location);
525 G_OBJECT_CLASS (parent_class)->finalize (object);
529 gst_qtdemux_dispose (GObject * object)
531 GstQTDemux *qtdemux = GST_QTDEMUX (object);
533 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
534 if (qtdemux->spherical_metadata) {
535 if (qtdemux->spherical_metadata->stitching_software)
536 free(qtdemux->spherical_metadata->stitching_software);
537 if (qtdemux->spherical_metadata->projection_type)
538 free(qtdemux->spherical_metadata->projection_type);
539 if (qtdemux->spherical_metadata->stereo_mode)
540 free(qtdemux->spherical_metadata->stereo_mode);
542 free(qtdemux->spherical_metadata);
543 qtdemux->spherical_metadata = NULL;
545 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
547 if (qtdemux->adapter) {
548 g_object_unref (G_OBJECT (qtdemux->adapter));
549 qtdemux->adapter = NULL;
551 gst_tag_list_unref (qtdemux->tag_list);
552 gst_flow_combiner_free (qtdemux->flowcombiner);
553 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
555 g_queue_clear (&qtdemux->protection_event_queue);
557 g_free (qtdemux->cenc_aux_info_sizes);
558 qtdemux->cenc_aux_info_sizes = NULL;
559 g_mutex_clear (&qtdemux->expose_lock);
561 g_ptr_array_free (qtdemux->active_streams, TRUE);
562 g_ptr_array_free (qtdemux->old_streams, TRUE);
564 G_OBJECT_CLASS (parent_class)->dispose (object);
568 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
570 if (qtdemux->redirect_location) {
571 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
572 (_("This file contains no playable streams.")),
573 ("no known streams found, a redirect message has been posted"),
574 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
576 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
577 (_("This file contains no playable streams.")),
578 ("no known streams found"));
583 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
585 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
586 mem, size, 0, size, mem, free_func);
590 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
597 if (G_UNLIKELY (size == 0)) {
599 GstBuffer *tmp = NULL;
601 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
602 if (ret != GST_FLOW_OK)
605 gst_buffer_map (tmp, &map, GST_MAP_READ);
606 size = QT_UINT32 (map.data);
607 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
609 gst_buffer_unmap (tmp, &map);
610 gst_buffer_unref (tmp);
613 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
614 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
615 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
616 /* we're pulling header but already got most interesting bits,
617 * so never mind the rest (e.g. tags) (that much) */
618 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
622 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
623 (_("This file is invalid and cannot be played.")),
624 ("atom has bogus size %" G_GUINT64_FORMAT, size));
625 return GST_FLOW_ERROR;
629 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
631 if (G_UNLIKELY (flow != GST_FLOW_OK))
634 bsize = gst_buffer_get_size (*buf);
635 /* Catch short reads - we don't want any partial atoms */
636 if (G_UNLIKELY (bsize < size)) {
637 GST_WARNING_OBJECT (qtdemux,
638 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
639 gst_buffer_unref (*buf);
649 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
650 GstFormat src_format, gint64 src_value, GstFormat dest_format,
654 QtDemuxStream *stream = gst_pad_get_element_private (pad);
657 if (stream->subtype != FOURCC_vide) {
662 switch (src_format) {
663 case GST_FORMAT_TIME:
664 switch (dest_format) {
665 case GST_FORMAT_BYTES:{
666 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
672 *dest_value = stream->samples[index].offset;
674 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
675 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
676 GST_TIME_ARGS (src_value), *dest_value);
684 case GST_FORMAT_BYTES:
685 switch (dest_format) {
686 case GST_FORMAT_TIME:{
688 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
697 QTSTREAMTIME_TO_GSTTIME (stream,
698 stream->samples[index].timestamp);
699 GST_DEBUG_OBJECT (qtdemux,
700 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
701 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
720 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
722 gboolean res = FALSE;
724 *duration = GST_CLOCK_TIME_NONE;
726 if (qtdemux->duration != 0 &&
727 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
728 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
731 *duration = GST_CLOCK_TIME_NONE;
738 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
741 gboolean res = FALSE;
742 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
744 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
746 switch (GST_QUERY_TYPE (query)) {
747 case GST_QUERY_POSITION:{
750 gst_query_parse_position (query, &fmt, NULL);
751 if (fmt == GST_FORMAT_TIME
752 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
753 gst_query_set_position (query, GST_FORMAT_TIME,
754 qtdemux->segment.position);
759 case GST_QUERY_DURATION:{
762 gst_query_parse_duration (query, &fmt, NULL);
763 if (fmt == GST_FORMAT_TIME) {
764 /* First try to query upstream */
765 res = gst_pad_query_default (pad, parent, query);
767 GstClockTime duration;
768 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
769 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
776 case GST_QUERY_CONVERT:{
777 GstFormat src_fmt, dest_fmt;
778 gint64 src_value, dest_value = 0;
780 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
782 res = gst_qtdemux_src_convert (qtdemux, pad,
783 src_fmt, src_value, dest_fmt, &dest_value);
785 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
789 case GST_QUERY_FORMATS:
790 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
793 case GST_QUERY_SEEKING:{
797 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
799 if (fmt == GST_FORMAT_BYTES) {
800 /* We always refuse BYTES seeks from downstream */
804 /* try upstream first */
805 res = gst_pad_query_default (pad, parent, query);
808 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
809 if (fmt == GST_FORMAT_TIME) {
810 GstClockTime duration;
812 gst_qtdemux_get_duration (qtdemux, &duration);
814 if (!qtdemux->pullbased) {
817 /* we might be able with help from upstream */
819 q = gst_query_new_seeking (GST_FORMAT_BYTES);
820 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
821 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
822 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
826 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
832 case GST_QUERY_SEGMENT:
837 format = qtdemux->segment.format;
840 gst_segment_to_stream_time (&qtdemux->segment, format,
841 qtdemux->segment.start);
842 if ((stop = qtdemux->segment.stop) == -1)
843 stop = qtdemux->segment.duration;
845 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
847 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
852 res = gst_pad_query_default (pad, parent, query);
860 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
862 if (G_LIKELY (stream->pad)) {
863 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
864 GST_DEBUG_PAD_NAME (stream->pad));
866 if (!gst_tag_list_is_empty (stream->stream_tags)) {
867 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
868 stream->stream_tags);
869 gst_pad_push_event (stream->pad,
870 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
871 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
872 /* post message qtdemux tag (for early recive application) */
873 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
874 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
875 gst_tag_list_copy (stream->stream_tags)));
879 if (G_UNLIKELY (stream->send_global_tags)) {
880 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
882 gst_pad_push_event (stream->pad,
883 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
884 stream->send_global_tags = FALSE;
889 /* push event on all source pads; takes ownership of the event */
891 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
893 gboolean has_valid_stream = FALSE;
894 GstEventType etype = GST_EVENT_TYPE (event);
897 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
898 GST_EVENT_TYPE_NAME (event));
900 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
902 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
903 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
905 if ((pad = stream->pad)) {
906 has_valid_stream = TRUE;
908 if (etype == GST_EVENT_EOS) {
909 /* let's not send twice */
910 if (stream->sent_eos)
912 stream->sent_eos = TRUE;
915 gst_pad_push_event (pad, gst_event_ref (event));
919 gst_event_unref (event);
921 /* if it is EOS and there are no pads, post an error */
922 if (!has_valid_stream && etype == GST_EVENT_EOS) {
923 gst_qtdemux_post_no_playable_stream_error (qtdemux);
933 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
935 if ((gint64) s1->timestamp > *media_time)
937 if ((gint64) s1->timestamp == *media_time)
943 /* find the index of the sample that includes the data for @media_time using a
944 * binary search. Only to be called in optimized cases of linear search below.
946 * Returns the index of the sample with the corresponding *DTS*.
949 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
952 QtDemuxSample *result;
955 /* convert media_time to mov format */
957 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
959 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
960 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
961 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
963 if (G_LIKELY (result))
964 index = result - str->samples;
973 /* find the index of the sample that includes the data for @media_offset using a
976 * Returns the index of the sample.
979 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
980 QtDemuxStream * str, gint64 media_offset)
982 QtDemuxSample *result = str->samples;
985 if (result == NULL || str->n_samples == 0)
988 if (media_offset == result->offset)
992 while (index < str->n_samples - 1) {
993 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
996 if (media_offset < result->offset)
1007 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1012 /* find the index of the sample that includes the data for @media_time using a
1013 * linear search, and keeping in mind that not all samples may have been parsed
1014 * yet. If possible, it will delegate to binary search.
1016 * Returns the index of the sample.
1019 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1020 GstClockTime media_time)
1024 QtDemuxSample *sample;
1026 /* convert media_time to mov format */
1028 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1030 sample = str->samples;
1031 if (mov_time == sample->timestamp + sample->pts_offset)
1034 /* use faster search if requested time in already parsed range */
1035 sample = str->samples + str->stbl_index;
1036 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
1037 index = gst_qtdemux_find_index (qtdemux, str, media_time);
1038 sample = str->samples + index;
1040 while (index < str->n_samples - 1) {
1041 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1044 sample = str->samples + index + 1;
1045 if (mov_time < sample->timestamp) {
1046 sample = str->samples + index;
1054 /* sample->timestamp is now <= media_time, need to find the corresponding
1055 * PTS now by looking backwards */
1056 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
1058 sample = str->samples + index;
1066 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1071 /* find the index of the keyframe needed to decode the sample at @index
1072 * of stream @str, or of a subsequent keyframe (depending on @next)
1074 * Returns the index of the keyframe.
1077 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1078 guint32 index, gboolean next)
1080 guint32 new_index = index;
1082 if (index >= str->n_samples) {
1083 new_index = str->n_samples;
1087 /* all keyframes, return index */
1088 if (str->all_keyframe) {
1093 /* else search until we have a keyframe */
1094 while (new_index < str->n_samples) {
1095 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1098 if (str->samples[new_index].keyframe)
1110 if (new_index == str->n_samples) {
1111 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1116 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1117 "gave %u", next ? "after" : "before", index, new_index);
1124 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1129 /* find the segment for @time_position for @stream
1131 * Returns the index of the segment containing @time_position.
1132 * Returns the last segment and sets the @eos variable to TRUE
1133 * if the time is beyond the end. @eos may be NULL
1136 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1137 GstClockTime time_position)
1142 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1143 GST_TIME_ARGS (time_position));
1146 for (i = 0; i < stream->n_segments; i++) {
1147 QtDemuxSegment *segment = &stream->segments[i];
1149 GST_LOG_OBJECT (stream->pad,
1150 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1151 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1153 /* For the last segment we include stop_time in the last segment */
1154 if (i < stream->n_segments - 1) {
1155 if (segment->time <= time_position && time_position < segment->stop_time) {
1156 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1161 /* Last segment always matches */
1169 /* move the stream @str to the sample position @index.
1171 * Updates @str->sample_index and marks discontinuity if needed.
1174 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1177 /* no change needed */
1178 if (index == str->sample_index)
1181 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1184 /* position changed, we have a discont */
1185 str->sample_index = index;
1186 str->offset_in_sample = 0;
1187 /* Each time we move in the stream we store the position where we are
1189 str->from_sample = index;
1190 str->discont = TRUE;
1194 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1195 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1198 gint64 min_byte_offset = -1;
1201 min_offset = desired_time;
1203 /* for each stream, find the index of the sample in the segment
1204 * and move back to the previous keyframe. */
1205 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1207 guint32 index, kindex;
1209 GstClockTime media_start;
1210 GstClockTime media_time;
1211 GstClockTime seg_time;
1212 QtDemuxSegment *seg;
1213 gboolean empty_segment = FALSE;
1215 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1217 if (CUR_STREAM (str)->sparse && !use_sparse)
1220 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1221 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1223 /* get segment and time in the segment */
1224 seg = &str->segments[seg_idx];
1225 seg_time = (desired_time - seg->time) * seg->rate;
1227 while (QTSEGMENT_IS_EMPTY (seg)) {
1229 empty_segment = TRUE;
1230 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1233 if (seg_idx == str->n_segments)
1235 seg = &str->segments[seg_idx];
1238 if (seg_idx == str->n_segments) {
1239 /* FIXME track shouldn't have the last segment as empty, but if it
1240 * happens we better handle it */
1244 /* get the media time in the segment */
1245 media_start = seg->media_start + seg_time;
1247 /* get the index of the sample with media time */
1248 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1249 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1250 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1251 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1254 /* shift to next frame if we are looking for next keyframe */
1255 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1256 && index < str->stbl_index)
1259 if (!empty_segment) {
1260 /* find previous keyframe */
1261 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1263 /* we will settle for one before if none found after */
1264 if (next && kindex == -1)
1265 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1267 /* Update the requested time whenever a keyframe was found, to make it
1268 * accurate and avoid having the first buffer fall outside of the segment
1273 /* get timestamp of keyframe */
1274 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1275 GST_DEBUG_OBJECT (qtdemux,
1276 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1277 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1278 str->samples[kindex].offset);
1280 /* keyframes in the segment get a chance to change the
1281 * desired_offset. keyframes out of the segment are
1283 if (media_time >= seg->media_start) {
1284 GstClockTime seg_time;
1286 /* this keyframe is inside the segment, convert back to
1288 seg_time = (media_time - seg->media_start) + seg->time;
1289 if ((!next && (seg_time < min_offset)) ||
1290 (next && (seg_time > min_offset)))
1291 min_offset = seg_time;
1296 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1297 min_byte_offset = str->samples[index].offset;
1301 *key_time = min_offset;
1303 *key_offset = min_byte_offset;
1307 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1308 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1312 g_return_val_if_fail (format != NULL, FALSE);
1313 g_return_val_if_fail (cur != NULL, FALSE);
1314 g_return_val_if_fail (stop != NULL, FALSE);
1316 if (*format == GST_FORMAT_TIME)
1320 if (cur_type != GST_SEEK_TYPE_NONE)
1321 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1322 if (res && stop_type != GST_SEEK_TYPE_NONE)
1323 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1326 *format = GST_FORMAT_TIME;
1331 /* perform seek in push based mode:
1332 find BYTE position to move to based on time and delegate to upstream
1335 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1340 GstSeekType cur_type, stop_type;
1341 gint64 cur, stop, key_cur;
1344 gint64 original_stop;
1347 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1349 gst_event_parse_seek (event, &rate, &format, &flags,
1350 &cur_type, &cur, &stop_type, &stop);
1351 seqnum = gst_event_get_seqnum (event);
1353 /* Directly send the instant-rate-change event here before taking the
1354 * stream-lock so that it can be applied as soon as possible */
1355 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1358 /* instant rate change only supported if direction does not change. All
1359 * other requirements are already checked before creating the seek event
1360 * but let's double-check here to be sure */
1361 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1362 (qtdemux->segment.rate < 0 && rate > 0) ||
1363 cur_type != GST_SEEK_TYPE_NONE ||
1364 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1365 GST_ERROR_OBJECT (qtdemux,
1366 "Instant rate change seeks only supported in the "
1367 "same direction, without flushing and position change");
1371 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1372 (GstSegmentFlags) flags);
1373 gst_event_set_seqnum (ev, seqnum);
1374 gst_qtdemux_push_event (qtdemux, ev);
1378 /* only forward streaming and seeking is possible */
1380 goto unsupported_seek;
1382 /* convert to TIME if needed and possible */
1383 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1387 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1388 * the original stop position to use when upstream pushes the new segment
1390 original_stop = stop;
1393 /* find reasonable corresponding BYTE position,
1394 * also try to mind about keyframes, since we can not go back a bit for them
1396 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1397 * mostly just work, but let's not yet boldly go there ... */
1398 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1403 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1404 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1407 GST_OBJECT_LOCK (qtdemux);
1408 qtdemux->seek_offset = byte_cur;
1409 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1410 qtdemux->push_seek_start = cur;
1412 qtdemux->push_seek_start = key_cur;
1415 if (stop_type == GST_SEEK_TYPE_NONE) {
1416 qtdemux->push_seek_stop = qtdemux->segment.stop;
1418 qtdemux->push_seek_stop = original_stop;
1420 GST_OBJECT_UNLOCK (qtdemux);
1422 qtdemux->segment_seqnum = seqnum;
1423 /* BYTE seek event */
1424 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1426 gst_event_set_seqnum (event, seqnum);
1427 res = gst_pad_push_event (qtdemux->sinkpad, event);
1434 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1440 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1445 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1450 /* perform the seek.
1452 * We set all segment_indexes in the streams to unknown and
1453 * adjust the time_position to the desired position. this is enough
1454 * to trigger a segment switch in the streaming thread to start
1455 * streaming from the desired position.
1457 * Keyframe seeking is a little more complicated when dealing with
1458 * segments. Ideally we want to move to the previous keyframe in
1459 * the segment but there might not be a keyframe in the segment. In
1460 * fact, none of the segments could contain a keyframe. We take a
1461 * practical approach: seek to the previous keyframe in the segment,
1462 * if there is none, seek to the beginning of the segment.
1464 * Called with STREAM_LOCK
1467 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1468 guint32 seqnum, GstSeekFlags flags)
1470 gint64 desired_offset;
1473 desired_offset = segment->position;
1475 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1476 GST_TIME_ARGS (desired_offset));
1478 /* may not have enough fragmented info to do this adjustment,
1479 * and we can't scan (and probably should not) at this time with
1480 * possibly flushing upstream */
1481 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1483 gboolean next, before, after;
1485 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1486 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1487 next = after && !before;
1488 if (segment->rate < 0)
1491 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1493 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1494 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1495 desired_offset = min_offset;
1498 /* and set all streams to the final position */
1499 GST_OBJECT_LOCK (qtdemux);
1500 gst_flow_combiner_reset (qtdemux->flowcombiner);
1501 GST_OBJECT_UNLOCK (qtdemux);
1502 qtdemux->segment_seqnum = seqnum;
1503 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1504 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1506 stream->time_position = desired_offset;
1507 stream->accumulated_base = 0;
1508 stream->sample_index = -1;
1509 stream->offset_in_sample = 0;
1510 stream->segment_index = -1;
1511 stream->sent_eos = FALSE;
1512 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1514 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1515 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1517 segment->position = desired_offset;
1518 if (segment->rate >= 0) {
1519 segment->start = desired_offset;
1520 /* We need to update time as we update start in that direction */
1521 segment->time = desired_offset;
1523 /* we stop at the end */
1524 if (segment->stop == -1)
1525 segment->stop = segment->duration;
1527 segment->stop = desired_offset;
1530 if (qtdemux->fragmented)
1531 qtdemux->fragmented_seek_pending = TRUE;
1536 /* do a seek in pull based mode */
1538 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1543 GstSeekType cur_type, stop_type;
1545 gboolean flush, instant_rate_change;
1547 GstSegment seeksegment;
1548 guint32 seqnum = GST_SEQNUM_INVALID;
1549 GstEvent *flush_event;
1552 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1554 gst_event_parse_seek (event, &rate, &format, &flags,
1555 &cur_type, &cur, &stop_type, &stop);
1556 seqnum = gst_event_get_seqnum (event);
1558 /* we have to have a format as the segment format. Try to convert
1560 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1564 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1566 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1567 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1569 /* Directly send the instant-rate-change event here before taking the
1570 * stream-lock so that it can be applied as soon as possible */
1571 if (instant_rate_change) {
1574 /* instant rate change only supported if direction does not change. All
1575 * other requirements are already checked before creating the seek event
1576 * but let's double-check here to be sure */
1577 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1578 (qtdemux->segment.rate < 0 && rate > 0) ||
1579 cur_type != GST_SEEK_TYPE_NONE ||
1580 stop_type != GST_SEEK_TYPE_NONE || flush) {
1581 GST_ERROR_OBJECT (qtdemux,
1582 "Instant rate change seeks only supported in the "
1583 "same direction, without flushing and position change");
1587 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1588 (GstSegmentFlags) flags);
1589 gst_event_set_seqnum (ev, seqnum);
1590 gst_qtdemux_push_event (qtdemux, ev);
1594 /* stop streaming, either by flushing or by pausing the task */
1596 flush_event = gst_event_new_flush_start ();
1597 if (seqnum != GST_SEQNUM_INVALID)
1598 gst_event_set_seqnum (flush_event, seqnum);
1599 /* unlock upstream pull_range */
1600 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1601 /* make sure out loop function exits */
1602 gst_qtdemux_push_event (qtdemux, flush_event);
1604 /* non flushing seek, pause the task */
1605 gst_pad_pause_task (qtdemux->sinkpad);
1608 /* wait for streaming to finish */
1609 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1611 /* copy segment, we need this because we still need the old
1612 * segment when we close the current segment. */
1613 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1615 /* configure the segment with the seek variables */
1616 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1617 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1618 cur_type, cur, stop_type, stop, &update)) {
1620 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1622 /* now do the seek */
1623 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1626 /* prepare for streaming again */
1628 flush_event = gst_event_new_flush_stop (TRUE);
1629 if (seqnum != GST_SEQNUM_INVALID)
1630 gst_event_set_seqnum (flush_event, seqnum);
1632 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1633 gst_qtdemux_push_event (qtdemux, flush_event);
1636 /* commit the new segment */
1637 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1639 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1640 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1641 qtdemux->segment.format, qtdemux->segment.position);
1642 if (seqnum != GST_SEQNUM_INVALID)
1643 gst_message_set_seqnum (msg, seqnum);
1644 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1647 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1648 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1649 qtdemux->sinkpad, NULL);
1651 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1658 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1664 qtdemux_ensure_index (GstQTDemux * qtdemux)
1668 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1670 /* Build complete index */
1671 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1672 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1674 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1675 GST_LOG_OBJECT (qtdemux,
1676 "Building complete index of track-id %u for seeking failed!",
1686 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1689 gboolean res = TRUE;
1690 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1692 switch (GST_EVENT_TYPE (event)) {
1693 case GST_EVENT_RECONFIGURE:
1694 GST_OBJECT_LOCK (qtdemux);
1695 gst_flow_combiner_reset (qtdemux->flowcombiner);
1696 GST_OBJECT_UNLOCK (qtdemux);
1697 res = gst_pad_event_default (pad, parent, event);
1699 case GST_EVENT_SEEK:
1701 GstSeekFlags flags = 0;
1702 GstFormat seek_format;
1703 gboolean instant_rate_change;
1705 #ifndef GST_DISABLE_GST_DEBUG
1706 GstClockTime ts = gst_util_get_timestamp ();
1708 guint32 seqnum = gst_event_get_seqnum (event);
1710 qtdemux->received_seek = TRUE;
1712 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1714 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1716 if (seqnum == qtdemux->segment_seqnum) {
1717 GST_LOG_OBJECT (pad,
1718 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1719 gst_event_unref (event);
1723 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1724 /* seek should be handled by upstream, we might need to re-download fragments */
1725 GST_DEBUG_OBJECT (qtdemux,
1726 "let upstream handle seek for fragmented playback");
1730 if (seek_format == GST_FORMAT_BYTES) {
1731 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1732 gst_event_unref (event);
1736 gst_event_parse_seek_trickmode_interval (event,
1737 &qtdemux->trickmode_interval);
1739 /* Build complete index for seeking;
1740 * if not a fragmented file at least and we're really doing a seek,
1741 * not just an instant-rate-change */
1742 if (!qtdemux->fragmented && !instant_rate_change) {
1743 if (!qtdemux_ensure_index (qtdemux))
1746 #ifndef GST_DISABLE_GST_DEBUG
1747 ts = gst_util_get_timestamp () - ts;
1748 GST_INFO_OBJECT (qtdemux,
1749 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1751 if (qtdemux->pullbased) {
1752 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1753 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1754 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1756 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1757 && QTDEMUX_N_STREAMS (qtdemux)
1758 && !qtdemux->fragmented) {
1759 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1761 GST_DEBUG_OBJECT (qtdemux,
1762 "ignoring seek in push mode in current state");
1765 gst_event_unref (event);
1770 res = gst_pad_event_default (pad, parent, event);
1780 GST_ERROR_OBJECT (qtdemux, "Index failed");
1781 gst_event_unref (event);
1787 /* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1789 * If @fw is false, the coding order is explored backwards.
1791 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1792 * sample is found for that track.
1794 * The stream and sample index of the sample with the minimum offset in the direction explored
1795 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1797 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1798 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1799 * @_stream and @_index. */
1801 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1802 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1805 gint64 time, min_time;
1806 QtDemuxStream *stream;
1813 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1816 gboolean set_sample;
1818 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1825 i = str->n_samples - 1;
1829 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1830 if (str->samples[i].size == 0)
1833 if (fw && (str->samples[i].offset < byte_pos))
1836 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1839 /* move stream to first available sample */
1841 gst_qtdemux_move_stream (qtdemux, str, i);
1845 /* avoid index from sparse streams since they might be far away */
1846 if (!CUR_STREAM (str)->sparse) {
1847 /* determine min/max time */
1848 time = QTSAMPLE_PTS (str, &str->samples[i]);
1849 if (min_time == -1 || (!fw && time > min_time) ||
1850 (fw && time < min_time)) {
1854 /* determine stream with leading sample, to get its position */
1856 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1857 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1865 /* no sample for this stream, mark eos */
1867 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1878 /* Copied from mpegtsbase code */
1879 /* FIXME: replace this function when we add new util function for stream-id creation */
1881 _get_upstream_id (GstQTDemux * demux)
1883 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1886 /* Try to create one from the upstream URI, else use a randome number */
1890 /* Try to generate one from the URI query and
1891 * if it fails take a random number instead */
1892 query = gst_query_new_uri ();
1893 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1894 gst_query_parse_uri (query, &uri);
1900 /* And then generate an SHA256 sum of the URI */
1901 cs = g_checksum_new (G_CHECKSUM_SHA256);
1902 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1904 upstream_id = g_strdup (g_checksum_get_string (cs));
1905 g_checksum_free (cs);
1907 /* Just get some random number if the URI query fails */
1908 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1909 "implementing a deterministic way of creating a stream-id");
1911 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1912 g_random_int (), g_random_int ());
1915 gst_query_unref (query);
1920 static QtDemuxStream *
1921 _create_stream (GstQTDemux * demux, guint32 track_id)
1923 QtDemuxStream *stream;
1926 stream = g_new0 (QtDemuxStream, 1);
1927 stream->demux = demux;
1928 stream->track_id = track_id;
1929 upstream_id = _get_upstream_id (demux);
1930 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1931 g_free (upstream_id);
1932 /* new streams always need a discont */
1933 stream->discont = TRUE;
1934 /* we enable clipping for raw audio/video streams */
1935 stream->need_clip = FALSE;
1936 stream->process_func = NULL;
1937 stream->segment_index = -1;
1938 stream->time_position = 0;
1939 stream->sample_index = -1;
1940 stream->offset_in_sample = 0;
1941 stream->new_stream = TRUE;
1942 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1943 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1944 stream->protected = FALSE;
1945 stream->protection_scheme_type = 0;
1946 stream->protection_scheme_version = 0;
1947 stream->protection_scheme_info = NULL;
1948 stream->n_samples_moof = 0;
1949 stream->duration_moof = 0;
1950 stream->duration_last_moof = 0;
1951 stream->alignment = 1;
1952 stream->stream_tags = gst_tag_list_new_empty ();
1953 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1954 g_queue_init (&stream->protection_scheme_event_queue);
1955 stream->ref_count = 1;
1956 /* consistent default for push based mode */
1957 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1962 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1964 GstStructure *structure;
1965 const gchar *variant;
1966 const GstCaps *mediacaps = NULL;
1968 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1970 structure = gst_caps_get_structure (caps, 0);
1971 variant = gst_structure_get_string (structure, "variant");
1973 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1974 QtDemuxStream *stream;
1975 const GValue *value;
1977 demux->fragmented = TRUE;
1978 demux->mss_mode = TRUE;
1980 if (QTDEMUX_N_STREAMS (demux) > 1) {
1981 /* can't do this, we can only renegotiate for another mss format */
1985 value = gst_structure_get_value (structure, "media-caps");
1988 const GValue *timescale_v;
1990 /* TODO update when stream changes during playback */
1992 if (QTDEMUX_N_STREAMS (demux) == 0) {
1993 stream = _create_stream (demux, 1);
1994 g_ptr_array_add (demux->active_streams, stream);
1995 /* mss has no stsd/stsd entry, use id 0 as default */
1996 stream->stsd_entries_length = 1;
1997 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
1998 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2000 stream = QTDEMUX_NTH_STREAM (demux, 0);
2003 timescale_v = gst_structure_get_value (structure, "timescale");
2005 stream->timescale = g_value_get_uint64 (timescale_v);
2007 /* default mss timescale */
2008 stream->timescale = 10000000;
2010 demux->timescale = stream->timescale;
2012 mediacaps = gst_value_get_caps (value);
2013 if (!CUR_STREAM (stream)->caps
2014 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2015 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2017 stream->new_caps = TRUE;
2019 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2020 structure = gst_caps_get_structure (mediacaps, 0);
2021 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2022 stream->subtype = FOURCC_vide;
2024 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2025 gst_structure_get_int (structure, "height",
2026 &CUR_STREAM (stream)->height);
2027 gst_structure_get_fraction (structure, "framerate",
2028 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2029 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2031 stream->subtype = FOURCC_soun;
2032 gst_structure_get_int (structure, "channels",
2033 &CUR_STREAM (stream)->n_channels);
2034 gst_structure_get_int (structure, "rate", &rate);
2035 CUR_STREAM (stream)->rate = rate;
2036 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
2037 if (gst_structure_has_field (structure, "original-media-type")) {
2038 const gchar *media_type =
2039 gst_structure_get_string (structure, "original-media-type");
2040 if (g_str_has_prefix (media_type, "video")) {
2041 stream->subtype = FOURCC_vide;
2042 } else if (g_str_has_prefix (media_type, "audio")) {
2043 stream->subtype = FOURCC_soun;
2048 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2050 demux->mss_mode = FALSE;
2057 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2061 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2062 gst_pad_stop_task (qtdemux->sinkpad);
2064 if (hard || qtdemux->upstream_format_is_time) {
2065 qtdemux->state = QTDEMUX_STATE_INITIAL;
2066 qtdemux->neededbytes = 16;
2067 qtdemux->todrop = 0;
2068 qtdemux->pullbased = FALSE;
2069 g_clear_pointer (&qtdemux->redirect_location, g_free);
2070 qtdemux->first_mdat = -1;
2071 qtdemux->header_size = 0;
2072 qtdemux->mdatoffset = -1;
2073 qtdemux->restoredata_offset = -1;
2074 if (qtdemux->mdatbuffer)
2075 gst_buffer_unref (qtdemux->mdatbuffer);
2076 if (qtdemux->restoredata_buffer)
2077 gst_buffer_unref (qtdemux->restoredata_buffer);
2078 qtdemux->mdatbuffer = NULL;
2079 qtdemux->restoredata_buffer = NULL;
2080 qtdemux->mdatleft = 0;
2081 qtdemux->mdatsize = 0;
2082 if (qtdemux->comp_brands)
2083 gst_buffer_unref (qtdemux->comp_brands);
2084 qtdemux->comp_brands = NULL;
2085 qtdemux->last_moov_offset = -1;
2086 if (qtdemux->moov_node_compressed) {
2087 g_node_destroy (qtdemux->moov_node_compressed);
2088 if (qtdemux->moov_node)
2089 g_free (qtdemux->moov_node->data);
2091 qtdemux->moov_node_compressed = NULL;
2092 if (qtdemux->moov_node)
2093 g_node_destroy (qtdemux->moov_node);
2094 qtdemux->moov_node = NULL;
2095 if (qtdemux->tag_list)
2096 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2097 qtdemux->tag_list = gst_tag_list_new_empty ();
2098 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2100 if (qtdemux->element_index)
2101 gst_object_unref (qtdemux->element_index);
2102 qtdemux->element_index = NULL;
2104 qtdemux->major_brand = 0;
2105 qtdemux->upstream_format_is_time = FALSE;
2106 qtdemux->upstream_seekable = FALSE;
2107 qtdemux->upstream_size = 0;
2109 qtdemux->fragment_start = -1;
2110 qtdemux->fragment_start_offset = -1;
2111 qtdemux->duration = 0;
2112 qtdemux->moof_offset = 0;
2113 qtdemux->chapters_track_id = 0;
2114 qtdemux->have_group_id = FALSE;
2115 qtdemux->group_id = G_MAXUINT;
2117 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2119 g_queue_clear (&qtdemux->protection_event_queue);
2121 qtdemux->received_seek = FALSE;
2122 qtdemux->first_moof_already_parsed = FALSE;
2124 qtdemux->offset = 0;
2125 gst_adapter_clear (qtdemux->adapter);
2126 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2127 qtdemux->need_segment = TRUE;
2130 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2131 qtdemux->trickmode_interval = 0;
2132 g_ptr_array_set_size (qtdemux->active_streams, 0);
2133 g_ptr_array_set_size (qtdemux->old_streams, 0);
2134 qtdemux->n_video_streams = 0;
2135 qtdemux->n_audio_streams = 0;
2136 qtdemux->n_sub_streams = 0;
2137 qtdemux->exposed = FALSE;
2138 qtdemux->fragmented = FALSE;
2139 qtdemux->mss_mode = FALSE;
2140 gst_caps_replace (&qtdemux->media_caps, NULL);
2141 qtdemux->timescale = 0;
2142 qtdemux->got_moov = FALSE;
2143 qtdemux->cenc_aux_info_offset = 0;
2144 qtdemux->cenc_aux_info_sizes = NULL;
2145 qtdemux->cenc_aux_sample_count = 0;
2146 if (qtdemux->protection_system_ids) {
2147 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2148 qtdemux->protection_system_ids = NULL;
2150 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2151 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2152 GST_BIN_FLAG_STREAMS_AWARE);
2154 if (qtdemux->preferred_protection_system_id) {
2155 g_free (qtdemux->preferred_protection_system_id);
2156 qtdemux->preferred_protection_system_id = NULL;
2158 } else if (qtdemux->mss_mode) {
2159 gst_flow_combiner_reset (qtdemux->flowcombiner);
2160 g_ptr_array_foreach (qtdemux->active_streams,
2161 (GFunc) gst_qtdemux_stream_clear, NULL);
2163 gst_flow_combiner_reset (qtdemux->flowcombiner);
2164 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2165 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2166 stream->sent_eos = FALSE;
2167 stream->time_position = 0;
2168 stream->accumulated_base = 0;
2169 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2175 /* Maps the @segment to the qt edts internal segments and pushes
2176 * the corresponding segment event.
2178 * If it ends up being at a empty segment, a gap will be pushed and the next
2179 * edts segment will be activated in sequence.
2181 * To be used in push-mode only */
2183 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2187 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2188 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2190 stream->time_position = segment->start;
2192 /* in push mode we should be guaranteed that we will have empty segments
2193 * at the beginning and then one segment after, other scenarios are not
2194 * supported and are discarded when parsing the edts */
2195 for (i = 0; i < stream->n_segments; i++) {
2196 if (stream->segments[i].stop_time > segment->start) {
2197 /* push the empty segment and move to the next one */
2198 gst_qtdemux_activate_segment (qtdemux, stream, i,
2199 stream->time_position);
2200 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2201 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2202 stream->time_position);
2204 /* accumulate previous segments */
2205 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2206 stream->accumulated_base +=
2207 (stream->segment.stop -
2208 stream->segment.start) / ABS (stream->segment.rate);
2212 g_assert (i == stream->n_segments - 1);
2219 gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2230 for (i = 0; i < len; i++) {
2231 QtDemuxStream *stream = g_ptr_array_index (src, i);
2233 #ifndef GST_DISABLE_GST_DEBUG
2234 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2235 stream, GST_STR_NULL (stream->stream_id), dest);
2237 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2240 g_ptr_array_set_size (src, 0);
2244 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2247 GstQTDemux *demux = GST_QTDEMUX (parent);
2248 gboolean res = TRUE;
2250 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2252 switch (GST_EVENT_TYPE (event)) {
2253 case GST_EVENT_SEGMENT:
2256 QtDemuxStream *stream;
2260 /* some debug output */
2261 gst_event_copy_segment (event, &segment);
2262 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2265 if (segment.format == GST_FORMAT_TIME) {
2266 demux->upstream_format_is_time = TRUE;
2267 demux->segment_seqnum = gst_event_get_seqnum (event);
2269 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2270 "not in time format");
2272 /* chain will send initial newsegment after pads have been added */
2273 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2274 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2279 /* check if this matches a time seek we received previously
2280 * FIXME for backwards compatibility reasons we use the
2281 * seek_offset here to compare. In the future we might want to
2282 * change this to use the seqnum as it uniquely should identify
2283 * the segment that corresponds to the seek. */
2284 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2285 ", received segment offset %" G_GINT64_FORMAT,
2286 demux->seek_offset, segment.start);
2287 if (segment.format == GST_FORMAT_BYTES
2288 && demux->seek_offset == segment.start) {
2289 GST_OBJECT_LOCK (demux);
2290 offset = segment.start;
2292 segment.format = GST_FORMAT_TIME;
2293 segment.start = demux->push_seek_start;
2294 segment.stop = demux->push_seek_stop;
2295 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2296 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2297 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2298 GST_OBJECT_UNLOCK (demux);
2301 /* we only expect a BYTE segment, e.g. following a seek */
2302 if (segment.format == GST_FORMAT_BYTES) {
2303 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2304 offset = segment.start;
2306 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2307 NULL, (gint64 *) & segment.start);
2308 if ((gint64) segment.start < 0)
2311 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2312 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2313 NULL, (gint64 *) & segment.stop);
2314 /* keyframe seeking should already arrange for start >= stop,
2315 * but make sure in other rare cases */
2316 segment.stop = MAX (segment.stop, segment.start);
2318 } else if (segment.format == GST_FORMAT_TIME) {
2319 /* push all data on the adapter before starting this
2321 gst_qtdemux_process_adapter (demux, TRUE);
2323 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2327 /* We shouldn't modify upstream driven TIME FORMAT segment */
2328 if (!demux->upstream_format_is_time) {
2329 /* accept upstream's notion of segment and distribute along */
2330 segment.format = GST_FORMAT_TIME;
2331 segment.position = segment.time = segment.start;
2332 segment.duration = demux->segment.duration;
2333 segment.base = gst_segment_to_running_time (&demux->segment,
2334 GST_FORMAT_TIME, demux->segment.position);
2337 gst_segment_copy_into (&segment, &demux->segment);
2338 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2340 /* map segment to internal qt segments and push on each stream */
2341 if (QTDEMUX_N_STREAMS (demux)) {
2342 demux->need_segment = TRUE;
2343 gst_qtdemux_check_send_pending_segment (demux);
2346 /* clear leftover in current segment, if any */
2347 gst_adapter_clear (demux->adapter);
2349 /* set up streaming thread */
2350 demux->offset = offset;
2351 if (demux->upstream_format_is_time) {
2352 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2353 "set values to restart reading from a new atom");
2354 demux->neededbytes = 16;
2357 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2360 demux->todrop = stream->samples[idx].offset - offset;
2361 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2363 /* set up for EOS */
2364 demux->neededbytes = -1;
2369 gst_event_unref (event);
2373 case GST_EVENT_FLUSH_START:
2375 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2376 gst_event_unref (event);
2379 QTDEMUX_EXPOSE_LOCK (demux);
2380 res = gst_pad_event_default (demux->sinkpad, parent, event);
2381 QTDEMUX_EXPOSE_UNLOCK (demux);
2384 case GST_EVENT_FLUSH_STOP:
2388 dur = demux->segment.duration;
2389 gst_qtdemux_reset (demux, FALSE);
2390 demux->segment.duration = dur;
2392 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2393 gst_event_unref (event);
2399 /* If we are in push mode, and get an EOS before we've seen any streams,
2400 * then error out - we have nowhere to send the EOS */
2401 if (!demux->pullbased) {
2403 gboolean has_valid_stream = FALSE;
2404 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2405 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2406 has_valid_stream = TRUE;
2410 if (!has_valid_stream)
2411 gst_qtdemux_post_no_playable_stream_error (demux);
2413 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2414 (guint) gst_adapter_available (demux->adapter));
2415 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2421 case GST_EVENT_CAPS:{
2422 GstCaps *caps = NULL;
2424 gst_event_parse_caps (event, &caps);
2425 gst_qtdemux_setcaps (demux, caps);
2427 gst_event_unref (event);
2430 case GST_EVENT_PROTECTION:
2432 const gchar *system_id = NULL;
2434 gst_event_parse_protection (event, &system_id, NULL, NULL);
2435 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2437 gst_qtdemux_append_protection_system_id (demux, system_id);
2438 /* save the event for later, for source pads that have not been created */
2439 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2440 /* send it to all pads that already exist */
2441 gst_qtdemux_push_event (demux, event);
2445 case GST_EVENT_STREAM_START:
2448 gst_event_unref (event);
2450 /* Drain all the buffers */
2451 gst_qtdemux_process_adapter (demux, TRUE);
2452 gst_qtdemux_reset (demux, FALSE);
2453 /* We expect new moov box after new stream-start event */
2454 if (demux->exposed) {
2455 gst_qtdemux_stream_concat (demux,
2456 demux->old_streams, demux->active_streams);
2465 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2472 gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2475 GstQTDemux *demux = GST_QTDEMUX (parent);
2476 gboolean res = FALSE;
2478 switch (GST_QUERY_TYPE (query)) {
2479 case GST_QUERY_BITRATE:
2481 GstClockTime duration;
2483 /* populate demux->upstream_size if not done yet */
2484 gst_qtdemux_check_seekability (demux);
2486 if (demux->upstream_size != -1
2487 && gst_qtdemux_get_duration (demux, &duration)) {
2489 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2492 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2493 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2494 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2496 /* TODO: better results based on ranges/index tables */
2497 gst_query_set_bitrate (query, bitrate);
2503 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2513 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2515 GstQTDemux *demux = GST_QTDEMUX (element);
2517 GST_OBJECT_LOCK (demux);
2518 if (demux->element_index)
2519 gst_object_unref (demux->element_index);
2521 demux->element_index = gst_object_ref (index);
2523 demux->element_index = NULL;
2525 GST_OBJECT_UNLOCK (demux);
2526 /* object lock might be taken again */
2528 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2529 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2530 demux->element_index, demux->index_id);
2534 gst_qtdemux_get_index (GstElement * element)
2536 GstIndex *result = NULL;
2537 GstQTDemux *demux = GST_QTDEMUX (element);
2539 GST_OBJECT_LOCK (demux);
2540 if (demux->element_index)
2541 result = gst_object_ref (demux->element_index);
2542 GST_OBJECT_UNLOCK (demux);
2544 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2551 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2553 g_free ((gpointer) stream->stco.data);
2554 stream->stco.data = NULL;
2555 g_free ((gpointer) stream->stsz.data);
2556 stream->stsz.data = NULL;
2557 g_free ((gpointer) stream->stsc.data);
2558 stream->stsc.data = NULL;
2559 g_free ((gpointer) stream->stts.data);
2560 stream->stts.data = NULL;
2561 g_free ((gpointer) stream->stss.data);
2562 stream->stss.data = NULL;
2563 g_free ((gpointer) stream->stps.data);
2564 stream->stps.data = NULL;
2565 g_free ((gpointer) stream->ctts.data);
2566 stream->ctts.data = NULL;
2570 gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2572 g_free (stream->segments);
2573 stream->segments = NULL;
2574 stream->segment_index = -1;
2575 stream->accumulated_base = 0;
2579 gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2581 g_free (stream->samples);
2582 stream->samples = NULL;
2583 gst_qtdemux_stbl_free (stream);
2586 g_free (stream->ra_entries);
2587 stream->ra_entries = NULL;
2588 stream->n_ra_entries = 0;
2590 stream->sample_index = -1;
2591 stream->stbl_index = -1;
2592 stream->n_samples = 0;
2593 stream->time_position = 0;
2595 stream->n_samples_moof = 0;
2596 stream->duration_moof = 0;
2597 stream->duration_last_moof = 0;
2601 gst_qtdemux_stream_clear (QtDemuxStream * stream)
2604 if (stream->allocator)
2605 gst_object_unref (stream->allocator);
2606 while (stream->buffers) {
2607 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2608 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2610 for (i = 0; i < stream->stsd_entries_length; i++) {
2611 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2612 if (entry->rgb8_palette) {
2613 gst_memory_unref (entry->rgb8_palette);
2614 entry->rgb8_palette = NULL;
2616 entry->sparse = FALSE;
2619 if (stream->stream_tags)
2620 gst_tag_list_unref (stream->stream_tags);
2622 stream->stream_tags = gst_tag_list_new_empty ();
2623 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2624 g_free (stream->redirect_uri);
2625 stream->redirect_uri = NULL;
2626 stream->sent_eos = FALSE;
2627 stream->protected = FALSE;
2628 if (stream->protection_scheme_info) {
2629 if (stream->protection_scheme_type == FOURCC_cenc
2630 || stream->protection_scheme_type == FOURCC_cbcs) {
2631 QtDemuxCencSampleSetInfo *info =
2632 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2633 if (info->default_properties)
2634 gst_structure_free (info->default_properties);
2635 if (info->crypto_info)
2636 g_ptr_array_free (info->crypto_info, TRUE);
2638 if (stream->protection_scheme_type == FOURCC_aavd) {
2639 QtDemuxAavdEncryptionInfo *info =
2640 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
2641 if (info->default_properties)
2642 gst_structure_free (info->default_properties);
2644 g_free (stream->protection_scheme_info);
2645 stream->protection_scheme_info = NULL;
2647 stream->protection_scheme_type = 0;
2648 stream->protection_scheme_version = 0;
2649 g_queue_foreach (&stream->protection_scheme_event_queue,
2650 (GFunc) gst_event_unref, NULL);
2651 g_queue_clear (&stream->protection_scheme_event_queue);
2652 gst_qtdemux_stream_flush_segments_data (stream);
2653 gst_qtdemux_stream_flush_samples_data (stream);
2657 gst_qtdemux_stream_reset (QtDemuxStream * stream)
2660 gst_qtdemux_stream_clear (stream);
2661 for (i = 0; i < stream->stsd_entries_length; i++) {
2662 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2664 gst_caps_unref (entry->caps);
2668 g_free (stream->stsd_entries);
2669 stream->stsd_entries = NULL;
2670 stream->stsd_entries_length = 0;
2673 static QtDemuxStream *
2674 gst_qtdemux_stream_ref (QtDemuxStream * stream)
2676 g_atomic_int_add (&stream->ref_count, 1);
2682 gst_qtdemux_stream_unref (QtDemuxStream * stream)
2684 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2685 gst_qtdemux_stream_reset (stream);
2686 gst_tag_list_unref (stream->stream_tags);
2688 GstQTDemux *demux = stream->demux;
2689 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2690 GST_OBJECT_LOCK (demux);
2691 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2692 GST_OBJECT_UNLOCK (demux);
2694 g_free (stream->stream_id);
2699 static GstStateChangeReturn
2700 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2702 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2703 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2705 switch (transition) {
2706 case GST_STATE_CHANGE_READY_TO_PAUSED:
2707 gst_qtdemux_reset (qtdemux, TRUE);
2713 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2715 switch (transition) {
2716 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2717 gst_qtdemux_reset (qtdemux, TRUE);
2728 gst_qtdemux_set_context (GstElement * element, GstContext * context)
2730 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2732 g_return_if_fail (GST_IS_CONTEXT (context));
2734 if (gst_context_has_context_type (context,
2735 "drm-preferred-decryption-system-id")) {
2736 const GstStructure *s;
2738 s = gst_context_get_structure (context);
2739 g_free (qtdemux->preferred_protection_system_id);
2740 qtdemux->preferred_protection_system_id =
2741 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2742 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2743 qtdemux->preferred_protection_system_id);
2746 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2750 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2752 /* counts as header data */
2753 qtdemux->header_size += length;
2755 /* only consider at least a sufficiently complete ftyp atom */
2759 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2760 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2761 GST_FOURCC_ARGS (qtdemux->major_brand));
2762 if (qtdemux->comp_brands)
2763 gst_buffer_unref (qtdemux->comp_brands);
2764 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2765 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2769 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2771 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2772 const char *xml_str, const char *param_name, int *value)
2774 char *value_start, *value_end, *endptr;
2775 const short value_length_max = 12;
2776 char init_view_ret[12];
2777 int value_length = 0;
2780 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2783 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2788 value_start += strlen (param_name);
2789 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2792 value_end = strchr (value_start, '<');
2794 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2798 value_length = value_end - value_start;
2799 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2800 || (value_start[value_length - 1] == '\t')))
2803 if (value_start[i] == '+' || value_start[i] == '-')
2805 while (i < value_length) {
2806 if (value_start[i] < '0' || value_start[i] > '9') {
2807 GST_ERROR_OBJECT (qtdemux,
2808 "error: incorrect value, integer was expected\n");
2814 if (value_length >= value_length_max || value_length < 1) {
2815 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2819 strncpy (init_view_ret, value_start, value_length_max);
2820 init_view_ret[value_length] = '\0';
2822 *value = strtol (init_view_ret, &endptr, 10);
2823 if (endptr == init_view_ret) {
2824 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2832 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2833 const char *xml_str, const char *param_name, char **value)
2835 char *value_start, *value_end;
2836 const short value_length_max = 256;
2837 int value_length = 0;
2839 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2842 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2847 value_start += strlen (param_name);
2848 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2851 value_end = strchr (value_start, '<');
2853 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2857 value_length = value_end - value_start;
2858 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2859 || (value_start[value_length - 1] == '\t')))
2862 if (value_length >= value_length_max || value_length < 1) {
2863 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2867 *value = strndup(value_start, value_length);
2873 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2874 const char *xml_str, const char *param_name, gboolean * value)
2876 char *value_start, *value_end;
2877 int value_length = 0;
2879 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2882 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2887 value_start += strlen (param_name);
2888 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2891 value_end = strchr (value_start, '<');
2893 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2897 value_length = value_end - value_start;
2898 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2899 || (value_start[value_length - 1] == '\t')))
2902 if (value_length < 1) {
2903 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2907 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
2913 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
2915 const char is_spherical_str[] = "<GSpherical:Spherical>";
2916 const char is_stitched_str[] = "<GSpherical:Stitched>";
2917 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2918 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2919 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2920 const char source_count_str[] = "<GSpherical:SourceCount>";
2921 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2922 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2923 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2924 const char timestamp_str[] = "<GSpherical:Timestamp>";
2925 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2926 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2927 const char cropped_area_image_width_str[] =
2928 "<GSpherical:CroppedAreaImageWidthPixels>";
2929 const char cropped_area_image_height_str[] =
2930 "<GSpherical:CroppedAreaImageHeightPixels>";
2931 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
2932 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
2934 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
2936 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
2937 (gboolean *) & spherical_metadata->is_spherical);
2938 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
2939 (gboolean *) & spherical_metadata->is_stitched);
2941 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
2942 _get_string_value_from_xml_string (qtdemux, xmlStr,
2943 stitching_software_str, &spherical_metadata->stitching_software);
2944 _get_string_value_from_xml_string (qtdemux, xmlStr,
2945 projection_type_str, &spherical_metadata->projection_type);
2946 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
2947 &spherical_metadata->stereo_mode);
2948 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
2949 &spherical_metadata->source_count);
2950 _get_int_value_from_xml_string (qtdemux, xmlStr,
2951 init_view_heading_str, &spherical_metadata->init_view_heading);
2952 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
2953 &spherical_metadata->init_view_pitch);
2954 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
2955 &spherical_metadata->init_view_roll);
2956 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
2957 &spherical_metadata->timestamp);
2958 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
2959 &spherical_metadata->full_pano_width_pixels);
2960 _get_int_value_from_xml_string (qtdemux, xmlStr,
2961 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
2962 _get_int_value_from_xml_string (qtdemux, xmlStr,
2963 cropped_area_image_width_str,
2964 &spherical_metadata->cropped_area_image_width);
2965 _get_int_value_from_xml_string (qtdemux, xmlStr,
2966 cropped_area_image_height_str,
2967 &spherical_metadata->cropped_area_image_height);
2968 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
2969 &spherical_metadata->cropped_area_left);
2970 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
2971 &spherical_metadata->cropped_area_top);
2978 gst_tag_register_spherical_tags (void) {
2979 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
2982 _("Flag indicating if the video is a spherical video"),
2984 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
2987 _("Flag indicating if the video is stitched"),
2989 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
2991 _("tag-stitching-software"),
2992 _("Software used to stitch the spherical video"),
2994 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
2996 _("tag-projection-type"),
2997 _("Projection type used in the video frames"),
2999 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
3001 _("tag-stereo-mode"),
3002 _("Description of stereoscopic 3D layout"),
3004 gst_tag_register ("source_count", GST_TAG_FLAG_META,
3006 _("tag-source-count"),
3007 _("Number of cameras used to create the spherical video"),
3009 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
3011 _("tag-init-view-heading"),
3012 _("The heading angle of the initial view in degrees"),
3014 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
3016 _("tag-init-view-pitch"),
3017 _("The pitch angle of the initial view in degrees"),
3019 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
3021 _("tag-init-view-roll"),
3022 _("The roll angle of the initial view in degrees"),
3024 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
3027 _("Epoch timestamp of when the first frame in the video was recorded"),
3029 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
3031 _("tag-full-pano-width"),
3032 _("Width of the encoded video frame in pixels"),
3034 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
3036 _("tag-full-pano-height"),
3037 _("Height of the encoded video frame in pixels"),
3039 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
3041 _("tag-cropped-area-image-width"),
3042 _("Width of the video frame to display (e.g. cropping)"),
3044 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
3046 _("tag-cropped-area-image-height"),
3047 _("Height of the video frame to display (e.g. cropping)"),
3049 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
3051 _("tag-cropped-area-left"),
3052 _("Column where the left edge of the image was cropped from the"
3053 " full sized panorama"),
3055 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3057 _("tag-cropped-area-top"),
3058 _("Row where the top edge of the image was cropped from the"
3059 " full sized panorama"),
3061 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3063 _("tag-ambisonic-type"),
3064 _("Specifies the type of ambisonic audio represented"),
3066 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3068 _("tag-ambisonic-format"),
3069 _("Specifies the ambisonic audio format"),
3071 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3073 _("tag-ambisonic-order"),
3074 _("Specifies the ambisonic audio channel order"),
3081 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3083 GstTagList *taglist;
3084 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3086 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3087 spherical_metadata->is_spherical);
3088 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3089 spherical_metadata->is_stitched);
3090 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3091 spherical_metadata->stitching_software);
3092 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3093 spherical_metadata->projection_type);
3094 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3095 spherical_metadata->stereo_mode);
3096 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3097 spherical_metadata->source_count);
3098 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3099 spherical_metadata->init_view_heading);
3100 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3101 spherical_metadata->init_view_pitch);
3102 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3103 spherical_metadata->init_view_roll);
3104 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3105 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3106 spherical_metadata->full_pano_width_pixels);
3107 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3108 spherical_metadata->full_pano_height_pixels);
3109 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3110 spherical_metadata->cropped_area_image_width);
3111 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3112 spherical_metadata->cropped_area_image_height);
3113 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3114 spherical_metadata->cropped_area_left);
3115 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3116 spherical_metadata->cropped_area_top);
3117 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3118 spherical_metadata->ambisonic_type);
3119 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3120 spherical_metadata->ambisonic_order);
3121 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3122 spherical_metadata->ambisonic_format);
3124 taglist = gst_tag_list_new_empty ();
3125 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3126 "is_spherical", spherical_metadata->is_spherical,
3127 "is_stitched", spherical_metadata->is_stitched,
3128 "source_count", spherical_metadata->source_count,
3129 "init_view_heading", spherical_metadata->init_view_heading,
3130 "init_view_pitch", spherical_metadata->init_view_pitch,
3131 "init_view_roll", spherical_metadata->init_view_roll,
3132 "timestamp", spherical_metadata->timestamp,
3133 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3134 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3135 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3136 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3137 "cropped_area_left", spherical_metadata->cropped_area_left,
3138 "cropped_area_top", spherical_metadata->cropped_area_top,
3139 "ambisonic_type", spherical_metadata->ambisonic_type,
3140 "ambisonic_format", spherical_metadata->ambisonic_format,
3141 "ambisonic_order", spherical_metadata->ambisonic_order,
3144 if (spherical_metadata->stitching_software)
3145 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3146 "stitching_software", spherical_metadata->stitching_software,
3148 if (spherical_metadata->projection_type)
3149 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3150 "projection_type", spherical_metadata->projection_type,
3152 if (spherical_metadata->stereo_mode)
3153 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3154 "stereo_mode", spherical_metadata->stereo_mode,
3157 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3158 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3159 gst_tag_list_copy (taglist)));
3161 gst_tag_list_unref(taglist);
3167 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3172 guint8 ambisonic_type = 0;
3173 guint32 ambisonic_order = 0;
3174 guint8 ambisonic_channel_ordering = 0;
3175 guint8 ambisonic_normalization = 0;
3176 guint32 num_channels = 0;
3177 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3181 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3183 qtdemux->header_size += length;
3184 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3186 if (length <= offset + 16) {
3187 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3191 version = QT_UINT8 (buffer + offset);
3192 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3193 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3194 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3195 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3196 num_channels = QT_UINT32 (buffer + offset + 8);
3197 for (i = 0; i < num_channels; ++i)
3198 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3200 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3201 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3202 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3203 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3204 ambisonic_channel_ordering);
3205 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3206 ambisonic_normalization);
3207 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3208 for (i = 0; i < num_channels; ++i)
3209 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3211 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3212 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3213 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3215 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3216 if (num_channels == 4) {
3217 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3219 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3220 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3221 && (channel_map[0] == 0) && (channel_map[1] == 1)
3222 && (channel_map[2] == 2) && (channel_map[3] == 3))
3223 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3225 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3226 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3227 && (channel_map[0] == 0) && (channel_map[1] == 3)
3228 && (channel_map[2] == 1) && (channel_map[3] == 2))
3229 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3236 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3239 qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
3240 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
3241 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
3242 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
3243 const guint8 * constant_iv)
3245 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3246 gst_buffer_fill (kid_buf, 0, kid, 16);
3247 if (info->default_properties)
3248 gst_structure_free (info->default_properties);
3249 info->default_properties =
3250 gst_structure_new ("application/x-cenc",
3251 "iv_size", G_TYPE_UINT, iv_size,
3252 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
3253 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3254 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3255 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3256 gst_buffer_unref (kid_buf);
3257 if (protection_scheme_type == FOURCC_cbcs) {
3258 if (crypt_byte_block != 0 || skip_byte_block != 0) {
3259 gst_structure_set (info->default_properties, "crypt_byte_block",
3260 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
3261 skip_byte_block, NULL);
3263 if (constant_iv != NULL) {
3264 GstBuffer *constant_iv_buf =
3265 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
3266 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
3267 gst_structure_set (info->default_properties, "constant_iv_size",
3268 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
3270 gst_buffer_unref (constant_iv_buf);
3272 gst_structure_set (info->default_properties, "cipher-mode",
3273 G_TYPE_STRING, "cbcs", NULL);
3275 gst_structure_set (info->default_properties, "cipher-mode",
3276 G_TYPE_STRING, "cenc", NULL);
3281 qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
3282 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
3284 guint32 algorithm_id = 0;
3286 gboolean is_encrypted = TRUE;
3289 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
3290 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3295 if (algorithm_id == 0) {
3296 is_encrypted = FALSE;
3297 } else if (algorithm_id == 1) {
3298 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3299 } else if (algorithm_id == 2) {
3300 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3303 if (!gst_byte_reader_get_uint8 (br, &iv_size))
3306 if (!gst_byte_reader_get_data (br, 16, &kid))
3309 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
3310 is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
3311 gst_structure_set (info->default_properties, "piff_algorithm_id",
3312 G_TYPE_UINT, algorithm_id, NULL);
3318 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3326 QtDemuxStream *stream;
3327 GstStructure *structure;
3328 QtDemuxCencSampleSetInfo *ss_info = NULL;
3329 const gchar *system_id;
3330 gboolean uses_sub_sample_encryption = FALSE;
3331 guint32 sample_count;
3333 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
3336 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
3338 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3339 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3340 GST_WARNING_OBJECT (qtdemux,
3341 "Attempting PIFF box parsing on an unencrypted stream.");
3345 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3346 G_TYPE_STRING, &system_id, NULL);
3347 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3349 stream->protected = TRUE;
3350 stream->protection_scheme_type = FOURCC_cenc;
3352 if (!stream->protection_scheme_info)
3353 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3355 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3356 if (!ss_info->default_properties) {
3357 ss_info->default_properties =
3358 gst_structure_new ("application/x-cenc",
3359 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
3364 if (ss_info->crypto_info) {
3365 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3366 g_ptr_array_free (ss_info->crypto_info, TRUE);
3367 ss_info->crypto_info = NULL;
3371 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3373 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3374 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3378 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3379 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3383 if ((flags & 0x000001)) {
3384 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
3387 } else if ((flags & 0x000002)) {
3388 uses_sub_sample_encryption = TRUE;
3391 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
3393 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
3397 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
3398 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3402 ss_info->crypto_info =
3403 g_ptr_array_new_full (sample_count,
3404 (GDestroyNotify) qtdemux_gst_structure_free);
3406 for (i = 0; i < sample_count; ++i) {
3407 GstStructure *properties;
3411 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3412 if (properties == NULL) {
3413 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3414 qtdemux->cenc_aux_sample_count = i;
3418 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3419 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3420 gst_structure_free (properties);
3421 qtdemux->cenc_aux_sample_count = i;
3424 buf = gst_buffer_new_wrapped (data, iv_size);
3425 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3426 gst_buffer_unref (buf);
3428 if (uses_sub_sample_encryption) {
3429 guint16 n_subsamples;
3430 const GValue *kid_buf_value;
3432 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3433 || n_subsamples == 0) {
3434 GST_ERROR_OBJECT (qtdemux,
3435 "failed to get subsample count for sample %u", i);
3436 gst_structure_free (properties);
3437 qtdemux->cenc_aux_sample_count = i;
3440 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3441 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3442 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3444 gst_structure_free (properties);
3445 qtdemux->cenc_aux_sample_count = i;
3448 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3451 gst_structure_get_value (ss_info->default_properties, "kid");
3453 gst_structure_set (properties,
3454 "subsample_count", G_TYPE_UINT, n_subsamples,
3455 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3456 gst_structure_set_value (properties, "kid", kid_buf_value);
3457 gst_buffer_unref (buf);
3459 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3462 g_ptr_array_add (ss_info->crypto_info, properties);
3465 qtdemux->cenc_aux_sample_count = sample_count;
3469 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3471 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3472 0x97, 0xA9, 0x42, 0xE8,
3473 0x9C, 0x71, 0x99, 0x94,
3474 0x91, 0xE3, 0xAF, 0xAC
3476 static const guint8 playready_uuid[] = {
3477 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3478 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3481 static const guint8 piff_sample_encryption_uuid[] = {
3482 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3483 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3486 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3487 static const guint8 spherical_uuid[] = {
3488 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3489 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3491 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3495 /* counts as header data */
3496 qtdemux->header_size += length;
3498 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3500 if (length <= offset + 16) {
3501 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3505 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3506 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3507 const char *contents;
3509 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3510 contents = (char *) (buffer + offset + 16);
3511 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3513 if (qtdemux->spherical_metadata)
3514 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3518 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3520 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3522 GstTagList *taglist;
3524 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3525 length - offset - 16, NULL);
3526 taglist = gst_tag_list_from_xmp_buffer (buf);
3527 gst_buffer_unref (buf);
3529 /* make sure we have a usable taglist */
3530 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3532 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3534 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3536 const gunichar2 *s_utf16;
3539 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3540 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3541 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3542 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3546 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3547 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3549 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3550 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3552 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3553 GST_READ_UINT32_LE (buffer + offset),
3554 GST_READ_UINT32_LE (buffer + offset + 4),
3555 GST_READ_UINT32_LE (buffer + offset + 8),
3556 GST_READ_UINT32_LE (buffer + offset + 12));
3561 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3563 GstSidxParser sidx_parser;
3564 GstIsoffParserResult res;
3567 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3570 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3572 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3573 if (res == GST_ISOFF_QT_PARSER_DONE) {
3574 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3576 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3579 /* caller verifies at least 8 bytes in buf */
3581 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3582 guint64 * plength, guint32 * pfourcc)
3587 length = QT_UINT32 (data);
3588 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3589 fourcc = QT_FOURCC (data + 4);
3590 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3593 length = G_MAXUINT64;
3594 } else if (length == 1 && size >= 16) {
3595 /* this means we have an extended size, which is the 64 bit value of
3596 * the next 8 bytes */
3597 length = QT_UINT64 (data + 8);
3598 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3608 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3610 guint32 version = 0;
3611 GstClockTime duration = 0;
3613 if (!gst_byte_reader_get_uint32_be (br, &version))
3618 if (!gst_byte_reader_get_uint64_be (br, &duration))
3623 if (!gst_byte_reader_get_uint32_be (br, &dur))
3628 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3629 qtdemux->duration = duration;
3635 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3641 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3642 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3644 if (!stream->parsed_trex && qtdemux->moov_node) {
3646 GstByteReader trex_data;
3648 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3650 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3653 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3655 /* skip version/flags */
3656 if (!gst_byte_reader_skip (&trex_data, 4))
3658 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3660 if (id != stream->track_id)
3662 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3664 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3666 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3668 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3671 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3672 "duration %d, size %d, flags 0x%x", stream->track_id,
3675 stream->parsed_trex = TRUE;
3676 stream->def_sample_description_index = sdi;
3677 stream->def_sample_duration = dur;
3678 stream->def_sample_size = size;
3679 stream->def_sample_flags = flags;
3682 /* iterate all siblings */
3683 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3689 *ds_duration = stream->def_sample_duration;
3690 *ds_size = stream->def_sample_size;
3691 *ds_flags = stream->def_sample_flags;
3693 /* even then, above values are better than random ... */
3694 if (G_UNLIKELY (!stream->parsed_trex)) {
3695 GST_WARNING_OBJECT (qtdemux,
3696 "failed to find fragment defaults for stream %d", stream->track_id);
3703 /* This method should be called whenever a more accurate duration might
3704 * have been found. It will update all relevant variables if/where needed
3707 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3711 GstClockTime prevdur;
3713 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3715 if (movdur > qtdemux->duration) {
3716 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3717 GST_DEBUG_OBJECT (qtdemux,
3718 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3719 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3720 qtdemux->duration = movdur;
3721 GST_DEBUG_OBJECT (qtdemux,
3722 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3723 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3724 GST_TIME_ARGS (qtdemux->segment.stop));
3725 if (qtdemux->segment.duration == prevdur) {
3726 /* If the current segment has duration/stop identical to previous duration
3727 * update them also (because they were set at that point in time with
3728 * the wrong duration */
3729 /* We convert the value *from* the timescale version to avoid rounding errors */
3730 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3731 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3732 qtdemux->segment.duration = fixeddur;
3733 qtdemux->segment.stop = fixeddur;
3737 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3738 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3740 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3741 if (movdur > stream->duration) {
3742 GST_DEBUG_OBJECT (qtdemux,
3743 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3744 GST_TIME_ARGS (duration));
3745 stream->duration = movdur;
3746 /* internal duration tracking state has been updated above, so */
3747 /* preserve an open-ended dummy segment rather than repeatedly updating
3748 * it and spamming downstream accordingly with segment events */
3749 /* also mangle the edit list end time when fragmented with a single edit
3750 * list that may only cover any non-fragmented data */
3751 if ((stream->dummy_segment ||
3752 (qtdemux->fragmented && stream->n_segments == 1)) &&
3753 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3754 /* Update all dummy values to new duration */
3755 stream->segments[0].stop_time = duration;
3756 stream->segments[0].duration = duration;
3757 stream->segments[0].media_stop = duration;
3759 /* let downstream know we possibly have a new stop time */
3760 if (stream->segment_index != -1) {
3763 if (qtdemux->segment.rate >= 0) {
3764 pos = stream->segment.start;
3766 pos = stream->segment.stop;
3769 gst_qtdemux_stream_update_segment (qtdemux, stream,
3770 stream->segment_index, pos, NULL, NULL);
3778 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3779 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3780 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3781 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3784 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3786 gint32 data_offset = 0;
3788 guint32 flags = 0, first_flags = 0, samples_count = 0;
3791 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3792 QtDemuxSample *sample;
3793 gboolean ismv = FALSE;
3794 gint64 initial_offset;
3797 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3798 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3799 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3800 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3802 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3803 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3807 /* presence of stss or not can't really tell us much,
3808 * and flags and so on tend to be marginally reliable in these files */
3809 if (stream->subtype == FOURCC_soun) {
3810 GST_DEBUG_OBJECT (qtdemux,
3811 "sound track in fragmented file; marking all keyframes");
3812 stream->all_keyframe = TRUE;
3815 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3816 !gst_byte_reader_get_uint24_be (trun, &flags))
3819 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3822 if (flags & TR_DATA_OFFSET) {
3823 /* note this is really signed */
3824 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3826 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3827 /* default base offset = first byte of moof */
3828 if (*base_offset == -1) {
3829 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3830 *base_offset = moof_offset;
3832 *running_offset = *base_offset + data_offset;
3834 /* if no offset at all, that would mean data starts at moof start,
3835 * which is a bit wrong and is ismv crappy way, so compensate
3836 * assuming data is in mdat following moof */
3837 if (*base_offset == -1) {
3838 *base_offset = moof_offset + moof_length + 8;
3839 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3842 if (*running_offset == -1)
3843 *running_offset = *base_offset;
3846 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3848 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3849 data_offset, flags, samples_count);
3851 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3852 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3853 GST_DEBUG_OBJECT (qtdemux,
3854 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3855 flags ^= TR_FIRST_SAMPLE_FLAGS;
3857 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3859 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3863 /* FIXME ? spec says other bits should also be checked to determine
3864 * entry size (and prefix size for that matter) */
3866 dur_offset = size_offset = 0;
3867 if (flags & TR_SAMPLE_DURATION) {
3868 GST_LOG_OBJECT (qtdemux, "entry duration present");
3869 dur_offset = entry_size;
3872 if (flags & TR_SAMPLE_SIZE) {
3873 GST_LOG_OBJECT (qtdemux, "entry size present");
3874 size_offset = entry_size;
3877 if (flags & TR_SAMPLE_FLAGS) {
3878 GST_LOG_OBJECT (qtdemux, "entry flags present");
3879 flags_offset = entry_size;
3882 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3883 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3884 ct_offset = entry_size;
3888 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3890 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3892 if (stream->n_samples + samples_count >=
3893 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3896 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3897 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3898 (stream->n_samples + samples_count) *
3899 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3901 /* create a new array of samples if it's the first sample parsed */
3902 if (stream->n_samples == 0) {
3903 g_assert (stream->samples == NULL);
3904 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3905 /* or try to reallocate it with space enough to insert the new samples */
3907 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3908 stream->n_samples + samples_count);
3909 if (stream->samples == NULL)
3912 if (qtdemux->fragment_start != -1) {
3913 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3914 qtdemux->fragment_start = -1;
3916 if (stream->n_samples == 0) {
3917 if (decode_ts > 0) {
3918 timestamp = decode_ts;
3919 } else if (stream->pending_seek != NULL) {
3920 /* if we don't have a timestamp from a tfdt box, we'll use the one
3921 * from the mfra seek table */
3922 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3923 GST_TIME_ARGS (stream->pending_seek->ts));
3925 /* FIXME: this is not fully correct, the timestamp refers to the random
3926 * access sample refered to in the tfra entry, which may not necessarily
3927 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3928 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3933 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3934 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3935 GST_TIME_ARGS (gst_ts));
3937 /* subsequent fragments extend stream */
3939 stream->samples[stream->n_samples - 1].timestamp +
3940 stream->samples[stream->n_samples - 1].duration;
3942 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3943 * difference (1 sec.) between decode_ts and timestamp, prefer the
3945 if (has_tfdt && !qtdemux->upstream_format_is_time
3946 && ABSDIFF (decode_ts, timestamp) >
3947 MAX (stream->duration_last_moof / 2,
3948 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3949 GST_INFO_OBJECT (qtdemux,
3950 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3951 ") are significantly different (more than %" GST_TIME_FORMAT
3952 "), using decode_ts",
3953 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3954 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3955 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3956 MAX (stream->duration_last_moof / 2,
3957 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3958 timestamp = decode_ts;
3961 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3962 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3963 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3967 initial_offset = *running_offset;
3969 sample = stream->samples + stream->n_samples;
3970 for (i = 0; i < samples_count; i++) {
3971 guint32 dur, size, sflags;
3974 /* first read sample data */
3975 if (flags & TR_SAMPLE_DURATION) {
3976 dur = QT_UINT32 (data + dur_offset);
3978 dur = d_sample_duration;
3980 if (flags & TR_SAMPLE_SIZE) {
3981 size = QT_UINT32 (data + size_offset);
3983 size = d_sample_size;
3985 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3987 sflags = first_flags;
3989 sflags = d_sample_flags;
3991 } else if (flags & TR_SAMPLE_FLAGS) {
3992 sflags = QT_UINT32 (data + flags_offset);
3994 sflags = d_sample_flags;
3997 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3998 /* Read offsets as signed numbers regardless of trun version as very
3999 * high offsets are unlikely and there are files out there that use
4000 * version=0 truns with negative offsets */
4001 ct = QT_UINT32 (data + ct_offset);
4003 /* FIXME: Set offset to 0 for "no decode samples". This needs
4004 * to be handled in a codec specific manner ideally. */
4005 if (ct == G_MININT32)
4012 /* fill the sample information */
4013 sample->offset = *running_offset;
4014 sample->pts_offset = ct;
4015 sample->size = size;
4016 sample->timestamp = timestamp;
4017 sample->duration = dur;
4018 /* sample-is-difference-sample */
4019 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
4020 * now idea how it relates to bitfield other than massive LE/BE confusion */
4021 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
4022 *running_offset += size;
4024 stream->duration_moof += dur;
4031 /* Shift PTS/DTS to allow for negative composition offsets while keeping
4032 * A/V sync in place. This is similar to the code handling ctts/cslg in the
4033 * non-fragmented case.
4036 stream->cslg_shift = -min_ct;
4038 stream->cslg_shift = 0;
4040 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
4041 stream->cslg_shift);
4043 /* Update total duration if needed */
4044 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
4046 /* Pre-emptively figure out size of mdat based on trun information.
4047 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
4048 * size, else we will still be able to use this when dealing with gap'ed
4050 qtdemux->mdatleft = *running_offset - initial_offset;
4051 qtdemux->mdatoffset = initial_offset;
4052 qtdemux->mdatsize = qtdemux->mdatleft;
4054 stream->n_samples += samples_count;
4055 stream->n_samples_moof += samples_count;
4057 if (stream->pending_seek != NULL)
4058 stream->pending_seek = NULL;
4064 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
4069 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
4075 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
4076 "be larger than %uMB (broken file?)", stream->n_samples,
4077 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
4082 /* find stream with @id */
4083 static inline QtDemuxStream *
4084 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
4086 QtDemuxStream *stream;
4090 if (G_UNLIKELY (!id)) {
4091 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
4095 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4096 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4097 if (stream->track_id == id)
4100 if (qtdemux->mss_mode) {
4101 /* mss should have only 1 stream anyway */
4102 return QTDEMUX_NTH_STREAM (qtdemux, 0);
4109 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
4110 guint32 * fragment_number)
4112 if (!gst_byte_reader_skip (mfhd, 4))
4114 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
4119 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
4125 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
4126 QtDemuxStream ** stream, guint32 * default_sample_duration,
4127 guint32 * default_sample_size, guint32 * default_sample_flags,
4128 gint64 * base_offset)
4131 guint32 track_id = 0;
4133 if (!gst_byte_reader_skip (tfhd, 1) ||
4134 !gst_byte_reader_get_uint24_be (tfhd, &flags))
4137 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4140 *stream = qtdemux_find_stream (qtdemux, track_id);
4141 if (G_UNLIKELY (!*stream))
4142 goto unknown_stream;
4144 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4145 *base_offset = qtdemux->moof_offset;
4147 if (flags & TF_BASE_DATA_OFFSET)
4148 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4151 /* obtain stream defaults */
4152 qtdemux_parse_trex (qtdemux, *stream,
4153 default_sample_duration, default_sample_size, default_sample_flags);
4155 (*stream)->stsd_sample_description_id =
4156 (*stream)->def_sample_description_index - 1;
4158 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4159 guint32 sample_description_index;
4160 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4162 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4165 if (qtdemux->mss_mode) {
4166 /* mss has no stsd entry */
4167 (*stream)->stsd_sample_description_id = 0;
4170 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4171 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4174 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4175 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4178 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4179 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4186 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4191 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
4197 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4198 guint64 * decode_time)
4200 guint32 version = 0;
4202 if (!gst_byte_reader_get_uint32_be (br, &version))
4207 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4210 guint32 dec_time = 0;
4211 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4213 *decode_time = dec_time;
4216 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4223 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4228 /* Returns a pointer to a GstStructure containing the properties of
4229 * the stream sample identified by @sample_index. The caller must unref
4230 * the returned object after use. Returns NULL if unsuccessful. */
4231 static GstStructure *
4232 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4233 QtDemuxStream * stream, guint sample_index)
4235 QtDemuxCencSampleSetInfo *info = NULL;
4237 g_return_val_if_fail (stream != NULL, NULL);
4238 g_return_val_if_fail (stream->protected, NULL);
4239 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4241 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4243 /* Currently, cenc properties for groups of samples are not supported, so
4244 * simply return a copy of the default sample properties */
4245 return gst_structure_copy (info->default_properties);
4248 /* Parses the sizes of sample auxiliary information contained within a stream,
4249 * as given in a saiz box. Returns array of sample_count guint8 size values,
4250 * or NULL on failure */
4252 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4253 GstByteReader * br, guint32 * sample_count)
4257 guint8 default_info_size;
4259 g_return_val_if_fail (qtdemux != NULL, NULL);
4260 g_return_val_if_fail (stream != NULL, NULL);
4261 g_return_val_if_fail (br != NULL, NULL);
4262 g_return_val_if_fail (sample_count != NULL, NULL);
4264 if (!gst_byte_reader_get_uint32_be (br, &flags))
4268 /* aux_info_type and aux_info_type_parameter are ignored */
4269 if (!gst_byte_reader_skip (br, 8))
4273 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4275 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4277 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4279 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4282 if (default_info_size == 0) {
4283 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4287 info_sizes = g_new (guint8, *sample_count);
4288 memset (info_sizes, default_info_size, *sample_count);
4294 /* Parses the offset of sample auxiliary information contained within a stream,
4295 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4297 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4298 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4303 guint32 aux_info_type = 0;
4304 guint32 aux_info_type_parameter = 0;
4305 guint32 entry_count;
4308 const guint8 *aux_info_type_data = NULL;
4310 g_return_val_if_fail (qtdemux != NULL, FALSE);
4311 g_return_val_if_fail (stream != NULL, FALSE);
4312 g_return_val_if_fail (br != NULL, FALSE);
4313 g_return_val_if_fail (offset != NULL, FALSE);
4315 if (!gst_byte_reader_get_uint8 (br, &version))
4318 if (!gst_byte_reader_get_uint24_be (br, &flags))
4323 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4325 aux_info_type = QT_FOURCC (aux_info_type_data);
4327 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4329 } else if (stream->protected) {
4330 aux_info_type = stream->protection_scheme_type;
4332 aux_info_type = CUR_STREAM (stream)->fourcc;
4336 *info_type = aux_info_type;
4337 if (info_type_parameter)
4338 *info_type_parameter = aux_info_type_parameter;
4340 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4341 "aux_info_type_parameter: %#06x",
4342 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4344 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4347 if (entry_count != 1) {
4348 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4353 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4355 *offset = (guint64) off_32;
4357 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4362 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4367 qtdemux_gst_structure_free (GstStructure * gststructure)
4370 gst_structure_free (gststructure);
4374 /* Parses auxiliary information relating to samples protected using
4375 * Common Encryption (cenc); the format of this information
4376 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
4379 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4380 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4382 QtDemuxCencSampleSetInfo *ss_info = NULL;
4385 GPtrArray *old_crypto_info = NULL;
4386 guint old_entries = 0;
4388 g_return_val_if_fail (qtdemux != NULL, FALSE);
4389 g_return_val_if_fail (stream != NULL, FALSE);
4390 g_return_val_if_fail (br != NULL, FALSE);
4391 g_return_val_if_fail (stream->protected, FALSE);
4392 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4394 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4396 if (ss_info->crypto_info) {
4397 old_crypto_info = ss_info->crypto_info;
4398 /* Count number of non-null entries remaining at the tail end */
4399 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4400 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4406 ss_info->crypto_info =
4407 g_ptr_array_new_full (sample_count + old_entries,
4408 (GDestroyNotify) qtdemux_gst_structure_free);
4410 /* We preserve old entries because we parse the next moof in advance
4411 * of consuming all samples from the previous moof, and otherwise
4412 * we'd discard the corresponding crypto info for the samples
4413 * from the previous fragment. */
4415 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4417 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4418 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4420 g_ptr_array_index (old_crypto_info, i) = NULL;
4424 if (old_crypto_info) {
4425 /* Everything now belongs to the new array */
4426 g_ptr_array_free (old_crypto_info, TRUE);
4429 for (i = 0; i < sample_count; ++i) {
4430 GstStructure *properties;
4431 guint16 n_subsamples = 0;
4435 gboolean could_read_iv;
4437 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4438 if (properties == NULL) {
4439 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4442 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4443 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4444 gst_structure_free (properties);
4448 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
4449 if (could_read_iv) {
4450 buf = gst_buffer_new_wrapped (data, iv_size);
4451 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4452 gst_buffer_unref (buf);
4453 } else if (stream->protection_scheme_type == FOURCC_cbcs) {
4454 const GValue *constant_iv_size_value =
4455 gst_structure_get_value (properties, "constant_iv_size");
4456 const GValue *constant_iv_value =
4457 gst_structure_get_value (properties, "iv");
4458 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
4459 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
4460 gst_structure_free (properties);
4463 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
4464 gst_structure_remove_field (properties, "constant_iv_size");
4465 } else if (stream->protection_scheme_type == FOURCC_cenc) {
4466 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4467 gst_structure_free (properties);
4470 size = info_sizes[i];
4471 if (size > iv_size) {
4472 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4473 || !(n_subsamples > 0)) {
4474 gst_structure_free (properties);
4475 GST_ERROR_OBJECT (qtdemux,
4476 "failed to get subsample count for sample %u", i);
4479 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4480 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4481 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4483 gst_structure_free (properties);
4486 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4488 gst_structure_free (properties);
4491 gst_structure_set (properties,
4492 "subsample_count", G_TYPE_UINT, n_subsamples,
4493 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4494 gst_buffer_unref (buf);
4496 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4498 g_ptr_array_add (ss_info->crypto_info, properties);
4503 /* Converts a UUID in raw byte form to a string representation, as defined in
4504 * RFC 4122. The caller takes ownership of the returned string and is
4505 * responsible for freeing it after use. */
4507 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4509 const guint8 *uuid = (const guint8 *) uuid_bytes;
4511 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4512 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4513 uuid[0], uuid[1], uuid[2], uuid[3],
4514 uuid[4], uuid[5], uuid[6], uuid[7],
4515 uuid[8], uuid[9], uuid[10], uuid[11],
4516 uuid[12], uuid[13], uuid[14], uuid[15]);
4519 /* Parses a Protection System Specific Header box (pssh), as defined in the
4520 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4521 * information needed by a specific content protection system in order to
4522 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4525 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4527 gchar *sysid_string;
4528 guint32 pssh_size = QT_UINT32 (node->data);
4529 GstBuffer *pssh = NULL;
4530 GstEvent *event = NULL;
4531 guint32 parent_box_type;
4534 if (G_UNLIKELY (pssh_size < 32U)) {
4535 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4540 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4542 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4544 pssh = gst_buffer_new_memdup (node->data, pssh_size);
4545 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4546 gst_buffer_get_size (pssh));
4548 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4550 /* Push an event containing the pssh box onto the queues of all streams. */
4551 event = gst_event_new_protection (sysid_string, pssh,
4552 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4553 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4554 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4555 GST_TRACE_OBJECT (qtdemux,
4556 "adding protection event for stream %s and system %s",
4557 stream->stream_id, sysid_string);
4558 g_queue_push_tail (&stream->protection_scheme_event_queue,
4559 gst_event_ref (event));
4561 g_free (sysid_string);
4562 gst_event_unref (event);
4563 gst_buffer_unref (pssh);
4568 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4569 guint64 moof_offset, QtDemuxStream * stream)
4571 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4573 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4574 GNode *saiz_node, *saio_node, *pssh_node;
4575 GstByteReader saiz_data, saio_data;
4576 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4577 gint64 base_offset, running_offset;
4579 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4581 /* NOTE @stream ignored */
4583 moof_node = g_node_new ((guint8 *) buffer);
4584 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4585 qtdemux_node_dump (qtdemux, moof_node);
4587 /* Get fragment number from mfhd and check it's valid */
4589 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4590 if (mfhd_node == NULL)
4592 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4594 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4596 /* unknown base_offset to start with */
4597 base_offset = running_offset = -1;
4598 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4600 guint64 decode_time = 0;
4602 /* Fragment Header node */
4604 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4608 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4609 &ds_size, &ds_flags, &base_offset))
4612 /* The following code assumes at most a single set of sample auxiliary
4613 * data in the fragment (consisting of a saiz box and a corresponding saio
4614 * box); in theory, however, there could be multiple sets of sample
4615 * auxiliary data in a fragment. */
4617 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4620 guint32 info_type = 0;
4622 guint32 info_type_parameter = 0;
4624 g_free (qtdemux->cenc_aux_info_sizes);
4626 qtdemux->cenc_aux_info_sizes =
4627 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4628 &qtdemux->cenc_aux_sample_count);
4629 if (qtdemux->cenc_aux_info_sizes == NULL) {
4630 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4634 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4637 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4638 g_free (qtdemux->cenc_aux_info_sizes);
4639 qtdemux->cenc_aux_info_sizes = NULL;
4643 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4644 &info_type, &info_type_parameter, &offset))) {
4645 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4646 g_free (qtdemux->cenc_aux_info_sizes);
4647 qtdemux->cenc_aux_info_sizes = NULL;
4650 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4651 offset += (guint64) (base_offset - qtdemux->moof_offset);
4652 if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs)
4653 && info_type_parameter == 0U) {
4655 if (offset > length) {
4656 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4657 qtdemux->cenc_aux_info_offset = offset;
4659 gst_byte_reader_init (&br, buffer + offset, length - offset);
4660 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4661 qtdemux->cenc_aux_info_sizes,
4662 qtdemux->cenc_aux_sample_count)) {
4663 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4664 g_free (qtdemux->cenc_aux_info_sizes);
4665 qtdemux->cenc_aux_info_sizes = NULL;
4673 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4676 /* We'll use decode_time to interpolate timestamps
4677 * in case the input timestamps are missing */
4678 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4680 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4681 " (%" GST_TIME_FORMAT ")", decode_time,
4682 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4683 decode_time) : GST_CLOCK_TIME_NONE));
4685 /* Discard the fragment buffer timestamp info to avoid using it.
4686 * Rely on tfdt instead as it is more accurate than the timestamp
4687 * that is fetched from a manifest/playlist and is usually
4689 qtdemux->fragment_start = -1;
4692 if (G_UNLIKELY (!stream)) {
4693 /* we lost track of offset, we'll need to regain it,
4694 * but can delay complaining until later or avoid doing so altogether */
4698 if (G_UNLIKELY (base_offset < -1))
4701 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4703 if (!qtdemux->pullbased) {
4704 /* Sample tables can grow enough to be problematic if the system memory
4705 * is very low (e.g. embedded devices) and the videos very long
4706 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4707 * Fortunately, we can easily discard them for each new fragment when
4708 * we know qtdemux will not receive seeks outside of the current fragment.
4709 * adaptivedemux honors this assumption.
4710 * This optimization is also useful for applications that use qtdemux as
4711 * a push-based simple demuxer, like Media Source Extensions. */
4712 gst_qtdemux_stream_flush_samples_data (stream);
4715 /* initialise moof sample data */
4716 stream->n_samples_moof = 0;
4717 stream->duration_last_moof = stream->duration_moof;
4718 stream->duration_moof = 0;
4720 /* Track Run node */
4722 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4725 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4726 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4727 &running_offset, decode_time, (tfdt_node != NULL));
4728 /* iterate all siblings */
4729 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4733 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4735 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4736 guint32 box_length = QT_UINT32 (uuid_buffer);
4738 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4741 /* if no new base_offset provided for next traf,
4742 * base is end of current traf */
4743 base_offset = running_offset;
4744 running_offset = -1;
4746 if (stream->n_samples_moof && stream->duration_moof)
4747 stream->new_caps = TRUE;
4750 /* iterate all siblings */
4751 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4754 /* parse any protection system info */
4755 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4757 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4758 qtdemux_parse_pssh (qtdemux, pssh_node);
4759 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4762 if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
4763 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4765 /* Unless the user has explicitly requested another seek, perform an
4766 * internal seek to the time specified in the tfdt.
4768 * This way if the user opens a file where the first tfdt is 1 hour
4769 * into the presentation, they will not have to wait 1 hour for run
4770 * time to catch up and actual playback to start. */
4773 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4774 "performing an internal seek to %" GST_TIME_FORMAT,
4775 GST_TIME_ARGS (min_dts));
4777 qtdemux->segment.start = min_dts;
4778 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4780 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4781 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4782 stream->time_position = min_dts;
4785 /* Before this code was run a segment was already sent when the moov was
4786 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4787 * be emitted after a moov, and we can emit a second segment anyway for
4788 * special cases like this. */
4789 qtdemux->need_segment = TRUE;
4792 qtdemux->first_moof_already_parsed = TRUE;
4794 g_node_destroy (moof_node);
4799 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4804 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4809 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4814 g_node_destroy (moof_node);
4815 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4816 (_("This file is corrupt and cannot be played.")), (NULL));
4822 /* might be used if some day we actually use mfra & co
4823 * for random access to fragments,
4824 * but that will require quite some modifications and much less relying
4825 * on a sample array */
4829 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4831 QtDemuxStream *stream;
4832 guint32 ver_flags, track_id, len, num_entries, i;
4833 guint value_size, traf_size, trun_size, sample_size;
4834 guint64 time = 0, moof_offset = 0;
4836 GstBuffer *buf = NULL;
4841 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4843 if (!gst_byte_reader_skip (&tfra, 8))
4846 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4849 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4850 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4851 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4854 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4856 stream = qtdemux_find_stream (qtdemux, track_id);
4858 goto unknown_trackid;
4860 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4861 sample_size = (len & 3) + 1;
4862 trun_size = ((len & 12) >> 2) + 1;
4863 traf_size = ((len & 48) >> 4) + 1;
4865 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4866 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4868 if (num_entries == 0)
4871 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4872 value_size + value_size + traf_size + trun_size + sample_size))
4875 g_free (stream->ra_entries);
4876 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4877 stream->n_ra_entries = num_entries;
4879 for (i = 0; i < num_entries; i++) {
4880 qt_atom_parser_get_offset (&tfra, value_size, &time);
4881 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4882 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4883 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4884 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4886 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4888 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4889 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4891 stream->ra_entries[i].ts = time;
4892 stream->ra_entries[i].moof_offset = moof_offset;
4894 /* don't want to go through the entire file and read all moofs at startup */
4896 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4897 if (ret != GST_FLOW_OK)
4899 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4900 moof_offset, stream);
4901 gst_buffer_unref (buf);
4905 check_update_duration (qtdemux, time);
4912 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4917 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4922 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4928 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4930 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4931 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4932 GstBuffer *mfro = NULL, *mfra = NULL;
4934 gboolean ret = FALSE;
4935 GNode *mfra_node, *tfra_node;
4936 guint64 mfra_offset = 0;
4937 guint32 fourcc, mfra_size;
4940 /* query upstream size in bytes */
4941 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4942 goto size_query_failed;
4944 /* mfro box should be at the very end of the file */
4945 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4946 if (flow != GST_FLOW_OK)
4949 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4951 fourcc = QT_FOURCC (mfro_map.data + 4);
4952 if (fourcc != FOURCC_mfro)
4955 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4956 if (mfro_map.size < 16)
4957 goto invalid_mfro_size;
4959 mfra_size = QT_UINT32 (mfro_map.data + 12);
4960 if (mfra_size >= len)
4961 goto invalid_mfra_size;
4963 mfra_offset = len - mfra_size;
4965 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4966 mfra_offset, mfra_size);
4968 /* now get and parse mfra box */
4969 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4970 if (flow != GST_FLOW_OK)
4973 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4975 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4976 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4978 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4981 qtdemux_parse_tfra (qtdemux, tfra_node);
4982 /* iterate all siblings */
4983 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4985 g_node_destroy (mfra_node);
4987 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4993 if (mfro_map.memory != NULL)
4994 gst_buffer_unmap (mfro, &mfro_map);
4995 gst_buffer_unref (mfro);
4998 if (mfra_map.memory != NULL)
4999 gst_buffer_unmap (mfra, &mfra_map);
5000 gst_buffer_unref (mfra);
5007 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
5012 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
5017 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
5022 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
5028 add_offset (guint64 offset, guint64 advance)
5030 /* Avoid 64-bit overflow by clamping */
5031 if (offset > G_MAXUINT64 - advance)
5033 return offset + advance;
5036 static GstFlowReturn
5037 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
5041 GstBuffer *buf = NULL;
5042 GstFlowReturn ret = GST_FLOW_OK;
5043 guint64 cur_offset = qtdemux->offset;
5046 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
5047 if (G_UNLIKELY (ret != GST_FLOW_OK))
5049 gst_buffer_map (buf, &map, GST_MAP_READ);
5050 if (G_LIKELY (map.size >= 8))
5051 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
5052 gst_buffer_unmap (buf, &map);
5053 gst_buffer_unref (buf);
5055 /* maybe we already got most we needed, so only consider this eof */
5056 if (G_UNLIKELY (length == 0)) {
5057 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5058 (_("Invalid atom size.")),
5059 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
5060 GST_FOURCC_ARGS (fourcc)));
5067 /* record for later parsing when needed */
5068 if (!qtdemux->moof_offset) {
5069 qtdemux->moof_offset = qtdemux->offset;
5071 if (qtdemux_pull_mfro_mfra (qtdemux)) {
5074 qtdemux->offset += length; /* skip moof and keep going */
5076 if (qtdemux->got_moov) {
5077 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
5089 GST_LOG_OBJECT (qtdemux,
5090 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5091 GST_FOURCC_ARGS (fourcc), cur_offset);
5092 qtdemux->offset = add_offset (qtdemux->offset, length);
5097 GstBuffer *moov = NULL;
5099 if (qtdemux->got_moov) {
5100 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
5101 qtdemux->offset = add_offset (qtdemux->offset, length);
5105 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
5106 if (ret != GST_FLOW_OK)
5108 gst_buffer_map (moov, &map, GST_MAP_READ);
5110 if (length != map.size) {
5111 /* Some files have a 'moov' atom at the end of the file which contains
5112 * a terminal 'free' atom where the body of the atom is missing.
5113 * Check for, and permit, this special case.
5115 if (map.size >= 8) {
5116 guint8 *final_data = map.data + (map.size - 8);
5117 guint32 final_length = QT_UINT32 (final_data);
5118 guint32 final_fourcc = QT_FOURCC (final_data + 4);
5120 if (final_fourcc == FOURCC_free
5121 && map.size + final_length - 8 == length) {
5122 /* Ok, we've found that special case. Allocate a new buffer with
5123 * that free atom actually present. */
5124 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
5125 gst_buffer_fill (newmoov, 0, map.data, map.size);
5126 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
5127 gst_buffer_unmap (moov, &map);
5128 gst_buffer_unref (moov);
5130 gst_buffer_map (moov, &map, GST_MAP_READ);
5135 if (length != map.size) {
5136 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5137 (_("This file is incomplete and cannot be played.")),
5138 ("We got less than expected (received %" G_GSIZE_FORMAT
5139 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
5140 (guint) length, cur_offset));
5141 gst_buffer_unmap (moov, &map);
5142 gst_buffer_unref (moov);
5143 ret = GST_FLOW_ERROR;
5146 qtdemux->offset += length;
5148 qtdemux_parse_moov (qtdemux, map.data, length);
5149 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
5151 qtdemux_parse_tree (qtdemux);
5152 if (qtdemux->moov_node_compressed) {
5153 g_node_destroy (qtdemux->moov_node_compressed);
5154 g_free (qtdemux->moov_node->data);
5156 qtdemux->moov_node_compressed = NULL;
5157 g_node_destroy (qtdemux->moov_node);
5158 qtdemux->moov_node = NULL;
5159 gst_buffer_unmap (moov, &map);
5160 gst_buffer_unref (moov);
5161 qtdemux->got_moov = TRUE;
5167 GstBuffer *ftyp = NULL;
5169 /* extract major brand; might come in handy for ISO vs QT issues */
5170 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
5171 if (ret != GST_FLOW_OK)
5173 qtdemux->offset += length;
5174 gst_buffer_map (ftyp, &map, GST_MAP_READ);
5175 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
5176 gst_buffer_unmap (ftyp, &map);
5177 gst_buffer_unref (ftyp);
5182 GstBuffer *uuid = NULL;
5184 /* uuid are extension atoms */
5185 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
5186 if (ret != GST_FLOW_OK)
5188 qtdemux->offset += length;
5189 gst_buffer_map (uuid, &map, GST_MAP_READ);
5190 qtdemux_parse_uuid (qtdemux, map.data, map.size);
5191 gst_buffer_unmap (uuid, &map);
5192 gst_buffer_unref (uuid);
5197 GstBuffer *sidx = NULL;
5198 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
5199 if (ret != GST_FLOW_OK)
5201 qtdemux->offset += length;
5202 gst_buffer_map (sidx, &map, GST_MAP_READ);
5203 qtdemux_parse_sidx (qtdemux, map.data, map.size);
5204 gst_buffer_unmap (sidx, &map);
5205 gst_buffer_unref (sidx);
5210 GstBuffer *unknown = NULL;
5212 GST_LOG_OBJECT (qtdemux,
5213 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5214 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5216 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5217 if (ret != GST_FLOW_OK)
5219 gst_buffer_map (unknown, &map, GST_MAP_READ);
5220 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5221 gst_buffer_unmap (unknown, &map);
5222 gst_buffer_unref (unknown);
5223 qtdemux->offset += length;
5229 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5230 /* digested all data, show what we have */
5231 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5232 if (qtdemux->spherical_metadata)
5233 _send_spherical_metadata_msg_to_bus (qtdemux);
5234 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5235 qtdemux_prepare_streams (qtdemux);
5236 QTDEMUX_EXPOSE_LOCK (qtdemux);
5237 ret = qtdemux_expose_streams (qtdemux);
5238 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
5240 qtdemux->state = QTDEMUX_STATE_MOVIE;
5241 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5248 /* Seeks to the previous keyframe of the indexed stream and
5249 * aligns other streams with respect to the keyframe timestamp
5250 * of indexed stream. Only called in case of Reverse Playback
5252 static GstFlowReturn
5253 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5255 guint32 seg_idx = 0, k_index = 0;
5256 guint32 ref_seg_idx, ref_k_index;
5257 GstClockTime k_pos = 0, last_stop = 0;
5258 QtDemuxSegment *seg = NULL;
5259 QtDemuxStream *ref_str = NULL;
5260 guint64 seg_media_start_mov; /* segment media start time in mov format */
5264 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5265 * and finally align all the other streams on that timestamp with their
5266 * respective keyframes */
5267 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5268 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5270 /* No candidate yet, take the first stream */
5276 /* So that stream has a segment, we prefer video streams */
5277 if (str->subtype == FOURCC_vide) {
5283 if (G_UNLIKELY (!ref_str)) {
5284 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5288 if (G_UNLIKELY (!ref_str->from_sample)) {
5289 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5293 /* So that stream has been playing from from_sample to to_sample. We will
5294 * get the timestamp of the previous sample and search for a keyframe before
5295 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5296 if (ref_str->subtype == FOURCC_vide) {
5297 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5298 ref_str->from_sample - 1, FALSE);
5300 if (ref_str->from_sample >= 10)
5301 k_index = ref_str->from_sample - 10;
5307 ref_str->samples[k_index].timestamp +
5308 ref_str->samples[k_index].pts_offset;
5310 /* get current segment for that stream */
5311 seg = &ref_str->segments[ref_str->segment_index];
5312 /* Use segment start in original timescale for comparisons */
5313 seg_media_start_mov = seg->trak_media_start;
5315 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5316 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
5317 k_index, target_ts, seg_media_start_mov,
5318 GST_TIME_ARGS (seg->media_start));
5320 /* Crawl back through segments to find the one containing this I frame */
5321 while (target_ts < seg_media_start_mov) {
5322 GST_DEBUG_OBJECT (qtdemux,
5323 "keyframe position (sample %u) is out of segment %u " " target %"
5324 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5325 ref_str->segment_index, target_ts, seg_media_start_mov);
5327 if (G_UNLIKELY (!ref_str->segment_index)) {
5328 /* Reached first segment, let's consider it's EOS */
5331 ref_str->segment_index--;
5332 seg = &ref_str->segments[ref_str->segment_index];
5333 /* Use segment start in original timescale for comparisons */
5334 seg_media_start_mov = seg->trak_media_start;
5336 /* Calculate time position of the keyframe and where we should stop */
5338 QTSTREAMTIME_TO_GSTTIME (ref_str,
5339 target_ts - seg->trak_media_start) + seg->time;
5341 QTSTREAMTIME_TO_GSTTIME (ref_str,
5342 ref_str->samples[ref_str->from_sample].timestamp -
5343 seg->trak_media_start) + seg->time;
5345 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5346 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5347 k_index, GST_TIME_ARGS (k_pos));
5349 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5350 qtdemux->segment.position = last_stop;
5351 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5352 GST_TIME_ARGS (last_stop));
5354 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5355 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5359 ref_seg_idx = ref_str->segment_index;
5360 ref_k_index = k_index;
5362 /* Align them all on this */
5363 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
5365 GstClockTime seg_time = 0;
5366 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
5368 /* aligning reference stream again might lead to backing up to yet another
5369 * keyframe (due to timestamp rounding issues),
5370 * potentially putting more load on downstream; so let's try to avoid */
5371 if (str == ref_str) {
5372 seg_idx = ref_seg_idx;
5373 seg = &str->segments[seg_idx];
5374 k_index = ref_k_index;
5375 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5376 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5378 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5379 GST_DEBUG_OBJECT (qtdemux,
5380 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5381 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5383 /* get segment and time in the segment */
5384 seg = &str->segments[seg_idx];
5385 seg_time = k_pos - seg->time;
5387 /* get the media time in the segment.
5388 * No adjustment for empty "filler" segments */
5389 if (seg->media_start != GST_CLOCK_TIME_NONE)
5390 seg_time += seg->media_start;
5392 /* get the index of the sample with media time */
5393 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5394 GST_DEBUG_OBJECT (qtdemux,
5395 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5396 GST_TIME_ARGS (seg_time), index);
5398 /* find previous keyframe */
5399 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5402 /* Remember until where we want to go */
5403 str->to_sample = str->from_sample - 1;
5404 /* Define our time position */
5406 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5407 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5408 if (seg->media_start != GST_CLOCK_TIME_NONE)
5409 str->time_position -= seg->media_start;
5411 /* Now seek back in time */
5412 gst_qtdemux_move_stream (qtdemux, str, k_index);
5413 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5414 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5415 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5421 return GST_FLOW_EOS;
5425 * Gets the current qt segment start, stop and position for the
5426 * given time offset. This is used in update_segment()
5429 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5430 QtDemuxStream * stream, GstClockTime offset,
5431 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5433 GstClockTime seg_time;
5434 GstClockTime start, stop, time;
5435 QtDemuxSegment *segment;
5437 segment = &stream->segments[stream->segment_index];
5439 /* get time in this segment */
5440 seg_time = (offset - segment->time) * segment->rate;
5442 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5443 GST_TIME_ARGS (seg_time));
5445 if (G_UNLIKELY (seg_time > segment->duration)) {
5446 GST_LOG_OBJECT (stream->pad,
5447 "seg_time > segment->duration %" GST_TIME_FORMAT,
5448 GST_TIME_ARGS (segment->duration));
5449 seg_time = segment->duration;
5452 /* qtdemux->segment.stop is in outside-time-realm, whereas
5453 * segment->media_stop is in track-time-realm.
5455 * In order to compare the two, we need to bring segment.stop
5456 * into the track-time-realm
5458 * FIXME - does this comment still hold? Don't see any conversion here */
5460 stop = qtdemux->segment.stop;
5461 if (stop == GST_CLOCK_TIME_NONE)
5462 stop = qtdemux->segment.duration;
5463 if (stop == GST_CLOCK_TIME_NONE)
5464 stop = segment->media_stop;
5467 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5469 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5470 start = segment->time + seg_time;
5472 stop = start - seg_time + segment->duration;
5473 } else if (qtdemux->segment.rate >= 0) {
5474 start = MIN (segment->media_start + seg_time, stop);
5477 if (segment->media_start >= qtdemux->segment.start) {
5478 time = segment->time;
5480 time = segment->time + (qtdemux->segment.start - segment->media_start);
5483 start = MAX (segment->media_start, qtdemux->segment.start);
5484 stop = MIN (segment->media_start + seg_time, stop);
5493 * Updates the qt segment used for the stream and pushes a new segment event
5494 * downstream on this stream's pad.
5497 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5498 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5499 GstClockTime * _stop)
5501 QtDemuxSegment *segment;
5502 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5506 /* update the current segment */
5507 stream->segment_index = seg_idx;
5509 /* get the segment */
5510 segment = &stream->segments[seg_idx];
5512 if (G_UNLIKELY (offset < segment->time)) {
5513 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5514 GST_TIME_ARGS (segment->time));
5518 /* segment lies beyond total indicated duration */
5519 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5520 segment->time > qtdemux->segment.duration)) {
5521 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5522 " < segment->time %" GST_TIME_FORMAT,
5523 GST_TIME_ARGS (qtdemux->segment.duration),
5524 GST_TIME_ARGS (segment->time));
5528 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5529 &start, &stop, &time);
5531 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5532 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5533 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5535 /* combine global rate with that of the segment */
5536 rate = segment->rate * qtdemux->segment.rate;
5538 /* Copy flags from main segment */
5539 stream->segment.flags = qtdemux->segment.flags;
5541 /* update the segment values used for clipping */
5542 stream->segment.offset = qtdemux->segment.offset;
5543 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5544 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5545 stream->segment.rate = rate;
5546 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5547 stream->cslg_shift);
5549 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5550 stream->cslg_shift);
5552 stream->segment.stop = stop;
5553 stream->segment.time = time;
5554 stream->segment.position = stream->segment.start;
5556 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5559 /* now prepare and send the segment */
5561 event = gst_event_new_segment (&stream->segment);
5562 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5563 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5565 gst_pad_push_event (stream->pad, event);
5566 /* assume we can send more data now */
5567 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5568 /* clear to send tags on this pad now */
5569 gst_qtdemux_push_tags (qtdemux, stream);
5580 /* activate the given segment number @seg_idx of @stream at time @offset.
5581 * @offset is an absolute global position over all the segments.
5583 * This will push out a NEWSEGMENT event with the right values and
5584 * position the stream index to the first decodable sample before
5588 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5589 guint32 seg_idx, GstClockTime offset)
5591 QtDemuxSegment *segment;
5592 guint32 index, kf_index;
5593 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5595 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5596 seg_idx, GST_TIME_ARGS (offset));
5598 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5602 segment = &stream->segments[stream->segment_index];
5604 /* in the fragmented case, we pick a fragment that starts before our
5605 * desired position and rely on downstream to wait for a keyframe
5606 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5607 * tfra entries tells us which trun/sample the key unit is in, but we don't
5608 * make use of this additional information at the moment) */
5609 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5610 stream->to_sample = G_MAXUINT32;
5613 /* well, it will be taken care of below */
5614 qtdemux->fragmented_seek_pending = FALSE;
5615 /* FIXME ideally the do_fragmented_seek can be done right here,
5616 * rather than at loop level
5617 * (which might even allow handling edit lists in a fragmented file) */
5620 /* We don't need to look for a sample in push-based */
5621 if (!qtdemux->pullbased)
5624 /* and move to the keyframe before the indicated media time of the
5626 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5627 if (qtdemux->segment.rate >= 0) {
5628 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5629 stream->to_sample = G_MAXUINT32;
5630 GST_DEBUG_OBJECT (stream->pad,
5631 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5632 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5633 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5635 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5636 stream->to_sample = index;
5637 GST_DEBUG_OBJECT (stream->pad,
5638 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5639 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5640 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5643 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5644 "this is an empty segment");
5648 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5649 * encountered an error and printed a message so we return appropriately */
5653 /* we're at the right spot */
5654 if (index == stream->sample_index) {
5655 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5659 /* find keyframe of the target index */
5660 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5662 /* go back two frames to provide lead-in for non-raw audio decoders */
5663 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
5664 guint32 lead_in = 2;
5665 guint32 old_index = kf_index;
5666 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
5668 if (gst_structure_has_name (s, "audio/mpeg")) {
5670 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5671 && mpegversion == 1) {
5672 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5677 kf_index = MAX (kf_index, lead_in) - lead_in;
5678 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
5679 GST_DEBUG_OBJECT (stream->pad,
5680 "Moving backwards %u frames to ensure sufficient sound lead-in",
5681 old_index - kf_index);
5683 kf_index = old_index;
5687 /* if we move forwards, we don't have to go back to the previous
5688 * keyframe since we already sent that. We can also just jump to
5689 * the keyframe right before the target index if there is one. */
5690 if (index > stream->sample_index) {
5691 /* moving forwards check if we move past a keyframe */
5692 if (kf_index > stream->sample_index) {
5693 GST_DEBUG_OBJECT (stream->pad,
5694 "moving forwards to keyframe at %u "
5695 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5697 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5698 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5699 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5701 GST_DEBUG_OBJECT (stream->pad,
5702 "moving forwards, keyframe at %u "
5703 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5705 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5706 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5709 GST_DEBUG_OBJECT (stream->pad,
5710 "moving backwards to %sframe at %u "
5711 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5712 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5713 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5714 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5715 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5721 /* prepare to get the current sample of @stream, getting essential values.
5723 * This function will also prepare and send the segment when needed.
5725 * Return FALSE if the stream is EOS.
5730 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5731 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5732 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5733 gboolean * keyframe)
5735 QtDemuxSample *sample;
5736 GstClockTime time_position;
5739 g_return_val_if_fail (stream != NULL, FALSE);
5741 time_position = stream->time_position;
5742 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5745 seg_idx = stream->segment_index;
5746 if (G_UNLIKELY (seg_idx == -1)) {
5747 /* find segment corresponding to time_position if we are looking
5749 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5752 /* different segment, activate it, sample_index will be set. */
5753 if (G_UNLIKELY (stream->segment_index != seg_idx))
5754 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5756 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5757 segments[stream->segment_index]))) {
5758 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5760 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5761 " prepare empty sample");
5764 *pts = *dts = time_position;
5765 *duration = seg->duration - (time_position - seg->time);
5772 if (stream->sample_index == -1)
5773 stream->sample_index = 0;
5775 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5776 stream->sample_index, stream->n_samples);
5778 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5779 if (!qtdemux->fragmented)
5782 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5786 GST_OBJECT_LOCK (qtdemux);
5787 flow = qtdemux_add_fragmented_samples (qtdemux);
5788 GST_OBJECT_UNLOCK (qtdemux);
5790 if (flow != GST_FLOW_OK)
5793 while (stream->sample_index >= stream->n_samples);
5796 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5797 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5798 stream->sample_index);
5802 /* now get the info for the sample we're at */
5803 sample = &stream->samples[stream->sample_index];
5805 *dts = QTSAMPLE_DTS (stream, sample);
5806 *pts = QTSAMPLE_PTS (stream, sample);
5807 *offset = sample->offset;
5808 *size = sample->size;
5809 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5810 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5817 stream->time_position = GST_CLOCK_TIME_NONE;
5822 /* move to the next sample in @stream.
5824 * Moves to the next segment when needed.
5827 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5829 QtDemuxSample *sample;
5830 QtDemuxSegment *segment;
5832 /* get current segment */
5833 segment = &stream->segments[stream->segment_index];
5835 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5836 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5840 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5841 /* Mark the stream as EOS */
5842 GST_DEBUG_OBJECT (qtdemux,
5843 "reached max allowed sample %u, mark EOS", stream->to_sample);
5844 stream->time_position = GST_CLOCK_TIME_NONE;
5848 /* move to next sample */
5849 stream->sample_index++;
5850 stream->offset_in_sample = 0;
5852 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
5855 /* reached the last sample, we need the next segment */
5856 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5859 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5860 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5861 stream->sample_index);
5865 /* get next sample */
5866 sample = &stream->samples[stream->sample_index];
5868 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
5869 GST_TIME_FORMAT, GST_TIME_ARGS (QTSAMPLE_DTS (stream, sample)),
5870 GST_TIME_ARGS (segment->media_stop));
5872 /* see if we are past the segment */
5873 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5876 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5877 /* inside the segment, update time_position, looks very familiar to
5878 * GStreamer segments, doesn't it? */
5879 stream->time_position =
5880 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5882 /* not yet in segment, time does not yet increment. This means
5883 * that we are still prerolling keyframes to the decoder so it can
5884 * decode the first sample of the segment. */
5885 stream->time_position = segment->time;
5889 /* move to the next segment */
5892 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5894 if (stream->segment_index == stream->n_segments - 1) {
5895 /* are we at the end of the last segment, we're EOS */
5896 stream->time_position = GST_CLOCK_TIME_NONE;
5898 /* else we're only at the end of the current segment */
5899 stream->time_position = segment->stop_time;
5901 /* make sure we select a new segment */
5903 /* accumulate previous segments */
5904 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5905 stream->accumulated_base +=
5906 (stream->segment.stop -
5907 stream->segment.start) / ABS (stream->segment.rate);
5909 stream->segment_index = -1;
5914 gst_qtdemux_sync_streams (GstQTDemux * demux)
5918 if (QTDEMUX_N_STREAMS (demux) <= 1)
5921 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5922 QtDemuxStream *stream;
5923 GstClockTime end_time;
5925 stream = QTDEMUX_NTH_STREAM (demux, i);
5930 /* TODO advance time on subtitle streams here, if any some day */
5932 /* some clips/trailers may have unbalanced streams at the end,
5933 * so send EOS on shorter stream to prevent stalling others */
5935 /* do not mess with EOS if SEGMENT seeking */
5936 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5939 if (demux->pullbased) {
5940 /* loop mode is sample time based */
5941 if (!STREAM_IS_EOS (stream))
5944 /* push mode is byte position based */
5945 if (stream->n_samples &&
5946 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5950 if (stream->sent_eos)
5953 /* only act if some gap */
5954 end_time = stream->segments[stream->n_segments - 1].stop_time;
5955 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5956 ", stream end: %" GST_TIME_FORMAT,
5957 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5958 if (GST_CLOCK_TIME_IS_VALID (end_time)
5959 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5962 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5963 GST_PAD_NAME (stream->pad));
5964 stream->sent_eos = TRUE;
5965 event = gst_event_new_eos ();
5966 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5967 gst_event_set_seqnum (event, demux->segment_seqnum);
5968 gst_pad_push_event (stream->pad, event);
5973 /* EOS and NOT_LINKED need to be combined. This means that we return:
5975 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5976 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5978 static GstFlowReturn
5979 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5982 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5985 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5988 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5990 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5994 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5995 * completely clipped
5997 * Should be used only with raw buffers */
5999 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6002 guint64 start, stop, cstart, cstop, diff;
6003 GstClockTime pts, duration;
6005 gint num_rate, denom_rate;
6010 osize = size = gst_buffer_get_size (buf);
6013 /* depending on the type, setup the clip parameters */
6014 if (stream->subtype == FOURCC_soun) {
6015 frame_size = CUR_STREAM (stream)->bytes_per_frame;
6016 num_rate = GST_SECOND;
6017 denom_rate = (gint) CUR_STREAM (stream)->rate;
6019 } else if (stream->subtype == FOURCC_vide) {
6021 num_rate = CUR_STREAM (stream)->fps_n;
6022 denom_rate = CUR_STREAM (stream)->fps_d;
6027 if (frame_size <= 0)
6028 goto bad_frame_size;
6030 /* we can only clip if we have a valid pts */
6031 pts = GST_BUFFER_PTS (buf);
6032 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
6035 duration = GST_BUFFER_DURATION (buf);
6037 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
6039 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
6043 stop = start + duration;
6045 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
6046 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
6049 /* see if some clipping happened */
6050 diff = cstart - start;
6056 /* bring clipped time to samples and to bytes */
6057 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6060 GST_DEBUG_OBJECT (qtdemux,
6061 "clipping start to %" GST_TIME_FORMAT " %"
6062 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
6068 diff = stop - cstop;
6073 /* bring clipped time to samples and then to bytes */
6074 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
6076 GST_DEBUG_OBJECT (qtdemux,
6077 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
6078 " bytes", GST_TIME_ARGS (cstop), diff);
6083 if (offset != 0 || size != osize)
6084 gst_buffer_resize (buf, offset, size);
6086 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
6087 GST_BUFFER_PTS (buf) = pts;
6088 GST_BUFFER_DURATION (buf) = duration;
6092 /* dropped buffer */
6095 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
6100 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
6105 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
6110 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
6111 gst_buffer_unref (buf);
6117 gst_qtdemux_align_buffer (GstQTDemux * demux,
6118 GstBuffer * buffer, gsize alignment)
6122 gst_buffer_map (buffer, &map, GST_MAP_READ);
6124 if (map.size < sizeof (guintptr)) {
6125 gst_buffer_unmap (buffer, &map);
6129 if (((guintptr) map.data) & (alignment - 1)) {
6130 GstBuffer *new_buffer;
6131 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
6133 new_buffer = gst_buffer_new_allocate (NULL,
6134 gst_buffer_get_size (buffer), ¶ms);
6136 /* Copy data "by hand", so ensure alignment is kept: */
6137 gst_buffer_fill (new_buffer, 0, map.data, map.size);
6139 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
6140 GST_DEBUG_OBJECT (demux,
6141 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
6144 gst_buffer_unmap (buffer, &map);
6145 gst_buffer_unref (buffer);
6150 gst_buffer_unmap (buffer, &map);
6155 convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
6161 /* We are converting from pairs to triplets */
6162 *res = ccpair_size / 2 * 3;
6163 storage = g_malloc (*res);
6164 for (i = 0; i * 2 < ccpair_size; i += 1) {
6165 /* FIXME: Use line offset 0 as we simply can't know here */
6167 storage[i * 3] = 0x80 | 0x00;
6169 storage[i * 3] = 0x00 | 0x00;
6170 storage[i * 3 + 1] = ccpair[i * 2];
6171 storage[i * 3 + 2] = ccpair[i * 2 + 1];
6178 extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
6182 guint32 atom_length, fourcc;
6183 QtDemuxStreamStsdEntry *stsd_entry;
6185 GST_MEMDUMP ("caption atom", data, size);
6187 /* There might be multiple atoms */
6192 atom_length = QT_UINT32 (data);
6193 fourcc = QT_FOURCC (data + 4);
6194 if (G_UNLIKELY (atom_length > size || atom_length == 8))
6197 GST_DEBUG_OBJECT (stream->pad, "here");
6199 /* Check if we have something compatible */
6200 stsd_entry = CUR_STREAM (stream);
6201 switch (stsd_entry->fourcc) {
6203 guint8 *cdat = NULL, *cdt2 = NULL;
6204 gsize cdat_size = 0, cdt2_size = 0;
6205 /* Should be cdat or cdt2 */
6206 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
6207 GST_WARNING_OBJECT (stream->pad,
6208 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
6209 GST_FOURCC_ARGS (fourcc));
6213 /* Convert to S334-1 Annex A byte triplet */
6214 if (fourcc == FOURCC_cdat)
6215 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
6217 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
6218 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
6221 /* Check for another atom ? */
6222 if (size > atom_length + 8) {
6223 guint32 new_atom_length = QT_UINT32 (data + atom_length);
6224 if (size >= atom_length + new_atom_length) {
6225 fourcc = QT_FOURCC (data + atom_length + 4);
6226 if (fourcc == FOURCC_cdat) {
6229 convert_to_s334_1a (data + atom_length + 8,
6230 new_atom_length - 8, 1, &cdat_size);
6232 GST_WARNING_OBJECT (stream->pad,
6233 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6237 convert_to_s334_1a (data + atom_length + 8,
6238 new_atom_length - 8, 2, &cdt2_size);
6240 GST_WARNING_OBJECT (stream->pad,
6241 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
6246 *cclen = cdat_size + cdt2_size;
6247 res = g_malloc (*cclen);
6249 memcpy (res, cdat, cdat_size);
6251 memcpy (res + cdat_size, cdt2, cdt2_size);
6257 if (fourcc != FOURCC_ccdp) {
6258 GST_WARNING_OBJECT (stream->pad,
6259 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
6260 GST_FOURCC_ARGS (fourcc));
6263 *cclen = atom_length - 8;
6264 res = g_memdup2 (data + 8, *cclen);
6267 /* Keep this here in case other closed caption formats are added */
6268 g_assert_not_reached ();
6272 GST_MEMDUMP ("Output", res, *cclen);
6277 GST_WARNING ("[cdat] atom is too small or invalid");
6281 /* Handle Closed Caption sample buffers.
6282 * The input buffer metadata must be writable,
6283 * but time/duration etc not yet set and need not be preserved */
6285 gst_qtdemux_process_buffer_clcp (GstQTDemux * qtdemux, QtDemuxStream * stream,
6288 GstBuffer *outbuf = NULL;
6293 gst_buffer_map (buf, &map, GST_MAP_READ);
6295 /* empty buffer is sent to terminate previous subtitle */
6296 if (map.size <= 2) {
6297 gst_buffer_unmap (buf, &map);
6298 gst_buffer_unref (buf);
6302 /* For closed caption, we need to extract the information from the
6303 * [cdat],[cdt2] or [ccdp] atom */
6304 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
6305 gst_buffer_unmap (buf, &map);
6307 outbuf = _gst_buffer_new_wrapped (cc, cclen, g_free);
6308 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6310 /* Conversion failed or there's nothing */
6312 gst_buffer_unref (buf);
6317 /* DVD subpicture specific sample handling.
6318 * the input buffer metadata must be writable,
6319 * but time/duration etc not yet set and need not be preserved */
6321 gst_qtdemux_process_buffer_dvd (GstQTDemux * qtdemux, QtDemuxStream * stream,
6324 /* send a one time dvd clut event */
6325 if (stream->pending_event && stream->pad)
6326 gst_pad_push_event (stream->pad, stream->pending_event);
6327 stream->pending_event = NULL;
6329 /* empty buffer is sent to terminate previous subtitle */
6330 if (gst_buffer_get_size (buf) <= 2) {
6331 gst_buffer_unref (buf);
6335 /* That's all the processing needed for subpictures */
6339 /* Timed text formats
6340 * the input buffer metadata must be writable,
6341 * but time/duration etc not yet set and need not be preserved */
6343 gst_qtdemux_process_buffer_text (GstQTDemux * qtdemux, QtDemuxStream * stream,
6346 GstBuffer *outbuf = NULL;
6351 /* not many cases for now */
6352 if (G_UNLIKELY (stream->subtype != FOURCC_text &&
6353 stream->subtype != FOURCC_sbtl)) {
6357 gst_buffer_map (buf, &map, GST_MAP_READ);
6359 /* empty buffer is sent to terminate previous subtitle */
6360 if (map.size <= 2) {
6361 gst_buffer_unmap (buf, &map);
6362 gst_buffer_unref (buf);
6366 nsize = GST_READ_UINT16_BE (map.data);
6367 nsize = MIN (nsize, map.size - 2);
6369 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6372 /* takes care of UTF-8 validation or UTF-16 recognition,
6373 * no other encoding expected */
6374 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6375 gst_buffer_unmap (buf, &map);
6378 outbuf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
6379 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6381 /* this should not really happen unless the subtitle is corrupted */
6383 gst_buffer_unref (buf);
6385 /* FIXME ? convert optional subsequent style info to markup */
6390 /* WebVTT sample handling according to 14496-30 */
6392 gst_qtdemux_process_buffer_wvtt (GstQTDemux * qtdemux, QtDemuxStream * stream,
6395 GstBuffer *outbuf = NULL;
6398 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
6399 g_assert_not_reached (); /* The buffer must be mappable */
6402 if (qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
6403 GstEvent *gap = NULL;
6404 /* Push a gap event */
6405 stream->segment.position = GST_BUFFER_PTS (buf);
6407 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
6408 gst_pad_push_event (stream->pad, gap);
6410 if (GST_BUFFER_DURATION_IS_VALID (buf))
6411 stream->segment.position += GST_BUFFER_DURATION (buf);
6414 qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
6415 GST_BUFFER_DURATION (buf), map.data, map.size);
6416 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6419 gst_buffer_unmap (buf, &map);
6420 gst_buffer_unref (buf);
6425 static GstFlowReturn
6426 gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6429 GstFlowReturn ret = GST_FLOW_OK;
6430 GstClockTime pts, duration;
6432 if (stream->need_clip)
6433 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6435 if (G_UNLIKELY (buf == NULL))
6438 if (G_UNLIKELY (stream->discont)) {
6439 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6440 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6441 stream->discont = FALSE;
6443 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6446 GST_LOG_OBJECT (qtdemux,
6447 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6448 ", duration %" GST_TIME_FORMAT " on pad %s",
6449 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6450 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6451 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
6453 if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
6454 GstStructure *crypto_info;
6455 QtDemuxAavdEncryptionInfo *info =
6456 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
6458 crypto_info = gst_structure_copy (info->default_properties);
6459 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6460 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
6463 if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
6464 || stream->protection_scheme_type == FOURCC_cbcs)) {
6465 GstStructure *crypto_info;
6466 QtDemuxCencSampleSetInfo *info =
6467 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6471 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6472 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6473 GST_PTR_FORMAT, event);
6474 gst_pad_push_event (stream->pad, event);
6477 if (info->crypto_info == NULL) {
6478 if (stream->protection_scheme_type == FOURCC_cbcs) {
6479 crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
6480 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) {
6481 GST_ERROR_OBJECT (qtdemux,
6482 "failed to attach cbcs metadata to buffer");
6483 qtdemux_gst_structure_free (crypto_info);
6485 GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
6488 GST_DEBUG_OBJECT (qtdemux,
6489 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
6492 /* The end of the crypto_info array matches our n_samples position,
6493 * so count backward from there */
6494 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6495 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6496 /* steal structure from array */
6497 crypto_info = g_ptr_array_index (info->crypto_info, index);
6498 g_ptr_array_index (info->crypto_info, index) = NULL;
6499 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6500 info->crypto_info->len);
6501 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6502 GST_ERROR_OBJECT (qtdemux,
6503 "failed to attach cenc metadata to buffer");
6505 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6506 index, stream->sample_index);
6511 if (stream->alignment > 1)
6512 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6514 pts = GST_BUFFER_PTS (buf);
6515 duration = GST_BUFFER_DURATION (buf);
6517 ret = gst_pad_push (stream->pad, buf);
6519 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6520 /* mark position in stream, we'll need this to know when to send GAP event */
6521 stream->segment.position = pts + duration;
6529 static GstFlowReturn
6530 gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6533 GstFlowReturn ret = GST_FLOW_OK;
6535 if (stream->subtype == FOURCC_clcp
6536 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6538 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6539 guint n_triplets, i;
6540 guint field1_off = 0, field2_off = 0;
6542 /* We have to split CEA608 buffers so that each outgoing buffer contains
6543 * one byte pair per field according to the framerate of the video track.
6545 * If there is only a single byte pair per field we don't have to do
6549 gst_buffer_map (buf, &map, GST_MAP_READ);
6551 n_triplets = map.size / 3;
6552 for (i = 0; i < n_triplets; i++) {
6553 if (map.data[3 * i] & 0x80)
6559 g_assert (n_field1 || n_field2);
6561 /* If there's more than 1 frame we have to split, otherwise we can just
6563 if (n_field1 > 1 || n_field2 > 1) {
6565 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6566 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6568 for (i = 0; i < n_output_buffers; i++) {
6570 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6574 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6575 outptr = outmap.data;
6578 gboolean found = FALSE;
6580 while (map.data + field1_off < map.data + map.size) {
6581 if (map.data[field1_off] & 0x80) {
6582 memcpy (outptr, &map.data[field1_off], 3);
6591 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6593 memcpy (outptr, empty, 3);
6600 gboolean found = FALSE;
6602 while (map.data + field2_off < map.data + map.size) {
6603 if ((map.data[field2_off] & 0x80) == 0) {
6604 memcpy (outptr, &map.data[field2_off], 3);
6613 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6615 memcpy (outptr, empty, 3);
6621 gst_buffer_unmap (outbuf, &outmap);
6623 GST_BUFFER_PTS (outbuf) =
6624 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6625 GST_SECOND * CUR_STREAM (stream)->fps_d,
6626 CUR_STREAM (stream)->fps_n);
6627 GST_BUFFER_DURATION (outbuf) =
6628 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6629 CUR_STREAM (stream)->fps_n);
6630 GST_BUFFER_OFFSET (outbuf) = -1;
6631 GST_BUFFER_OFFSET_END (outbuf) = -1;
6633 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6635 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6638 gst_buffer_unmap (buf, &map);
6639 gst_buffer_unref (buf);
6641 gst_buffer_unmap (buf, &map);
6642 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6645 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6651 /* Sets a buffer's attributes properly and pushes it downstream.
6652 * Also checks for additional actions and custom processing that may
6653 * need to be done first.
6655 static GstFlowReturn
6656 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6657 QtDemuxStream * stream, GstBuffer * buf,
6658 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6659 gboolean keyframe, GstClockTime position, guint64 byte_position)
6661 GstFlowReturn ret = GST_FLOW_OK;
6663 /* offset the timestamps according to the edit list */
6665 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6669 gst_buffer_map (buf, &map, GST_MAP_READ);
6670 url = g_strndup ((gchar *) map.data, map.size);
6671 gst_buffer_unmap (buf, &map);
6672 if (url != NULL && strlen (url) != 0) {
6673 /* we have RTSP redirect now */
6674 g_free (qtdemux->redirect_location);
6675 qtdemux->redirect_location = g_strdup (url);
6676 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6677 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6678 gst_structure_new ("redirect",
6679 "new-location", G_TYPE_STRING, url, NULL)));
6681 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6687 /* position reporting */
6688 if (qtdemux->segment.rate >= 0) {
6689 qtdemux->segment.position = position;
6690 gst_qtdemux_sync_streams (qtdemux);
6693 if (G_UNLIKELY (!stream->pad)) {
6694 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6695 gst_buffer_unref (buf);
6699 /* send out pending buffers */
6700 while (stream->buffers) {
6701 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6703 if (G_UNLIKELY (stream->discont)) {
6704 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6705 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6706 stream->discont = FALSE;
6708 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6711 if (stream->alignment > 1)
6712 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6713 gst_pad_push (stream->pad, buffer);
6715 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6718 /* we're going to modify the metadata */
6719 buf = gst_buffer_make_writable (buf);
6721 GST_BUFFER_DTS (buf) = dts;
6722 GST_BUFFER_PTS (buf) = pts;
6723 GST_BUFFER_DURATION (buf) = duration;
6724 GST_BUFFER_OFFSET (buf) = -1;
6725 GST_BUFFER_OFFSET_END (buf) = -1;
6727 if (G_UNLIKELY (stream->process_func))
6728 buf = stream->process_func (qtdemux, stream, buf);
6735 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6736 stream->on_keyframe = FALSE;
6738 stream->on_keyframe = TRUE;
6741 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6742 gst_buffer_append_memory (buf,
6743 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6745 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6746 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6749 if (G_UNLIKELY (qtdemux->element_index)) {
6750 GstClockTime stream_time;
6753 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6755 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6756 GST_LOG_OBJECT (qtdemux,
6757 "adding association %" GST_TIME_FORMAT "-> %"
6758 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6759 gst_index_add_association (qtdemux->element_index,
6761 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6762 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6763 GST_FORMAT_BYTES, byte_position, NULL);
6768 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6774 static const QtDemuxRandomAccessEntry *
6775 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6776 GstClockTime pos, gboolean after)
6778 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6779 guint n_entries = stream->n_ra_entries;
6782 /* we assume the table is sorted */
6783 for (i = 0; i < n_entries; ++i) {
6784 if (entries[i].ts > pos)
6788 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6789 * probably okay to assume that the index lists the very first fragment */
6796 return &entries[i - 1];
6800 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6802 const QtDemuxRandomAccessEntry *best_entry = NULL;
6805 GST_OBJECT_LOCK (qtdemux);
6807 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6809 /* first see if we can determine where to go to using mfra,
6810 * before we start clearing things */
6811 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6812 const QtDemuxRandomAccessEntry *entry;
6813 QtDemuxStream *stream;
6814 gboolean is_audio_or_video;
6816 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6818 if (stream->ra_entries == NULL)
6821 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6822 is_audio_or_video = TRUE;
6824 is_audio_or_video = FALSE;
6827 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6828 stream->time_position, !is_audio_or_video);
6830 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6831 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6833 stream->pending_seek = entry;
6835 /* decide position to jump to just based on audio/video tracks, not subs */
6836 if (!is_audio_or_video)
6839 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6843 /* no luck, will handle seek otherwise */
6844 if (best_entry == NULL) {
6845 GST_OBJECT_UNLOCK (qtdemux);
6849 /* ok, now we can prepare for processing as of located moof */
6850 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6851 QtDemuxStream *stream;
6853 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6855 g_free (stream->samples);
6856 stream->samples = NULL;
6857 stream->n_samples = 0;
6858 stream->stbl_index = -1; /* no samples have yet been parsed */
6859 stream->sample_index = -1;
6861 if (stream->protection_scheme_info) {
6862 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6863 if (stream->protection_scheme_type == FOURCC_cenc
6864 || stream->protection_scheme_type == FOURCC_cbcs) {
6865 QtDemuxCencSampleSetInfo *info =
6866 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6867 if (info->crypto_info) {
6868 g_ptr_array_free (info->crypto_info, TRUE);
6869 info->crypto_info = NULL;
6875 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6876 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6877 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6878 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6880 qtdemux->moof_offset = best_entry->moof_offset;
6882 qtdemux_add_fragmented_samples (qtdemux);
6884 GST_OBJECT_UNLOCK (qtdemux);
6888 static GstFlowReturn
6889 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6891 GstFlowReturn ret = GST_FLOW_OK;
6892 GstBuffer *buf = NULL;
6893 QtDemuxStream *stream, *target_stream = NULL;
6894 GstClockTime min_time;
6896 GstClockTime dts = GST_CLOCK_TIME_NONE;
6897 GstClockTime pts = GST_CLOCK_TIME_NONE;
6898 GstClockTime duration = 0;
6899 gboolean keyframe = FALSE;
6900 guint sample_size = 0;
6901 guint num_samples = 1;
6906 if (qtdemux->fragmented_seek_pending) {
6907 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6908 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6909 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6910 qtdemux->fragmented_seek_pending = FALSE;
6912 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6916 /* Figure out the next stream sample to output, min_time is expressed in
6917 * global time and runs over the edit list segments. */
6918 min_time = G_MAXUINT64;
6919 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6920 GstClockTime position;
6922 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6923 position = stream->time_position;
6925 if (!GST_CLOCK_TIME_IS_VALID (position))
6928 if (stream->segment_index != -1) {
6929 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6930 position += segment->media_start;
6933 /* position of -1 is EOS */
6934 if (position < min_time) {
6935 min_time = position;
6936 target_stream = stream;
6940 if (G_UNLIKELY (target_stream == NULL)) {
6941 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6945 /* check for segment end */
6946 if (G_UNLIKELY (qtdemux->segment.stop != -1
6947 && qtdemux->segment.rate >= 0
6948 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
6949 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6950 target_stream->time_position = GST_CLOCK_TIME_NONE;
6954 /* gap events for subtitle streams */
6955 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6956 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6958 GstClockTime gap_threshold;
6960 /* Only send gap events on non-subtitle streams if lagging way behind. */
6961 if (stream->subtype == FOURCC_subp
6962 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl ||
6963 stream->subtype == FOURCC_wvtt)
6964 gap_threshold = 1 * GST_SECOND;
6966 gap_threshold = 3 * GST_SECOND;
6968 /* send gap events until the stream catches up */
6969 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6970 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6971 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6972 stream->segment.position + gap_threshold < min_time) {
6974 gst_event_new_gap (stream->segment.position, gap_threshold);
6975 gst_pad_push_event (stream->pad, gap);
6976 stream->segment.position += gap_threshold;
6981 stream = target_stream;
6982 /* fetch info for the current sample of this stream */
6983 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6984 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6987 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6988 if (stream->new_caps) {
6989 gst_qtdemux_configure_stream (qtdemux, stream);
6990 qtdemux_do_allocation (stream, qtdemux);
6993 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6994 if (G_UNLIKELY (qtdemux->segment.
6995 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6996 if (stream->subtype == FOURCC_vide) {
6998 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
7001 } else if (qtdemux->trickmode_interval > 0) {
7002 GstClockTimeDiff interval;
7004 if (qtdemux->segment.rate > 0)
7005 interval = stream->time_position - stream->last_keyframe_dts;
7007 interval = stream->last_keyframe_dts - stream->time_position;
7009 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
7010 && interval < qtdemux->trickmode_interval) {
7011 GST_LOG_OBJECT (qtdemux,
7012 "Skipping keyframe within interval on track-id %u",
7016 stream->last_keyframe_dts = stream->time_position;
7022 GST_DEBUG_OBJECT (qtdemux,
7023 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
7024 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
7025 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
7026 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
7027 GST_TIME_ARGS (duration));
7029 if (G_UNLIKELY (empty)) {
7030 /* empty segment, push a gap if there's a second or more
7031 * difference and move to the next one */
7032 if ((pts + duration - stream->segment.position) >= GST_SECOND)
7033 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
7034 stream->segment.position = pts + duration;
7038 /* hmm, empty sample, skip and move to next sample */
7039 if (G_UNLIKELY (sample_size <= 0))
7042 /* last pushed sample was out of boundary, goto next sample */
7043 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
7046 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
7047 GST_DEBUG_OBJECT (qtdemux,
7048 "size %d larger than stream max_buffer_size %d, trimming",
7049 sample_size, stream->max_buffer_size);
7051 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
7052 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
7053 && sample_size < stream->min_buffer_size) {
7054 guint start_sample_index = stream->sample_index;
7055 guint accumulated_size = sample_size;
7056 guint64 expected_next_offset = offset + sample_size;
7058 GST_DEBUG_OBJECT (qtdemux,
7059 "size %d smaller than stream min_buffer_size %d, combining with the next",
7060 sample_size, stream->min_buffer_size);
7062 while (stream->sample_index < stream->to_sample
7063 && stream->sample_index + 1 < stream->n_samples) {
7064 const QtDemuxSample *next_sample;
7066 /* Increment temporarily */
7067 stream->sample_index++;
7069 /* Failed to parse sample so let's go back to the previous one that was
7070 * still successful */
7071 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
7072 stream->sample_index--;
7076 next_sample = &stream->samples[stream->sample_index];
7078 /* Not contiguous with the previous sample so let's go back to the
7079 * previous one that was still successful */
7080 if (next_sample->offset != expected_next_offset) {
7081 stream->sample_index--;
7085 accumulated_size += next_sample->size;
7086 expected_next_offset += next_sample->size;
7087 if (accumulated_size >= stream->min_buffer_size)
7091 num_samples = stream->sample_index + 1 - start_sample_index;
7092 stream->sample_index = start_sample_index;
7093 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
7094 num_samples, accumulated_size);
7095 size = accumulated_size;
7100 if (qtdemux->cenc_aux_info_offset > 0) {
7103 GstBuffer *aux_info = NULL;
7105 /* pull the data stored before the sample */
7107 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
7108 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
7109 if (G_UNLIKELY (ret != GST_FLOW_OK))
7111 gst_buffer_map (aux_info, &map, GST_MAP_READ);
7112 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
7113 gst_byte_reader_init (&br, map.data + 8, map.size);
7114 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
7115 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
7116 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
7117 gst_buffer_unmap (aux_info, &map);
7118 gst_buffer_unref (aux_info);
7119 ret = GST_FLOW_ERROR;
7122 gst_buffer_unmap (aux_info, &map);
7123 gst_buffer_unref (aux_info);
7126 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
7129 if (stream->use_allocator) {
7130 /* if we have a per-stream allocator, use it */
7131 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
7134 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
7136 if (G_UNLIKELY (ret != GST_FLOW_OK))
7139 /* Update for both splitting and combining of samples */
7140 if (size != sample_size) {
7141 pts += gst_util_uint64_scale_int (GST_SECOND,
7142 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7145 gst_util_uint64_scale_int (GST_SECOND,
7146 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
7149 gst_util_uint64_scale_int (GST_SECOND,
7150 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
7153 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
7154 dts, pts, duration, keyframe, min_time, offset);
7156 if (size < sample_size) {
7157 QtDemuxSample *sample = &stream->samples[stream->sample_index];
7158 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
7160 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
7162 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
7163 if (time_position >= segment->media_start) {
7164 /* inside the segment, update time_position, looks very familiar to
7165 * GStreamer segments, doesn't it? */
7166 stream->time_position = (time_position - segment->media_start) +
7169 /* not yet in segment, time does not yet increment. This means
7170 * that we are still prerolling keyframes to the decoder so it can
7171 * decode the first sample of the segment. */
7172 stream->time_position = segment->time;
7174 } else if (size > sample_size) {
7175 /* Increase to the last sample we already pulled so that advancing
7176 * below brings us to the next sample we need to pull */
7177 stream->sample_index += num_samples - 1;
7181 GST_OBJECT_LOCK (qtdemux);
7182 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
7183 GST_OBJECT_UNLOCK (qtdemux);
7184 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
7185 * we have no more data for the pad to push */
7186 if (ret == GST_FLOW_EOS)
7189 stream->offset_in_sample += size;
7190 if (stream->offset_in_sample >= sample_size) {
7191 gst_qtdemux_advance_sample (qtdemux, stream);
7196 gst_qtdemux_advance_sample (qtdemux, stream);
7204 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
7210 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
7211 /* EOS will be raised if all are EOS */
7218 gst_qtdemux_loop (GstPad * pad)
7220 GstQTDemux *qtdemux;
7224 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
7226 cur_offset = qtdemux->offset;
7227 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
7228 cur_offset, qt_demux_state_string (qtdemux->state));
7230 switch (qtdemux->state) {
7231 case QTDEMUX_STATE_INITIAL:
7232 case QTDEMUX_STATE_HEADER:
7233 ret = gst_qtdemux_loop_state_header (qtdemux);
7235 case QTDEMUX_STATE_MOVIE:
7236 ret = gst_qtdemux_loop_state_movie (qtdemux);
7237 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
7238 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
7246 /* if something went wrong, pause */
7247 if (ret != GST_FLOW_OK)
7251 gst_object_unref (qtdemux);
7257 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7258 (NULL), ("streaming stopped, invalid state"));
7259 gst_pad_pause_task (pad);
7260 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7265 const gchar *reason = gst_flow_get_name (ret);
7267 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7269 gst_pad_pause_task (pad);
7271 /* fatal errors need special actions */
7273 if (ret == GST_FLOW_EOS) {
7274 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
7275 /* we have no streams, post an error */
7276 gst_qtdemux_post_no_playable_stream_error (qtdemux);
7278 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7281 if ((stop = qtdemux->segment.stop) == -1)
7282 stop = qtdemux->segment.duration;
7284 if (qtdemux->segment.rate >= 0) {
7285 GstMessage *message;
7288 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7289 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7290 GST_FORMAT_TIME, stop);
7291 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7292 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7293 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7294 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7296 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7297 gst_qtdemux_push_event (qtdemux, event);
7299 GstMessage *message;
7302 /* For Reverse Playback */
7303 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7304 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7305 GST_FORMAT_TIME, qtdemux->segment.start);
7306 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7307 qtdemux->segment.start);
7308 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7309 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7310 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7312 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
7313 gst_qtdemux_push_event (qtdemux, event);
7318 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7319 event = gst_event_new_eos ();
7320 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7321 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7322 gst_qtdemux_push_event (qtdemux, event);
7324 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7325 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
7326 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
7335 * Returns if there are samples to be played.
7338 has_next_entry (GstQTDemux * demux)
7340 QtDemuxStream *stream;
7343 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7345 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7346 stream = QTDEMUX_NTH_STREAM (demux, i);
7348 if (stream->sample_index == -1) {
7349 stream->sample_index = 0;
7350 stream->offset_in_sample = 0;
7353 if (stream->sample_index >= stream->n_samples) {
7354 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7357 GST_DEBUG_OBJECT (demux, "Found a sample");
7361 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7368 * Returns the size of the first entry at the current offset.
7369 * If -1, there are none (which means EOS or empty file).
7372 next_entry_size (GstQTDemux * demux)
7374 QtDemuxStream *stream, *target_stream = NULL;
7375 guint64 smalloffs = (guint64) - 1;
7376 QtDemuxSample *sample;
7379 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7382 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7383 stream = QTDEMUX_NTH_STREAM (demux, i);
7385 if (stream->sample_index == -1) {
7386 stream->sample_index = 0;
7387 stream->offset_in_sample = 0;
7390 if (stream->sample_index >= stream->n_samples) {
7391 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7395 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
7396 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7397 stream->sample_index);
7401 sample = &stream->samples[stream->sample_index];
7403 GST_LOG_OBJECT (demux,
7404 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7405 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7406 stream->sample_index, sample->offset, sample->size);
7408 if (((smalloffs == -1)
7409 || (sample->offset < smalloffs)) && (sample->size)) {
7410 smalloffs = sample->offset;
7411 target_stream = stream;
7418 GST_LOG_OBJECT (demux,
7419 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7420 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7422 stream = target_stream;
7423 sample = &stream->samples[stream->sample_index];
7425 if (sample->offset >= demux->offset) {
7426 demux->todrop = sample->offset - demux->offset;
7427 return sample->size + demux->todrop;
7430 GST_DEBUG_OBJECT (demux,
7431 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7436 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
7438 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7440 gst_element_post_message (GST_ELEMENT_CAST (demux),
7441 gst_message_new_element (GST_OBJECT_CAST (demux),
7442 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7446 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
7451 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7454 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7455 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7456 GST_SEEK_TYPE_NONE, -1);
7458 /* store seqnum to drop flush events, they don't need to reach downstream */
7459 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7460 res = gst_pad_push_event (demux->sinkpad, event);
7461 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7466 /* check for seekable upstream, above and beyond a mere query */
7468 gst_qtdemux_check_seekability (GstQTDemux * demux)
7471 gboolean seekable = FALSE;
7472 gint64 start = -1, stop = -1;
7474 if (demux->upstream_size)
7477 if (demux->upstream_format_is_time)
7480 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7481 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7482 GST_DEBUG_OBJECT (demux, "seeking query failed");
7486 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7488 /* try harder to query upstream size if we didn't get it the first time */
7489 if (seekable && stop == -1) {
7490 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7491 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7494 /* if upstream doesn't know the size, it's likely that it's not seekable in
7495 * practice even if it technically may be seekable */
7496 if (seekable && (start != 0 || stop <= start)) {
7497 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7502 gst_query_unref (query);
7504 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7505 G_GUINT64_FORMAT ")", seekable, start, stop);
7506 demux->upstream_seekable = seekable;
7507 demux->upstream_size = seekable ? stop : -1;
7511 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7513 g_return_if_fail (bytes <= demux->todrop);
7515 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7516 gst_adapter_flush (demux->adapter, bytes);
7517 demux->neededbytes -= bytes;
7518 demux->offset += bytes;
7519 demux->todrop -= bytes;
7522 /* PUSH-MODE only: Send a segment, if not done already. */
7524 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7526 if (G_UNLIKELY (demux->need_segment)) {
7529 if (!demux->upstream_format_is_time) {
7530 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7532 GstEvent *segment_event;
7533 segment_event = gst_event_new_segment (&demux->segment);
7534 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7535 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7536 gst_qtdemux_push_event (demux, segment_event);
7539 demux->need_segment = FALSE;
7541 /* clear to send tags on all streams */
7542 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7543 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7544 gst_qtdemux_push_tags (demux, stream);
7545 if (CUR_STREAM (stream)->sparse) {
7546 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7547 gst_pad_push_event (stream->pad,
7548 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7554 /* Used for push mode only. */
7556 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7557 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7559 GstClockTime ts, dur;
7563 stream->segments[segment_index].duration - (pos -
7564 stream->segments[segment_index].time);
7565 stream->time_position += dur;
7567 /* Only gaps with a duration of at least one second are propagated.
7568 * Same workaround as in pull mode.
7569 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7570 if (dur >= GST_SECOND) {
7572 gap = gst_event_new_gap (ts, dur);
7574 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7575 "segment: %" GST_PTR_FORMAT, gap);
7576 gst_pad_push_event (stream->pad, gap);
7580 static GstFlowReturn
7581 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7585 demux = GST_QTDEMUX (parent);
7587 GST_DEBUG_OBJECT (demux,
7588 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7589 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7590 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7591 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7592 gst_buffer_get_size (inbuf), demux->offset);
7594 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7595 gboolean is_gap_input = FALSE;
7598 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7600 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7601 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7604 /* Check if we can land back on our feet in the case where upstream is
7605 * handling the seeking/pushing of samples with gaps in between (like
7606 * in the case of trick-mode DASH for example) */
7607 if (demux->upstream_format_is_time
7608 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7609 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7611 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7612 GST_LOG_OBJECT (demux,
7613 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7614 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7616 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7617 stream, GST_BUFFER_OFFSET (inbuf));
7619 QtDemuxSample *sample = &stream->samples[res];
7620 GST_LOG_OBJECT (demux,
7621 "Checking if sample %d from track-id %u is valid (offset:%"
7622 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7623 stream->track_id, sample->offset, sample->size);
7624 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7625 GST_LOG_OBJECT (demux,
7626 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7628 is_gap_input = TRUE;
7629 /* We can go back to standard playback mode */
7630 demux->state = QTDEMUX_STATE_MOVIE;
7631 /* Remember which sample this stream is at */
7632 stream->sample_index = res;
7633 /* Finally update all push-based values to the expected values */
7634 demux->neededbytes = stream->samples[res].size;
7635 demux->offset = GST_BUFFER_OFFSET (inbuf);
7637 demux->mdatsize - demux->offset + demux->mdatoffset;
7642 if (!is_gap_input) {
7643 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7644 /* Reset state if it's a real discont */
7645 demux->neededbytes = 16;
7646 demux->state = QTDEMUX_STATE_INITIAL;
7647 demux->offset = GST_BUFFER_OFFSET (inbuf);
7648 gst_adapter_clear (demux->adapter);
7651 /* Reverse fragmented playback, need to flush all we have before
7652 * consuming a new fragment.
7653 * The samples array have the timestamps calculated by accumulating the
7654 * durations but this won't work for reverse playback of fragments as
7655 * the timestamps of a subsequent fragment should be smaller than the
7656 * previously received one. */
7657 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7658 gst_qtdemux_process_adapter (demux, TRUE);
7659 g_ptr_array_foreach (demux->active_streams,
7660 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7664 gst_adapter_push (demux->adapter, inbuf);
7666 GST_DEBUG_OBJECT (demux,
7667 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7668 demux->neededbytes, gst_adapter_available (demux->adapter));
7670 return gst_qtdemux_process_adapter (demux, FALSE);
7673 static GstFlowReturn
7674 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7676 GstFlowReturn ret = GST_FLOW_OK;
7678 /* we never really mean to buffer that much */
7679 if (demux->neededbytes == -1) {
7683 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7684 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7686 #ifndef GST_DISABLE_GST_DEBUG
7688 guint64 discont_offset, distance_from_discont;
7690 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7691 distance_from_discont =
7692 gst_adapter_distance_from_discont (demux->adapter);
7694 GST_DEBUG_OBJECT (demux,
7695 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7696 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7697 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7698 demux->offset, discont_offset, distance_from_discont);
7702 switch (demux->state) {
7703 case QTDEMUX_STATE_INITIAL:{
7708 gst_qtdemux_check_seekability (demux);
7710 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7712 /* get fourcc/length, set neededbytes */
7713 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7715 gst_adapter_unmap (demux->adapter);
7717 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7718 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7720 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7721 (_("This file is invalid and cannot be played.")),
7722 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7723 GST_FOURCC_ARGS (fourcc)));
7724 ret = GST_FLOW_ERROR;
7727 if (fourcc == FOURCC_mdat) {
7728 gint next_entry = next_entry_size (demux);
7729 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7730 || !demux->fragmented)) {
7731 /* we have the headers, start playback */
7732 demux->state = QTDEMUX_STATE_MOVIE;
7733 demux->neededbytes = next_entry;
7734 demux->mdatleft = size;
7735 demux->mdatsize = demux->mdatleft;
7737 /* no headers yet, try to get them */
7740 guint64 old, target;
7743 old = demux->offset;
7744 target = old + size;
7746 /* try to jump over the atom with a seek */
7747 /* only bother if it seems worth doing so,
7748 * and avoids possible upstream/server problems */
7749 if (demux->upstream_seekable &&
7750 demux->upstream_size > 4 * (1 << 20)) {
7751 res = qtdemux_seek_offset (demux, target);
7753 GST_DEBUG_OBJECT (demux, "skipping seek");
7758 GST_DEBUG_OBJECT (demux, "seek success");
7759 /* remember the offset fo the first mdat so we can seek back to it
7760 * after we have the headers */
7761 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7762 demux->first_mdat = old;
7763 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7766 /* seek worked, continue reading */
7767 demux->offset = target;
7768 demux->neededbytes = 16;
7769 demux->state = QTDEMUX_STATE_INITIAL;
7771 /* seek failed, need to buffer */
7772 demux->offset = old;
7773 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7774 /* there may be multiple mdat (or alike) buffers */
7776 if (demux->mdatbuffer)
7777 bs = gst_buffer_get_size (demux->mdatbuffer);
7780 if (size + bs > 10 * (1 << 20))
7782 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7783 demux->neededbytes = size;
7784 if (!demux->mdatbuffer)
7785 demux->mdatoffset = demux->offset;
7788 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7789 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7790 (_("This file is invalid and cannot be played.")),
7791 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7792 GST_FOURCC_ARGS (fourcc), size));
7793 ret = GST_FLOW_ERROR;
7796 /* this means we already started buffering and still no moov header,
7797 * let's continue buffering everything till we get moov */
7798 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7799 || fourcc == FOURCC_moof))
7801 demux->neededbytes = size;
7802 demux->state = QTDEMUX_STATE_HEADER;
7806 case QTDEMUX_STATE_HEADER:{
7810 GST_DEBUG_OBJECT (demux, "In header");
7812 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7814 /* parse the header */
7815 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7817 if (fourcc == FOURCC_moov) {
7818 /* in usual fragmented setup we could try to scan for more
7819 * and end up at the the moov (after mdat) again */
7820 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7822 || demux->last_moov_offset == demux->offset)) {
7823 GST_DEBUG_OBJECT (demux,
7824 "Skipping moov atom as we have (this) one already");
7826 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7828 if (demux->got_moov && demux->fragmented) {
7829 GST_DEBUG_OBJECT (demux,
7830 "Got a second moov, clean up data from old one");
7831 if (demux->moov_node_compressed) {
7832 g_node_destroy (demux->moov_node_compressed);
7833 if (demux->moov_node)
7834 g_free (demux->moov_node->data);
7836 demux->moov_node_compressed = NULL;
7837 if (demux->moov_node)
7838 g_node_destroy (demux->moov_node);
7839 demux->moov_node = NULL;
7842 demux->last_moov_offset = demux->offset;
7844 /* Update streams with new moov */
7845 gst_qtdemux_stream_concat (demux,
7846 demux->old_streams, demux->active_streams);
7848 qtdemux_parse_moov (demux, data, demux->neededbytes);
7849 qtdemux_node_dump (demux, demux->moov_node);
7850 qtdemux_parse_tree (demux);
7851 qtdemux_prepare_streams (demux);
7852 QTDEMUX_EXPOSE_LOCK (demux);
7853 qtdemux_expose_streams (demux);
7854 QTDEMUX_EXPOSE_UNLOCK (demux);
7856 demux->got_moov = TRUE;
7858 gst_qtdemux_check_send_pending_segment (demux);
7860 if (demux->moov_node_compressed) {
7861 g_node_destroy (demux->moov_node_compressed);
7862 g_free (demux->moov_node->data);
7864 demux->moov_node_compressed = NULL;
7865 g_node_destroy (demux->moov_node);
7866 demux->moov_node = NULL;
7867 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7869 } else if (fourcc == FOURCC_moof) {
7870 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7872 GstClockTime prev_pts;
7873 guint64 prev_offset;
7874 guint64 adapter_discont_offset, adapter_discont_dist;
7876 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7879 * The timestamp of the moof buffer is relevant as some scenarios
7880 * won't have the initial timestamp in the atoms. Whenever a new
7881 * buffer has started, we get that buffer's PTS and use it as a base
7882 * timestamp for the trun entries.
7884 * To keep track of the current buffer timestamp and starting point
7885 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7886 * from the beginning of the buffer, with the distance and demux->offset
7887 * we know if it is still the same buffer or not.
7889 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7890 prev_offset = demux->offset - dist;
7891 if (demux->fragment_start_offset == -1
7892 || prev_offset > demux->fragment_start_offset) {
7893 demux->fragment_start_offset = prev_offset;
7894 demux->fragment_start = prev_pts;
7895 GST_DEBUG_OBJECT (demux,
7896 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7897 GST_TIME_FORMAT, demux->fragment_start_offset,
7898 GST_TIME_ARGS (demux->fragment_start));
7901 /* We can't use prev_offset() here because this would require
7902 * upstream to set consistent and correct offsets on all buffers
7903 * since the discont. Nothing ever did that in the past and we
7904 * would break backwards compatibility here then.
7905 * Instead take the offset we had at the last discont and count
7906 * the bytes from there. This works with old code as there would
7907 * be no discont between moov and moof, and also works with
7908 * adaptivedemux which correctly sets offset and will set the
7909 * DISCONT flag accordingly when needed.
7911 * We also only do this for upstream TIME segments as otherwise
7912 * there are potential backwards compatibility problems with
7913 * seeking in PUSH mode and upstream providing inconsistent
7915 adapter_discont_offset =
7916 gst_adapter_offset_at_discont (demux->adapter);
7917 adapter_discont_dist =
7918 gst_adapter_distance_from_discont (demux->adapter);
7920 GST_DEBUG_OBJECT (demux,
7921 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7922 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7923 demux->offset, adapter_discont_offset, adapter_discont_dist);
7925 if (demux->upstream_format_is_time) {
7926 demux->moof_offset = adapter_discont_offset;
7927 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7928 demux->moof_offset += adapter_discont_dist;
7929 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7930 demux->moof_offset = demux->offset;
7932 demux->moof_offset = demux->offset;
7935 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7936 demux->moof_offset, NULL)) {
7937 gst_adapter_unmap (demux->adapter);
7938 ret = GST_FLOW_ERROR;
7942 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7943 if (demux->mss_mode && !demux->exposed) {
7944 QTDEMUX_EXPOSE_LOCK (demux);
7945 qtdemux_expose_streams (demux);
7946 QTDEMUX_EXPOSE_UNLOCK (demux);
7949 gst_qtdemux_check_send_pending_segment (demux);
7951 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7953 } else if (fourcc == FOURCC_ftyp) {
7954 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7955 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7956 } else if (fourcc == FOURCC_uuid) {
7957 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7958 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7959 } else if (fourcc == FOURCC_sidx) {
7960 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7961 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7965 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7969 /* [free] and [skip] are padding atoms */
7970 GST_DEBUG_OBJECT (demux,
7971 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7972 GST_FOURCC_ARGS (fourcc));
7975 GST_WARNING_OBJECT (demux,
7976 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7977 GST_FOURCC_ARGS (fourcc));
7978 /* Let's jump that one and go back to initial state */
7982 gst_adapter_unmap (demux->adapter);
7985 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7986 gsize remaining_data_size = 0;
7988 /* the mdat was before the header */
7989 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7990 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7991 /* restore our adapter/offset view of things with upstream;
7992 * put preceding buffered data ahead of current moov data.
7993 * This should also handle evil mdat, moov, mdat cases and alike */
7994 gst_adapter_flush (demux->adapter, demux->neededbytes);
7996 /* Store any remaining data after the mdat for later usage */
7997 remaining_data_size = gst_adapter_available (demux->adapter);
7998 if (remaining_data_size > 0) {
7999 g_assert (demux->restoredata_buffer == NULL);
8000 demux->restoredata_buffer =
8001 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
8002 demux->restoredata_offset = demux->offset + demux->neededbytes;
8003 GST_DEBUG_OBJECT (demux,
8004 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
8005 G_GUINT64_FORMAT, remaining_data_size,
8006 demux->restoredata_offset);
8009 gst_adapter_push (demux->adapter, demux->mdatbuffer);
8010 demux->mdatbuffer = NULL;
8011 demux->offset = demux->mdatoffset;
8012 demux->neededbytes = next_entry_size (demux);
8013 demux->state = QTDEMUX_STATE_MOVIE;
8014 demux->mdatleft = gst_adapter_available (demux->adapter);
8015 demux->mdatsize = demux->mdatleft;
8017 GST_DEBUG_OBJECT (demux, "Carrying on normally");
8018 gst_adapter_flush (demux->adapter, demux->neededbytes);
8020 /* only go back to the mdat if there are samples to play */
8021 if (demux->got_moov && demux->first_mdat != -1
8022 && has_next_entry (demux)) {
8025 /* we need to seek back */
8026 res = qtdemux_seek_offset (demux, demux->first_mdat);
8028 demux->offset = demux->first_mdat;
8030 GST_DEBUG_OBJECT (demux, "Seek back failed");
8033 demux->offset += demux->neededbytes;
8035 demux->neededbytes = 16;
8036 demux->state = QTDEMUX_STATE_INITIAL;
8041 case QTDEMUX_STATE_BUFFER_MDAT:{
8045 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
8047 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8048 gst_buffer_extract (buf, 0, fourcc, 4);
8049 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
8050 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
8051 if (demux->mdatbuffer)
8052 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
8054 demux->mdatbuffer = buf;
8055 demux->offset += demux->neededbytes;
8056 demux->neededbytes = 16;
8057 demux->state = QTDEMUX_STATE_INITIAL;
8058 gst_qtdemux_post_progress (demux, 1, 1);
8062 case QTDEMUX_STATE_MOVIE:{
8063 QtDemuxStream *stream = NULL;
8064 QtDemuxSample *sample;
8065 GstClockTime dts, pts, duration;
8069 GST_DEBUG_OBJECT (demux,
8070 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
8072 if (demux->fragmented) {
8073 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
8075 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
8076 /* if needed data starts within this atom,
8077 * then it should not exceed this atom */
8078 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
8079 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8080 (_("This file is invalid and cannot be played.")),
8081 ("sample data crosses atom boundary"));
8082 ret = GST_FLOW_ERROR;
8085 demux->mdatleft -= demux->neededbytes;
8087 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
8088 /* so we are dropping more than left in this atom */
8089 gst_qtdemux_drop_data (demux, demux->mdatleft);
8090 demux->mdatleft = 0;
8092 /* need to resume atom parsing so we do not miss any other pieces */
8093 demux->state = QTDEMUX_STATE_INITIAL;
8094 demux->neededbytes = 16;
8096 /* check if there was any stored post mdat data from previous buffers */
8097 if (demux->restoredata_buffer) {
8098 g_assert (gst_adapter_available (demux->adapter) == 0);
8100 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
8101 demux->restoredata_buffer = NULL;
8102 demux->offset = demux->restoredata_offset;
8109 if (demux->todrop) {
8110 if (demux->cenc_aux_info_offset > 0) {
8114 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
8115 data = gst_adapter_map (demux->adapter, demux->todrop);
8116 gst_byte_reader_init (&br, data + 8, demux->todrop);
8117 if (!qtdemux_parse_cenc_aux_info (demux,
8118 QTDEMUX_NTH_STREAM (demux, 0), &br,
8119 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
8120 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
8121 ret = GST_FLOW_ERROR;
8122 gst_adapter_unmap (demux->adapter);
8123 g_free (demux->cenc_aux_info_sizes);
8124 demux->cenc_aux_info_sizes = NULL;
8127 demux->cenc_aux_info_offset = 0;
8128 g_free (demux->cenc_aux_info_sizes);
8129 demux->cenc_aux_info_sizes = NULL;
8130 gst_adapter_unmap (demux->adapter);
8132 gst_qtdemux_drop_data (demux, demux->todrop);
8136 /* initial newsegment sent here after having added pads,
8137 * possible others in sink_event */
8138 gst_qtdemux_check_send_pending_segment (demux);
8140 /* Figure out which stream this packet belongs to */
8141 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8142 stream = QTDEMUX_NTH_STREAM (demux, i);
8143 if (stream->sample_index >= stream->n_samples) {
8144 /* reset to be checked below G_UNLIKELY (stream == NULL) */
8148 GST_LOG_OBJECT (demux,
8149 "Checking track-id %u (sample_index:%d / offset:%"
8150 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
8151 stream->sample_index,
8152 stream->samples[stream->sample_index].offset,
8153 stream->samples[stream->sample_index].size);
8155 if (stream->samples[stream->sample_index].offset == demux->offset)
8159 if (G_UNLIKELY (stream == NULL))
8160 goto unknown_stream;
8162 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
8164 if (stream->new_caps) {
8165 gst_qtdemux_configure_stream (demux, stream);
8168 /* Put data in a buffer, set timestamps, caps, ... */
8169 sample = &stream->samples[stream->sample_index];
8171 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
8172 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
8173 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
8175 dts = QTSAMPLE_DTS (stream, sample);
8176 pts = QTSAMPLE_PTS (stream, sample);
8177 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
8178 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
8180 /* check for segment end */
8181 if (G_UNLIKELY (demux->segment.stop != -1
8182 && demux->segment.stop <= pts && stream->on_keyframe)
8183 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
8184 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
8185 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
8187 /* skip this data, stream is EOS */
8188 gst_adapter_flush (demux->adapter, demux->neededbytes);
8189 demux->offset += demux->neededbytes;
8191 /* check if all streams are eos */
8193 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
8194 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
8203 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8205 /* FIXME: should either be an assert or a plain check */
8206 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8208 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
8209 dts, pts, duration, keyframe, dts, demux->offset);
8213 GST_OBJECT_LOCK (demux);
8214 ret = gst_qtdemux_combine_flows (demux, stream, ret);
8215 GST_OBJECT_UNLOCK (demux);
8217 /* skip this data, stream is EOS */
8218 gst_adapter_flush (demux->adapter, demux->neededbytes);
8221 stream->sample_index++;
8222 stream->offset_in_sample = 0;
8224 /* update current offset and figure out size of next buffer */
8225 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8226 demux->offset, demux->neededbytes);
8227 demux->offset += demux->neededbytes;
8228 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8232 if (ret == GST_FLOW_EOS) {
8233 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8234 demux->neededbytes = -1;
8238 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
8239 if (demux->fragmented) {
8240 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8241 /* there may be more to follow, only finish this atom */
8242 demux->todrop = demux->mdatleft;
8243 demux->neededbytes = demux->todrop;
8248 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8249 goto non_ok_unlinked_flow;
8258 /* when buffering movie data, at least show user something is happening */
8259 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
8260 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
8261 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
8262 demux->neededbytes);
8269 non_ok_unlinked_flow:
8271 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8272 gst_flow_get_name (ret));
8277 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8278 ret = GST_FLOW_ERROR;
8283 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8289 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8290 (NULL), ("qtdemuxer invalid state %d", demux->state));
8291 ret = GST_FLOW_ERROR;
8296 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8297 (NULL), ("no 'moov' atom within the first 10 MB"));
8298 ret = GST_FLOW_ERROR;
8304 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
8309 query = gst_query_new_scheduling ();
8311 if (!gst_pad_peer_query (sinkpad, query)) {
8312 gst_query_unref (query);
8316 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8317 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8318 gst_query_unref (query);
8323 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8324 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8328 GST_DEBUG_OBJECT (sinkpad, "activating push");
8329 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8334 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
8335 GstPadMode mode, gboolean active)
8338 GstQTDemux *demux = GST_QTDEMUX (parent);
8341 case GST_PAD_MODE_PUSH:
8342 demux->pullbased = FALSE;
8345 case GST_PAD_MODE_PULL:
8347 demux->pullbased = TRUE;
8348 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
8351 res = gst_pad_stop_task (sinkpad);
8363 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8369 memset (&z, 0, sizeof (z));
8374 if ((ret = inflateInit (&z)) != Z_OK) {
8375 GST_ERROR ("inflateInit() returned %d", ret);
8379 z.next_in = z_buffer;
8380 z.avail_in = z_length;
8382 buffer = (guint8 *) g_malloc (*length);
8383 z.avail_out = *length;
8384 z.next_out = (Bytef *) buffer;
8386 ret = inflate (&z, Z_NO_FLUSH);
8387 if (ret == Z_STREAM_END) {
8389 } else if (ret != Z_OK) {
8390 GST_WARNING ("inflate() returned %d", ret);
8395 buffer = (guint8 *) g_realloc (buffer, *length);
8396 z.next_out = (Bytef *) (buffer + z.total_out);
8397 z.avail_out += 4096;
8398 } while (z.avail_in > 0);
8400 if (ret != Z_STREAM_END) {
8405 *length = z.total_out;
8412 #endif /* HAVE_ZLIB */
8415 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
8419 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8421 /* counts as header data */
8422 qtdemux->header_size += length;
8424 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
8425 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
8427 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
8434 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
8435 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
8436 if (dcom == NULL || cmvd == NULL)
8437 goto invalid_compression;
8439 dcom_len = QT_UINT32 (dcom->data);
8441 goto invalid_compression;
8443 method = QT_FOURCC ((guint8 *) dcom->data + 8);
8447 guint uncompressed_length;
8448 guint compressed_length;
8452 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
8454 goto invalid_compression;
8456 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8457 compressed_length = cmvd_len - 12;
8458 GST_LOG ("length = %u", uncompressed_length);
8461 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8462 compressed_length, &uncompressed_length);
8465 qtdemux->moov_node_compressed = qtdemux->moov_node;
8466 qtdemux->moov_node = g_node_new (buf);
8468 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8469 uncompressed_length);
8473 #endif /* HAVE_ZLIB */
8475 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8476 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8483 invalid_compression:
8485 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8491 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8494 while (G_UNLIKELY (buf < end)) {
8498 if (G_UNLIKELY (buf + 4 > end)) {
8499 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8502 len = QT_UINT32 (buf);
8503 if (G_UNLIKELY (len == 0)) {
8504 GST_LOG_OBJECT (qtdemux, "empty container");
8507 if (G_UNLIKELY (len < 8)) {
8508 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8511 if (G_UNLIKELY (len > (end - buf))) {
8512 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8513 (gint) (end - buf));
8517 child = g_node_new ((guint8 *) buf);
8518 g_node_append (node, child);
8519 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8520 qtdemux_parse_node (qtdemux, child, buf, len);
8528 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8531 int len = QT_UINT32 (xdxt->data);
8532 guint8 *buf = xdxt->data;
8533 guint8 *end = buf + len;
8536 /* skip size and type */
8544 size = QT_UINT32 (buf);
8545 type = QT_FOURCC (buf + 4);
8547 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8549 if (buf + size > end || size <= 0)
8555 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8556 GST_FOURCC_ARGS (type));
8560 buffer = gst_buffer_new_and_alloc (size);
8561 gst_buffer_fill (buffer, 0, buf, size);
8562 stream->buffers = g_slist_append (stream->buffers, buffer);
8563 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8566 buffer = gst_buffer_new_and_alloc (size);
8567 gst_buffer_fill (buffer, 0, buf, size);
8568 stream->buffers = g_slist_append (stream->buffers, buffer);
8569 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8572 buffer = gst_buffer_new_and_alloc (size);
8573 gst_buffer_fill (buffer, 0, buf, size);
8574 stream->buffers = g_slist_append (stream->buffers, buffer);
8575 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8578 GST_WARNING_OBJECT (qtdemux,
8579 "unknown theora cookie %" GST_FOURCC_FORMAT,
8580 GST_FOURCC_ARGS (type));
8589 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8593 guint32 node_length = 0;
8594 const QtNodeType *type;
8597 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8599 if (G_UNLIKELY (length < 8))
8600 goto not_enough_data;
8602 node_length = QT_UINT32 (buffer);
8603 fourcc = QT_FOURCC (buffer + 4);
8605 /* ignore empty nodes */
8606 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8609 type = qtdemux_type_get (fourcc);
8611 end = buffer + length;
8613 GST_LOG_OBJECT (qtdemux,
8614 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8615 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8617 if (node_length > length)
8618 goto broken_atom_size;
8620 if (type->flags & QT_FLAG_CONTAINER) {
8621 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8626 if (node_length < 20) {
8627 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8630 GST_DEBUG_OBJECT (qtdemux,
8631 "parsing stsd (sample table, sample description) atom");
8632 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8633 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8645 /* also read alac (or whatever) in stead of mp4a in the following,
8646 * since a similar layout is used in other cases as well */
8647 if (fourcc == FOURCC_mp4a)
8649 else if (fourcc == FOURCC_fLaC)
8654 /* There are two things we might encounter here: a true mp4a atom, and
8655 an mp4a entry in an stsd atom. The latter is what we're interested
8656 in, and it looks like an atom, but isn't really one. The true mp4a
8657 atom is short, so we detect it based on length here. */
8658 if (length < min_size) {
8659 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8660 GST_FOURCC_ARGS (fourcc));
8664 /* 'version' here is the sound sample description version. Types 0 and
8665 1 are documented in the QTFF reference, but type 2 is not: it's
8666 described in Apple header files instead (struct SoundDescriptionV2
8668 version = QT_UINT16 (buffer + 16);
8670 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8671 GST_FOURCC_ARGS (fourcc), version);
8673 /* parse any esds descriptors */
8685 GST_WARNING_OBJECT (qtdemux,
8686 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8687 GST_FOURCC_ARGS (fourcc), version);
8692 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8720 /* codec_data is contained inside these atoms, which all have
8721 * the same format. */
8722 /* video sample description size is 86 bytes without extension.
8723 * node_length have to be bigger than 86 bytes because video sample
8724 * description can include extensions such as esds, fiel, glbl, etc. */
8725 if (node_length < 86) {
8726 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8727 " sample description length too short (%u < 86)",
8728 GST_FOURCC_ARGS (fourcc), node_length);
8732 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8733 GST_FOURCC_ARGS (fourcc));
8735 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8737 * revision level (2 bytes) : must be set to 0. */
8738 version = QT_UINT32 (buffer + 16);
8739 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8741 /* compressor name : PASCAL string and informative purposes
8742 * first byte : the number of bytes to be displayed.
8743 * it has to be less than 32 because it is reserved
8744 * space of 32 bytes total including itself. */
8745 str_len = QT_UINT8 (buffer + 50);
8747 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8748 (char *) buffer + 51);
8750 GST_WARNING_OBJECT (qtdemux,
8751 "compressorname length too big (%u > 31)", str_len);
8753 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8755 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8760 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8762 /* You are reading this correctly. QTFF specifies that the
8763 * metadata atom is a short atom, whereas ISO BMFF specifies
8764 * it's a full atom. But since so many people are doing things
8765 * differently, we actually peek into the atom to see which
8768 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8769 GST_FOURCC_ARGS (fourcc));
8772 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8773 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8774 * starts with a 'hdlr' atom */
8775 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8776 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8777 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8778 * with version/flags both set to zero */
8779 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8781 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8786 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8787 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8788 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8797 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8798 GST_FOURCC_ARGS (fourcc));
8802 version = QT_UINT32 (buffer + 12);
8803 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8810 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8815 if (length < offset) {
8816 GST_WARNING_OBJECT (qtdemux,
8817 "skipping too small %" GST_FOURCC_FORMAT " box",
8818 GST_FOURCC_ARGS (fourcc));
8821 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8827 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8832 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8837 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8840 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
8843 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
8846 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
8848 if (!strcmp (type->name, "unknown"))
8849 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8853 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8854 GST_FOURCC_ARGS (fourcc));
8860 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8861 (_("This file is corrupt and cannot be played.")),
8862 ("Not enough data for an atom header, got only %u bytes", length));
8867 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8868 (_("This file is corrupt and cannot be played.")),
8869 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8870 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8877 qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8879 /* FIXME: This can only reliably work if demuxers have a
8880 * separate streaming thread per srcpad. This should be
8881 * done in a demuxer base class, which integrates parts
8884 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8889 query = gst_query_new_allocation (stream->caps, FALSE);
8891 if (!gst_pad_peer_query (stream->pad, query)) {
8892 /* not a problem, just debug a little */
8893 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8896 if (stream->allocator)
8897 gst_object_unref (stream->allocator);
8899 if (gst_query_get_n_allocation_params (query) > 0) {
8900 /* try the allocator */
8901 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8903 stream->use_allocator = TRUE;
8905 stream->allocator = NULL;
8906 gst_allocation_params_init (&stream->params);
8907 stream->use_allocator = FALSE;
8909 gst_query_unref (query);
8914 pad_query (const GValue * item, GValue * value, gpointer user_data)
8916 GstPad *pad = g_value_get_object (item);
8917 GstQuery *query = user_data;
8920 res = gst_pad_peer_query (pad, query);
8923 g_value_set_boolean (value, TRUE);
8927 GST_INFO_OBJECT (pad, "pad peer query failed");
8932 gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8933 GstPadDirection direction)
8936 GstIteratorFoldFunction func = pad_query;
8937 GValue res = { 0, };
8939 g_value_init (&res, G_TYPE_BOOLEAN);
8940 g_value_set_boolean (&res, FALSE);
8943 if (direction == GST_PAD_SRC)
8944 it = gst_element_iterate_src_pads (element);
8946 it = gst_element_iterate_sink_pads (element);
8948 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8949 gst_iterator_resync (it);
8951 gst_iterator_free (it);
8953 return g_value_get_boolean (&res);
8957 gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8958 QtDemuxStream * stream)
8962 GstElement *element = GST_ELEMENT (qtdemux);
8964 gchar **filtered_sys_ids;
8965 GValue event_list = G_VALUE_INIT;
8968 /* 1. Check if we already have the context. */
8969 if (qtdemux->preferred_protection_system_id != NULL) {
8970 GST_LOG_OBJECT (element,
8971 "already have the protection context, no need to request it again");
8975 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8976 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8977 (const gchar **) qtdemux->protection_system_ids->pdata);
8979 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8980 qtdemux->protection_system_ids->len - 1);
8981 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8982 "decryptors for %u of them, running context request",
8983 qtdemux->protection_system_ids->len,
8984 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8987 if (stream->protection_scheme_event_queue.length) {
8988 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8989 stream->protection_scheme_event_queue.length);
8990 walk = stream->protection_scheme_event_queue.tail;
8992 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8993 qtdemux->protection_event_queue.length);
8994 walk = qtdemux->protection_event_queue.tail;
8997 g_value_init (&event_list, GST_TYPE_LIST);
8998 for (; walk; walk = g_list_previous (walk)) {
8999 GValue *event_value = g_new0 (GValue, 1);
9000 g_value_init (event_value, GST_TYPE_EVENT);
9001 g_value_set_boxed (event_value, walk->data);
9002 gst_value_list_append_and_take_value (&event_list, event_value);
9005 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
9006 * check if downstream already has a context of the specific type
9007 * 2b) Query upstream as above.
9009 query = gst_query_new_context ("drm-preferred-decryption-system-id");
9010 st = gst_query_writable_structure (query);
9011 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9012 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9014 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9015 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
9016 gst_query_parse_context (query, &ctxt);
9017 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
9018 gst_element_set_context (element, ctxt);
9019 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
9020 gst_query_parse_context (query, &ctxt);
9021 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
9022 gst_element_set_context (element, ctxt);
9024 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
9025 * the required context type and afterwards check if a
9026 * usable context was set now as in 1). The message could
9027 * be handled by the parent bins of the element and the
9032 GST_INFO_OBJECT (element, "posting need context message");
9033 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
9034 "drm-preferred-decryption-system-id");
9035 st = (GstStructure *) gst_message_get_structure (msg);
9036 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
9037 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
9040 gst_structure_set_value (st, "stream-encryption-events", &event_list);
9041 gst_element_post_message (element, msg);
9044 g_strfreev (filtered_sys_ids);
9045 g_value_unset (&event_list);
9046 gst_query_unref (query);
9050 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
9051 QtDemuxStream * stream)
9054 const gchar *selected_system = NULL;
9056 g_return_val_if_fail (qtdemux != NULL, FALSE);
9057 g_return_val_if_fail (stream != NULL, FALSE);
9058 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
9061 if (stream->protection_scheme_type == FOURCC_aavd) {
9062 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9063 if (!gst_structure_has_name (s, "application/x-aavd")) {
9064 gst_structure_set (s,
9065 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
9067 gst_structure_set_name (s, "application/x-aavd");
9072 if (stream->protection_scheme_type != FOURCC_cenc
9073 && stream->protection_scheme_type != FOURCC_cbcs) {
9074 GST_ERROR_OBJECT (qtdemux,
9075 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
9076 GST_FOURCC_ARGS (stream->protection_scheme_type));
9080 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9081 if (!gst_structure_has_name (s, "application/x-cenc")) {
9082 gst_structure_set (s,
9083 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
9084 gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
9085 (stream->protection_scheme_type == FOURCC_cbcs) ? "cbcs" : "cenc",
9087 gst_structure_set_name (s, "application/x-cenc");
9090 if (qtdemux->protection_system_ids == NULL) {
9091 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
9092 "cenc protection system information has been found, not setting a "
9093 "protection system UUID");
9097 gst_qtdemux_request_protection_context (qtdemux, stream);
9098 if (qtdemux->preferred_protection_system_id != NULL) {
9099 const gchar *preferred_system_array[] =
9100 { qtdemux->preferred_protection_system_id, NULL };
9102 selected_system = gst_protection_select_system (preferred_system_array);
9104 if (selected_system) {
9105 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
9106 qtdemux->preferred_protection_system_id);
9108 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
9109 "because there is no available decryptor",
9110 qtdemux->preferred_protection_system_id);
9114 if (!selected_system) {
9115 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
9116 selected_system = gst_protection_select_system ((const gchar **)
9117 qtdemux->protection_system_ids->pdata);
9118 g_ptr_array_remove_index (qtdemux->protection_system_ids,
9119 qtdemux->protection_system_ids->len - 1);
9122 if (!selected_system) {
9123 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
9124 "suitable decryptor element has been found");
9128 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
9131 gst_structure_set (s,
9132 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
9139 gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
9141 /* fps is calculated base on the duration of the average framerate since
9142 * qt does not have a fixed framerate. */
9143 gboolean fps_available = TRUE;
9144 guint32 first_duration = 0;
9146 if (stream->n_samples > 0)
9147 first_duration = stream->samples[0].duration;
9149 if ((stream->n_samples == 1 && first_duration == 0)
9150 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
9152 CUR_STREAM (stream)->fps_n = 0;
9153 CUR_STREAM (stream)->fps_d = 1;
9155 if (stream->duration == 0 || stream->n_samples < 2) {
9156 CUR_STREAM (stream)->fps_n = stream->timescale;
9157 CUR_STREAM (stream)->fps_d = 1;
9158 fps_available = FALSE;
9160 GstClockTime avg_duration;
9164 /* duration and n_samples can be updated for fragmented format
9165 * so, framerate of fragmented format is calculated using data in a moof */
9166 if (qtdemux->fragmented && stream->n_samples_moof > 0
9167 && stream->duration_moof > 0) {
9168 n_samples = stream->n_samples_moof;
9169 duration = stream->duration_moof;
9171 n_samples = stream->n_samples;
9172 duration = stream->duration;
9175 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
9176 /* stream->duration is guint64, timescale, n_samples are guint32 */
9178 gst_util_uint64_scale_round (duration -
9179 first_duration, GST_SECOND,
9180 (guint64) (stream->timescale) * (n_samples - 1));
9182 GST_LOG_OBJECT (qtdemux,
9183 "Calculating avg sample duration based on stream (or moof) duration %"
9185 " minus first sample %u, leaving %d samples gives %"
9186 GST_TIME_FORMAT, duration, first_duration,
9187 n_samples - 1, GST_TIME_ARGS (avg_duration));
9189 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
9190 gst_video_guess_framerate (avg_duration,
9191 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9192 if (CUR_STREAM (stream)->fps_d == 0)
9193 fps_available = FALSE;
9196 gst_video_guess_framerate (avg_duration,
9197 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
9200 GST_DEBUG_OBJECT (qtdemux,
9201 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
9202 stream->timescale, CUR_STREAM (stream)->fps_n,
9203 CUR_STREAM (stream)->fps_d);
9207 return fps_available;
9211 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
9213 if (stream->subtype == FOURCC_vide) {
9214 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9216 if (CUR_STREAM (stream)->caps) {
9217 CUR_STREAM (stream)->caps =
9218 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9220 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
9221 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9222 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
9223 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
9225 /* set framerate if calculated framerate is reliable */
9226 if (fps_available) {
9227 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9228 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9229 CUR_STREAM (stream)->fps_d, NULL);
9232 /* calculate pixel-aspect-ratio using display width and height */
9233 GST_DEBUG_OBJECT (qtdemux,
9234 "video size %dx%d, target display size %dx%d",
9235 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
9236 stream->display_width, stream->display_height);
9237 /* qt file might have pasp atom */
9238 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9239 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
9240 CUR_STREAM (stream)->par_h);
9241 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9242 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9243 CUR_STREAM (stream)->par_h, NULL);
9244 } else if (stream->display_width > 0 && stream->display_height > 0
9245 && CUR_STREAM (stream)->width > 0
9246 && CUR_STREAM (stream)->height > 0) {
9249 /* calculate the pixel aspect ratio using the display and pixel w/h */
9250 n = stream->display_width * CUR_STREAM (stream)->height;
9251 d = stream->display_height * CUR_STREAM (stream)->width;
9254 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
9255 CUR_STREAM (stream)->par_w = n;
9256 CUR_STREAM (stream)->par_h = d;
9257 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9258 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
9259 CUR_STREAM (stream)->par_h, NULL);
9262 if (CUR_STREAM (stream)->interlace_mode > 0) {
9263 if (CUR_STREAM (stream)->interlace_mode == 1) {
9264 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9265 G_TYPE_STRING, "progressive", NULL);
9266 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
9267 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
9268 G_TYPE_STRING, "interleaved", NULL);
9269 if (CUR_STREAM (stream)->field_order == 9) {
9270 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9271 G_TYPE_STRING, "top-field-first", NULL);
9272 } else if (CUR_STREAM (stream)->field_order == 14) {
9273 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
9274 G_TYPE_STRING, "bottom-field-first", NULL);
9279 /* Create incomplete colorimetry here if needed */
9280 if (CUR_STREAM (stream)->colorimetry.range ||
9281 CUR_STREAM (stream)->colorimetry.matrix ||
9282 CUR_STREAM (stream)->colorimetry.transfer
9283 || CUR_STREAM (stream)->colorimetry.primaries) {
9284 gchar *colorimetry =
9285 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
9286 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
9287 G_TYPE_STRING, colorimetry, NULL);
9288 g_free (colorimetry);
9291 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9292 guint par_w = 1, par_h = 1;
9294 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
9295 par_w = CUR_STREAM (stream)->par_w;
9296 par_h = CUR_STREAM (stream)->par_h;
9299 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
9300 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
9302 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9305 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9306 "multiview-mode", G_TYPE_STRING,
9307 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9308 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9309 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9314 else if (stream->subtype == FOURCC_soun) {
9315 if (CUR_STREAM (stream)->caps) {
9316 CUR_STREAM (stream)->caps =
9317 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9318 if (CUR_STREAM (stream)->rate > 0)
9319 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9320 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
9321 if (CUR_STREAM (stream)->n_channels > 0)
9322 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9323 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
9324 if (CUR_STREAM (stream)->n_channels > 2) {
9325 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9326 * correctly; this is just the minimum we can do - assume
9327 * we don't actually have any channel positions. */
9328 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9329 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9334 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
9335 const GstStructure *s;
9336 QtDemuxStream *fps_stream = NULL;
9337 gboolean fps_available = FALSE;
9339 /* CEA608 closed caption tracks are a bit special in that each sample
9340 * can contain CCs for multiple frames, and CCs can be omitted and have to
9341 * be inferred from the duration of the sample then.
9343 * As such we take the framerate from the (first) video track here for
9344 * CEA608 as there must be one CC byte pair for every video frame
9345 * according to the spec.
9347 * For CEA708 all is fine and there is one sample per frame.
9350 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
9351 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9354 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
9355 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
9357 if (tmp->subtype == FOURCC_vide) {
9364 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
9365 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
9366 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
9369 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
9370 fps_stream = stream;
9373 CUR_STREAM (stream)->caps =
9374 gst_caps_make_writable (CUR_STREAM (stream)->caps);
9376 /* set framerate if calculated framerate is reliable */
9377 if (fps_available) {
9378 gst_caps_set_simple (CUR_STREAM (stream)->caps,
9379 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
9380 CUR_STREAM (stream)->fps_d, NULL);
9385 GstCaps *prev_caps = NULL;
9387 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
9388 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
9389 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
9390 gst_pad_set_active (stream->pad, TRUE);
9392 gst_pad_use_fixed_caps (stream->pad);
9394 if (stream->protected) {
9395 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
9396 GST_ERROR_OBJECT (qtdemux,
9397 "Failed to configure protected stream caps.");
9402 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9403 CUR_STREAM (stream)->caps);
9404 if (stream->new_stream) {
9406 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9409 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9412 gst_event_parse_stream_flags (event, &stream_flags);
9413 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9414 qtdemux->have_group_id = TRUE;
9416 qtdemux->have_group_id = FALSE;
9417 gst_event_unref (event);
9418 } else if (!qtdemux->have_group_id) {
9419 qtdemux->have_group_id = TRUE;
9420 qtdemux->group_id = gst_util_group_id_next ();
9423 stream->new_stream = FALSE;
9424 event = gst_event_new_stream_start (stream->stream_id);
9425 if (qtdemux->have_group_id)
9426 gst_event_set_group_id (event, qtdemux->group_id);
9427 if (stream->disabled)
9428 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9429 if (CUR_STREAM (stream)->sparse) {
9430 stream_flags |= GST_STREAM_FLAG_SPARSE;
9432 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9434 gst_event_set_stream_flags (event, stream_flags);
9435 gst_pad_push_event (stream->pad, event);
9438 prev_caps = gst_pad_get_current_caps (stream->pad);
9440 if (CUR_STREAM (stream)->caps) {
9442 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9443 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9444 CUR_STREAM (stream)->caps);
9445 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9447 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9450 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9454 gst_caps_unref (prev_caps);
9455 stream->new_caps = FALSE;
9461 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9462 QtDemuxStream * stream)
9464 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9467 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9468 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9469 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9470 stream->stsd_entries_length)) {
9471 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9472 (_("This file is invalid and cannot be played.")),
9473 ("New sample description id is out of bounds (%d >= %d)",
9474 stream->stsd_sample_description_id, stream->stsd_entries_length));
9476 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9477 stream->new_caps = TRUE;
9482 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9483 QtDemuxStream * stream, GstTagList * list)
9485 gboolean ret = TRUE;
9487 if (stream->subtype == FOURCC_vide) {
9488 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9491 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9494 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9495 gst_object_unref (stream->pad);
9501 qtdemux->n_video_streams++;
9502 } else if (stream->subtype == FOURCC_soun) {
9503 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9506 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9508 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9509 gst_object_unref (stream->pad);
9514 qtdemux->n_audio_streams++;
9515 } else if (stream->subtype == FOURCC_strm) {
9516 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9517 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9518 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9519 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
9520 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9523 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9525 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9526 gst_object_unref (stream->pad);
9531 qtdemux->n_sub_streams++;
9532 } else if (CUR_STREAM (stream)->caps) {
9533 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9536 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9538 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9539 gst_object_unref (stream->pad);
9544 qtdemux->n_video_streams++;
9546 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9553 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9554 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9555 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9556 GST_OBJECT_LOCK (qtdemux);
9557 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9558 GST_OBJECT_UNLOCK (qtdemux);
9560 if (stream->stream_tags)
9561 gst_tag_list_unref (stream->stream_tags);
9562 stream->stream_tags = list;
9564 /* global tags go on each pad anyway */
9565 stream->send_global_tags = TRUE;
9566 /* send upstream GST_EVENT_PROTECTION events that were received before
9567 this source pad was created */
9568 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9569 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9573 gst_tag_list_unref (list);
9577 /* find next atom with @fourcc starting at @offset */
9578 static GstFlowReturn
9579 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9580 guint64 * length, guint32 fourcc)
9586 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9587 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9593 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9594 if (G_UNLIKELY (ret != GST_FLOW_OK))
9596 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9599 gst_buffer_unref (buf);
9602 gst_buffer_map (buf, &map, GST_MAP_READ);
9603 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9604 gst_buffer_unmap (buf, &map);
9605 gst_buffer_unref (buf);
9607 if (G_UNLIKELY (*length == 0)) {
9608 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9609 ret = GST_FLOW_ERROR;
9613 if (lfourcc == fourcc) {
9614 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9615 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9618 GST_LOG_OBJECT (qtdemux,
9619 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9620 GST_FOURCC_ARGS (lfourcc), *offset);
9621 if (*offset == G_MAXUINT64)
9631 /* might simply have had last one */
9632 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9637 /* should only do something in pull mode */
9638 /* call with OBJECT lock */
9639 static GstFlowReturn
9640 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9642 guint64 length, offset;
9643 GstBuffer *buf = NULL;
9644 GstFlowReturn ret = GST_FLOW_OK;
9645 GstFlowReturn res = GST_FLOW_OK;
9648 offset = qtdemux->moof_offset;
9649 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9652 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9653 return GST_FLOW_EOS;
9656 /* best not do pull etc with lock held */
9657 GST_OBJECT_UNLOCK (qtdemux);
9659 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9660 if (ret != GST_FLOW_OK)
9663 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9664 if (G_UNLIKELY (ret != GST_FLOW_OK))
9666 gst_buffer_map (buf, &map, GST_MAP_READ);
9667 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9668 gst_buffer_unmap (buf, &map);
9669 gst_buffer_unref (buf);
9674 gst_buffer_unmap (buf, &map);
9675 gst_buffer_unref (buf);
9679 /* look for next moof */
9680 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9681 if (G_UNLIKELY (ret != GST_FLOW_OK))
9685 GST_OBJECT_LOCK (qtdemux);
9687 qtdemux->moof_offset = offset;
9693 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9695 res = GST_FLOW_ERROR;
9700 /* maybe upstream temporarily flushing */
9701 if (ret != GST_FLOW_FLUSHING) {
9702 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9705 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9706 /* resume at current position next time */
9714 qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
9718 gint32 stts_duration;
9719 GstByteWriter stsc, stts, stsz;
9721 /* Each sample has a different size, which we don't support for merging */
9722 if (stream->sample_size == 0) {
9723 GST_DEBUG_OBJECT (qtdemux,
9724 "Not all samples have the same size, not merging");
9728 /* The stream has a ctts table, we don't support that */
9729 if (stream->ctts_present) {
9730 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
9734 /* If there's a sync sample table also ignore this stream */
9735 if (stream->stps_present || stream->stss_present) {
9736 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
9740 /* If chunks are considered samples already ignore this stream */
9741 if (stream->chunks_are_samples) {
9742 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
9746 /* Require that all samples have the same duration */
9747 if (stream->n_sample_times > 1) {
9748 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
9752 /* Parse the stts to get the sample duration and number of samples */
9753 gst_byte_reader_skip_unchecked (&stream->stts, 4);
9754 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9756 /* Parse the number of chunks from the stco manually because the
9757 * reader is already behind that */
9758 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
9760 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
9763 /* Now parse stsc, convert chunks into single samples and generate a
9764 * new stsc, stts and stsz from this information */
9765 gst_byte_writer_init (&stsc);
9766 gst_byte_writer_init (&stts);
9767 gst_byte_writer_init (&stsz);
9769 /* Note: we skip fourccs, size, version, flags and other fields of the new
9770 * atoms as the byte readers with them are already behind that position
9771 * anyway and only update the values of those inside the stream directly.
9773 stream->n_sample_times = 0;
9774 stream->n_samples = 0;
9775 for (i = 0; i < stream->n_samples_per_chunk; i++) {
9777 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
9779 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9780 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9781 sample_description_id =
9782 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9784 if (i == stream->n_samples_per_chunk - 1) {
9785 /* +1 because first_chunk is 1-based */
9786 last_chunk = num_chunks + 1;
9788 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9791 GST_DEBUG_OBJECT (qtdemux,
9792 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
9793 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
9795 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
9796 /* One sample in this chunk */
9797 gst_byte_writer_put_uint32_be (&stsc, 1);
9798 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
9800 /* For each chunk write a stts and stsz entry now */
9801 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
9802 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
9803 for (j = first_chunk; j < last_chunk; j++) {
9804 gst_byte_writer_put_uint32_be (&stsz,
9805 stream->sample_size * samples_per_chunk);
9808 stream->n_sample_times += 1;
9809 stream->n_samples += last_chunk - first_chunk;
9812 g_assert_cmpint (stream->n_samples, ==, num_chunks);
9814 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
9815 stream->n_samples, stream->n_sample_times);
9817 /* We don't have a fixed sample size anymore */
9818 stream->sample_size = 0;
9820 /* Free old data for the atoms */
9821 g_free ((gpointer) stream->stsz.data);
9822 stream->stsz.data = NULL;
9823 g_free ((gpointer) stream->stsc.data);
9824 stream->stsc.data = NULL;
9825 g_free ((gpointer) stream->stts.data);
9826 stream->stts.data = NULL;
9828 /* Store new data and replace byte readers */
9829 stream->stsz.size = gst_byte_writer_get_size (&stsz);
9830 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
9831 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
9832 stream->stts.size = gst_byte_writer_get_size (&stts);
9833 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
9834 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
9835 stream->stsc.size = gst_byte_writer_get_size (&stsc);
9836 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
9837 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
9840 /* initialise bytereaders for stbl sub-atoms */
9842 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9844 stream->stbl_index = -1; /* no samples have yet been parsed */
9845 stream->sample_index = -1;
9847 /* time-to-sample atom */
9848 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9851 /* copy atom data into a new buffer for later use */
9852 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
9854 /* skip version + flags */
9855 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9856 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9858 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9860 /* make sure there's enough data */
9861 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9862 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9863 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9864 stream->n_sample_times);
9865 if (!stream->n_sample_times)
9869 /* sync sample atom */
9870 stream->stps_present = FALSE;
9871 if ((stream->stss_present =
9872 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9873 &stream->stss) ? TRUE : FALSE) == TRUE) {
9874 /* copy atom data into a new buffer for later use */
9875 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
9877 /* skip version + flags */
9878 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9879 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9882 if (stream->n_sample_syncs) {
9883 /* make sure there's enough data */
9884 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9888 /* partial sync sample atom */
9889 if ((stream->stps_present =
9890 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9891 &stream->stps) ? TRUE : FALSE) == TRUE) {
9892 /* copy atom data into a new buffer for later use */
9893 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
9895 /* skip version + flags */
9896 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9897 !gst_byte_reader_get_uint32_be (&stream->stps,
9898 &stream->n_sample_partial_syncs))
9901 /* if there are no entries, the stss table contains the real
9903 if (stream->n_sample_partial_syncs) {
9904 /* make sure there's enough data */
9905 if (!qt_atom_parser_has_chunks (&stream->stps,
9906 stream->n_sample_partial_syncs, 4))
9913 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9916 /* copy atom data into a new buffer for later use */
9917 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
9919 /* skip version + flags */
9920 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9921 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9924 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9927 if (!stream->n_samples)
9930 /* sample-to-chunk atom */
9931 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9934 /* copy atom data into a new buffer for later use */
9935 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
9937 /* skip version + flags */
9938 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9939 !gst_byte_reader_get_uint32_be (&stream->stsc,
9940 &stream->n_samples_per_chunk))
9943 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9944 stream->n_samples_per_chunk);
9946 /* make sure there's enough data */
9947 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9953 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9954 stream->co_size = sizeof (guint32);
9955 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9957 stream->co_size = sizeof (guint64);
9961 /* copy atom data into a new buffer for later use */
9962 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
9964 /* skip version + flags */
9965 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9968 /* chunks_are_samples == TRUE means treat chunks as samples */
9969 stream->chunks_are_samples = stream->sample_size
9970 && !CUR_STREAM (stream)->sampled;
9971 if (stream->chunks_are_samples) {
9972 /* treat chunks as samples */
9973 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9976 /* skip number of entries */
9977 if (!gst_byte_reader_skip (&stream->stco, 4))
9980 /* make sure there are enough data in the stsz atom */
9981 if (!stream->sample_size) {
9982 /* different sizes for each sample */
9983 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9988 /* composition time-to-sample */
9989 if ((stream->ctts_present =
9990 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9991 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9992 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9993 guint8 ctts_version;
9994 gboolean checked_ctts = FALSE;
9996 /* copy atom data into a new buffer for later use */
9997 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
9999 /* version 1 has signed offsets */
10000 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
10004 if (!gst_byte_reader_skip (&stream->ctts, 3)
10005 || !gst_byte_reader_get_uint32_be (&stream->ctts,
10006 &stream->n_composition_times))
10009 /* make sure there's enough data */
10010 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
10014 /* This is optional, if missing we iterate the ctts */
10015 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
10016 guint8 cslg_version;
10018 /* cslg version 1 has 64 bit fields */
10019 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
10023 if (!gst_byte_reader_skip (&cslg, 3))
10026 if (cslg_version == 0) {
10027 gint32 composition_to_dts_shift;
10029 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
10032 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10034 gint64 composition_to_dts_shift;
10036 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
10039 stream->cslg_shift = MAX (0, composition_to_dts_shift);
10042 gint32 cslg_least = 0;
10043 guint num_entries, pos;
10046 pos = gst_byte_reader_get_pos (&stream->ctts);
10047 num_entries = stream->n_composition_times;
10049 checked_ctts = TRUE;
10051 stream->cslg_shift = 0;
10053 for (i = 0; i < num_entries; i++) {
10056 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10057 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10058 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10059 * slightly inaccurate PTS could be more usable than corrupted one */
10060 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10061 && ABS (offset) / 2 > stream->duration)) {
10062 GST_WARNING_OBJECT (qtdemux,
10063 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10064 " larger than duration %" G_GUINT64_FORMAT, offset,
10067 stream->cslg_shift = 0;
10068 stream->ctts_present = FALSE;
10072 /* Don't consider "no decode samples" with offset G_MININT32
10073 * for the DTS/PTS shift */
10074 if (offset != G_MININT32 && offset < cslg_least)
10075 cslg_least = offset;
10078 if (cslg_least < 0)
10079 stream->cslg_shift = -cslg_least;
10081 stream->cslg_shift = 0;
10083 /* reset the reader so we can generate sample table */
10084 gst_byte_reader_set_pos (&stream->ctts, pos);
10087 /* Check if ctts values are looking reasonable if that didn't happen above */
10088 if (!checked_ctts) {
10089 guint num_entries, pos;
10092 pos = gst_byte_reader_get_pos (&stream->ctts);
10093 num_entries = stream->n_composition_times;
10095 for (i = 0; i < num_entries; i++) {
10098 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
10099 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10100 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
10101 * slightly inaccurate PTS could be more usable than corrupted one */
10102 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
10103 && ABS (offset) / 2 > stream->duration)) {
10104 GST_WARNING_OBJECT (qtdemux,
10105 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
10106 " larger than duration %" G_GUINT64_FORMAT, offset,
10109 stream->cslg_shift = 0;
10110 stream->ctts_present = FALSE;
10115 /* reset the reader so we can generate sample table */
10116 gst_byte_reader_set_pos (&stream->ctts, pos);
10119 /* Ensure the cslg_shift value is consistent so we can use it
10120 * unconditionally to produce TS and Segment */
10121 stream->cslg_shift = 0;
10124 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
10125 stream->cslg_shift);
10127 /* For raw audio streams especially we might want to merge the samples
10128 * to not output one audio sample per buffer. We're doing this here
10129 * before allocating the sample tables so that from this point onwards
10130 * the number of container samples are static */
10131 if (stream->min_buffer_size > 0) {
10132 qtdemux_merge_sample_table (qtdemux, stream);
10136 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
10137 stream->n_samples, (guint) sizeof (QtDemuxSample),
10138 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
10140 if (stream->n_samples >=
10141 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
10142 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
10143 "be larger than %uMB (broken file?)", stream->n_samples,
10144 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
10148 g_assert (stream->samples == NULL);
10149 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
10150 if (!stream->samples) {
10151 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
10152 stream->n_samples);
10160 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10161 (_("This file is corrupt and cannot be played.")), (NULL));
10166 gst_qtdemux_stbl_free (stream);
10167 if (!qtdemux->fragmented) {
10168 /* not quite good */
10169 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
10172 /* may pick up samples elsewhere */
10178 /* collect samples from the next sample to be parsed up to sample @n for @stream
10179 * by reading the info from @stbl
10181 * This code can be executed from both the streaming thread and the seeking
10182 * thread so it takes the object lock to protect itself
10185 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
10188 QtDemuxSample *samples, *first, *cur, *last;
10189 guint32 n_samples_per_chunk;
10192 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
10193 GST_FOURCC_FORMAT ", pad %s",
10194 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
10195 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
10197 n_samples = stream->n_samples;
10199 if (n >= n_samples)
10200 goto out_of_samples;
10202 GST_OBJECT_LOCK (qtdemux);
10203 if (n <= stream->stbl_index)
10204 goto already_parsed;
10206 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
10208 if (!stream->stsz.data) {
10209 /* so we already parsed and passed all the moov samples;
10210 * onto fragmented ones */
10211 g_assert (qtdemux->fragmented);
10215 /* pointer to the sample table */
10216 samples = stream->samples;
10218 /* starts from -1, moves to the next sample index to parse */
10219 stream->stbl_index++;
10221 /* keep track of the first and last sample to fill */
10222 first = &samples[stream->stbl_index];
10223 last = &samples[n];
10225 if (!stream->chunks_are_samples) {
10226 /* set the sample sizes */
10227 if (stream->sample_size == 0) {
10228 /* different sizes for each sample */
10229 for (cur = first; cur <= last; cur++) {
10230 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
10231 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
10232 (guint) (cur - samples), cur->size);
10235 /* samples have the same size */
10236 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
10237 for (cur = first; cur <= last; cur++)
10238 cur->size = stream->sample_size;
10242 n_samples_per_chunk = stream->n_samples_per_chunk;
10245 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
10246 guint32 last_chunk;
10248 if (stream->stsc_chunk_index >= stream->last_chunk
10249 || stream->stsc_chunk_index < stream->first_chunk) {
10250 stream->first_chunk =
10251 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10252 stream->samples_per_chunk =
10253 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10254 /* starts from 1 */
10255 stream->stsd_sample_description_id =
10256 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
10258 /* chunk numbers are counted from 1 it seems */
10259 if (G_UNLIKELY (stream->first_chunk == 0))
10262 --stream->first_chunk;
10264 /* the last chunk of each entry is calculated by taking the first chunk
10265 * of the next entry; except if there is no next, where we fake it with
10267 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
10268 stream->last_chunk = G_MAXUINT32;
10270 stream->last_chunk =
10271 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10272 if (G_UNLIKELY (stream->last_chunk == 0))
10275 --stream->last_chunk;
10278 GST_LOG_OBJECT (qtdemux,
10279 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
10280 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
10281 stream->samples_per_chunk, stream->stsd_sample_description_id);
10283 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
10286 if (stream->last_chunk != G_MAXUINT32) {
10287 if (!qt_atom_parser_peek_sub (&stream->stco,
10288 stream->first_chunk * stream->co_size,
10289 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10290 &stream->co_chunk))
10294 stream->co_chunk = stream->stco;
10295 if (!gst_byte_reader_skip (&stream->co_chunk,
10296 stream->first_chunk * stream->co_size))
10300 stream->stsc_chunk_index = stream->first_chunk;
10303 last_chunk = stream->last_chunk;
10305 if (stream->chunks_are_samples) {
10306 cur = &samples[stream->stsc_chunk_index];
10308 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10311 stream->stsc_chunk_index = j;
10316 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
10319 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10320 "%" G_GUINT64_FORMAT, j, cur->offset);
10322 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
10323 CUR_STREAM (stream)->bytes_per_frame > 0) {
10325 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
10326 CUR_STREAM (stream)->samples_per_frame *
10327 CUR_STREAM (stream)->bytes_per_frame;
10329 cur->size = stream->samples_per_chunk;
10332 GST_DEBUG_OBJECT (qtdemux,
10333 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
10334 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
10335 stream->stco_sample_index)), cur->size);
10337 cur->timestamp = stream->stco_sample_index;
10338 cur->duration = stream->samples_per_chunk;
10339 cur->keyframe = TRUE;
10342 stream->stco_sample_index += stream->samples_per_chunk;
10344 stream->stsc_chunk_index = j;
10346 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10347 guint32 samples_per_chunk;
10348 guint64 chunk_offset;
10350 if (!stream->stsc_sample_index
10351 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
10352 &stream->chunk_offset))
10355 samples_per_chunk = stream->samples_per_chunk;
10356 chunk_offset = stream->chunk_offset;
10358 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10359 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10360 G_GUINT64_FORMAT " and size %d",
10361 (guint) (cur - samples), chunk_offset, cur->size);
10363 cur->offset = chunk_offset;
10364 chunk_offset += cur->size;
10367 if (G_UNLIKELY (cur > last)) {
10369 stream->stsc_sample_index = k + 1;
10370 stream->chunk_offset = chunk_offset;
10371 stream->stsc_chunk_index = j;
10375 stream->stsc_sample_index = 0;
10377 stream->stsc_chunk_index = j;
10379 stream->stsc_index++;
10382 if (stream->chunks_are_samples)
10386 guint32 n_sample_times;
10388 n_sample_times = stream->n_sample_times;
10391 for (i = stream->stts_index; i < n_sample_times; i++) {
10392 guint32 stts_samples;
10393 gint32 stts_duration;
10396 if (stream->stts_sample_index >= stream->stts_samples
10397 || !stream->stts_sample_index) {
10399 stream->stts_samples =
10400 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10401 stream->stts_duration =
10402 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10404 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10405 i, stream->stts_samples, stream->stts_duration);
10407 stream->stts_sample_index = 0;
10410 stts_samples = stream->stts_samples;
10411 stts_duration = stream->stts_duration;
10412 stts_time = stream->stts_time;
10414 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10415 GST_DEBUG_OBJECT (qtdemux,
10416 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10417 (guint) (cur - samples), j,
10418 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10420 cur->timestamp = stts_time;
10421 cur->duration = stts_duration;
10423 /* avoid 32-bit wrap-around,
10424 * but still mind possible 'negative' duration */
10425 stts_time += (gint64) stts_duration;
10428 if (G_UNLIKELY (cur > last)) {
10430 stream->stts_time = stts_time;
10431 stream->stts_sample_index = j + 1;
10432 if (stream->stts_sample_index >= stream->stts_samples)
10433 stream->stts_index++;
10437 stream->stts_sample_index = 0;
10438 stream->stts_time = stts_time;
10439 stream->stts_index++;
10441 /* fill up empty timestamps with the last timestamp, this can happen when
10442 * the last samples do not decode and so we don't have timestamps for them.
10443 * We however look at the last timestamp to estimate the track length so we
10444 * need something in here. */
10445 for (; cur < last; cur++) {
10446 GST_DEBUG_OBJECT (qtdemux,
10447 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10448 (guint) (cur - samples),
10449 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10450 cur->timestamp = stream->stts_time;
10451 cur->duration = -1;
10456 /* sample sync, can be NULL */
10457 if (stream->stss_present == TRUE) {
10458 guint32 n_sample_syncs;
10460 n_sample_syncs = stream->n_sample_syncs;
10462 if (!n_sample_syncs) {
10463 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10464 stream->all_keyframe = TRUE;
10466 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10467 /* note that the first sample is index 1, not 0 */
10470 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10472 if (G_LIKELY (index > 0 && index <= n_samples)) {
10474 samples[index].keyframe = TRUE;
10475 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10476 /* and exit if we have enough samples */
10477 if (G_UNLIKELY (index >= n)) {
10484 stream->stss_index = i;
10487 /* stps marks partial sync frames like open GOP I-Frames */
10488 if (stream->stps_present == TRUE) {
10489 guint32 n_sample_partial_syncs;
10491 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10493 /* if there are no entries, the stss table contains the real
10495 if (n_sample_partial_syncs) {
10496 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10497 /* note that the first sample is index 1, not 0 */
10500 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10502 if (G_LIKELY (index > 0 && index <= n_samples)) {
10504 samples[index].keyframe = TRUE;
10505 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10506 /* and exit if we have enough samples */
10507 if (G_UNLIKELY (index >= n)) {
10514 stream->stps_index = i;
10518 /* no stss, all samples are keyframes */
10519 stream->all_keyframe = TRUE;
10520 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10525 /* composition time to sample */
10526 if (stream->ctts_present == TRUE) {
10527 guint32 n_composition_times;
10528 guint32 ctts_count;
10529 gint32 ctts_soffset;
10531 /* Fill in the pts_offsets */
10533 n_composition_times = stream->n_composition_times;
10535 for (i = stream->ctts_index; i < n_composition_times; i++) {
10536 if (stream->ctts_sample_index >= stream->ctts_count
10537 || !stream->ctts_sample_index) {
10538 stream->ctts_count =
10539 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10540 stream->ctts_soffset =
10541 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10542 stream->ctts_sample_index = 0;
10545 ctts_count = stream->ctts_count;
10546 ctts_soffset = stream->ctts_soffset;
10548 /* FIXME: Set offset to 0 for "no decode samples". This needs
10549 * to be handled in a codec specific manner ideally. */
10550 if (ctts_soffset == G_MININT32)
10553 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
10554 cur->pts_offset = ctts_soffset;
10557 if (G_UNLIKELY (cur > last)) {
10559 stream->ctts_sample_index = j + 1;
10563 stream->ctts_sample_index = 0;
10564 stream->ctts_index++;
10568 stream->stbl_index = n;
10569 /* if index has been completely parsed, free data that is no-longer needed */
10570 if (n + 1 == stream->n_samples) {
10571 gst_qtdemux_stbl_free (stream);
10572 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10573 if (qtdemux->pullbased) {
10574 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10575 while (n + 1 == stream->n_samples)
10576 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10580 GST_OBJECT_UNLOCK (qtdemux);
10587 GST_LOG_OBJECT (qtdemux,
10588 "Tried to parse up to sample %u but this sample has already been parsed",
10590 /* if fragmented, there may be more */
10591 if (qtdemux->fragmented && n == stream->stbl_index)
10593 GST_OBJECT_UNLOCK (qtdemux);
10599 GST_LOG_OBJECT (qtdemux,
10600 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10601 stream->n_samples);
10602 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10603 (_("This file is corrupt and cannot be played.")), (NULL));
10608 GST_OBJECT_UNLOCK (qtdemux);
10609 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10610 (_("This file is corrupt and cannot be played.")), (NULL));
10615 /* collect all segment info for @stream.
10618 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10622 /* accept edts if they contain gaps at start and there is only
10623 * one media segment */
10624 gboolean allow_pushbased_edts = TRUE;
10625 gint media_segments_count = 0;
10627 /* parse and prepare segment info from the edit list */
10628 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10629 stream->n_segments = 0;
10630 stream->segments = NULL;
10631 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10634 gint segment_number, entry_size;
10636 GstClockTime stime;
10637 const guint8 *buffer;
10641 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10642 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10645 buffer = elst->data;
10647 size = QT_UINT32 (buffer);
10648 /* version, flags, n_segments */
10650 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10653 version = QT_UINT8 (buffer + 8);
10654 entry_size = (version == 1) ? 20 : 12;
10656 n_segments = QT_UINT32 (buffer + 12);
10658 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10659 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10663 /* we might allocate a bit too much, at least allocate 1 segment */
10664 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10666 /* segments always start from 0 */
10670 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10672 guint64 media_time;
10673 gboolean empty_edit = FALSE;
10674 QtDemuxSegment *segment;
10676 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10678 if (version == 1) {
10679 media_time = QT_UINT64 (buffer + 8);
10680 duration = QT_UINT64 (buffer);
10681 if (media_time == G_MAXUINT64)
10684 media_time = QT_UINT32 (buffer + 4);
10685 duration = QT_UINT32 (buffer);
10686 if (media_time == G_MAXUINT32)
10691 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
10693 segment = &stream->segments[segment_number];
10695 /* time and duration expressed in global timescale */
10696 segment->time = stime;
10697 if (duration != 0 || empty_edit) {
10698 /* edge case: empty edits with duration=zero are treated here.
10699 * (files should not have these anyway). */
10701 /* add non scaled values so we don't cause roundoff errors */
10703 stime = QTTIME_TO_GSTTIME (qtdemux, time);
10704 segment->duration = stime - segment->time;
10706 /* zero duration does not imply media_start == media_stop
10707 * but, only specify media_start. The edit ends with the track. */
10708 stime = segment->duration = GST_CLOCK_TIME_NONE;
10709 /* Don't allow more edits after this one. */
10710 n_segments = segment_number + 1;
10712 segment->stop_time = stime;
10714 segment->trak_media_start = media_time;
10715 /* media_time expressed in stream timescale */
10717 segment->media_start = media_start;
10718 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10719 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10720 media_segments_count++;
10722 segment->media_start = GST_CLOCK_TIME_NONE;
10723 segment->media_stop = GST_CLOCK_TIME_NONE;
10725 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
10727 if (rate_int <= 1) {
10728 /* 0 is not allowed, some programs write 1 instead of the floating point
10730 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10734 segment->rate = rate_int / 65536.0;
10737 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10738 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10739 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10740 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10741 segment_number, GST_TIME_ARGS (segment->time),
10742 GST_TIME_ARGS (segment->duration),
10743 GST_TIME_ARGS (segment->media_start), media_time,
10744 GST_TIME_ARGS (segment->media_stop),
10745 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10746 stream->timescale);
10747 if (segment->stop_time > qtdemux->segment.stop &&
10748 !qtdemux->upstream_format_is_time) {
10749 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10750 " extends to %" GST_TIME_FORMAT
10751 " past the end of the declared movie duration %" GST_TIME_FORMAT
10752 " movie segment will be extended", segment_number,
10753 GST_TIME_ARGS (segment->stop_time),
10754 GST_TIME_ARGS (qtdemux->segment.stop));
10755 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10758 buffer += entry_size;
10760 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10761 stream->n_segments = n_segments;
10762 if (media_segments_count != 1)
10763 allow_pushbased_edts = FALSE;
10767 /* push based does not handle segments, so act accordingly here,
10768 * and warn if applicable */
10769 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10770 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10771 /* remove and use default one below, we stream like it anyway */
10772 g_free (stream->segments);
10773 stream->segments = NULL;
10774 stream->n_segments = 0;
10777 /* no segments, create one to play the complete trak */
10778 if (stream->n_segments == 0) {
10779 GstClockTime stream_duration =
10780 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
10782 if (stream->segments == NULL)
10783 stream->segments = g_new (QtDemuxSegment, 1);
10785 /* represent unknown our way */
10786 if (stream_duration == 0)
10787 stream_duration = GST_CLOCK_TIME_NONE;
10789 stream->segments[0].time = 0;
10790 stream->segments[0].stop_time = stream_duration;
10791 stream->segments[0].duration = stream_duration;
10792 stream->segments[0].media_start = 0;
10793 stream->segments[0].media_stop = stream_duration;
10794 stream->segments[0].rate = 1.0;
10795 stream->segments[0].trak_media_start = 0;
10797 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10798 GST_TIME_ARGS (stream_duration));
10799 stream->n_segments = 1;
10800 stream->dummy_segment = TRUE;
10802 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10808 * Parses the stsd atom of a svq3 trak looking for
10809 * the SMI and gama atoms.
10812 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
10813 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10815 const guint8 *_gamma = NULL;
10816 GstBuffer *_seqh = NULL;
10817 const guint8 *stsd_data = stsd_entry_data;
10818 guint32 length = QT_UINT32 (stsd_data);
10822 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10828 version = QT_UINT16 (stsd_data);
10829 if (version == 3) {
10830 if (length >= 70) {
10833 while (length > 8) {
10834 guint32 fourcc, size;
10835 const guint8 *data;
10836 size = QT_UINT32 (stsd_data);
10837 fourcc = QT_FOURCC (stsd_data + 4);
10838 data = stsd_data + 8;
10841 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10842 "svq3 atom parsing");
10851 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10852 " for gama atom, expected 12", size);
10857 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
10859 if (_seqh != NULL) {
10860 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10861 " found, ignoring");
10863 seqh_size = QT_UINT32 (data + 4);
10864 if (seqh_size > 0) {
10865 _seqh = gst_buffer_new_and_alloc (seqh_size);
10866 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10873 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10874 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10878 if (size <= length) {
10884 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10887 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10888 G_GUINT16_FORMAT, version);
10898 } else if (_seqh) {
10899 gst_buffer_unref (_seqh);
10904 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10907 GstByteReader dref;
10911 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10912 * atom that might contain a 'data' atom with the rtsp uri.
10913 * This case was reported in bug #597497, some info about
10914 * the hndl atom can be found in TN1195
10916 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10917 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10920 guint32 dref_num_entries = 0;
10921 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10922 gst_byte_reader_skip (&dref, 4) &&
10923 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10926 /* search dref entries for hndl atom */
10927 for (i = 0; i < dref_num_entries; i++) {
10928 guint32 size = 0, type;
10929 guint8 string_len = 0;
10930 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10931 qt_atom_parser_get_fourcc (&dref, &type)) {
10932 if (type == FOURCC_hndl) {
10933 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10935 /* skip data reference handle bytes and the
10936 * following pascal string and some extra 4
10937 * bytes I have no idea what are */
10938 if (!gst_byte_reader_skip (&dref, 4) ||
10939 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10940 !gst_byte_reader_skip (&dref, string_len + 4)) {
10941 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10945 /* iterate over the atoms to find the data atom */
10946 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10950 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10951 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10952 if (atom_type == FOURCC_data) {
10953 const guint8 *uri_aux = NULL;
10955 /* found the data atom that might contain the rtsp uri */
10956 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10957 "hndl atom, interpreting it as an URI");
10958 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10960 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10961 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10963 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10964 "didn't contain a rtsp address");
10966 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10971 /* skipping to the next entry */
10972 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10975 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10982 /* skip to the next entry */
10983 if (!gst_byte_reader_skip (&dref, size - 8))
10986 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10989 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10995 #define AMR_NB_ALL_MODES 0x81ff
10996 #define AMR_WB_ALL_MODES 0x83ff
10998 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
11000 /* The 'damr' atom is of the form:
11002 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
11003 * 32 b 8 b 16 b 8 b 8 b
11005 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
11006 * represents the highest mode used in the stream (and thus the maximum
11007 * bitrate), with a couple of special cases as seen below.
11010 /* Map of frame type ID -> bitrate */
11011 static const guint nb_bitrates[] = {
11012 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
11014 static const guint wb_bitrates[] = {
11015 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
11021 gst_buffer_map (buf, &map, GST_MAP_READ);
11023 if (map.size != 0x11) {
11024 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
11028 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
11029 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
11030 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
11034 mode_set = QT_UINT16 (map.data + 13);
11036 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
11037 max_mode = 7 + (wb ? 1 : 0);
11039 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
11040 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
11042 if (max_mode == -1) {
11043 GST_DEBUG ("No mode indication was found (mode set) = %x",
11048 gst_buffer_unmap (buf, &map);
11049 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
11052 gst_buffer_unmap (buf, &map);
11057 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
11058 GstByteReader * reader, guint32 * matrix, const gchar * atom)
11061 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
11067 if (gst_byte_reader_get_remaining (reader) < 36)
11070 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
11071 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
11072 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
11073 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
11074 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
11075 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
11076 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
11077 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
11078 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
11080 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
11081 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
11082 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
11084 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
11085 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
11087 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
11088 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
11095 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
11096 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
11103 * This macro will only compare value abdegh, it expects cfi to have already
11106 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
11107 (m)[3] == (d << 16) && (m)[4] == (e << 16))
11109 /* only handle the cases where the last column has standard values */
11110 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
11111 const gchar *rotation_tag = NULL;
11113 /* no rotation needed */
11114 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
11116 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
11117 rotation_tag = "rotate-90";
11118 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
11119 rotation_tag = "rotate-180";
11120 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
11121 rotation_tag = "rotate-270";
11123 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11126 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
11127 GST_STR_NULL (rotation_tag));
11128 if (rotation_tag != NULL) {
11129 if (*taglist == NULL)
11130 *taglist = gst_tag_list_new_empty ();
11131 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
11132 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
11135 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
11140 qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
11141 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11145 GstBuffer *adrm_buf = NULL;
11146 QtDemuxAavdEncryptionInfo *info;
11148 adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
11149 if (G_UNLIKELY (!adrm)) {
11150 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
11153 adrm_size = QT_UINT32 (adrm->data);
11154 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
11156 stream->protection_scheme_type = FOURCC_aavd;
11158 if (!stream->protection_scheme_info)
11159 stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
11161 info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
11163 if (info->default_properties)
11164 gst_structure_free (info->default_properties);
11165 info->default_properties = gst_structure_new ("application/x-aavd",
11166 "encrypted", G_TYPE_BOOLEAN, TRUE,
11167 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
11168 gst_buffer_unref (adrm_buf);
11170 *original_fmt = FOURCC_mp4a;
11174 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
11175 * protected streams (sinf, frma, schm and schi); if the protection scheme is
11176 * Common Encryption (cenc), the function will also parse the tenc box (defined
11177 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
11178 * (typically an enc[v|a|t|s] sample entry); the function will set
11179 * @original_fmt to the fourcc of the original unencrypted stream format.
11180 * Returns TRUE if successful; FALSE otherwise. */
11182 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
11183 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
11189 QtDemuxCencSampleSetInfo *info;
11191 const guint8 *tenc_data;
11193 g_return_val_if_fail (qtdemux != NULL, FALSE);
11194 g_return_val_if_fail (stream != NULL, FALSE);
11195 g_return_val_if_fail (container != NULL, FALSE);
11196 g_return_val_if_fail (original_fmt != NULL, FALSE);
11198 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
11199 if (G_UNLIKELY (!sinf)) {
11200 if (stream->protection_scheme_type == FOURCC_cenc
11201 || stream->protection_scheme_type == FOURCC_cbcs) {
11202 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
11203 "mandatory for Common Encryption");
11209 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
11210 if (G_UNLIKELY (!frma)) {
11211 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
11215 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
11216 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
11217 GST_FOURCC_ARGS (*original_fmt));
11219 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
11221 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
11224 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
11225 stream->protection_scheme_version =
11226 QT_UINT32 ((const guint8 *) schm->data + 16);
11228 GST_DEBUG_OBJECT (qtdemux,
11229 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
11230 "protection_scheme_version: %#010x",
11231 GST_FOURCC_ARGS (stream->protection_scheme_type),
11232 stream->protection_scheme_version);
11234 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
11236 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
11239 if (stream->protection_scheme_type != FOURCC_cenc &&
11240 stream->protection_scheme_type != FOURCC_piff &&
11241 stream->protection_scheme_type != FOURCC_cbcs) {
11242 GST_ERROR_OBJECT (qtdemux,
11243 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
11244 GST_FOURCC_ARGS (stream->protection_scheme_type));
11248 if (G_UNLIKELY (!stream->protection_scheme_info))
11249 stream->protection_scheme_info =
11250 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
11252 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
11254 if (stream->protection_scheme_type == FOURCC_cenc
11255 || stream->protection_scheme_type == FOURCC_cbcs) {
11256 guint8 is_encrypted;
11258 guint8 constant_iv_size = 0;
11259 const guint8 *default_kid;
11260 guint8 crypt_byte_block = 0;
11261 guint8 skip_byte_block = 0;
11262 const guint8 *constant_iv = NULL;
11264 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
11266 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11267 "which is mandatory for Common Encryption");
11270 tenc_data = (const guint8 *) tenc->data + 12;
11271 is_encrypted = QT_UINT8 (tenc_data + 2);
11272 iv_size = QT_UINT8 (tenc_data + 3);
11273 default_kid = (tenc_data + 4);
11274 if (stream->protection_scheme_type == FOURCC_cbcs) {
11275 guint8 possible_pattern_info;
11276 if (iv_size == 0) {
11277 constant_iv_size = QT_UINT8 (tenc_data + 20);
11278 if (constant_iv_size != 8 && constant_iv_size != 16) {
11279 GST_ERROR_OBJECT (qtdemux,
11280 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
11283 constant_iv = (tenc_data + 21);
11285 possible_pattern_info = QT_UINT8 (tenc_data + 1);
11286 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
11287 skip_byte_block = possible_pattern_info & 0x0f;
11289 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
11290 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
11291 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
11292 } else if (stream->protection_scheme_type == FOURCC_piff) {
11294 static const guint8 piff_track_encryption_uuid[] = {
11295 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
11296 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
11299 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
11301 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11302 "which is mandatory for Common Encryption");
11306 tenc_data = (const guint8 *) tenc->data + 8;
11307 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
11308 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
11309 GST_ERROR_OBJECT (qtdemux,
11310 "Unsupported track encryption box with uuid: %s", box_uuid);
11314 tenc_data = (const guint8 *) tenc->data + 16 + 12;
11315 gst_byte_reader_init (&br, tenc_data, 20);
11316 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
11317 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
11320 stream->protection_scheme_type = FOURCC_cenc;
11327 qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
11328 QtDemuxStream ** stream2)
11330 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
11334 qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
11339 /*parse svmi header if existing */
11340 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11342 guint len = QT_UINT32 ((guint8 *) svmi->data);
11343 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11345 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11346 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11347 guint8 frame_type, frame_layout;
11348 guint32 stereo_mono_change_count;
11353 /* MPEG-A stereo video */
11354 if (qtdemux->major_brand == FOURCC_ss02)
11355 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11357 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11358 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11359 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
11361 switch (frame_type) {
11363 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11366 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11369 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11372 /* mode 3 is primary/secondary view sequence, ie
11373 * left/right views in separate tracks. See section 7.2
11374 * of ISO/IEC 23000-11:2009 */
11375 /* In the future this might be supported using related
11376 * streams, like an enhancement track - if files like this
11378 GST_FIXME_OBJECT (qtdemux,
11379 "Implement stereo video in separate streams");
11382 if ((frame_layout & 0x1) == 0)
11383 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11385 GST_LOG_OBJECT (qtdemux,
11386 "StereoVideo: composition type: %u, is_left_first: %u",
11387 frame_type, frame_layout);
11389 if (stereo_mono_change_count > 1) {
11390 GST_FIXME_OBJECT (qtdemux,
11391 "Mixed-mono flags are not yet supported in qtdemux.");
11394 stream->multiview_mode = mode;
11395 stream->multiview_flags = flags;
11402 /* parse the traks.
11403 * With each track we associate a new QtDemuxStream that contains all the info
11405 * traks that do not decode to something (like strm traks) will not have a pad.
11408 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
11410 GstByteReader tkhd;
11424 QtDemuxStream *stream = NULL;
11425 const guint8 *stsd_data;
11426 const guint8 *stsd_entry_data;
11427 guint remaining_stsd_len;
11428 guint stsd_entry_count;
11430 guint16 lang_code; /* quicktime lang code or packed iso code */
11432 guint32 tkhd_flags = 0;
11433 guint8 tkhd_version = 0;
11434 guint32 w = 0, h = 0;
11435 guint value_size, stsd_len, len;
11439 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11441 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11442 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11443 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11446 /* pick between 64 or 32 bits */
11447 value_size = tkhd_version == 1 ? 8 : 4;
11448 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11449 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11452 /* Check if current moov has duplicated track_id */
11453 if (qtdemux_find_stream (qtdemux, track_id))
11454 goto existing_stream;
11456 stream = _create_stream (qtdemux, track_id);
11457 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11459 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11460 if (!gst_byte_reader_skip (&tkhd, 4))
11463 if (tkhd_version == 1) {
11464 if (!gst_byte_reader_get_uint64_be (&tkhd, &stream->tkhd_duration))
11468 if (!gst_byte_reader_get_uint32_be (&tkhd, &dur))
11470 stream->tkhd_duration = dur;
11472 GST_INFO_OBJECT (qtdemux, "tkhd duration: %" G_GUINT64_FORMAT,
11473 stream->tkhd_duration);
11475 /* need defaults for fragments */
11476 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11478 if ((tkhd_flags & 1) == 0)
11479 stream->disabled = TRUE;
11481 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11482 tkhd_version, tkhd_flags, stream->track_id);
11484 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11487 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11488 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11489 if (qtdemux->major_brand != FOURCC_mjp2 ||
11490 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11494 len = QT_UINT32 ((guint8 *) mdhd->data);
11495 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11496 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11497 if (version == 0x01000000) {
11500 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11501 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11502 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11506 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11507 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11508 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11511 if (lang_code < 0x400) {
11512 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11513 } else if (lang_code == 0x7fff) {
11514 stream->lang_id[0] = 0; /* unspecified */
11516 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11517 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11518 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11519 stream->lang_id[3] = 0;
11522 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11523 stream->timescale);
11524 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11526 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11527 lang_code, stream->lang_id);
11529 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11532 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11533 /* chapters track reference */
11534 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11536 gsize length = GST_READ_UINT32_BE (chap->data);
11537 if (qtdemux->chapters_track_id)
11538 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11540 if (length >= 12) {
11541 qtdemux->chapters_track_id =
11542 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11547 /* fragmented files may have bogus duration in moov */
11548 if (!qtdemux->fragmented &&
11549 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11550 guint64 tdur1, tdur2;
11552 /* don't overflow */
11553 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11554 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11557 * some of those trailers, nowadays, have prologue images that are
11558 * themselves video tracks as well. I haven't really found a way to
11559 * identify those yet, except for just looking at their duration. */
11560 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11561 GST_WARNING_OBJECT (qtdemux,
11562 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11563 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11564 "found, assuming preview image or something; skipping track",
11565 stream->duration, stream->timescale, qtdemux->duration,
11566 qtdemux->timescale);
11567 gst_qtdemux_stream_unref (stream);
11572 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11575 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11576 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11578 len = QT_UINT32 ((guint8 *) hdlr->data);
11580 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11581 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11582 GST_FOURCC_ARGS (stream->subtype));
11584 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11587 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11590 /* Parse out svmi (and later st3d/sv3d) atoms */
11591 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
11594 /* parse rest of tkhd */
11595 if (stream->subtype == FOURCC_vide) {
11598 /* version 1 uses some 64-bit ints */
11599 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
11600 if (!gst_byte_reader_skip (&tkhd, 16))
11602 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11606 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11609 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11610 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11613 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11614 &stream->stream_tags);
11618 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11620 stsd_data = (const guint8 *) stsd->data;
11622 /* stsd should at least have one entry */
11623 stsd_len = QT_UINT32 (stsd_data);
11624 if (stsd_len < 24) {
11625 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11626 if (stream->subtype == FOURCC_vivo) {
11627 gst_qtdemux_stream_unref (stream);
11634 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11635 /* each stsd entry must contain at least 8 bytes */
11636 if (stream->stsd_entries_length == 0
11637 || stream->stsd_entries_length > stsd_len / 8) {
11638 stream->stsd_entries_length = 0;
11641 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11642 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11643 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11645 stsd_entry_data = stsd_data + 16;
11646 remaining_stsd_len = stsd_len - 16;
11647 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11649 gchar *codec = NULL;
11650 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11652 /* and that entry should fit within stsd */
11653 len = QT_UINT32 (stsd_entry_data);
11654 if (len > remaining_stsd_len)
11657 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11658 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11659 GST_FOURCC_ARGS (entry->fourcc));
11660 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11662 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11663 goto error_encrypted;
11665 if (fourcc == FOURCC_aavd) {
11666 if (stream->subtype != FOURCC_soun) {
11667 GST_ERROR_OBJECT (qtdemux,
11668 "Unexpeced stsd type 'aavd' outside 'soun' track");
11670 /* encrypted audio with sound sample description v0 */
11671 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11672 stream->protected = TRUE;
11673 if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
11674 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11678 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11679 /* FIXME this looks wrong, there might be multiple children
11680 * with the same type */
11681 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11682 stream->protected = TRUE;
11683 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11684 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11687 if (stream->subtype == FOURCC_vide) {
11692 gint depth, palette_size, palette_count;
11693 guint32 *palette_data = NULL;
11695 entry->sampled = TRUE;
11697 stream->display_width = w >> 16;
11698 stream->display_height = h >> 16;
11701 if (len < 86) /* TODO verify */
11704 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
11705 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
11706 entry->fps_n = 0; /* this is filled in later */
11707 entry->fps_d = 0; /* this is filled in later */
11708 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
11709 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
11711 /* if color_table_id is 0, ctab atom must follow; however some files
11712 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11713 * if color table is not present we'll correct the value */
11714 if (entry->color_table_id == 0 &&
11716 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
11717 entry->color_table_id = -1;
11720 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11721 entry->width, entry->height, entry->bits_per_sample,
11722 entry->color_table_id);
11724 depth = entry->bits_per_sample;
11726 /* more than 32 bits means grayscale */
11727 gray = (depth > 32);
11728 /* low 32 bits specify the depth */
11731 /* different number of palette entries is determined by depth. */
11733 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11734 palette_count = (1 << depth);
11735 palette_size = palette_count * 4;
11737 if (entry->color_table_id) {
11738 switch (palette_count) {
11742 palette_data = g_memdup2 (ff_qt_default_palette_2, palette_size);
11745 palette_data = g_memdup2 (ff_qt_default_palette_4, palette_size);
11750 g_memdup2 (ff_qt_grayscale_palette_16, palette_size);
11752 palette_data = g_memdup2 (ff_qt_default_palette_16, palette_size);
11757 g_memdup2 (ff_qt_grayscale_palette_256, palette_size);
11760 g_memdup2 (ff_qt_default_palette_256, palette_size);
11763 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11764 (_("The video in this file might not play correctly.")),
11765 ("unsupported palette depth %d", depth));
11769 gint i, j, start, end;
11775 start = QT_UINT32 (stsd_entry_data + offset + 70);
11776 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
11777 end = QT_UINT16 (stsd_entry_data + offset + 76);
11779 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11780 start, end, palette_count);
11787 if (len < 94 + (end - start) * 8)
11790 /* palette is always the same size */
11791 palette_data = g_malloc0 (256 * 4);
11792 palette_size = 256 * 4;
11794 for (j = 0, i = start; i <= end; j++, i++) {
11795 guint32 a, r, g, b;
11797 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11798 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11799 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11800 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
11802 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11803 (g & 0xff00) | (b >> 8);
11808 gst_caps_unref (entry->caps);
11811 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
11813 if (G_UNLIKELY (!entry->caps)) {
11814 g_free (palette_data);
11815 goto unknown_stream;
11819 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11820 GST_TAG_VIDEO_CODEC, codec, NULL);
11825 if (palette_data) {
11828 if (entry->rgb8_palette)
11829 gst_memory_unref (entry->rgb8_palette);
11830 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
11831 palette_data, palette_size, 0, palette_size, palette_data, g_free);
11833 s = gst_caps_get_structure (entry->caps, 0);
11835 /* non-raw video has a palette_data property. raw video has the palette as
11836 * an extra plane that we append to the output buffers before we push
11838 if (!gst_structure_has_name (s, "video/x-raw")) {
11839 GstBuffer *palette;
11841 palette = gst_buffer_new ();
11842 gst_buffer_append_memory (palette, entry->rgb8_palette);
11843 entry->rgb8_palette = NULL;
11845 gst_caps_set_simple (entry->caps, "palette_data",
11846 GST_TYPE_BUFFER, palette, NULL);
11847 gst_buffer_unref (palette);
11849 } else if (palette_count != 0) {
11850 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
11851 (NULL), ("Unsupported palette depth %d", depth));
11854 GST_LOG_OBJECT (qtdemux, "frame count: %u",
11855 QT_UINT16 (stsd_entry_data + offset + 32));
11861 /* pick 'the' stsd child */
11862 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11863 // We should skip parsing the stsd for non-protected streams if
11864 // the entry doesn't match the fourcc, since they don't change
11865 // format. However, for protected streams we can have partial
11866 // encryption, where parts of the stream are encrypted and parts
11867 // not. For both parts of such streams, we should ensure the
11868 // esds overrides are parsed for both from the stsd.
11869 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
11870 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
11872 else if (!stream->protected)
11877 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
11878 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
11879 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
11880 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
11884 const guint8 *pasp_data = (const guint8 *) pasp->data;
11885 gint len = QT_UINT32 (pasp_data);
11888 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
11889 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
11891 CUR_STREAM (stream)->par_w = 0;
11892 CUR_STREAM (stream)->par_h = 0;
11895 CUR_STREAM (stream)->par_w = 0;
11896 CUR_STREAM (stream)->par_h = 0;
11900 const guint8 *fiel_data = (const guint8 *) fiel->data;
11901 gint len = QT_UINT32 (fiel_data);
11904 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
11905 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
11910 const guint8 *colr_data = (const guint8 *) colr->data;
11911 gint len = QT_UINT32 (colr_data);
11913 if (len == 19 || len == 18) {
11914 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
11916 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
11917 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
11918 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
11919 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
11920 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
11922 CUR_STREAM (stream)->colorimetry.primaries =
11923 gst_video_color_primaries_from_iso (primaries);
11924 CUR_STREAM (stream)->colorimetry.transfer =
11925 gst_video_transfer_function_from_iso (transfer_function);
11926 CUR_STREAM (stream)->colorimetry.matrix =
11927 gst_video_color_matrix_from_iso (matrix);
11928 CUR_STREAM (stream)->colorimetry.range =
11929 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
11930 GST_VIDEO_COLOR_RANGE_16_235;
11932 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
11935 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
11940 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11941 stream->stream_tags);
11948 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11949 const guint8 *avc_data = stsd_entry_data + 0x56;
11952 while (len >= 0x8) {
11955 if (QT_UINT32 (avc_data) <= len)
11956 size = QT_UINT32 (avc_data) - 0x8;
11961 /* No real data, so break out */
11964 switch (QT_FOURCC (avc_data + 0x4)) {
11967 /* parse, if found */
11970 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11972 /* First 4 bytes are the length of the atom, the next 4 bytes
11973 * are the fourcc, the next 1 byte is the version, and the
11974 * subsequent bytes are profile_tier_level structure like data. */
11975 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11976 avc_data + 8 + 1, size - 1);
11977 buf = gst_buffer_new_and_alloc (size);
11978 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11979 gst_caps_set_simple (entry->caps,
11980 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11981 gst_buffer_unref (buf);
11989 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11991 /* First 4 bytes are the length of the atom, the next 4 bytes
11992 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11993 * next 1 byte is the version, and the
11994 * subsequent bytes are sequence parameter set like data. */
11996 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11998 gst_codec_utils_h264_caps_set_level_and_profile
11999 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
12001 buf = gst_buffer_new_and_alloc (size);
12002 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
12003 gst_caps_set_simple (entry->caps,
12004 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12005 gst_buffer_unref (buf);
12011 guint avg_bitrate, max_bitrate;
12013 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
12017 max_bitrate = QT_UINT32 (avc_data + 0xc);
12018 avg_bitrate = QT_UINT32 (avc_data + 0x10);
12020 if (!max_bitrate && !avg_bitrate)
12023 /* Some muxers seem to swap the average and maximum bitrates
12024 * (I'm looking at you, YouTube), so we swap for sanity. */
12025 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
12026 guint temp = avg_bitrate;
12028 avg_bitrate = max_bitrate;
12029 max_bitrate = temp;
12032 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12033 gst_tag_list_add (stream->stream_tags,
12034 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
12035 max_bitrate, NULL);
12037 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12038 gst_tag_list_add (stream->stream_tags,
12039 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
12051 avc_data += size + 8;
12062 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12063 const guint8 *hevc_data = stsd_entry_data + 0x56;
12066 while (len >= 0x8) {
12069 if (QT_UINT32 (hevc_data) <= len)
12070 size = QT_UINT32 (hevc_data) - 0x8;
12075 /* No real data, so break out */
12078 switch (QT_FOURCC (hevc_data + 0x4)) {
12081 /* parse, if found */
12084 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
12086 /* First 4 bytes are the length of the atom, the next 4 bytes
12087 * are the fourcc, the next 1 byte is the version, and the
12088 * subsequent bytes are sequence parameter set like data. */
12089 gst_codec_utils_h265_caps_set_level_tier_and_profile
12090 (entry->caps, hevc_data + 8 + 1, size - 1);
12092 buf = gst_buffer_new_and_alloc (size);
12093 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
12094 gst_caps_set_simple (entry->caps,
12095 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12096 gst_buffer_unref (buf);
12103 hevc_data += size + 8;
12116 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
12117 GST_FOURCC_ARGS (fourcc));
12119 /* codec data might be in glbl extension atom */
12121 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
12127 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
12129 len = QT_UINT32 (data);
12132 buf = gst_buffer_new_and_alloc (len);
12133 gst_buffer_fill (buf, 0, data + 8, len);
12134 gst_caps_set_simple (entry->caps,
12135 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12136 gst_buffer_unref (buf);
12143 /* see annex I of the jpeg2000 spec */
12144 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
12145 const guint8 *data;
12146 const gchar *colorspace = NULL;
12148 guint32 ncomp_map = 0;
12149 gint32 *comp_map = NULL;
12150 guint32 nchan_def = 0;
12151 gint32 *chan_def = NULL;
12153 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12154 /* some required atoms */
12155 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12158 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
12162 /* number of components; redundant with info in codestream, but useful
12164 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
12165 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
12167 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
12169 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
12172 GST_DEBUG_OBJECT (qtdemux, "found colr");
12173 /* extract colour space info */
12174 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12175 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
12177 colorspace = "sRGB";
12180 colorspace = "GRAY";
12183 colorspace = "sYUV";
12191 /* colr is required, and only values 16, 17, and 18 are specified,
12192 so error if we have no colorspace */
12195 /* extract component mapping */
12196 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
12198 guint32 cmap_len = 0;
12200 cmap_len = QT_UINT32 (cmap->data);
12201 if (cmap_len >= 8) {
12202 /* normal box, subtract off header */
12204 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12205 if (cmap_len % 4 == 0) {
12206 ncomp_map = (cmap_len / 4);
12207 comp_map = g_new0 (gint32, ncomp_map);
12208 for (i = 0; i < ncomp_map; i++) {
12211 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12212 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12213 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
12214 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12219 /* extract channel definitions */
12220 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
12222 guint32 cdef_len = 0;
12224 cdef_len = QT_UINT32 (cdef->data);
12225 if (cdef_len >= 10) {
12226 /* normal box, subtract off header and len */
12228 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12229 if (cdef_len % 6 == 0) {
12230 nchan_def = (cdef_len / 6);
12231 chan_def = g_new0 (gint32, nchan_def);
12232 for (i = 0; i < nchan_def; i++)
12234 for (i = 0; i < nchan_def; i++) {
12235 guint16 cn, typ, asoc;
12236 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12237 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12238 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
12239 if (cn < nchan_def) {
12242 chan_def[cn] = asoc;
12245 chan_def[cn] = 0; /* alpha */
12248 chan_def[cn] = -typ;
12256 gst_caps_set_simple (entry->caps,
12257 "num-components", G_TYPE_INT, ncomp, NULL);
12258 gst_caps_set_simple (entry->caps,
12259 "colorspace", G_TYPE_STRING, colorspace, NULL);
12262 GValue arr = { 0, };
12263 GValue elt = { 0, };
12265 g_value_init (&arr, GST_TYPE_ARRAY);
12266 g_value_init (&elt, G_TYPE_INT);
12267 for (i = 0; i < ncomp_map; i++) {
12268 g_value_set_int (&elt, comp_map[i]);
12269 gst_value_array_append_value (&arr, &elt);
12271 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12272 "component-map", &arr);
12273 g_value_unset (&elt);
12274 g_value_unset (&arr);
12279 GValue arr = { 0, };
12280 GValue elt = { 0, };
12282 g_value_init (&arr, GST_TYPE_ARRAY);
12283 g_value_init (&elt, G_TYPE_INT);
12284 for (i = 0; i < nchan_def; i++) {
12285 g_value_set_int (&elt, chan_def[i]);
12286 gst_value_array_append_value (&arr, &elt);
12288 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12289 "channel-definitions", &arr);
12290 g_value_unset (&elt);
12291 g_value_unset (&arr);
12295 /* some optional atoms */
12296 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
12297 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
12299 /* indicate possible fields in caps */
12301 data = (guint8 *) field->data + 8;
12303 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12304 (gint) * data, NULL);
12306 /* add codec_data if provided */
12311 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12312 data = prefix->data;
12313 len = QT_UINT32 (data);
12316 buf = gst_buffer_new_and_alloc (len);
12317 gst_buffer_fill (buf, 0, data + 8, len);
12318 gst_caps_set_simple (entry->caps,
12319 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12320 gst_buffer_unref (buf);
12329 GstBuffer *seqh = NULL;
12330 const guint8 *gamma_data = NULL;
12331 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
12333 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
12336 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
12337 QT_FP32 (gamma_data), NULL);
12340 /* sorry for the bad name, but we don't know what this is, other
12341 * than its own fourcc */
12342 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12344 gst_buffer_unref (seqh);
12347 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12348 buf = gst_buffer_new_and_alloc (len);
12349 gst_buffer_fill (buf, 0, stsd_data, len);
12350 gst_caps_set_simple (entry->caps,
12351 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12352 gst_buffer_unref (buf);
12357 /* https://developer.apple.com/standards/qtff-2001.pdf,
12358 * page 92, "Video Sample Description", under table 3.1 */
12361 const gint compressor_offset =
12362 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12363 const gint min_size = compressor_offset + 32 + 2 + 2;
12366 guint16 color_table_id = 0;
12369 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12371 /* recover information on interlaced/progressive */
12372 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
12376 len = QT_UINT32 (jpeg->data);
12377 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12379 if (len >= min_size) {
12380 gst_byte_reader_init (&br, jpeg->data, len);
12382 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12383 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12384 if (color_table_id != 0) {
12385 /* the spec says there can be concatenated chunks in the data, and we want
12386 * to find one called field. Walk through them. */
12387 gint offset = min_size;
12388 while (offset + 8 < len) {
12389 guint32 size = 0, tag;
12390 ok = gst_byte_reader_get_uint32_le (&br, &size);
12391 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12392 if (!ok || size < 8) {
12393 GST_WARNING_OBJECT (qtdemux,
12394 "Failed to walk optional chunk list");
12397 GST_DEBUG_OBJECT (qtdemux,
12398 "Found optional %4.4s chunk, size %u",
12399 (const char *) &tag, size);
12400 if (tag == FOURCC_fiel) {
12401 guint8 n_fields = 0, ordering = 0;
12402 gst_byte_reader_get_uint8 (&br, &n_fields);
12403 gst_byte_reader_get_uint8 (&br, &ordering);
12404 if (n_fields == 1 || n_fields == 2) {
12405 GST_DEBUG_OBJECT (qtdemux,
12406 "Found fiel tag with %u fields, ordering %u",
12407 n_fields, ordering);
12409 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12410 "interlace-mode", G_TYPE_STRING, "interleaved",
12413 GST_WARNING_OBJECT (qtdemux,
12414 "Found fiel tag with invalid fields (%u)", n_fields);
12420 GST_DEBUG_OBJECT (qtdemux,
12421 "Color table ID is 0, not trying to get interlacedness");
12424 GST_WARNING_OBJECT (qtdemux,
12425 "Length of jpeg chunk is too small, not trying to get interlacedness");
12433 gst_caps_set_simple (entry->caps,
12434 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12440 GNode *xith, *xdxt;
12442 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12443 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12447 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12451 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12452 /* collect the headers and store them in a stream list so that we can
12453 * send them out first */
12454 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12464 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12465 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12468 ovc1_data = ovc1->data;
12469 ovc1_len = QT_UINT32 (ovc1_data);
12470 if (ovc1_len <= 198) {
12471 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12474 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12475 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12476 gst_caps_set_simple (entry->caps,
12477 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12478 gst_buffer_unref (buf);
12483 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12484 const guint8 *vc1_data = stsd_entry_data + 0x56;
12490 if (QT_UINT32 (vc1_data) <= len)
12491 size = QT_UINT32 (vc1_data) - 8;
12496 /* No real data, so break out */
12499 switch (QT_FOURCC (vc1_data + 0x4)) {
12500 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12504 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12505 buf = gst_buffer_new_and_alloc (size);
12506 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12507 gst_caps_set_simple (entry->caps,
12508 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12509 gst_buffer_unref (buf);
12516 vc1_data += size + 8;
12522 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12523 const guint8 *av1_data = stsd_entry_data + 0x56;
12526 while (len >= 0x8) {
12529 if (QT_UINT32 (av1_data) <= len)
12530 size = QT_UINT32 (av1_data) - 0x8;
12535 /* No real data, so break out */
12538 switch (QT_FOURCC (av1_data + 0x4)) {
12541 /* parse, if found */
12543 guint8 pres_delay_field;
12545 GST_DEBUG_OBJECT (qtdemux,
12546 "found av1C codec_data in stsd of size %d", size);
12548 /* not enough data, just ignore and hope for the best */
12553 * 4 bytes: atom length
12558 * 1 bits: initial_presentation_delay_present
12559 * 4 bits: initial_presentation_delay (if present else reserved
12563 if (av1_data[9] != 0) {
12564 GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
12568 /* We skip initial_presentation_delay* for now */
12569 pres_delay_field = *(av1_data + 12);
12570 if (pres_delay_field & (1 << 5)) {
12571 gst_caps_set_simple (entry->caps,
12572 "presentation-delay", G_TYPE_INT,
12573 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12576 buf = gst_buffer_new_and_alloc (size - 5);
12577 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12578 gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
12579 gst_caps_set_simple (entry->caps,
12580 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12581 gst_buffer_unref (buf);
12590 av1_data += size + 8;
12596 /* TODO: Need to parse vpcC for VP8 codec too.
12597 * Note that VPCodecConfigurationBox (vpcC) is defined for
12598 * vp08, vp09, and vp10 fourcc. */
12601 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
12602 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12605 while (len >= 0x8) {
12608 if (QT_UINT32 (vpcc_data) <= len)
12609 size = QT_UINT32 (vpcc_data) - 0x8;
12614 /* No real data, so break out */
12617 switch (QT_FOURCC (vpcc_data + 0x4)) {
12620 const gchar *profile_str = NULL;
12621 const gchar *chroma_format_str = NULL;
12624 guint8 chroma_format;
12625 GstVideoColorimetry cinfo;
12627 /* parse, if found */
12628 GST_DEBUG_OBJECT (qtdemux,
12629 "found vp codec_data in stsd of size %d", size);
12631 /* the meaning of "size" is length of the atom body, excluding
12632 * atom length and fourcc fields */
12637 * 4 bytes: atom length
12644 * 3 bits: chromaSubsampling
12645 * 1 bit: videoFullRangeFlag
12646 * 1 byte: colourPrimaries
12647 * 1 byte: transferCharacteristics
12648 * 1 byte: matrixCoefficients
12649 * 2 bytes: codecIntializationDataSize (should be zero for vp8 and vp9)
12650 * rest: codecIntializationData (not used for vp8 and vp9)
12653 if (vpcc_data[8] != 1) {
12654 GST_WARNING_OBJECT (qtdemux,
12655 "unknown vpcC version %d", vpcc_data[8]);
12659 profile = vpcc_data[12];
12678 gst_caps_set_simple (entry->caps,
12679 "profile", G_TYPE_STRING, profile_str, NULL);
12682 /* skip level, the VP9 spec v0.6 defines only one level atm,
12683 * but webm spec define various ones. Add level to caps
12684 * if we really need it then */
12686 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
12687 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
12688 gst_caps_set_simple (entry->caps,
12689 "bit-depth-luma", G_TYPE_UINT, bitdepth,
12690 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
12693 chroma_format = (vpcc_data[14] & 0xe) >> 1;
12694 switch (chroma_format) {
12697 chroma_format_str = "4:2:0";
12700 chroma_format_str = "4:2:2";
12703 chroma_format_str = "4:4:4";
12709 if (chroma_format_str) {
12710 gst_caps_set_simple (entry->caps,
12711 "chroma-format", G_TYPE_STRING, chroma_format_str,
12715 if ((vpcc_data[14] & 0x1) != 0)
12716 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
12718 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
12720 gst_video_color_primaries_from_iso (vpcc_data[15]);
12722 gst_video_transfer_function_from_iso (vpcc_data[16]);
12724 gst_video_color_matrix_from_iso (vpcc_data[17]);
12726 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
12727 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
12728 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
12729 /* set this only if all values are known, otherwise this
12730 * might overwrite valid ones parsed from other color box */
12731 CUR_STREAM (stream)->colorimetry = cinfo;
12740 vpcc_data += size + 8;
12750 GST_INFO_OBJECT (qtdemux,
12751 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12752 GST_FOURCC_ARGS (fourcc), entry->caps);
12754 } else if (stream->subtype == FOURCC_soun) {
12756 int version, samplesize;
12757 guint16 compression_id;
12758 gboolean amrwb = FALSE;
12761 /* sample description entry (16) + sound sample description v0 (20) */
12765 version = QT_UINT32 (stsd_entry_data + offset);
12766 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
12767 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
12768 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
12769 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
12771 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
12772 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
12773 QT_UINT32 (stsd_entry_data + offset + 4));
12774 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12775 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
12776 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
12777 GST_LOG_OBJECT (qtdemux, "packet size: %d",
12778 QT_UINT16 (stsd_entry_data + offset + 14));
12779 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12781 if (compression_id == 0xfffe)
12782 entry->sampled = TRUE;
12784 /* first assume uncompressed audio */
12785 entry->bytes_per_sample = samplesize / 8;
12786 entry->samples_per_frame = entry->n_channels;
12787 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12788 entry->samples_per_packet = entry->samples_per_frame;
12789 entry->bytes_per_packet = entry->bytes_per_sample;
12793 if (version == 0x00010000) {
12794 /* sample description entry (16) + sound sample description v1 (20+16) */
12798 /* take information from here over the normal sample description */
12799 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
12800 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
12801 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
12802 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
12804 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
12805 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
12806 entry->samples_per_packet);
12807 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12808 entry->bytes_per_packet);
12809 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
12810 entry->bytes_per_frame);
12811 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
12812 entry->bytes_per_sample);
12814 if (!entry->sampled && entry->bytes_per_packet) {
12815 entry->samples_per_frame = (entry->bytes_per_frame /
12816 entry->bytes_per_packet) * entry->samples_per_packet;
12817 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
12818 entry->samples_per_frame);
12820 } else if (version == 0x00020000) {
12821 /* sample description entry (16) + sound sample description v2 (56) */
12825 /* take information from here over the normal sample description */
12826 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
12827 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
12828 entry->samples_per_frame = entry->n_channels;
12829 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
12830 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
12831 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
12832 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
12834 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
12835 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12836 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12837 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
12838 entry->bytes_per_sample * 8);
12839 GST_LOG_OBJECT (qtdemux, "format flags: %X",
12840 QT_UINT32 (stsd_entry_data + offset + 24));
12841 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12842 entry->bytes_per_packet);
12843 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
12844 entry->samples_per_packet);
12845 } else if (version != 0x00000) {
12846 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
12851 /* Yes, these have to be hard-coded */
12854 entry->samples_per_packet = 6;
12855 entry->bytes_per_packet = 1;
12856 entry->bytes_per_frame = 1 * entry->n_channels;
12857 entry->bytes_per_sample = 1;
12858 entry->samples_per_frame = 6 * entry->n_channels;
12863 entry->samples_per_packet = 3;
12864 entry->bytes_per_packet = 1;
12865 entry->bytes_per_frame = 1 * entry->n_channels;
12866 entry->bytes_per_sample = 1;
12867 entry->samples_per_frame = 3 * entry->n_channels;
12872 entry->samples_per_packet = 64;
12873 entry->bytes_per_packet = 34;
12874 entry->bytes_per_frame = 34 * entry->n_channels;
12875 entry->bytes_per_sample = 2;
12876 entry->samples_per_frame = 64 * entry->n_channels;
12882 entry->samples_per_packet = 1;
12883 entry->bytes_per_packet = 1;
12884 entry->bytes_per_frame = 1 * entry->n_channels;
12885 entry->bytes_per_sample = 1;
12886 entry->samples_per_frame = 1 * entry->n_channels;
12891 entry->samples_per_packet = 160;
12892 entry->bytes_per_packet = 33;
12893 entry->bytes_per_frame = 33 * entry->n_channels;
12894 entry->bytes_per_sample = 2;
12895 entry->samples_per_frame = 160 * entry->n_channels;
12898 /* fix up any invalid header information from above */
12903 /* Sometimes these are set to 0 in the sound sample descriptions so
12904 * let's try to infer useful values from the other information we
12905 * have available */
12906 if (entry->bytes_per_sample == 0)
12907 entry->bytes_per_sample =
12908 entry->bytes_per_frame / entry->n_channels;
12909 if (entry->bytes_per_sample == 0)
12910 entry->bytes_per_sample = samplesize / 8;
12912 if (entry->bytes_per_frame == 0)
12913 entry->bytes_per_frame =
12914 entry->bytes_per_sample * entry->n_channels;
12916 if (entry->bytes_per_packet == 0)
12917 entry->bytes_per_packet = entry->bytes_per_sample;
12919 if (entry->samples_per_frame == 0)
12920 entry->samples_per_frame = entry->n_channels;
12922 if (entry->samples_per_packet == 0)
12923 entry->samples_per_packet = entry->samples_per_frame;
12933 entry->bytes_per_sample = 3;
12937 entry->bytes_per_sample = 4;
12940 entry->bytes_per_sample = 8;
12943 entry->bytes_per_sample = 2;
12946 g_assert_not_reached ();
12949 entry->samples_per_frame = entry->n_channels;
12950 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12951 entry->samples_per_packet = entry->samples_per_frame;
12952 entry->bytes_per_packet = entry->bytes_per_sample;
12960 gst_caps_unref (entry->caps);
12962 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
12963 stsd_entry_data + 32, len - 16, &codec);
12974 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
12976 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
12978 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
12980 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
12983 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
12984 const gchar *format_str;
12988 format_str = (enda_value) ? "S24LE" : "S24BE";
12991 format_str = (enda_value) ? "S32LE" : "S32BE";
12994 format_str = (enda_value) ? "F32LE" : "F32BE";
12997 format_str = (enda_value) ? "F64LE" : "F64BE";
13000 g_assert_not_reached ();
13003 gst_caps_set_simple (entry->caps,
13004 "format", G_TYPE_STRING, format_str, NULL);
13010 const guint8 *owma_data;
13011 const gchar *codec_name = NULL;
13015 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13016 /* FIXME this should also be gst_riff_strf_auds,
13017 * but the latter one is actually missing bits-per-sample :( */
13022 gint32 nSamplesPerSec;
13023 gint32 nAvgBytesPerSec;
13024 gint16 nBlockAlign;
13025 gint16 wBitsPerSample;
13028 WAVEFORMATEX *wfex;
13030 GST_DEBUG_OBJECT (qtdemux, "parse owma");
13031 owma_data = stsd_entry_data;
13032 owma_len = QT_UINT32 (owma_data);
13033 if (owma_len <= 54) {
13034 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
13037 wfex = (WAVEFORMATEX *) (owma_data + 36);
13038 buf = gst_buffer_new_and_alloc (owma_len - 54);
13039 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
13040 if (wfex->wFormatTag == 0x0161) {
13041 codec_name = "Windows Media Audio";
13043 } else if (wfex->wFormatTag == 0x0162) {
13044 codec_name = "Windows Media Audio 9 Pro";
13046 } else if (wfex->wFormatTag == 0x0163) {
13047 codec_name = "Windows Media Audio 9 Lossless";
13048 /* is that correct? gstffmpegcodecmap.c is missing it, but
13049 * fluendo codec seems to support it */
13053 gst_caps_set_simple (entry->caps,
13054 "codec_data", GST_TYPE_BUFFER, buf,
13055 "wmaversion", G_TYPE_INT, version,
13056 "block_align", G_TYPE_INT,
13057 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
13058 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13059 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13060 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13061 gst_buffer_unref (buf);
13065 codec = g_strdup (codec_name);
13071 gint len = QT_UINT32 (stsd_entry_data) - offset;
13072 const guint8 *wfex_data = stsd_entry_data + offset;
13073 const gchar *codec_name = NULL;
13075 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13076 /* FIXME this should also be gst_riff_strf_auds,
13077 * but the latter one is actually missing bits-per-sample :( */
13082 gint32 nSamplesPerSec;
13083 gint32 nAvgBytesPerSec;
13084 gint16 nBlockAlign;
13085 gint16 wBitsPerSample;
13090 /* FIXME: unify with similar wavformatex parsing code above */
13091 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13097 if (QT_UINT32 (wfex_data) <= len)
13098 size = QT_UINT32 (wfex_data) - 8;
13103 /* No real data, so break out */
13106 switch (QT_FOURCC (wfex_data + 4)) {
13107 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13109 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13114 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13115 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13116 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13117 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13118 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13119 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13120 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13122 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13123 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13124 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13125 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13126 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13127 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13129 if (wfex.wFormatTag == 0x0161) {
13130 codec_name = "Windows Media Audio";
13132 } else if (wfex.wFormatTag == 0x0162) {
13133 codec_name = "Windows Media Audio 9 Pro";
13135 } else if (wfex.wFormatTag == 0x0163) {
13136 codec_name = "Windows Media Audio 9 Lossless";
13137 /* is that correct? gstffmpegcodecmap.c is missing it, but
13138 * fluendo codec seems to support it */
13142 gst_caps_set_simple (entry->caps,
13143 "wmaversion", G_TYPE_INT, version,
13144 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13145 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13146 "width", G_TYPE_INT, wfex.wBitsPerSample,
13147 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13149 if (size > wfex.cbSize) {
13152 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13153 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13154 size - wfex.cbSize);
13155 gst_caps_set_simple (entry->caps,
13156 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13157 gst_buffer_unref (buf);
13159 GST_WARNING_OBJECT (qtdemux, "no codec data");
13164 codec = g_strdup (codec_name);
13172 wfex_data += size + 8;
13178 const guint8 *dops_data;
13179 guint8 *channel_mapping = NULL;
13182 guint8 channel_mapping_family;
13183 guint8 stream_count;
13184 guint8 coupled_count;
13187 version = GST_READ_UINT16_BE (stsd_entry_data + 16);
13189 dops_data = stsd_entry_data + 51;
13191 dops_data = stsd_entry_data + 35;
13193 channels = GST_READ_UINT8 (dops_data + 10);
13194 rate = GST_READ_UINT32_LE (dops_data + 13);
13195 channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
13196 stream_count = GST_READ_UINT8 (dops_data + 20);
13197 coupled_count = GST_READ_UINT8 (dops_data + 21);
13199 if (channels > 0) {
13200 channel_mapping = g_malloc (channels * sizeof (guint8));
13201 for (i = 0; i < channels; i++)
13202 channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
13205 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
13206 channel_mapping_family, stream_count, coupled_count,
13218 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13219 GST_TAG_AUDIO_CODEC, codec, NULL);
13223 /* some bitrate info may have ended up in caps */
13224 s = gst_caps_get_structure (entry->caps, 0);
13225 gst_structure_get_int (s, "bitrate", &bitrate);
13227 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13228 GST_TAG_BITRATE, bitrate, NULL);
13232 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
13233 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
13234 if (stream->protected) {
13235 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
13236 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13238 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
13248 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
13250 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
13252 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13256 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13257 16 bits is a byte-swapped wave-style codec identifier,
13258 and we can find a WAVE header internally to a 'wave' atom here.
13259 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13260 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13263 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13264 if (len < offset + 20) {
13265 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13267 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
13268 const guint8 *data = stsd_entry_data + offset + 16;
13270 GNode *waveheadernode;
13272 wavenode = g_node_new ((guint8 *) data);
13273 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
13274 const guint8 *waveheader;
13277 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
13278 if (waveheadernode) {
13279 waveheader = (const guint8 *) waveheadernode->data;
13280 headerlen = QT_UINT32 (waveheader);
13282 if (headerlen > 8) {
13283 gst_riff_strf_auds *header = NULL;
13284 GstBuffer *headerbuf;
13290 headerbuf = gst_buffer_new_and_alloc (headerlen);
13291 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13293 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13294 headerbuf, &header, &extra)) {
13295 gst_caps_unref (entry->caps);
13296 /* FIXME: Need to do something with the channel reorder map */
13298 gst_riff_create_audio_caps (header->format, NULL, header,
13299 extra, NULL, NULL, NULL);
13302 gst_buffer_unref (extra);
13307 GST_DEBUG ("Didn't find waveheadernode for this codec");
13309 g_node_destroy (wavenode);
13312 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13313 stream->stream_tags);
13317 /* FIXME: what is in the chunk? */
13320 gint len = QT_UINT32 (stsd_data);
13322 /* seems to be always = 116 = 0x74 */
13328 gint len = QT_UINT32 (stsd_entry_data);
13331 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13333 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13334 gst_caps_set_simple (entry->caps,
13335 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13336 gst_buffer_unref (buf);
13338 gst_caps_set_simple (entry->caps,
13339 "samplesize", G_TYPE_INT, samplesize, NULL);
13344 GNode *alac, *wave = NULL;
13346 /* apparently, m4a has this atom appended directly in the stsd entry,
13347 * while mov has it in a wave atom */
13348 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
13350 /* alac now refers to stsd entry atom */
13351 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
13353 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
13355 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
13358 const guint8 *alac_data = alac->data;
13359 gint len = QT_UINT32 (alac->data);
13363 GST_DEBUG_OBJECT (qtdemux,
13364 "discarding alac atom with unexpected len %d", len);
13366 /* codec-data contains alac atom size and prefix,
13367 * ffmpeg likes it that way, not quite gst-ish though ...*/
13368 buf = gst_buffer_new_and_alloc (len);
13369 gst_buffer_fill (buf, 0, alac->data, len);
13370 gst_caps_set_simple (entry->caps,
13371 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13372 gst_buffer_unref (buf);
13374 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
13375 entry->n_channels = QT_UINT8 (alac_data + 21);
13376 entry->rate = QT_UINT32 (alac_data + 32);
13377 samplesize = QT_UINT8 (alac_data + 16 + 1);
13380 gst_caps_set_simple (entry->caps,
13381 "samplesize", G_TYPE_INT, samplesize, NULL);
13386 /* The codingname of the sample entry is 'fLaC' */
13387 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
13390 /* The 'dfLa' box is added to the sample entry to convey
13391 initializing information for the decoder. */
13392 const GNode *dfla =
13393 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
13396 const guint32 len = QT_UINT32 (dfla->data);
13398 /* Must contain at least dfLa box header (12),
13399 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13401 GST_DEBUG_OBJECT (qtdemux,
13402 "discarding dfla atom with unexpected len %d", len);
13404 /* skip dfLa header to get the METADATA_BLOCKs */
13405 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13406 const guint32 metadata_blocks_len = len - 12;
13408 gchar *stream_marker = g_strdup ("fLaC");
13409 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13410 strlen (stream_marker));
13413 guint32 remainder = 0;
13414 guint32 block_size = 0;
13415 gboolean is_last = FALSE;
13417 GValue array = G_VALUE_INIT;
13418 GValue value = G_VALUE_INIT;
13420 g_value_init (&array, GST_TYPE_ARRAY);
13421 g_value_init (&value, GST_TYPE_BUFFER);
13423 gst_value_set_buffer (&value, block);
13424 gst_value_array_append_value (&array, &value);
13425 g_value_reset (&value);
13427 gst_buffer_unref (block);
13429 /* check there's at least one METADATA_BLOCK_HEADER's worth
13430 * of data, and we haven't already finished parsing */
13431 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13432 remainder = metadata_blocks_len - index;
13434 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13436 (metadata_blocks[index + 1] << 16) +
13437 (metadata_blocks[index + 2] << 8) +
13438 metadata_blocks[index + 3];
13440 /* be careful not to read off end of box */
13441 if (block_size > remainder) {
13445 is_last = metadata_blocks[index] >> 7;
13447 block = gst_buffer_new_and_alloc (block_size);
13449 gst_buffer_fill (block, 0, &metadata_blocks[index],
13452 gst_value_set_buffer (&value, block);
13453 gst_value_array_append_value (&array, &value);
13454 g_value_reset (&value);
13456 gst_buffer_unref (block);
13458 index += block_size;
13461 /* only append the metadata if we successfully read all of it */
13463 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
13464 (stream)->caps, 0), "streamheader", &array);
13466 GST_WARNING_OBJECT (qtdemux,
13467 "discarding all METADATA_BLOCKs due to invalid "
13468 "block_size %d at idx %d, rem %d", block_size, index,
13472 g_value_unset (&value);
13473 g_value_unset (&array);
13475 /* The sample rate obtained from the stsd may not be accurate
13476 * since it cannot represent rates greater than 65535Hz, so
13477 * override that value with the sample rate from the
13478 * METADATA_BLOCK_STREAMINFO block */
13479 CUR_STREAM (stream)->rate =
13480 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
13491 gint len = QT_UINT32 (stsd_entry_data);
13494 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13497 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13499 /* If we have enough data, let's try to get the 'damr' atom. See
13500 * the 3GPP container spec (26.244) for more details. */
13501 if ((len - 0x34) > 8 &&
13502 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
13503 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13504 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13507 gst_caps_set_simple (entry->caps,
13508 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13509 gst_buffer_unref (buf);
13515 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
13516 gint len = QT_UINT32 (stsd_entry_data);
13517 guint16 sound_version = 0;
13518 /* FIXME: Can this be determined somehow? There doesn't seem to be
13519 * anything in mp4a atom that specifis compression */
13521 guint16 channels = entry->n_channels;
13522 guint32 time_scale = (guint32) entry->rate;
13523 gint sample_rate_index = -1;
13526 sound_version = QT_UINT16 (stsd_entry_data + 16);
13528 if (sound_version == 1) {
13529 channels = QT_UINT16 (stsd_entry_data + 24);
13530 time_scale = QT_UINT32 (stsd_entry_data + 30);
13532 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13536 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13540 sample_rate_index =
13541 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13542 if (sample_rate_index >= 0 && channels > 0) {
13543 guint8 codec_data[2];
13546 /* build AAC codec data */
13547 codec_data[0] = profile << 3;
13548 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13549 codec_data[1] = (sample_rate_index & 0x01) << 7;
13550 codec_data[1] |= (channels & 0xF) << 3;
13552 buf = gst_buffer_new_and_alloc (2);
13553 gst_buffer_fill (buf, 0, codec_data, 2);
13554 gst_caps_set_simple (entry->caps,
13555 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13556 gst_buffer_unref (buf);
13566 /* Fully handled elsewhere */
13569 GST_INFO_OBJECT (qtdemux,
13570 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13574 GST_INFO_OBJECT (qtdemux,
13575 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13576 GST_FOURCC_ARGS (fourcc), entry->caps);
13578 } else if (stream->subtype == FOURCC_strm) {
13579 if (fourcc == FOURCC_rtsp) {
13580 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
13582 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
13583 GST_FOURCC_ARGS (fourcc));
13584 goto unknown_stream;
13586 entry->sampled = TRUE;
13587 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
13588 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
13589 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
13591 entry->sampled = TRUE;
13592 entry->sparse = TRUE;
13595 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13598 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13599 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13604 /* hunt for sort-of codec data */
13608 GNode *mp4s = NULL;
13609 GNode *esds = NULL;
13611 /* look for palette in a stsd->mp4s->esds sub-atom */
13612 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
13614 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
13615 if (esds == NULL) {
13617 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13621 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13622 stream->stream_tags);
13626 GST_INFO_OBJECT (qtdemux,
13627 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13630 GST_INFO_OBJECT (qtdemux,
13631 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13632 GST_FOURCC_ARGS (fourcc), entry->caps);
13634 /* everything in 1 sample */
13635 entry->sampled = TRUE;
13638 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13641 if (entry->caps == NULL)
13642 goto unknown_stream;
13645 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13646 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13652 /* promote to sampled format */
13653 if (entry->fourcc == FOURCC_samr) {
13654 /* force mono 8000 Hz for AMR */
13655 entry->sampled = TRUE;
13656 entry->n_channels = 1;
13657 entry->rate = 8000;
13658 } else if (entry->fourcc == FOURCC_sawb) {
13659 /* force mono 16000 Hz for AMR-WB */
13660 entry->sampled = TRUE;
13661 entry->n_channels = 1;
13662 entry->rate = 16000;
13663 } else if (entry->fourcc == FOURCC_mp4a) {
13664 entry->sampled = TRUE;
13668 stsd_entry_data += len;
13669 remaining_stsd_len -= len;
13673 /* collect sample information */
13674 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
13675 goto samples_failed;
13677 if (qtdemux->fragmented) {
13680 /* need all moov samples as basis; probably not many if any at all */
13681 /* prevent moof parsing taking of at this time */
13682 offset = qtdemux->moof_offset;
13683 qtdemux->moof_offset = 0;
13684 if (stream->n_samples &&
13685 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
13686 qtdemux->moof_offset = offset;
13687 goto samples_failed;
13689 qtdemux->moof_offset = offset;
13690 /* movie duration more reliable in this case (e.g. mehd) */
13691 if (qtdemux->segment.duration &&
13692 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13694 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
13697 /* configure segments */
13698 if (!qtdemux_parse_segments (qtdemux, stream, trak))
13699 goto segments_failed;
13701 /* add some language tag, if useful */
13702 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13703 strcmp (stream->lang_id, "und")) {
13704 const gchar *lang_code;
13706 /* convert ISO 639-2 code to ISO 639-1 */
13707 lang_code = gst_tag_get_language_code (stream->lang_id);
13708 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13709 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13712 /* Check for UDTA tags */
13713 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
13714 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
13717 /* Insert and sort new stream in track-id order.
13718 * This will help in comparing old/new streams during stream update check */
13719 g_ptr_array_add (qtdemux->active_streams, stream);
13720 g_ptr_array_sort (qtdemux->active_streams,
13721 (GCompareFunc) qtdemux_track_id_compare_func);
13722 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
13723 QTDEMUX_N_STREAMS (qtdemux));
13730 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
13731 (_("This file is corrupt and cannot be played.")), (NULL));
13733 gst_qtdemux_stream_unref (stream);
13738 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
13739 gst_qtdemux_stream_unref (stream);
13745 /* we posted an error already */
13746 /* free stbl sub-atoms */
13747 gst_qtdemux_stbl_free (stream);
13748 gst_qtdemux_stream_unref (stream);
13753 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
13759 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
13760 GST_FOURCC_ARGS (stream->subtype));
13761 gst_qtdemux_stream_unref (stream);
13766 /* If we can estimate the overall bitrate, and don't have information about the
13767 * stream bitrate for exactly one stream, this guesses the stream bitrate as
13768 * the overall bitrate minus the sum of the bitrates of all other streams. This
13769 * should be useful for the common case where we have one audio and one video
13770 * stream and can estimate the bitrate of one, but not the other. */
13772 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
13774 QtDemuxStream *stream = NULL;
13775 gint64 size, sys_bitrate, sum_bitrate = 0;
13776 GstClockTime duration;
13780 if (qtdemux->fragmented)
13783 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
13785 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
13787 GST_DEBUG_OBJECT (qtdemux,
13788 "Size in bytes of the stream not known - bailing");
13792 /* Subtract the header size */
13793 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
13794 size, qtdemux->header_size);
13796 if (size < qtdemux->header_size)
13799 size = size - qtdemux->header_size;
13801 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
13802 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
13806 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13807 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
13808 switch (str->subtype) {
13811 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
13812 CUR_STREAM (str)->caps);
13813 /* retrieve bitrate, prefer avg then max */
13815 if (str->stream_tags) {
13816 if (gst_tag_list_get_uint (str->stream_tags,
13817 GST_TAG_MAXIMUM_BITRATE, &bitrate))
13818 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
13819 if (gst_tag_list_get_uint (str->stream_tags,
13820 GST_TAG_NOMINAL_BITRATE, &bitrate))
13821 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
13822 if (gst_tag_list_get_uint (str->stream_tags,
13823 GST_TAG_BITRATE, &bitrate))
13824 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
13827 sum_bitrate += bitrate;
13830 GST_DEBUG_OBJECT (qtdemux,
13831 ">1 stream with unknown bitrate - bailing");
13838 /* For other subtypes, we assume no significant impact on bitrate */
13844 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
13848 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
13850 if (sys_bitrate < sum_bitrate) {
13851 /* This can happen, since sum_bitrate might be derived from maximum
13852 * bitrates and not average bitrates */
13853 GST_DEBUG_OBJECT (qtdemux,
13854 "System bitrate less than sum bitrate - bailing");
13858 bitrate = sys_bitrate - sum_bitrate;
13859 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
13860 ", Stream bitrate = %u", sys_bitrate, bitrate);
13862 if (!stream->stream_tags)
13863 stream->stream_tags = gst_tag_list_new_empty ();
13865 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
13867 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13868 GST_TAG_BITRATE, bitrate, NULL);
13871 static GstFlowReturn
13872 qtdemux_prepare_streams (GstQTDemux * qtdemux)
13874 GstFlowReturn ret = GST_FLOW_OK;
13875 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13876 guint64 tkhd_max_duration = 0;
13880 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
13882 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13883 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13884 guint32 sample_num = 0;
13886 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13887 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13889 if (qtdemux->fragmented && qtdemux->pullbased) {
13890 /* need all moov samples first */
13891 GST_OBJECT_LOCK (qtdemux);
13892 while (stream->n_samples == 0)
13893 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
13895 GST_OBJECT_UNLOCK (qtdemux);
13897 /* discard any stray moof */
13898 qtdemux->moof_offset = 0;
13899 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13900 if (tkhd_max_duration < stream->tkhd_duration)
13901 tkhd_max_duration = stream->tkhd_duration;
13905 /* prepare braking */
13906 if (ret != GST_FLOW_ERROR)
13909 /* in pull mode, we should have parsed some sample info by now;
13910 * and quite some code will not handle no samples.
13911 * in push mode, we'll just have to deal with it */
13912 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
13913 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
13914 g_ptr_array_remove_index (qtdemux->active_streams, i);
13917 } else if (stream->track_id == qtdemux->chapters_track_id &&
13918 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
13919 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
13920 so that it doesn't look like a subtitle track */
13921 g_ptr_array_remove_index (qtdemux->active_streams, i);
13926 /* parse the initial sample for use in setting the frame rate cap */
13927 while (sample_num == 0 && sample_num < stream->n_samples) {
13928 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
13934 #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
13935 if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
13936 GST_INFO_OBJECT (qtdemux,
13937 "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
13938 qtdemux->duration, tkhd_max_duration);
13939 qtdemux->duration = tkhd_max_duration;
13947 _stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
13949 return g_strcmp0 (stream->stream_id, stream_id) == 0;
13953 qtdemux_is_streams_update (GstQTDemux * qtdemux)
13957 /* Different length, updated */
13958 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
13961 /* streams in list are sorted in track-id order */
13962 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13963 /* Different stream-id, updated */
13964 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
13965 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
13973 qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
13974 QtDemuxStream * oldstream, QtDemuxStream * newstream)
13976 /* Connect old stream's srcpad to new stream */
13977 newstream->pad = oldstream->pad;
13978 oldstream->pad = NULL;
13980 /* unset new_stream to prevent stream-start event, unless we are EOS in which
13981 * case we need to force one through */
13982 newstream->new_stream = GST_PAD_IS_EOS (newstream->pad);
13984 return gst_qtdemux_configure_stream (qtdemux, newstream);
13988 qtdemux_update_streams (GstQTDemux * qtdemux)
13991 g_assert (qtdemux->streams_aware);
13993 /* At below, figure out which stream in active_streams has identical stream-id
13994 * with that of in old_streams. If there is matching stream-id,
13995 * corresponding newstream will not be exposed again,
13996 * but demux will reuse srcpad of matched old stream
13998 * active_streams : newly created streams from the latest moov
13999 * old_streams : existing streams (belong to previous moov)
14002 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14003 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14004 QtDemuxStream *oldstream = NULL;
14007 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
14008 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
14010 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
14011 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
14012 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
14014 /* null pad stream cannot be reused */
14015 if (oldstream->pad == NULL)
14020 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
14022 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
14025 /* we don't need to preserve order of old streams */
14026 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
14030 /* now we have all info and can expose */
14031 list = stream->stream_tags;
14032 stream->stream_tags = NULL;
14033 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14041 /* Must be called with expose lock */
14042 static GstFlowReturn
14043 qtdemux_expose_streams (GstQTDemux * qtdemux)
14047 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
14049 if (!qtdemux_is_streams_update (qtdemux)) {
14050 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
14051 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14052 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14053 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14054 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
14055 return GST_FLOW_ERROR;
14058 g_ptr_array_set_size (qtdemux->old_streams, 0);
14059 qtdemux->need_segment = TRUE;
14061 return GST_FLOW_OK;
14064 if (qtdemux->streams_aware) {
14065 if (!qtdemux_update_streams (qtdemux))
14066 return GST_FLOW_ERROR;
14068 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
14069 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14072 /* now we have all info and can expose */
14073 list = stream->stream_tags;
14074 stream->stream_tags = NULL;
14075 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14076 return GST_FLOW_ERROR;
14081 gst_qtdemux_guess_bitrate (qtdemux);
14083 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14085 /* If we have still old_streams, it's no more used stream */
14086 for (i = 0; i < qtdemux->old_streams->len; i++) {
14087 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14092 event = gst_event_new_eos ();
14093 if (qtdemux->segment_seqnum)
14094 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14096 gst_pad_push_event (stream->pad, event);
14100 g_ptr_array_set_size (qtdemux->old_streams, 0);
14102 /* check if we should post a redirect in case there is a single trak
14103 * and it is a redirecting trak */
14104 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14105 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
14108 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14109 "an external content");
14110 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14111 gst_structure_new ("redirect",
14112 "new-location", G_TYPE_STRING,
14113 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
14114 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14115 g_free (qtdemux->redirect_location);
14116 qtdemux->redirect_location =
14117 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
14120 g_ptr_array_foreach (qtdemux->active_streams,
14121 (GFunc) qtdemux_do_allocation, qtdemux);
14123 qtdemux->need_segment = TRUE;
14125 qtdemux->exposed = TRUE;
14126 return GST_FLOW_OK;
14131 GstStructure *structure; /* helper for sort function */
14133 guint min_req_bitrate;
14134 guint min_req_qt_version;
14138 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14140 GstQtReference *ref_a = (GstQtReference *) a;
14141 GstQtReference *ref_b = (GstQtReference *) b;
14143 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14144 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14146 /* known bitrates go before unknown; higher bitrates go first */
14147 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14150 /* sort the redirects and post a message for the application.
14153 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14155 GstQtReference *best;
14158 GValue list_val = { 0, };
14161 g_assert (references != NULL);
14163 references = g_list_sort (references, qtdemux_redirects_sort_func);
14165 best = (GstQtReference *) references->data;
14167 g_value_init (&list_val, GST_TYPE_LIST);
14169 for (l = references; l != NULL; l = l->next) {
14170 GstQtReference *ref = (GstQtReference *) l->data;
14171 GValue struct_val = { 0, };
14173 ref->structure = gst_structure_new ("redirect",
14174 "new-location", G_TYPE_STRING, ref->location, NULL);
14176 if (ref->min_req_bitrate > 0) {
14177 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14178 ref->min_req_bitrate, NULL);
14181 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14182 g_value_set_boxed (&struct_val, ref->structure);
14183 gst_value_list_append_value (&list_val, &struct_val);
14184 g_value_unset (&struct_val);
14185 /* don't free anything here yet, since we need best->structure below */
14188 g_assert (best != NULL);
14189 s = gst_structure_copy (best->structure);
14191 if (g_list_length (references) > 1) {
14192 gst_structure_set_value (s, "locations", &list_val);
14195 g_value_unset (&list_val);
14197 for (l = references; l != NULL; l = l->next) {
14198 GstQtReference *ref = (GstQtReference *) l->data;
14200 gst_structure_free (ref->structure);
14201 g_free (ref->location);
14204 g_list_free (references);
14206 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14207 g_free (qtdemux->redirect_location);
14208 qtdemux->redirect_location =
14209 g_strdup (gst_structure_get_string (s, "new-location"));
14210 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14211 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14214 /* look for redirect nodes, collect all redirect information and
14218 qtdemux_parse_redirects (GstQTDemux * qtdemux)
14220 GNode *rmra, *rmda, *rdrf;
14222 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14224 GList *redirects = NULL;
14226 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14228 GstQtReference ref = { NULL, NULL, 0, 0 };
14229 GNode *rmdr, *rmvc;
14231 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14232 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14233 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14234 ref.min_req_bitrate);
14237 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14238 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14239 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14241 #ifndef GST_DISABLE_GST_DEBUG
14242 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14244 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14246 GST_LOG_OBJECT (qtdemux,
14247 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14248 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14249 bitmask, check_type);
14250 if (package == FOURCC_qtim && check_type == 0) {
14251 ref.min_req_qt_version = version;
14255 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14261 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14262 if (ref_len > 20) {
14263 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14264 ref_data = (guint8 *) rdrf->data + 20;
14265 if (ref_type == FOURCC_alis) {
14266 guint record_len, record_version, fn_len;
14268 if (ref_len > 70) {
14269 /* MacOSX alias record, google for alias-layout.txt */
14270 record_len = QT_UINT16 (ref_data + 4);
14271 record_version = QT_UINT16 (ref_data + 4 + 2);
14272 fn_len = QT_UINT8 (ref_data + 50);
14273 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14274 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14277 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14280 } else if (ref_type == FOURCC_url_) {
14281 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14283 GST_DEBUG_OBJECT (qtdemux,
14284 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14285 GST_FOURCC_ARGS (ref_type));
14287 if (ref.location != NULL) {
14288 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14290 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14292 GST_WARNING_OBJECT (qtdemux,
14293 "Failed to extract redirect location from rdrf atom");
14296 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14300 /* look for others */
14301 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14304 if (redirects != NULL) {
14305 qtdemux_process_redirects (qtdemux, redirects);
14311 static GstTagList *
14312 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14316 if (tags == NULL) {
14317 tags = gst_tag_list_new_empty ();
14318 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14321 if (qtdemux->major_brand == FOURCC_mjp2)
14322 fmt = "Motion JPEG 2000";
14323 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14325 else if (qtdemux->major_brand == FOURCC_qt__)
14327 else if (qtdemux->fragmented)
14330 fmt = "ISO MP4/M4A";
14332 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14333 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14335 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14341 /* we have read the complete moov node now.
14342 * This function parses all of the relevant info, creates the traks and
14343 * prepares all data structures for playback
14346 qtdemux_parse_tree (GstQTDemux * qtdemux)
14353 guint64 creation_time;
14354 GstDateTime *datetime = NULL;
14357 /* make sure we have a usable taglist */
14358 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14360 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14361 if (mvhd == NULL) {
14362 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14363 return qtdemux_parse_redirects (qtdemux);
14366 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14367 if (version == 1) {
14368 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14369 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14370 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14371 } else if (version == 0) {
14372 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14373 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14374 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14376 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14380 /* Moving qt creation time (secs since 1904) to unix time */
14381 if (creation_time != 0) {
14382 /* Try to use epoch first as it should be faster and more commonly found */
14383 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14386 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14387 /* some data cleansing sanity */
14388 now_s = g_get_real_time () / G_USEC_PER_SEC;
14389 if (now_s + 24 * 3600 < creation_time) {
14390 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14392 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14395 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14396 GDateTime *dt, *dt_local;
14398 dt = g_date_time_add_seconds (base_dt, creation_time);
14399 dt_local = g_date_time_to_local (dt);
14400 datetime = gst_date_time_new_from_g_date_time (dt_local);
14402 g_date_time_unref (base_dt);
14403 g_date_time_unref (dt);
14407 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14408 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14410 gst_date_time_unref (datetime);
14413 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14414 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14416 /* check for fragmented file and get some (default) data */
14417 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14420 GstByteReader mehd_data;
14422 /* let track parsing or anyone know weird stuff might happen ... */
14423 qtdemux->fragmented = TRUE;
14425 /* compensate for total duration */
14426 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14428 qtdemux_parse_mehd (qtdemux, &mehd_data);
14431 /* Update the movie segment duration, unless it was directly given to us
14432 * by upstream. Otherwise let it as is, as we don't want to mangle the
14433 * duration provided by upstream that may come e.g. from a MPD file. */
14434 if (!qtdemux->upstream_format_is_time) {
14435 GstClockTime duration;
14436 /* set duration in the segment info */
14437 gst_qtdemux_get_duration (qtdemux, &duration);
14438 qtdemux->segment.duration = duration;
14439 /* also do not exceed duration; stop is set that way post seek anyway,
14440 * and segment activation falls back to duration,
14441 * whereas loop only checks stop, so let's align this here as well */
14442 qtdemux->segment.stop = duration;
14445 /* parse all traks */
14446 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14448 qtdemux_parse_trak (qtdemux, trak);
14449 /* iterate all siblings */
14450 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14453 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14456 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14458 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14460 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14463 /* maybe also some tags in meta box */
14464 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14466 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14467 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14469 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14472 /* parse any protection system info */
14473 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14475 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14476 qtdemux_parse_pssh (qtdemux, pssh);
14477 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14480 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14485 /* taken from ffmpeg */
14487 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14499 len = (len << 7) | (c & 0x7f);
14508 parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14509 gsize codec_data_size)
14511 GList *list = NULL;
14512 guint8 *p = codec_data;
14513 gint i, offset, num_packets;
14514 guint *length, last;
14516 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14518 if (codec_data == NULL || codec_data_size == 0)
14521 /* start of the stream and vorbis audio or theora video, need to
14522 * send the codec_priv data as first three packets */
14523 num_packets = p[0] + 1;
14524 GST_DEBUG_OBJECT (qtdemux,
14525 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14526 (guint) num_packets, codec_data_size);
14528 /* Let's put some limits, Don't think there even is a xiph codec
14529 * with more than 3-4 headers */
14530 if (G_UNLIKELY (num_packets > 16)) {
14531 GST_WARNING_OBJECT (qtdemux,
14532 "Unlikely number of xiph headers, most likely not valid");
14536 length = g_alloca (num_packets * sizeof (guint));
14540 /* first packets, read length values */
14541 for (i = 0; i < num_packets - 1; i++) {
14543 while (offset < codec_data_size) {
14544 length[i] += p[offset];
14545 if (p[offset++] != 0xff)
14550 if (offset + last > codec_data_size)
14553 /* last packet is the remaining size */
14554 length[i] = codec_data_size - offset - last;
14556 for (i = 0; i < num_packets; i++) {
14559 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14561 if (offset + length[i] > codec_data_size)
14564 hdr = gst_buffer_new_memdup (p + offset, length[i]);
14565 list = g_list_append (list, hdr);
14567 offset += length[i];
14576 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14582 /* this can change the codec originally present in @list */
14584 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14585 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14587 int len = QT_UINT32 (esds->data);
14588 guint8 *ptr = esds->data;
14589 guint8 *end = ptr + len;
14591 guint8 *data_ptr = NULL;
14593 guint8 object_type_id = 0;
14594 guint8 stream_type = 0;
14595 const char *codec_name = NULL;
14596 GstCaps *caps = NULL;
14598 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14600 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14602 while (ptr + 1 < end) {
14603 tag = QT_UINT8 (ptr);
14604 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14606 len = read_descr_size (ptr, end, &ptr);
14607 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14609 /* Check the stated amount of data is available for reading */
14610 if (len < 0 || ptr + len > end)
14614 case ES_DESCRIPTOR_TAG:
14615 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14616 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14619 case DECODER_CONFIG_DESC_TAG:{
14620 guint max_bitrate, avg_bitrate;
14622 object_type_id = QT_UINT8 (ptr);
14623 stream_type = QT_UINT8 (ptr + 1) >> 2;
14624 max_bitrate = QT_UINT32 (ptr + 5);
14625 avg_bitrate = QT_UINT32 (ptr + 9);
14626 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14627 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14628 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14629 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14630 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14631 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14632 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14633 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14635 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14636 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14637 avg_bitrate, NULL);
14642 case DECODER_SPECIFIC_INFO_TAG:
14643 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14644 if (object_type_id == 0xe0 && len == 0x40) {
14650 GST_DEBUG_OBJECT (qtdemux,
14651 "Have VOBSUB palette. Creating palette event");
14652 /* move to decConfigDescr data and read palette */
14654 for (i = 0; i < 16; i++) {
14655 clut[i] = QT_UINT32 (data);
14659 s = gst_structure_new ("application/x-gst-dvd", "event",
14660 G_TYPE_STRING, "dvd-spu-clut-change",
14661 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14662 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14663 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14664 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14665 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14666 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14667 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14668 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14671 /* store event and trigger custom processing */
14672 stream->pending_event =
14673 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14675 /* Generic codec_data handler puts it on the caps */
14682 case SL_CONFIG_DESC_TAG:
14683 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
14687 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14689 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14695 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14696 * in use, and should also be used to override some other parameters for some
14698 switch (object_type_id) {
14699 case 0x20: /* MPEG-4 */
14700 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14701 * profile_and_level_indication */
14702 if (data_ptr != NULL && data_len >= 5 &&
14703 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14704 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14705 data_ptr + 4, data_len - 4);
14707 break; /* Nothing special needed here */
14708 case 0x21: /* H.264 */
14709 codec_name = "H.264 / AVC";
14710 caps = gst_caps_new_simple ("video/x-h264",
14711 "stream-format", G_TYPE_STRING, "avc",
14712 "alignment", G_TYPE_STRING, "au", NULL);
14714 case 0x40: /* AAC (any) */
14715 case 0x66: /* AAC Main */
14716 case 0x67: /* AAC LC */
14717 case 0x68: /* AAC SSR */
14718 /* Override channels and rate based on the codec_data, as it's often
14720 /* Only do so for basic setup without HE-AAC extension */
14721 if (data_ptr && data_len == 2) {
14722 guint channels, rate;
14724 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14726 entry->n_channels = channels;
14728 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
14730 entry->rate = rate;
14733 /* Set level and profile if possible */
14734 if (data_ptr != NULL && data_len >= 2) {
14735 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
14736 data_ptr, data_len);
14738 const gchar *profile_str = NULL;
14741 guint8 *codec_data;
14742 gint rate_idx, profile;
14744 /* No codec_data, let's invent something.
14745 * FIXME: This is wrong for SBR! */
14747 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
14749 buffer = gst_buffer_new_and_alloc (2);
14750 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
14751 codec_data = map.data;
14754 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
14757 switch (object_type_id) {
14759 profile_str = "main";
14763 profile_str = "lc";
14767 profile_str = "ssr";
14775 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14777 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14779 gst_buffer_unmap (buffer, &map);
14780 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14781 GST_TYPE_BUFFER, buffer, NULL);
14782 gst_buffer_unref (buffer);
14785 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14786 G_TYPE_STRING, profile_str, NULL);
14790 case 0x60: /* MPEG-2, various profiles */
14796 codec_name = "MPEG-2 video";
14797 caps = gst_caps_new_simple ("video/mpeg",
14798 "mpegversion", G_TYPE_INT, 2,
14799 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14801 case 0x69: /* MPEG-2 BC audio */
14802 case 0x6B: /* MPEG-1 audio */
14803 caps = gst_caps_new_simple ("audio/mpeg",
14804 "mpegversion", G_TYPE_INT, 1, NULL);
14805 codec_name = "MPEG-1 audio";
14807 case 0x6A: /* MPEG-1 */
14808 codec_name = "MPEG-1 video";
14809 caps = gst_caps_new_simple ("video/mpeg",
14810 "mpegversion", G_TYPE_INT, 1,
14811 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14813 case 0x6C: /* MJPEG */
14815 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14817 codec_name = "Motion-JPEG";
14819 case 0x6D: /* PNG */
14821 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14823 codec_name = "PNG still images";
14825 case 0x6E: /* JPEG2000 */
14826 codec_name = "JPEG-2000";
14827 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14829 case 0xA4: /* Dirac */
14830 codec_name = "Dirac";
14831 caps = gst_caps_new_empty_simple ("video/x-dirac");
14833 case 0xA5: /* AC3 */
14834 codec_name = "AC-3 audio";
14835 caps = gst_caps_new_simple ("audio/x-ac3",
14836 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14838 case 0xA9: /* AC3 */
14839 codec_name = "DTS audio";
14840 caps = gst_caps_new_simple ("audio/x-dts",
14841 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14844 if (stream_type == 0x05 && data_ptr) {
14846 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
14849 GValue arr_val = G_VALUE_INIT;
14850 GValue buf_val = G_VALUE_INIT;
14853 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
14854 codec_name = "Vorbis";
14855 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
14856 g_value_init (&arr_val, GST_TYPE_ARRAY);
14857 g_value_init (&buf_val, GST_TYPE_BUFFER);
14858 for (tmp = headers; tmp; tmp = tmp->next) {
14859 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
14860 gst_value_array_append_value (&arr_val, &buf_val);
14862 s = gst_caps_get_structure (caps, 0);
14863 gst_structure_take_value (s, "streamheader", &arr_val);
14864 g_value_unset (&buf_val);
14865 g_list_free (headers);
14872 case 0xE1: /* QCELP */
14873 /* QCELP, the codec_data is a riff tag (little endian) with
14874 * 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). */
14875 caps = gst_caps_new_empty_simple ("audio/qcelp");
14876 codec_name = "QCELP";
14882 /* If we have a replacement caps, then change our caps for this stream */
14884 gst_caps_unref (entry->caps);
14885 entry->caps = caps;
14888 if (codec_name && list)
14889 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14890 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14892 /* Add the codec_data attribute to caps, if we have it */
14896 buffer = gst_buffer_new_and_alloc (data_len);
14897 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14899 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14900 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14902 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14904 gst_buffer_unref (buffer);
14909 static inline GstCaps *
14910 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14914 char *s, fourstr[5];
14916 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14917 for (i = 0; i < 4; i++) {
14918 if (!g_ascii_isalnum (fourstr[i]))
14921 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14922 caps = gst_caps_new_empty_simple (s);
14927 #define _codec(name) \
14929 if (codec_name) { \
14930 *codec_name = g_strdup (name); \
14935 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14936 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14937 const guint8 * stsd_entry_data, gchar ** codec_name)
14939 GstCaps *caps = NULL;
14940 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14944 _codec ("PNG still images");
14945 caps = gst_caps_new_empty_simple ("image/png");
14948 _codec ("JPEG still images");
14950 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14953 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14954 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14955 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14956 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14957 _codec ("Motion-JPEG");
14959 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14962 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14963 _codec ("Motion-JPEG format B");
14964 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14967 _codec ("JPEG-2000");
14968 /* override to what it should be according to spec, avoid palette_data */
14969 entry->bits_per_sample = 24;
14970 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14973 _codec ("Sorensen video v.3");
14974 caps = gst_caps_new_simple ("video/x-svq",
14975 "svqversion", G_TYPE_INT, 3, NULL);
14977 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14978 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14979 _codec ("Sorensen video v.1");
14980 caps = gst_caps_new_simple ("video/x-svq",
14981 "svqversion", G_TYPE_INT, 1, NULL);
14983 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14984 caps = gst_caps_new_empty_simple ("video/x-raw");
14985 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14986 _codec ("Windows Raw RGB");
14987 stream->alignment = 32;
14993 bps = QT_UINT16 (stsd_entry_data + 82);
14996 format = GST_VIDEO_FORMAT_RGB15;
14999 format = GST_VIDEO_FORMAT_RGB16;
15002 format = GST_VIDEO_FORMAT_RGB;
15005 format = GST_VIDEO_FORMAT_ARGB;
15013 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15014 format = GST_VIDEO_FORMAT_I420;
15016 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15017 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15018 format = GST_VIDEO_FORMAT_I420;
15021 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15022 format = GST_VIDEO_FORMAT_UYVY;
15024 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15025 format = GST_VIDEO_FORMAT_v308;
15027 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15028 format = GST_VIDEO_FORMAT_v216;
15031 format = GST_VIDEO_FORMAT_v210;
15033 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15034 format = GST_VIDEO_FORMAT_r210;
15036 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15037 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15038 format = GST_VIDEO_FORMAT_v410;
15041 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15042 * but different order than AYUV
15043 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15044 format = GST_VIDEO_FORMAT_v408;
15047 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15048 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
15049 _codec ("MPEG-1 video");
15050 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15051 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15053 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15054 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15055 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15056 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15057 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15058 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15059 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15060 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15061 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15062 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15063 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15064 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15065 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15066 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15067 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15068 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15069 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15070 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15071 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15072 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15073 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15074 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15075 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15076 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15077 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15078 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15079 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15080 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15081 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15082 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15083 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15084 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15085 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15086 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15087 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15088 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15089 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15090 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15091 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15092 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15093 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15094 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15095 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15096 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15097 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15098 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15099 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15100 _codec ("MPEG-2 video");
15101 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15102 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15104 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15105 _codec ("GIF still images");
15106 caps = gst_caps_new_empty_simple ("image/gif");
15109 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15111 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15113 /* ffmpeg uses the height/width props, don't know why */
15114 caps = gst_caps_new_simple ("video/x-h263",
15115 "variant", G_TYPE_STRING, "itu", NULL);
15119 _codec ("MPEG-4 video");
15120 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15121 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15123 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15124 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15125 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15126 caps = gst_caps_new_simple ("video/x-msmpeg",
15127 "msmpegversion", G_TYPE_INT, 43, NULL);
15129 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15131 caps = gst_caps_new_simple ("video/x-divx",
15132 "divxversion", G_TYPE_INT, 3, NULL);
15134 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15135 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15137 caps = gst_caps_new_simple ("video/x-divx",
15138 "divxversion", G_TYPE_INT, 4, NULL);
15140 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15142 caps = gst_caps_new_simple ("video/x-divx",
15143 "divxversion", G_TYPE_INT, 5, NULL);
15146 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15148 caps = gst_caps_new_simple ("video/x-ffv",
15149 "ffvversion", G_TYPE_INT, 1, NULL);
15152 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15153 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15158 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15159 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15160 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15164 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15165 _codec ("Cinepak");
15166 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15168 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15169 _codec ("Apple QuickDraw");
15170 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15172 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15173 _codec ("Apple video");
15174 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15179 _codec ("H.264 / AVC");
15180 caps = gst_caps_new_simple ("video/x-h264",
15181 "stream-format", G_TYPE_STRING, "avc",
15182 "alignment", G_TYPE_STRING, "au", NULL);
15186 _codec ("H.264 / AVC");
15187 caps = gst_caps_new_simple ("video/x-h264",
15188 "stream-format", G_TYPE_STRING, "avc3",
15189 "alignment", G_TYPE_STRING, "au", NULL);
15194 _codec ("H.265 / HEVC");
15195 caps = gst_caps_new_simple ("video/x-h265",
15196 "stream-format", G_TYPE_STRING, "hvc1",
15197 "alignment", G_TYPE_STRING, "au", NULL);
15201 _codec ("H.265 / HEVC");
15202 caps = gst_caps_new_simple ("video/x-h265",
15203 "stream-format", G_TYPE_STRING, "hev1",
15204 "alignment", G_TYPE_STRING, "au", NULL);
15207 _codec ("Run-length encoding");
15208 caps = gst_caps_new_simple ("video/x-rle",
15209 "layout", G_TYPE_STRING, "quicktime", NULL);
15212 _codec ("Run-length encoding");
15213 caps = gst_caps_new_simple ("video/x-rle",
15214 "layout", G_TYPE_STRING, "microsoft", NULL);
15216 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15217 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15218 _codec ("Indeo Video 3");
15219 caps = gst_caps_new_simple ("video/x-indeo",
15220 "indeoversion", G_TYPE_INT, 3, NULL);
15222 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15223 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15224 _codec ("Intel Video 4");
15225 caps = gst_caps_new_simple ("video/x-indeo",
15226 "indeoversion", G_TYPE_INT, 4, NULL);
15230 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15231 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15232 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15233 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15234 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15235 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15236 _codec ("DV Video");
15237 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15238 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15240 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15241 case FOURCC_dv5p: /* DVCPRO50 PAL */
15242 _codec ("DVCPro50 Video");
15243 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15244 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15246 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15247 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15248 _codec ("DVCProHD Video");
15249 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15250 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15252 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15253 _codec ("Apple Graphics (SMC)");
15254 caps = gst_caps_new_empty_simple ("video/x-smc");
15256 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15258 caps = gst_caps_new_empty_simple ("video/x-vp3");
15260 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15261 _codec ("VP6 Flash");
15262 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15266 caps = gst_caps_new_empty_simple ("video/x-theora");
15267 /* theora uses one byte of padding in the data stream because it does not
15268 * allow 0 sized packets while theora does */
15269 entry->padding = 1;
15273 caps = gst_caps_new_empty_simple ("video/x-dirac");
15275 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15276 _codec ("TIFF still images");
15277 caps = gst_caps_new_empty_simple ("image/tiff");
15279 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15280 _codec ("Apple Intermediate Codec");
15281 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15283 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15284 _codec ("AVID DNxHD");
15285 caps = gst_caps_from_string ("video/x-dnxhd");
15289 _codec ("On2 VP8");
15290 caps = gst_caps_from_string ("video/x-vp8");
15293 _codec ("Google VP9");
15294 caps = gst_caps_from_string ("video/x-vp9");
15297 _codec ("Apple ProRes LT");
15299 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15303 _codec ("Apple ProRes HQ");
15305 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15309 _codec ("Apple ProRes");
15311 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15315 _codec ("Apple ProRes Proxy");
15317 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15321 _codec ("Apple ProRes 4444");
15323 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15326 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15327 if (entry->bits_per_sample > 0) {
15328 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15333 _codec ("Apple ProRes 4444 XQ");
15335 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15338 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15339 if (entry->bits_per_sample > 0) {
15340 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15345 _codec ("GoPro CineForm");
15346 caps = gst_caps_from_string ("video/x-cineform");
15351 caps = gst_caps_new_simple ("video/x-wmv",
15352 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15356 caps = gst_caps_new_empty_simple ("video/x-av1");
15358 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15361 caps = _get_unknown_codec_name ("video", fourcc);
15366 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15369 gst_video_info_init (&info);
15370 gst_video_info_set_format (&info, format, entry->width, entry->height);
15372 caps = gst_video_info_to_caps (&info);
15373 *codec_name = gst_pb_utils_get_codec_description (caps);
15375 /* enable clipping for raw video streams */
15376 stream->need_clip = TRUE;
15377 stream->alignment = 32;
15384 round_up_pow2 (guint n)
15396 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15397 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15398 int len, gchar ** codec_name)
15401 const GstStructure *s;
15404 GstAudioFormat format = 0;
15407 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15409 depth = entry->bytes_per_packet * 8;
15412 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15414 /* 8-bit audio is unsigned */
15416 format = GST_AUDIO_FORMAT_U8;
15417 /* otherwise it's signed and big-endian just like 'twos' */
15419 endian = G_BIG_ENDIAN;
15426 endian = G_LITTLE_ENDIAN;
15429 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15431 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15435 caps = gst_caps_new_simple ("audio/x-raw",
15436 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15437 "layout", G_TYPE_STRING, "interleaved", NULL);
15438 stream->alignment = GST_ROUND_UP_8 (depth);
15439 stream->alignment = round_up_pow2 (stream->alignment);
15443 _codec ("Raw 64-bit floating-point audio");
15444 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15446 caps = gst_caps_new_simple ("audio/x-raw",
15447 "format", G_TYPE_STRING, "F64BE",
15448 "layout", G_TYPE_STRING, "interleaved", NULL);
15449 stream->alignment = 8;
15452 _codec ("Raw 32-bit floating-point audio");
15453 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15455 caps = gst_caps_new_simple ("audio/x-raw",
15456 "format", G_TYPE_STRING, "F32BE",
15457 "layout", G_TYPE_STRING, "interleaved", NULL);
15458 stream->alignment = 4;
15461 _codec ("Raw 24-bit PCM audio");
15462 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15464 caps = gst_caps_new_simple ("audio/x-raw",
15465 "format", G_TYPE_STRING, "S24BE",
15466 "layout", G_TYPE_STRING, "interleaved", NULL);
15467 stream->alignment = 4;
15470 _codec ("Raw 32-bit PCM audio");
15471 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15473 caps = gst_caps_new_simple ("audio/x-raw",
15474 "format", G_TYPE_STRING, "S32BE",
15475 "layout", G_TYPE_STRING, "interleaved", NULL);
15476 stream->alignment = 4;
15479 _codec ("Raw 16-bit PCM audio");
15480 caps = gst_caps_new_simple ("audio/x-raw",
15481 "format", G_TYPE_STRING, "S16LE",
15482 "layout", G_TYPE_STRING, "interleaved", NULL);
15483 stream->alignment = 2;
15486 _codec ("Mu-law audio");
15487 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15490 _codec ("A-law audio");
15491 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15495 _codec ("Microsoft ADPCM");
15496 /* Microsoft ADPCM-ACM code 2 */
15497 caps = gst_caps_new_simple ("audio/x-adpcm",
15498 "layout", G_TYPE_STRING, "microsoft", NULL);
15502 _codec ("DVI/IMA ADPCM");
15503 caps = gst_caps_new_simple ("audio/x-adpcm",
15504 "layout", G_TYPE_STRING, "dvi", NULL);
15508 _codec ("DVI/Intel IMA ADPCM");
15509 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15510 caps = gst_caps_new_simple ("audio/x-adpcm",
15511 "layout", G_TYPE_STRING, "quicktime", NULL);
15515 /* MPEG layer 3, CBR only (pre QT4.1) */
15518 _codec ("MPEG-1 layer 3");
15519 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15520 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15521 "mpegversion", G_TYPE_INT, 1, NULL);
15523 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15524 _codec ("MPEG-1 layer 2");
15526 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15527 "mpegversion", G_TYPE_INT, 1, NULL);
15530 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15531 _codec ("EAC-3 audio");
15532 caps = gst_caps_new_simple ("audio/x-eac3",
15533 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15534 entry->sampled = TRUE;
15536 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15538 _codec ("AC-3 audio");
15539 caps = gst_caps_new_simple ("audio/x-ac3",
15540 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15541 entry->sampled = TRUE;
15543 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15544 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15545 _codec ("DTS audio");
15546 caps = gst_caps_new_simple ("audio/x-dts",
15547 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15548 entry->sampled = TRUE;
15550 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15551 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15552 _codec ("DTS-HD audio");
15553 caps = gst_caps_new_simple ("audio/x-dts",
15554 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15555 entry->sampled = TRUE;
15559 caps = gst_caps_new_simple ("audio/x-mace",
15560 "maceversion", G_TYPE_INT, 3, NULL);
15564 caps = gst_caps_new_simple ("audio/x-mace",
15565 "maceversion", G_TYPE_INT, 6, NULL);
15567 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15569 caps = gst_caps_new_empty_simple ("application/ogg");
15571 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15572 _codec ("DV audio");
15573 caps = gst_caps_new_empty_simple ("audio/x-dv");
15576 _codec ("MPEG-4 AAC audio");
15577 caps = gst_caps_new_simple ("audio/mpeg",
15578 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15579 "stream-format", G_TYPE_STRING, "raw", NULL);
15581 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15582 _codec ("QDesign Music");
15583 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15586 _codec ("QDesign Music v.2");
15587 /* FIXME: QDesign music version 2 (no constant) */
15588 if (FALSE && data) {
15589 caps = gst_caps_new_simple ("audio/x-qdm2",
15590 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15591 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15592 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15594 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15598 _codec ("GSM audio");
15599 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15602 _codec ("AMR audio");
15603 caps = gst_caps_new_empty_simple ("audio/AMR");
15606 _codec ("AMR-WB audio");
15607 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15610 _codec ("Quicktime IMA ADPCM");
15611 caps = gst_caps_new_simple ("audio/x-adpcm",
15612 "layout", G_TYPE_STRING, "quicktime", NULL);
15615 _codec ("Apple lossless audio");
15616 caps = gst_caps_new_empty_simple ("audio/x-alac");
15619 _codec ("Free Lossless Audio Codec");
15620 caps = gst_caps_new_simple ("audio/x-flac",
15621 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15623 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15624 _codec ("QualComm PureVoice");
15625 caps = gst_caps_from_string ("audio/qcelp");
15630 caps = gst_caps_new_empty_simple ("audio/x-wma");
15634 caps = gst_caps_new_empty_simple ("audio/x-opus");
15641 GstAudioFormat format;
15644 FLAG_IS_FLOAT = 0x1,
15645 FLAG_IS_BIG_ENDIAN = 0x2,
15646 FLAG_IS_SIGNED = 0x4,
15647 FLAG_IS_PACKED = 0x8,
15648 FLAG_IS_ALIGNED_HIGH = 0x10,
15649 FLAG_IS_NON_INTERLEAVED = 0x20
15651 _codec ("Raw LPCM audio");
15653 if (data && len >= 36) {
15654 depth = QT_UINT32 (data + 24);
15655 flags = QT_UINT32 (data + 28);
15656 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15658 if ((flags & FLAG_IS_FLOAT) == 0) {
15663 if ((flags & FLAG_IS_ALIGNED_HIGH))
15666 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15667 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15668 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15669 caps = gst_caps_new_simple ("audio/x-raw",
15670 "format", G_TYPE_STRING,
15672 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15673 "UNKNOWN", "layout", G_TYPE_STRING,
15674 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15675 "interleaved", NULL);
15676 stream->alignment = GST_ROUND_UP_8 (depth);
15677 stream->alignment = round_up_pow2 (stream->alignment);
15682 if (flags & FLAG_IS_BIG_ENDIAN)
15683 format = GST_AUDIO_FORMAT_F64BE;
15685 format = GST_AUDIO_FORMAT_F64LE;
15687 if (flags & FLAG_IS_BIG_ENDIAN)
15688 format = GST_AUDIO_FORMAT_F32BE;
15690 format = GST_AUDIO_FORMAT_F32LE;
15692 caps = gst_caps_new_simple ("audio/x-raw",
15693 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15694 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15695 "non-interleaved" : "interleaved", NULL);
15696 stream->alignment = width / 8;
15700 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
15703 caps = gst_caps_new_empty_simple ("audio/x-ac4");
15706 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15710 caps = _get_unknown_codec_name ("audio", fourcc);
15716 GstCaps *templ_caps =
15717 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
15718 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
15719 gst_caps_unref (caps);
15720 gst_caps_unref (templ_caps);
15721 caps = intersection;
15724 /* enable clipping for raw audio streams */
15725 s = gst_caps_get_structure (caps, 0);
15726 name = gst_structure_get_name (s);
15727 if (g_str_has_prefix (name, "audio/x-raw")) {
15728 stream->need_clip = TRUE;
15729 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
15730 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
15731 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
15732 stream->max_buffer_size);
15738 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15739 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15740 const guint8 * stsd_entry_data, gchar ** codec_name)
15744 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15748 _codec ("DVD subtitle");
15749 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
15750 stream->process_func = gst_qtdemux_process_buffer_dvd;
15753 _codec ("Quicktime timed text");
15756 _codec ("3GPP timed text");
15758 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
15760 /* actual text piece needs to be extracted */
15761 stream->process_func = gst_qtdemux_process_buffer_text;
15764 _codec ("XML subtitles");
15765 caps = gst_caps_new_empty_simple ("application/ttml+xml");
15770 const gchar *buf = "WEBVTT\n\n";
15772 _codec ("WebVTT subtitles");
15773 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
15774 stream->process_func = gst_qtdemux_process_buffer_wvtt;
15776 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
15777 buffer = gst_buffer_new_and_alloc (8);
15778 gst_buffer_fill (buffer, 0, buf, 8);
15779 stream->buffers = g_slist_append (stream->buffers, buffer);
15784 _codec ("CEA 608 Closed Caption");
15786 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
15787 G_TYPE_STRING, "s334-1a", NULL);
15788 stream->process_func = gst_qtdemux_process_buffer_clcp;
15789 stream->need_split = TRUE;
15792 _codec ("CEA 708 Closed Caption");
15794 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
15795 G_TYPE_STRING, "cdp", NULL);
15796 stream->process_func = gst_qtdemux_process_buffer_clcp;
15801 caps = _get_unknown_codec_name ("text", fourcc);
15809 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15810 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15811 const guint8 * stsd_entry_data, gchar ** codec_name)
15817 _codec ("MPEG 1 video");
15818 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15819 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15829 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
15830 const gchar * system_id)
15834 if (!qtdemux->protection_system_ids)
15835 qtdemux->protection_system_ids =
15836 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
15837 /* Check whether we already have an entry for this system ID. */
15838 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
15839 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
15840 if (g_ascii_strcasecmp (system_id, id) == 0) {
15844 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
15845 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,