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
33 * Demuxes a .mov file into raw or compressed audio and/or video streams.
35 * This element supports both push and pull-based scheduling, depending on the
36 * capabilities of the upstream elements.
39 * <title>Example launch line</title>
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/tag/tag.h>
57 #include <gst/audio/audio.h>
58 #include <gst/video/video.h>
60 #include "qtatomparser.h"
61 #include "qtdemux_types.h"
62 #include "qtdemux_dump.h"
64 #include "descriptors.h"
65 #include "qtdemux_lang.h"
67 #include "qtpalette.h"
69 #include "gst/riff/riff-media.h"
70 #include "gst/riff/riff-read.h"
72 #include <gst/pbutils/pbutils.h>
79 #include <gst/math-compat.h>
85 /* max. size considered 'sane' for non-mdat atoms */
86 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
88 /* if the sample index is larger than this, something is likely wrong */
89 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*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 GST_DEBUG_CATEGORY (qtdemux_debug);
105 typedef struct _QtDemuxSegment QtDemuxSegment;
106 typedef struct _QtDemuxSample QtDemuxSample;
108 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
110 struct _QtDemuxSample
113 gint32 pts_offset; /* Add this value to timestamp to get the pts */
115 guint64 timestamp; /* DTS In mov time */
116 guint32 duration; /* In mov time */
117 gboolean keyframe; /* TRUE when this packet is a keyframe */
120 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
121 typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
123 struct _QtDemuxSphericalMetadata
125 gboolean is_spherical;
126 gboolean is_stitched;
127 char *stitching_software;
128 char *projection_type;
131 int init_view_heading;
135 int full_pano_width_pixels;
136 int full_pano_height_pixels;
137 int cropped_area_image_width;
138 int cropped_area_image_height;
139 int cropped_area_left;
140 int cropped_area_top;
141 QTDEMUX_AMBISONIC_TYPE ambisonic_type;
142 QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
143 QTDEMUX_AMBISONIC_ORDER ambisonic_order;
146 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
148 /* Macros for converting to/from timescale */
149 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
150 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
152 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
153 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
155 /* timestamp is the DTS */
156 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
157 /* timestamp + offset + cslg_shift is the outgoing PTS */
158 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
159 /* timestamp + offset is the PTS used for internal seek calcuations */
160 #define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
161 /* timestamp + duration - dts is the duration */
162 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
164 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
167 * Quicktime has tracks and segments. A track is a continuous piece of
168 * multimedia content. The track is not always played from start to finish but
169 * instead, pieces of the track are 'cut out' and played in sequence. This is
170 * what the segments do.
172 * Inside the track we have keyframes (K) and delta frames. The track has its
173 * own timing, which starts from 0 and extends to end. The position in the track
174 * is called the media_time.
176 * The segments now describe the pieces that should be played from this track
177 * and are basically tuples of media_time/duration/rate entries. We can have
178 * multiple segments and they are all played after one another. An example:
180 * segment 1: media_time: 1 second, duration: 1 second, rate 1
181 * segment 2: media_time: 3 second, duration: 2 second, rate 2
183 * To correctly play back this track, one must play: 1 second of media starting
184 * from media_time 1 followed by 2 seconds of media starting from media_time 3
187 * Each of the segments will be played at a specific time, the first segment at
188 * time 0, the second one after the duration of the first one, etc.. Note that
189 * the time in resulting playback is not identical to the media_time of the
192 * Visually, assuming the track has 4 second of media_time:
195 * .-----------------------------------------------------------.
196 * track: | K.....K.........K........K.......K.......K...........K... |
197 * '-----------------------------------------------------------'
199 * .------------^ ^ .----------^ ^
200 * / .-------------' / .------------------'
202 * .--------------. .--------------.
203 * | segment 1 | | segment 2 |
204 * '--------------' '--------------'
206 * The challenge here is to cut out the right pieces of the track for each of
207 * the playback segments. This fortunately can easily be done with the SEGMENT
208 * events of GStreamer.
210 * For playback of segment 1, we need to provide the decoder with the keyframe
211 * (a), in the above figure, but we must instruct it only to output the decoded
212 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
213 * position set to the time of the segment: 0.
215 * We then proceed to push data from keyframe (a) to frame (b). The decoder
216 * decodes but clips all before media_time 1.
218 * After finishing a segment, we push out a new SEGMENT event with the clipping
219 * boundaries of the new data.
221 * This is a good usecase for the GStreamer accumulated SEGMENT events.
224 struct _QtDemuxSegment
226 /* global time and duration, all gst time */
228 GstClockTime stop_time;
229 GstClockTime duration;
230 /* media time of trak, all gst time */
231 GstClockTime media_start;
232 GstClockTime media_stop;
234 /* Media start time in trak timescale units */
235 guint32 trak_media_start;
238 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
240 /* Used with fragmented MP4 files (mfra atom) */
245 } QtDemuxRandomAccessEntry;
247 typedef struct _QtDemuxStreamStsdEntry
258 /* Numerator/denominator framerate */
261 GstVideoColorimetry colorimetry;
262 guint16 bits_per_sample;
263 guint16 color_table_id;
264 GstMemory *rgb8_palette;
265 guint interlace_mode;
271 guint samples_per_packet;
272 guint samples_per_frame;
273 guint bytes_per_packet;
274 guint bytes_per_sample;
275 guint bytes_per_frame;
278 /* if we use chunks or samples */
282 } QtDemuxStreamStsdEntry;
284 #define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
286 struct _QtDemuxStream
290 QtDemuxStreamStsdEntry *stsd_entries;
291 guint stsd_entries_length;
292 guint cur_stsd_entry_index;
297 gboolean new_caps; /* If TRUE, caps need to be generated (by
298 * calling _configure_stream()) This happens
299 * for MSS and fragmented streams */
301 gboolean new_stream; /* signals that a stream_start is required */
302 gboolean on_keyframe; /* if this stream last pushed buffer was a
303 * keyframe. This is important to identify
304 * where to stop pushing buffers after a
305 * segment stop time */
307 /* if the stream has a redirect URI in its headers, we store it here */
314 guint64 duration; /* in timescale units */
318 gchar lang_id[4]; /* ISO 639-2T language code */
322 QtDemuxSample *samples;
323 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
324 guint32 first_duration; /* duration in timescale of first sample, used for figuring out
326 guint32 n_samples_moof; /* sample count in a moof */
327 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
328 * the framerate of fragmented format stream */
329 guint64 duration_last_moof;
331 guint32 offset_in_sample; /* Offset in the current sample, used for
332 * streams which have got exceedingly big
333 * sample size (such as 24s of raw audio).
334 * Only used when max_buffer_size is non-NULL */
335 guint32 max_buffer_size; /* Maximum allowed size for output buffers.
336 * Currently only set for raw audio streams*/
344 gboolean use_allocator;
345 GstAllocator *allocator;
346 GstAllocationParams params;
350 /* when a discontinuity is pending */
353 /* list of buffers to push first */
356 /* if we need to clip this buffer. This is only needed for uncompressed
360 /* buffer needs some custom processing, e.g. subtitles */
361 gboolean need_process;
363 /* current position */
364 guint32 segment_index;
365 guint32 sample_index;
366 GstClockTime time_position; /* in gst time */
367 guint64 accumulated_base;
369 /* the Gst segment we are processing out, used for clipping */
372 /* quicktime segments */
374 QtDemuxSegment *segments;
375 gboolean dummy_segment;
380 GstTagList *stream_tags;
381 gboolean send_global_tags;
383 GstEvent *pending_event;
393 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
397 GstByteReader co_chunk;
399 guint32 current_chunk;
401 guint32 samples_per_chunk;
402 guint32 stsd_sample_description_id;
403 guint32 stco_sample_index;
405 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
408 guint32 n_samples_per_chunk;
409 guint32 stsc_chunk_index;
410 guint32 stsc_sample_index;
411 guint64 chunk_offset;
414 guint32 stts_samples;
415 guint32 n_sample_times;
416 guint32 stts_sample_index;
418 guint32 stts_duration;
420 gboolean stss_present;
421 guint32 n_sample_syncs;
424 gboolean stps_present;
425 guint32 n_sample_partial_syncs;
427 QtDemuxRandomAccessEntry *ra_entries;
430 const QtDemuxRandomAccessEntry *pending_seek;
433 gboolean ctts_present;
434 guint32 n_composition_times;
436 guint32 ctts_sample_index;
444 gboolean parsed_trex;
445 guint32 def_sample_description_index; /* index is 1-based */
446 guint32 def_sample_duration;
447 guint32 def_sample_size;
448 guint32 def_sample_flags;
452 /* stereoscopic video streams */
453 GstVideoMultiviewMode multiview_mode;
454 GstVideoMultiviewFlags multiview_flags;
456 /* protected streams */
458 guint32 protection_scheme_type;
459 guint32 protection_scheme_version;
460 gpointer protection_scheme_info; /* specific to the protection scheme */
461 GQueue protection_scheme_event_queue;
464 /* Contains properties and cryptographic info for a set of samples from a
465 * track protected using Common Encryption (cenc) */
466 struct _QtDemuxCencSampleSetInfo
468 GstStructure *default_properties;
470 /* @crypto_info holds one GstStructure per sample */
471 GPtrArray *crypto_info;
475 qt_demux_state_string (enum QtDemuxState state)
478 case QTDEMUX_STATE_INITIAL:
480 case QTDEMUX_STATE_HEADER:
482 case QTDEMUX_STATE_MOVIE:
484 case QTDEMUX_STATE_BUFFER_MDAT:
485 return "<BUFFER_MDAT>";
491 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
492 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
493 guint32 fourcc, GstByteReader * parser);
494 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
495 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
496 guint32 fourcc, GstByteReader * parser);
498 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
500 static GstStaticPadTemplate gst_qtdemux_sink_template =
501 GST_STATIC_PAD_TEMPLATE ("sink",
504 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
508 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
509 GST_STATIC_PAD_TEMPLATE ("video_%u",
512 GST_STATIC_CAPS_ANY);
514 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
515 GST_STATIC_PAD_TEMPLATE ("audio_%u",
518 GST_STATIC_CAPS_ANY);
520 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
521 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
524 GST_STATIC_CAPS_ANY);
526 #define gst_qtdemux_parent_class parent_class
527 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
529 static void gst_qtdemux_dispose (GObject * object);
532 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
533 GstClockTime media_time);
535 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
536 QtDemuxStream * str, gint64 media_offset);
539 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
540 static GstIndex *gst_qtdemux_get_index (GstElement * element);
542 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
543 GstStateChange transition);
544 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
545 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
546 GstObject * parent, GstPadMode mode, gboolean active);
548 static void gst_qtdemux_loop (GstPad * pad);
549 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
551 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
553 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
554 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
555 QtDemuxStream * stream);
556 static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
557 QtDemuxStream * stream);
558 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
561 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
562 const guint8 * buffer, guint length);
563 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
564 const guint8 * buffer, guint length);
565 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
566 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
569 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
570 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
572 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
573 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
574 const guint8 * stsd_entry_data, gchar ** codec_name);
575 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
576 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
577 const guint8 * data, int len, gchar ** codec_name);
578 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
579 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
580 gchar ** codec_name);
581 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
582 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
583 const guint8 * stsd_entry_data, gchar ** codec_name);
585 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
586 QtDemuxStream * stream, guint32 n);
587 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
588 static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
589 QtDemuxStream * stream);
590 static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux,
591 QtDemuxStream * stream);
592 static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index);
593 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
594 static void qtdemux_do_allocation (GstQTDemux * qtdemux,
595 QtDemuxStream * stream);
596 static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
597 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
598 static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
599 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
600 GstClockTime * _start, GstClockTime * _stop);
601 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
602 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
604 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
605 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
607 static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
609 static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
610 QtDemuxStream * stream, guint sample_index);
611 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
613 static void qtdemux_gst_structure_free (GstStructure * gststructure);
615 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
616 static void gst_tag_register_spherical_tags (void);
617 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
620 gst_qtdemux_class_init (GstQTDemuxClass * klass)
622 GObjectClass *gobject_class;
623 GstElementClass *gstelement_class;
625 gobject_class = (GObjectClass *) klass;
626 gstelement_class = (GstElementClass *) klass;
628 parent_class = g_type_class_peek_parent (klass);
630 gobject_class->dispose = gst_qtdemux_dispose;
632 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
634 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
635 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
638 gst_tag_register_musicbrainz_tags ();
640 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
641 gst_tag_register_spherical_tags ();
642 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
644 gst_element_class_add_static_pad_template (gstelement_class,
645 &gst_qtdemux_sink_template);
646 gst_element_class_add_static_pad_template (gstelement_class,
647 &gst_qtdemux_videosrc_template);
648 gst_element_class_add_static_pad_template (gstelement_class,
649 &gst_qtdemux_audiosrc_template);
650 gst_element_class_add_static_pad_template (gstelement_class,
651 &gst_qtdemux_subsrc_template);
652 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
654 "Demultiplex a QuickTime file into audio and video streams",
655 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
657 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
662 gst_qtdemux_init (GstQTDemux * qtdemux)
665 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
666 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
667 gst_pad_set_activatemode_function (qtdemux->sinkpad,
668 qtdemux_sink_activate_mode);
669 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
670 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
671 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
673 qtdemux->state = QTDEMUX_STATE_INITIAL;
674 qtdemux->pullbased = FALSE;
675 qtdemux->posted_redirect = FALSE;
676 qtdemux->neededbytes = 16;
678 qtdemux->adapter = gst_adapter_new ();
680 qtdemux->first_mdat = -1;
681 qtdemux->got_moov = FALSE;
682 qtdemux->mdatoffset = -1;
683 qtdemux->mdatbuffer = NULL;
684 qtdemux->restoredata_buffer = NULL;
685 qtdemux->restoredata_offset = -1;
686 qtdemux->fragment_start = -1;
687 qtdemux->fragment_start_offset = -1;
688 qtdemux->media_caps = NULL;
689 qtdemux->exposed = FALSE;
690 qtdemux->mss_mode = FALSE;
691 qtdemux->pending_newsegment = NULL;
692 qtdemux->upstream_format_is_time = FALSE;
693 qtdemux->have_group_id = FALSE;
694 qtdemux->group_id = G_MAXUINT;
695 qtdemux->cenc_aux_info_offset = 0;
696 qtdemux->cenc_aux_info_sizes = NULL;
697 qtdemux->cenc_aux_sample_count = 0;
698 qtdemux->protection_system_ids = NULL;
699 g_queue_init (&qtdemux->protection_event_queue);
700 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
701 qtdemux->tag_list = gst_tag_list_new_empty ();
702 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
703 qtdemux->flowcombiner = gst_flow_combiner_new ();
705 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
706 qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
707 malloc (sizeof (QtDemuxSphericalMetadata));
709 if (qtdemux->spherical_metadata) {
710 qtdemux->spherical_metadata->is_spherical = FALSE;
711 qtdemux->spherical_metadata->is_stitched = FALSE;
712 qtdemux->spherical_metadata->stitching_software = NULL;
713 qtdemux->spherical_metadata->projection_type = NULL;
714 qtdemux->spherical_metadata->stereo_mode = NULL;
715 qtdemux->spherical_metadata->source_count = 0;
716 qtdemux->spherical_metadata->init_view_heading = 0;
717 qtdemux->spherical_metadata->init_view_pitch = 0;
718 qtdemux->spherical_metadata->init_view_roll = 0;
719 qtdemux->spherical_metadata->timestamp = 0;
720 qtdemux->spherical_metadata->full_pano_width_pixels = 0;
721 qtdemux->spherical_metadata->full_pano_height_pixels = 0;
722 qtdemux->spherical_metadata->cropped_area_image_width = 0;
723 qtdemux->spherical_metadata->cropped_area_image_height = 0;
724 qtdemux->spherical_metadata->cropped_area_left = 0;
725 qtdemux->spherical_metadata->cropped_area_top = 0;
726 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
727 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
728 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
730 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
732 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
736 gst_qtdemux_dispose (GObject * object)
738 GstQTDemux *qtdemux = GST_QTDEMUX (object);
740 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
741 if (qtdemux->spherical_metadata) {
742 if (qtdemux->spherical_metadata->stitching_software)
743 free(qtdemux->spherical_metadata->stitching_software);
744 if (qtdemux->spherical_metadata->projection_type)
745 free(qtdemux->spherical_metadata->projection_type);
746 if (qtdemux->spherical_metadata->stereo_mode)
747 free(qtdemux->spherical_metadata->stereo_mode);
749 free(qtdemux->spherical_metadata);
750 qtdemux->spherical_metadata = NULL;
752 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
754 if (qtdemux->adapter) {
755 g_object_unref (G_OBJECT (qtdemux->adapter));
756 qtdemux->adapter = NULL;
758 gst_tag_list_unref (qtdemux->tag_list);
759 gst_flow_combiner_free (qtdemux->flowcombiner);
760 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
762 g_queue_clear (&qtdemux->protection_event_queue);
764 g_free (qtdemux->cenc_aux_info_sizes);
765 qtdemux->cenc_aux_info_sizes = NULL;
767 G_OBJECT_CLASS (parent_class)->dispose (object);
771 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
773 if (qtdemux->posted_redirect) {
774 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
775 (_("This file contains no playable streams.")),
776 ("no known streams found, a redirect message has been posted"));
778 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
779 (_("This file contains no playable streams.")),
780 ("no known streams found"));
785 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
787 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
788 mem, size, 0, size, mem, free_func);
792 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
799 if (G_UNLIKELY (size == 0)) {
801 GstBuffer *tmp = NULL;
803 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
804 if (ret != GST_FLOW_OK)
807 gst_buffer_map (tmp, &map, GST_MAP_READ);
808 size = QT_UINT32 (map.data);
809 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
811 gst_buffer_unmap (tmp, &map);
812 gst_buffer_unref (tmp);
815 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
816 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
817 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
818 /* we're pulling header but already got most interesting bits,
819 * so never mind the rest (e.g. tags) (that much) */
820 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
824 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
825 (_("This file is invalid and cannot be played.")),
826 ("atom has bogus size %" G_GUINT64_FORMAT, size));
827 return GST_FLOW_ERROR;
831 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
833 if (G_UNLIKELY (flow != GST_FLOW_OK))
836 bsize = gst_buffer_get_size (*buf);
837 /* Catch short reads - we don't want any partial atoms */
838 if (G_UNLIKELY (bsize < size)) {
839 GST_WARNING_OBJECT (qtdemux,
840 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
841 gst_buffer_unref (*buf);
851 gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
852 GstFormat src_format, gint64 src_value, GstFormat dest_format,
856 QtDemuxStream *stream = gst_pad_get_element_private (pad);
859 if (stream->subtype != FOURCC_vide) {
864 switch (src_format) {
865 case GST_FORMAT_TIME:
866 switch (dest_format) {
867 case GST_FORMAT_BYTES:{
868 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
874 *dest_value = stream->samples[index].offset;
876 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
877 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
878 GST_TIME_ARGS (src_value), *dest_value);
886 case GST_FORMAT_BYTES:
887 switch (dest_format) {
888 case GST_FORMAT_TIME:{
890 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
899 QTSTREAMTIME_TO_GSTTIME (stream,
900 stream->samples[index].timestamp);
901 GST_DEBUG_OBJECT (qtdemux,
902 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
903 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
922 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
924 gboolean res = FALSE;
926 *duration = GST_CLOCK_TIME_NONE;
928 if (qtdemux->duration != 0 &&
929 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
930 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
933 *duration = GST_CLOCK_TIME_NONE;
940 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
943 gboolean res = FALSE;
944 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
946 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
948 switch (GST_QUERY_TYPE (query)) {
949 case GST_QUERY_POSITION:{
952 gst_query_parse_position (query, &fmt, NULL);
953 if (fmt == GST_FORMAT_TIME
954 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
955 gst_query_set_position (query, GST_FORMAT_TIME,
956 qtdemux->segment.position);
961 case GST_QUERY_DURATION:{
964 gst_query_parse_duration (query, &fmt, NULL);
965 if (fmt == GST_FORMAT_TIME) {
966 /* First try to query upstream */
967 res = gst_pad_query_default (pad, parent, query);
969 GstClockTime duration;
970 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
971 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
978 case GST_QUERY_CONVERT:{
979 GstFormat src_fmt, dest_fmt;
980 gint64 src_value, dest_value = 0;
982 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
984 res = gst_qtdemux_src_convert (qtdemux, pad,
985 src_fmt, src_value, dest_fmt, &dest_value);
987 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
992 case GST_QUERY_FORMATS:
993 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
996 case GST_QUERY_SEEKING:{
1000 /* try upstream first */
1001 res = gst_pad_query_default (pad, parent, query);
1004 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
1005 if (fmt == GST_FORMAT_TIME) {
1006 GstClockTime duration;
1008 gst_qtdemux_get_duration (qtdemux, &duration);
1010 if (!qtdemux->pullbased) {
1013 /* we might be able with help from upstream */
1015 q = gst_query_new_seeking (GST_FORMAT_BYTES);
1016 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
1017 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
1018 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
1020 gst_query_unref (q);
1022 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
1028 case GST_QUERY_SEGMENT:
1033 format = qtdemux->segment.format;
1036 gst_segment_to_stream_time (&qtdemux->segment, format,
1037 qtdemux->segment.start);
1038 if ((stop = qtdemux->segment.stop) == -1)
1039 stop = qtdemux->segment.duration;
1041 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
1043 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
1048 res = gst_pad_query_default (pad, parent, query);
1056 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
1058 if (G_LIKELY (stream->pad)) {
1059 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
1060 GST_DEBUG_PAD_NAME (stream->pad));
1062 if (!gst_tag_list_is_empty (stream->stream_tags)) {
1063 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
1064 stream->stream_tags);
1065 gst_pad_push_event (stream->pad,
1066 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
1067 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
1068 /* post message qtdemux tag (for early recive application) */
1069 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1070 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
1071 gst_tag_list_copy (stream->stream_tags)));
1075 if (G_UNLIKELY (stream->send_global_tags)) {
1076 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1078 gst_pad_push_event (stream->pad,
1079 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1080 stream->send_global_tags = FALSE;
1085 /* push event on all source pads; takes ownership of the event */
1087 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1090 gboolean has_valid_stream = FALSE;
1091 GstEventType etype = GST_EVENT_TYPE (event);
1093 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1094 GST_EVENT_TYPE_NAME (event));
1096 for (n = 0; n < qtdemux->n_streams; n++) {
1098 QtDemuxStream *stream = qtdemux->streams[n];
1099 GST_DEBUG_OBJECT (qtdemux, "pushing on pad %i", n);
1101 if ((pad = stream->pad)) {
1102 has_valid_stream = TRUE;
1104 if (etype == GST_EVENT_EOS) {
1105 /* let's not send twice */
1106 if (stream->sent_eos)
1108 stream->sent_eos = TRUE;
1111 gst_pad_push_event (pad, gst_event_ref (event));
1115 gst_event_unref (event);
1117 /* if it is EOS and there are no pads, post an error */
1118 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1119 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1123 /* push a pending newsegment event, if any from the streaming thread */
1125 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
1127 if (qtdemux->pending_newsegment) {
1128 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
1129 qtdemux->pending_newsegment = NULL;
1139 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1141 if ((gint64) s1->timestamp + s1->pts_offset > *media_time)
1143 if ((gint64) s1->timestamp + s1->pts_offset == *media_time)
1149 /* find the index of the sample that includes the data for @media_time using a
1150 * binary search. Only to be called in optimized cases of linear search below.
1152 * Returns the index of the sample.
1155 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1158 QtDemuxSample *result;
1161 /* convert media_time to mov format */
1163 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1165 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1166 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1167 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1169 if (G_LIKELY (result))
1170 index = result - str->samples;
1179 /* find the index of the sample that includes the data for @media_offset using a
1182 * Returns the index of the sample.
1185 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1186 QtDemuxStream * str, gint64 media_offset)
1188 QtDemuxSample *result = str->samples;
1191 if (result == NULL || str->n_samples == 0)
1194 if (media_offset == result->offset)
1198 while (index < str->n_samples - 1) {
1199 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1202 if (media_offset < result->offset)
1213 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1218 /* find the index of the sample that includes the data for @media_time using a
1219 * linear search, and keeping in mind that not all samples may have been parsed
1220 * yet. If possible, it will delegate to binary search.
1222 * Returns the index of the sample.
1225 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1226 GstClockTime media_time)
1230 QtDemuxSample *sample;
1232 /* convert media_time to mov format */
1234 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1236 sample = str->samples;
1237 if (mov_time == sample->timestamp + sample->pts_offset)
1240 /* use faster search if requested time in already parsed range */
1241 sample = str->samples + str->stbl_index;
1242 if (str->stbl_index >= 0 &&
1243 mov_time <= (sample->timestamp + sample->pts_offset))
1244 return gst_qtdemux_find_index (qtdemux, str, media_time);
1246 while (index < str->n_samples - 1) {
1247 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1250 sample = str->samples + index + 1;
1251 if (mov_time < (sample->timestamp + sample->pts_offset))
1261 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1266 /* find the index of the keyframe needed to decode the sample at @index
1267 * of stream @str, or of a subsequent keyframe (depending on @next)
1269 * Returns the index of the keyframe.
1272 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1273 guint32 index, gboolean next)
1275 guint32 new_index = index;
1277 if (index >= str->n_samples) {
1278 new_index = str->n_samples;
1282 /* all keyframes, return index */
1283 if (str->all_keyframe) {
1288 /* else search until we have a keyframe */
1289 while (new_index < str->n_samples) {
1290 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1293 if (str->samples[new_index].keyframe)
1305 if (new_index == str->n_samples) {
1306 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1311 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1312 "gave %u", next ? "after" : "before", index, new_index);
1319 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1324 /* find the segment for @time_position for @stream
1326 * Returns the index of the segment containing @time_position.
1327 * Returns the last segment and sets the @eos variable to TRUE
1328 * if the time is beyond the end. @eos may be NULL
1331 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1332 GstClockTime time_position)
1337 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1338 GST_TIME_ARGS (time_position));
1341 for (i = 0; i < stream->n_segments; i++) {
1342 QtDemuxSegment *segment = &stream->segments[i];
1344 GST_LOG_OBJECT (stream->pad,
1345 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1346 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1348 /* For the last segment we include stop_time in the last segment */
1349 if (i < stream->n_segments - 1) {
1350 if (segment->time <= time_position && time_position < segment->stop_time) {
1351 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1356 /* Last segment always matches */
1364 /* move the stream @str to the sample position @index.
1366 * Updates @str->sample_index and marks discontinuity if needed.
1369 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1372 /* no change needed */
1373 if (index == str->sample_index)
1376 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1379 /* position changed, we have a discont */
1380 str->sample_index = index;
1381 str->offset_in_sample = 0;
1382 /* Each time we move in the stream we store the position where we are
1384 str->from_sample = index;
1385 str->discont = TRUE;
1389 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1390 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1393 gint64 min_byte_offset = -1;
1396 min_offset = desired_time;
1398 /* for each stream, find the index of the sample in the segment
1399 * and move back to the previous keyframe. */
1400 for (n = 0; n < qtdemux->n_streams; n++) {
1402 guint32 index, kindex;
1404 GstClockTime media_start;
1405 GstClockTime media_time;
1406 GstClockTime seg_time;
1407 QtDemuxSegment *seg;
1408 gboolean empty_segment = FALSE;
1410 str = qtdemux->streams[n];
1412 if (CUR_STREAM (str)->sparse && !use_sparse)
1415 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1416 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1418 /* get segment and time in the segment */
1419 seg = &str->segments[seg_idx];
1420 seg_time = (desired_time - seg->time) * seg->rate;
1422 while (QTSEGMENT_IS_EMPTY (seg)) {
1424 empty_segment = TRUE;
1425 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1428 if (seg_idx == str->n_segments)
1430 seg = &str->segments[seg_idx];
1433 if (seg_idx == str->n_segments) {
1434 /* FIXME track shouldn't have the last segment as empty, but if it
1435 * happens we better handle it */
1439 /* get the media time in the segment */
1440 media_start = seg->media_start + seg_time;
1442 /* get the index of the sample with media time */
1443 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1444 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1445 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1446 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1449 /* shift to next frame if we are looking for next keyframe */
1450 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1451 && index < str->stbl_index)
1454 if (!empty_segment) {
1455 /* find previous keyframe */
1456 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1458 /* we will settle for one before if none found after */
1459 if (next && kindex == -1)
1460 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1462 /* if the keyframe is at a different position, we need to update the
1463 * requested seek time */
1464 if (index != kindex) {
1467 /* get timestamp of keyframe */
1468 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1469 GST_DEBUG_OBJECT (qtdemux,
1470 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1471 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1472 str->samples[kindex].offset);
1474 /* keyframes in the segment get a chance to change the
1475 * desired_offset. keyframes out of the segment are
1477 if (media_time >= seg->media_start) {
1478 GstClockTime seg_time;
1480 /* this keyframe is inside the segment, convert back to
1482 seg_time = (media_time - seg->media_start) + seg->time;
1483 if ((!next && (seg_time < min_offset)) ||
1484 (next && (seg_time > min_offset)))
1485 min_offset = seg_time;
1490 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1491 min_byte_offset = str->samples[index].offset;
1495 *key_time = min_offset;
1497 *key_offset = min_byte_offset;
1501 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1502 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1506 g_return_val_if_fail (format != NULL, FALSE);
1507 g_return_val_if_fail (cur != NULL, FALSE);
1508 g_return_val_if_fail (stop != NULL, FALSE);
1510 if (*format == GST_FORMAT_TIME)
1514 if (cur_type != GST_SEEK_TYPE_NONE)
1515 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1516 if (res && stop_type != GST_SEEK_TYPE_NONE)
1517 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1520 *format = GST_FORMAT_TIME;
1525 /* perform seek in push based mode:
1526 find BYTE position to move to based on time and delegate to upstream
1529 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1534 GstSeekType cur_type, stop_type;
1535 gint64 cur, stop, key_cur;
1538 gint64 original_stop;
1541 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1543 gst_event_parse_seek (event, &rate, &format, &flags,
1544 &cur_type, &cur, &stop_type, &stop);
1545 seqnum = gst_event_get_seqnum (event);
1547 /* only forward streaming and seeking is possible */
1549 goto unsupported_seek;
1551 /* convert to TIME if needed and possible */
1552 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1556 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1557 * the original stop position to use when upstream pushes the new segment
1559 original_stop = stop;
1562 /* find reasonable corresponding BYTE position,
1563 * also try to mind about keyframes, since we can not go back a bit for them
1565 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1566 * mostly just work, but let's not yet boldly go there ... */
1567 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1572 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1573 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1576 GST_OBJECT_LOCK (qtdemux);
1577 qtdemux->seek_offset = byte_cur;
1578 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1579 qtdemux->push_seek_start = cur;
1581 qtdemux->push_seek_start = key_cur;
1584 if (stop_type == GST_SEEK_TYPE_NONE) {
1585 qtdemux->push_seek_stop = qtdemux->segment.stop;
1587 qtdemux->push_seek_stop = original_stop;
1589 GST_OBJECT_UNLOCK (qtdemux);
1591 /* BYTE seek event */
1592 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1594 gst_event_set_seqnum (event, seqnum);
1595 res = gst_pad_push_event (qtdemux->sinkpad, event);
1602 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1608 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1613 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1618 /* perform the seek.
1620 * We set all segment_indexes in the streams to unknown and
1621 * adjust the time_position to the desired position. this is enough
1622 * to trigger a segment switch in the streaming thread to start
1623 * streaming from the desired position.
1625 * Keyframe seeking is a little more complicated when dealing with
1626 * segments. Ideally we want to move to the previous keyframe in
1627 * the segment but there might not be a keyframe in the segment. In
1628 * fact, none of the segments could contain a keyframe. We take a
1629 * practical approach: seek to the previous keyframe in the segment,
1630 * if there is none, seek to the beginning of the segment.
1632 * Called with STREAM_LOCK
1635 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1636 guint32 seqnum, GstSeekFlags flags)
1638 gint64 desired_offset;
1641 desired_offset = segment->position;
1643 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1644 GST_TIME_ARGS (desired_offset));
1646 /* may not have enough fragmented info to do this adjustment,
1647 * and we can't scan (and probably should not) at this time with
1648 * possibly flushing upstream */
1649 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1651 gboolean next, before, after;
1653 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1654 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1655 next = after && !before;
1656 if (segment->rate < 0)
1659 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1661 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1662 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1663 desired_offset = min_offset;
1666 /* and set all streams to the final position */
1667 gst_flow_combiner_reset (qtdemux->flowcombiner);
1668 qtdemux->segment_seqnum = seqnum;
1669 for (n = 0; n < qtdemux->n_streams; n++) {
1670 QtDemuxStream *stream = qtdemux->streams[n];
1672 stream->time_position = desired_offset;
1673 stream->accumulated_base = 0;
1674 stream->sample_index = -1;
1675 stream->offset_in_sample = 0;
1676 stream->segment_index = -1;
1677 stream->sent_eos = FALSE;
1679 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1680 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1682 segment->position = desired_offset;
1683 segment->time = desired_offset;
1684 if (segment->rate >= 0) {
1685 segment->start = desired_offset;
1687 /* we stop at the end */
1688 if (segment->stop == -1)
1689 segment->stop = segment->duration;
1691 segment->stop = desired_offset;
1694 if (qtdemux->fragmented)
1695 qtdemux->fragmented_seek_pending = TRUE;
1700 /* do a seek in pull based mode */
1702 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1707 GstSeekType cur_type, stop_type;
1711 GstSegment seeksegment;
1713 GstEvent *flush_event;
1717 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1719 gst_event_parse_seek (event, &rate, &format, &flags,
1720 &cur_type, &cur, &stop_type, &stop);
1721 seqnum = gst_event_get_seqnum (event);
1723 /* we have to have a format as the segment format. Try to convert
1725 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1729 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1731 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1735 flush = flags & GST_SEEK_FLAG_FLUSH;
1737 /* stop streaming, either by flushing or by pausing the task */
1739 flush_event = gst_event_new_flush_start ();
1741 gst_event_set_seqnum (flush_event, seqnum);
1742 /* unlock upstream pull_range */
1743 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1744 /* make sure out loop function exits */
1745 gst_qtdemux_push_event (qtdemux, flush_event);
1747 /* non flushing seek, pause the task */
1748 gst_pad_pause_task (qtdemux->sinkpad);
1751 /* wait for streaming to finish */
1752 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1754 /* copy segment, we need this because we still need the old
1755 * segment when we close the current segment. */
1756 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1759 /* configure the segment with the seek variables */
1760 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1761 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1762 cur_type, cur, stop_type, stop, &update)) {
1764 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1766 /* now do the seek */
1767 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1770 /* now do the seek */
1771 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1774 /* prepare for streaming again */
1776 flush_event = gst_event_new_flush_stop (TRUE);
1778 gst_event_set_seqnum (flush_event, seqnum);
1780 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1781 gst_qtdemux_push_event (qtdemux, flush_event);
1784 /* commit the new segment */
1785 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1787 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1788 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1789 qtdemux->segment.format, qtdemux->segment.position);
1791 gst_message_set_seqnum (msg, seqnum);
1792 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1795 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1796 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1797 qtdemux->sinkpad, NULL);
1799 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1806 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1812 qtdemux_ensure_index (GstQTDemux * qtdemux)
1816 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1818 /* Build complete index */
1819 for (i = 0; i < qtdemux->n_streams; i++) {
1820 QtDemuxStream *stream = qtdemux->streams[i];
1822 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1830 GST_LOG_OBJECT (qtdemux,
1831 "Building complete index of stream %u for seeking failed!", i);
1837 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1840 gboolean res = TRUE;
1841 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1843 switch (GST_EVENT_TYPE (event)) {
1844 case GST_EVENT_SEEK:
1846 #ifndef GST_DISABLE_GST_DEBUG
1847 GstClockTime ts = gst_util_get_timestamp ();
1849 guint32 seqnum = gst_event_get_seqnum (event);
1851 if (seqnum == qtdemux->segment_seqnum) {
1852 GST_LOG_OBJECT (pad,
1853 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1854 gst_event_unref (event);
1858 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1859 /* seek should be handled by upstream, we might need to re-download fragments */
1860 GST_DEBUG_OBJECT (qtdemux,
1861 "let upstream handle seek for fragmented playback");
1865 /* Build complete index for seeking;
1866 * if not a fragmented file at least */
1867 if (!qtdemux->fragmented)
1868 if (!qtdemux_ensure_index (qtdemux))
1870 #ifndef GST_DISABLE_GST_DEBUG
1871 ts = gst_util_get_timestamp () - ts;
1872 GST_INFO_OBJECT (qtdemux,
1873 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1876 if (qtdemux->pullbased) {
1877 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1878 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1879 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1881 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
1882 && !qtdemux->fragmented) {
1883 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1885 GST_DEBUG_OBJECT (qtdemux,
1886 "ignoring seek in push mode in current state");
1889 gst_event_unref (event);
1893 res = gst_pad_event_default (pad, parent, event);
1903 GST_ERROR_OBJECT (qtdemux, "Index failed");
1904 gst_event_unref (event);
1910 /* stream/index return sample that is min/max w.r.t. byte position,
1911 * time is min/max w.r.t. time of samples,
1912 * the latter need not be time of the former sample */
1914 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1915 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1918 gint64 time, min_time;
1919 QtDemuxStream *stream;
1925 for (n = 0; n < qtdemux->n_streams; ++n) {
1928 gboolean set_sample;
1930 str = qtdemux->streams[n];
1937 i = str->n_samples - 1;
1941 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1942 if (str->samples[i].size == 0)
1945 if (fw && (str->samples[i].offset < byte_pos))
1948 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1951 /* move stream to first available sample */
1953 gst_qtdemux_move_stream (qtdemux, str, i);
1957 /* avoid index from sparse streams since they might be far away */
1958 if (!CUR_STREAM (str)->sparse) {
1959 /* determine min/max time */
1960 time = QTSAMPLE_PTS (str, &str->samples[i]);
1961 if (min_time == -1 || (!fw && time > min_time) ||
1962 (fw && time < min_time)) {
1966 /* determine stream with leading sample, to get its position */
1968 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1969 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1977 /* no sample for this stream, mark eos */
1979 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1990 static QtDemuxStream *
1991 _create_stream (void)
1993 QtDemuxStream *stream;
1995 stream = g_new0 (QtDemuxStream, 1);
1996 /* new streams always need a discont */
1997 stream->discont = TRUE;
1998 /* we enable clipping for raw audio/video streams */
1999 stream->need_clip = FALSE;
2000 stream->need_process = FALSE;
2001 stream->segment_index = -1;
2002 stream->time_position = 0;
2003 stream->sample_index = -1;
2004 stream->offset_in_sample = 0;
2005 stream->new_stream = TRUE;
2006 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
2007 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2008 stream->protected = FALSE;
2009 stream->protection_scheme_type = 0;
2010 stream->protection_scheme_version = 0;
2011 stream->protection_scheme_info = NULL;
2012 stream->n_samples_moof = 0;
2013 stream->duration_moof = 0;
2014 stream->duration_last_moof = 0;
2015 stream->alignment = 1;
2016 stream->stream_tags = gst_tag_list_new_empty ();
2017 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2018 g_queue_init (&stream->protection_scheme_event_queue);
2023 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
2025 GstStructure *structure;
2026 const gchar *variant;
2027 const GstCaps *mediacaps = NULL;
2029 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
2031 structure = gst_caps_get_structure (caps, 0);
2032 variant = gst_structure_get_string (structure, "variant");
2034 if (variant && strcmp (variant, "mss-fragmented") == 0) {
2035 QtDemuxStream *stream;
2036 const GValue *value;
2038 demux->fragmented = TRUE;
2039 demux->mss_mode = TRUE;
2041 if (demux->n_streams > 1) {
2042 /* can't do this, we can only renegotiate for another mss format */
2046 value = gst_structure_get_value (structure, "media-caps");
2049 const GValue *timescale_v;
2051 /* TODO update when stream changes during playback */
2053 if (demux->n_streams == 0) {
2054 stream = _create_stream ();
2055 demux->streams[demux->n_streams] = stream;
2056 demux->n_streams = 1;
2057 /* mss has no stsd/stsd entry, use id 0 as default */
2058 stream->stsd_entries_length = 1;
2059 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
2060 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
2062 stream = demux->streams[0];
2065 timescale_v = gst_structure_get_value (structure, "timescale");
2067 stream->timescale = g_value_get_uint64 (timescale_v);
2069 /* default mss timescale */
2070 stream->timescale = 10000000;
2072 demux->timescale = stream->timescale;
2074 mediacaps = gst_value_get_caps (value);
2075 if (!CUR_STREAM (stream)->caps
2076 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
2077 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
2079 stream->new_caps = TRUE;
2081 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
2082 structure = gst_caps_get_structure (mediacaps, 0);
2083 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
2084 stream->subtype = FOURCC_vide;
2086 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
2087 gst_structure_get_int (structure, "height",
2088 &CUR_STREAM (stream)->height);
2089 gst_structure_get_fraction (structure, "framerate",
2090 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
2091 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
2093 stream->subtype = FOURCC_soun;
2094 gst_structure_get_int (structure, "channels",
2095 &CUR_STREAM (stream)->n_channels);
2096 gst_structure_get_int (structure, "rate", &rate);
2097 CUR_STREAM (stream)->rate = rate;
2100 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
2102 demux->mss_mode = FALSE;
2109 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
2113 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2114 gst_pad_stop_task (qtdemux->sinkpad);
2116 if (hard || qtdemux->upstream_format_is_time) {
2117 qtdemux->state = QTDEMUX_STATE_INITIAL;
2118 qtdemux->neededbytes = 16;
2119 qtdemux->todrop = 0;
2120 qtdemux->pullbased = FALSE;
2121 qtdemux->posted_redirect = FALSE;
2122 qtdemux->first_mdat = -1;
2123 qtdemux->header_size = 0;
2124 qtdemux->mdatoffset = -1;
2125 qtdemux->restoredata_offset = -1;
2126 if (qtdemux->mdatbuffer)
2127 gst_buffer_unref (qtdemux->mdatbuffer);
2128 if (qtdemux->restoredata_buffer)
2129 gst_buffer_unref (qtdemux->restoredata_buffer);
2130 qtdemux->mdatbuffer = NULL;
2131 qtdemux->restoredata_buffer = NULL;
2132 qtdemux->mdatleft = 0;
2133 qtdemux->mdatsize = 0;
2134 if (qtdemux->comp_brands)
2135 gst_buffer_unref (qtdemux->comp_brands);
2136 qtdemux->comp_brands = NULL;
2137 qtdemux->last_moov_offset = -1;
2138 if (qtdemux->moov_node_compressed) {
2139 g_node_destroy (qtdemux->moov_node_compressed);
2140 if (qtdemux->moov_node)
2141 g_free (qtdemux->moov_node->data);
2143 qtdemux->moov_node_compressed = NULL;
2144 if (qtdemux->moov_node)
2145 g_node_destroy (qtdemux->moov_node);
2146 qtdemux->moov_node = NULL;
2147 if (qtdemux->tag_list)
2148 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2149 qtdemux->tag_list = gst_tag_list_new_empty ();
2150 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2152 if (qtdemux->element_index)
2153 gst_object_unref (qtdemux->element_index);
2154 qtdemux->element_index = NULL;
2156 qtdemux->major_brand = 0;
2157 if (qtdemux->pending_newsegment)
2158 gst_event_unref (qtdemux->pending_newsegment);
2159 qtdemux->pending_newsegment = NULL;
2160 qtdemux->upstream_format_is_time = FALSE;
2161 qtdemux->upstream_seekable = FALSE;
2162 qtdemux->upstream_size = 0;
2164 qtdemux->fragment_start = -1;
2165 qtdemux->fragment_start_offset = -1;
2166 qtdemux->duration = 0;
2167 qtdemux->moof_offset = 0;
2168 qtdemux->chapters_track_id = 0;
2169 qtdemux->have_group_id = FALSE;
2170 qtdemux->group_id = G_MAXUINT;
2172 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2174 g_queue_clear (&qtdemux->protection_event_queue);
2176 qtdemux->offset = 0;
2177 gst_adapter_clear (qtdemux->adapter);
2178 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2179 qtdemux->segment_seqnum = 0;
2182 for (n = 0; n < qtdemux->n_streams; n++) {
2183 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2184 qtdemux->streams[n] = NULL;
2186 qtdemux->n_streams = 0;
2187 qtdemux->n_video_streams = 0;
2188 qtdemux->n_audio_streams = 0;
2189 qtdemux->n_sub_streams = 0;
2190 qtdemux->exposed = FALSE;
2191 qtdemux->fragmented = FALSE;
2192 qtdemux->mss_mode = FALSE;
2193 gst_caps_replace (&qtdemux->media_caps, NULL);
2194 qtdemux->timescale = 0;
2195 qtdemux->got_moov = FALSE;
2196 if (qtdemux->protection_system_ids) {
2197 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2198 qtdemux->protection_system_ids = NULL;
2200 } else if (qtdemux->mss_mode) {
2201 gst_flow_combiner_reset (qtdemux->flowcombiner);
2202 for (n = 0; n < qtdemux->n_streams; n++)
2203 gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]);
2205 gst_flow_combiner_reset (qtdemux->flowcombiner);
2206 for (n = 0; n < qtdemux->n_streams; n++) {
2207 qtdemux->streams[n]->sent_eos = FALSE;
2208 qtdemux->streams[n]->time_position = 0;
2209 qtdemux->streams[n]->accumulated_base = 0;
2211 if (!qtdemux->pending_newsegment) {
2212 qtdemux->pending_newsegment = gst_event_new_segment (&qtdemux->segment);
2213 if (qtdemux->segment_seqnum)
2214 gst_event_set_seqnum (qtdemux->pending_newsegment,
2215 qtdemux->segment_seqnum);
2221 /* Maps the @segment to the qt edts internal segments and pushes
2222 * the correspnding segment event.
2224 * If it ends up being at a empty segment, a gap will be pushed and the next
2225 * edts segment will be activated in sequence.
2227 * To be used in push-mode only */
2229 gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2233 for (n = 0; n < qtdemux->n_streams; n++) {
2234 QtDemuxStream *stream = qtdemux->streams[n];
2236 stream->time_position = segment->start;
2238 /* in push mode we should be guaranteed that we will have empty segments
2239 * at the beginning and then one segment after, other scenarios are not
2240 * supported and are discarded when parsing the edts */
2241 for (i = 0; i < stream->n_segments; i++) {
2242 if (stream->segments[i].stop_time > segment->start) {
2243 gst_qtdemux_activate_segment (qtdemux, stream, i,
2244 stream->time_position);
2245 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2246 /* push the empty segment and move to the next one */
2247 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2248 stream->time_position);
2252 g_assert (i == stream->n_segments - 1);
2259 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2262 GstQTDemux *demux = GST_QTDEMUX (parent);
2263 gboolean res = TRUE;
2265 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2267 switch (GST_EVENT_TYPE (event)) {
2268 case GST_EVENT_SEGMENT:
2271 QtDemuxStream *stream;
2275 /* some debug output */
2276 gst_event_copy_segment (event, &segment);
2277 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2280 /* erase any previously set segment */
2281 gst_event_replace (&demux->pending_newsegment, NULL);
2283 if (segment.format == GST_FORMAT_TIME) {
2284 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
2285 gst_event_replace (&demux->pending_newsegment, event);
2286 demux->upstream_format_is_time = TRUE;
2288 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2289 "not in time format");
2291 /* chain will send initial newsegment after pads have been added */
2292 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
2293 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2298 /* check if this matches a time seek we received previously
2299 * FIXME for backwards compatibility reasons we use the
2300 * seek_offset here to compare. In the future we might want to
2301 * change this to use the seqnum as it uniquely should identify
2302 * the segment that corresponds to the seek. */
2303 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2304 ", received segment offset %" G_GINT64_FORMAT,
2305 demux->seek_offset, segment.start);
2306 if (segment.format == GST_FORMAT_BYTES
2307 && demux->seek_offset == segment.start) {
2308 GST_OBJECT_LOCK (demux);
2309 offset = segment.start;
2311 segment.format = GST_FORMAT_TIME;
2312 segment.start = demux->push_seek_start;
2313 segment.stop = demux->push_seek_stop;
2314 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2315 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2316 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2317 GST_OBJECT_UNLOCK (demux);
2320 /* we only expect a BYTE segment, e.g. following a seek */
2321 if (segment.format == GST_FORMAT_BYTES) {
2322 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2323 offset = segment.start;
2325 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2326 NULL, (gint64 *) & segment.start);
2327 if ((gint64) segment.start < 0)
2330 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2331 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2332 NULL, (gint64 *) & segment.stop);
2333 /* keyframe seeking should already arrange for start >= stop,
2334 * but make sure in other rare cases */
2335 segment.stop = MAX (segment.stop, segment.start);
2337 } else if (segment.format == GST_FORMAT_TIME) {
2338 /* push all data on the adapter before starting this
2340 gst_qtdemux_process_adapter (demux, TRUE);
2342 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2346 /* We shouldn't modify upstream driven TIME FORMAT segment */
2347 if (!demux->upstream_format_is_time) {
2348 /* accept upstream's notion of segment and distribute along */
2349 segment.format = GST_FORMAT_TIME;
2350 segment.position = segment.time = segment.start;
2351 segment.duration = demux->segment.duration;
2352 segment.base = gst_segment_to_running_time (&demux->segment,
2353 GST_FORMAT_TIME, demux->segment.position);
2356 gst_segment_copy_into (&segment, &demux->segment);
2357 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2359 /* map segment to internal qt segments and push on each stream */
2360 if (demux->n_streams) {
2361 if (demux->fragmented) {
2362 GstEvent *segment_event = gst_event_new_segment (&segment);
2364 gst_event_replace (&demux->pending_newsegment, NULL);
2365 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
2366 gst_qtdemux_push_event (demux, segment_event);
2368 gst_event_replace (&demux->pending_newsegment, NULL);
2369 gst_qtdemux_map_and_push_segments (demux, &segment);
2373 /* clear leftover in current segment, if any */
2374 gst_adapter_clear (demux->adapter);
2376 /* set up streaming thread */
2377 demux->offset = offset;
2378 if (demux->upstream_format_is_time) {
2379 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2380 "set values to restart reading from a new atom");
2381 demux->neededbytes = 16;
2384 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2387 demux->todrop = stream->samples[idx].offset - offset;
2388 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2390 /* set up for EOS */
2391 demux->neededbytes = -1;
2396 gst_event_unref (event);
2400 case GST_EVENT_FLUSH_START:
2402 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2403 gst_event_unref (event);
2408 case GST_EVENT_FLUSH_STOP:
2412 dur = demux->segment.duration;
2413 gst_qtdemux_reset (demux, FALSE);
2414 demux->segment.duration = dur;
2416 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2417 gst_event_unref (event);
2423 /* If we are in push mode, and get an EOS before we've seen any streams,
2424 * then error out - we have nowhere to send the EOS */
2425 if (!demux->pullbased) {
2427 gboolean has_valid_stream = FALSE;
2428 for (i = 0; i < demux->n_streams; i++) {
2429 if (demux->streams[i]->pad != NULL) {
2430 has_valid_stream = TRUE;
2434 if (!has_valid_stream)
2435 gst_qtdemux_post_no_playable_stream_error (demux);
2437 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2438 (guint) gst_adapter_available (demux->adapter));
2439 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2445 case GST_EVENT_CAPS:{
2446 GstCaps *caps = NULL;
2448 gst_event_parse_caps (event, &caps);
2449 gst_qtdemux_setcaps (demux, caps);
2451 gst_event_unref (event);
2454 case GST_EVENT_PROTECTION:
2456 const gchar *system_id = NULL;
2458 gst_event_parse_protection (event, &system_id, NULL, NULL);
2459 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2461 gst_qtdemux_append_protection_system_id (demux, system_id);
2462 /* save the event for later, for source pads that have not been created */
2463 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2464 /* send it to all pads that already exist */
2465 gst_qtdemux_push_event (demux, event);
2473 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2481 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2483 GstQTDemux *demux = GST_QTDEMUX (element);
2485 GST_OBJECT_LOCK (demux);
2486 if (demux->element_index)
2487 gst_object_unref (demux->element_index);
2489 demux->element_index = gst_object_ref (index);
2491 demux->element_index = NULL;
2493 GST_OBJECT_UNLOCK (demux);
2494 /* object lock might be taken again */
2496 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2497 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2498 demux->element_index, demux->index_id);
2502 gst_qtdemux_get_index (GstElement * element)
2504 GstIndex *result = NULL;
2505 GstQTDemux *demux = GST_QTDEMUX (element);
2507 GST_OBJECT_LOCK (demux);
2508 if (demux->element_index)
2509 result = gst_object_ref (demux->element_index);
2510 GST_OBJECT_UNLOCK (demux);
2512 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2519 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2521 g_free ((gpointer) stream->stco.data);
2522 stream->stco.data = NULL;
2523 g_free ((gpointer) stream->stsz.data);
2524 stream->stsz.data = NULL;
2525 g_free ((gpointer) stream->stsc.data);
2526 stream->stsc.data = NULL;
2527 g_free ((gpointer) stream->stts.data);
2528 stream->stts.data = NULL;
2529 g_free ((gpointer) stream->stss.data);
2530 stream->stss.data = NULL;
2531 g_free ((gpointer) stream->stps.data);
2532 stream->stps.data = NULL;
2533 g_free ((gpointer) stream->ctts.data);
2534 stream->ctts.data = NULL;
2538 gst_qtdemux_stream_flush_segments_data (GstQTDemux * qtdemux,
2539 QtDemuxStream * stream)
2541 g_free (stream->segments);
2542 stream->segments = NULL;
2543 stream->segment_index = -1;
2544 stream->accumulated_base = 0;
2548 gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux,
2549 QtDemuxStream * stream)
2551 g_free (stream->samples);
2552 stream->samples = NULL;
2553 gst_qtdemux_stbl_free (stream);
2556 g_free (stream->ra_entries);
2557 stream->ra_entries = NULL;
2558 stream->n_ra_entries = 0;
2560 stream->sample_index = -1;
2561 stream->stbl_index = -1;
2562 stream->n_samples = 0;
2563 stream->time_position = 0;
2565 stream->n_samples_moof = 0;
2566 stream->duration_moof = 0;
2567 stream->duration_last_moof = 0;
2571 gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream)
2574 if (stream->allocator)
2575 gst_object_unref (stream->allocator);
2576 while (stream->buffers) {
2577 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2578 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2580 for (i = 0; i < stream->stsd_entries_length; i++) {
2581 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2582 if (entry->rgb8_palette) {
2583 gst_memory_unref (entry->rgb8_palette);
2584 entry->rgb8_palette = NULL;
2586 entry->sparse = FALSE;
2589 gst_tag_list_unref (stream->stream_tags);
2590 stream->stream_tags = gst_tag_list_new_empty ();
2591 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2592 g_free (stream->redirect_uri);
2593 stream->redirect_uri = NULL;
2594 stream->sent_eos = FALSE;
2595 stream->protected = FALSE;
2596 if (stream->protection_scheme_info) {
2597 if (stream->protection_scheme_type == FOURCC_cenc) {
2598 QtDemuxCencSampleSetInfo *info =
2599 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2600 if (info->default_properties)
2601 gst_structure_free (info->default_properties);
2602 if (info->crypto_info)
2603 g_ptr_array_free (info->crypto_info, TRUE);
2605 g_free (stream->protection_scheme_info);
2606 stream->protection_scheme_info = NULL;
2608 stream->protection_scheme_type = 0;
2609 stream->protection_scheme_version = 0;
2610 g_queue_foreach (&stream->protection_scheme_event_queue,
2611 (GFunc) gst_event_unref, NULL);
2612 g_queue_clear (&stream->protection_scheme_event_queue);
2613 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
2614 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
2618 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
2621 gst_qtdemux_stream_clear (qtdemux, stream);
2622 for (i = 0; i < stream->stsd_entries_length; i++) {
2623 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2625 gst_caps_unref (entry->caps);
2629 gst_tag_list_unref (stream->stream_tags);
2631 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
2632 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
2634 g_free (stream->stsd_entries);
2639 gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int i)
2641 g_assert (i >= 0 && i < qtdemux->n_streams && qtdemux->streams[i] != NULL);
2643 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[i]);
2644 qtdemux->streams[i] = qtdemux->streams[qtdemux->n_streams - 1];
2645 qtdemux->streams[qtdemux->n_streams - 1] = NULL;
2646 qtdemux->n_streams--;
2649 static GstStateChangeReturn
2650 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2652 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2653 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2655 switch (transition) {
2656 case GST_STATE_CHANGE_PAUSED_TO_READY:
2662 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2664 switch (transition) {
2665 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2666 gst_qtdemux_reset (qtdemux, TRUE);
2677 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2679 /* counts as header data */
2680 qtdemux->header_size += length;
2682 /* only consider at least a sufficiently complete ftyp atom */
2686 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2687 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2688 GST_FOURCC_ARGS (qtdemux->major_brand));
2689 if (qtdemux->comp_brands)
2690 gst_buffer_unref (qtdemux->comp_brands);
2691 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2692 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2697 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2698 GstTagList * xmptaglist)
2700 /* Strip out bogus fields */
2702 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2703 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2704 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2706 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2709 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2711 /* prioritize native tags using _KEEP mode */
2712 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2713 gst_tag_list_unref (xmptaglist);
2717 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2719 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2720 const char *xml_str, const char *param_name, int *value)
2722 char *value_start, *value_end, *endptr;
2723 const short value_length_max = 12;
2724 char init_view_ret[12];
2725 int value_length = 0;
2728 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2731 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2736 value_start += strlen (param_name);
2737 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2740 value_end = strchr (value_start, '<');
2742 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2746 value_length = value_end - value_start;
2747 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2748 || (value_start[value_length - 1] == '\t')))
2751 if (value_start[i] == '+' || value_start[i] == '-')
2753 while (i < value_length) {
2754 if (value_start[i] < '0' || value_start[i] > '9') {
2755 GST_ERROR_OBJECT (qtdemux,
2756 "error: incorrect value, integer was expected\n");
2762 if (value_length >= value_length_max || value_length < 1) {
2763 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2767 strncpy (init_view_ret, value_start, value_length_max);
2768 init_view_ret[value_length] = '\0';
2770 *value = strtol (init_view_ret, &endptr, 10);
2771 if (endptr == init_view_ret) {
2772 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2780 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2781 const char *xml_str, const char *param_name, char **value)
2783 char *value_start, *value_end;
2784 const short value_length_max = 256;
2785 int value_length = 0;
2787 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2790 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2795 value_start += strlen (param_name);
2796 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2799 value_end = strchr (value_start, '<');
2801 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2805 value_length = value_end - value_start;
2806 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2807 || (value_start[value_length - 1] == '\t')))
2810 if (value_length >= value_length_max || value_length < 1) {
2811 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2815 *value = strndup(value_start, value_length);
2821 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2822 const char *xml_str, const char *param_name, gboolean * value)
2824 char *value_start, *value_end;
2825 int value_length = 0;
2827 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2830 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2835 value_start += strlen (param_name);
2836 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2839 value_end = strchr (value_start, '<');
2841 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2845 value_length = value_end - value_start;
2846 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2847 || (value_start[value_length - 1] == '\t')))
2850 if (value_length < 1) {
2851 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2855 *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
2861 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
2863 const char is_spherical_str[] = "<GSpherical:Spherical>";
2864 const char is_stitched_str[] = "<GSpherical:Stitched>";
2865 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2866 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2867 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2868 const char source_count_str[] = "<GSpherical:SourceCount>";
2869 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2870 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2871 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2872 const char timestamp_str[] = "<GSpherical:Timestamp>";
2873 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2874 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2875 const char cropped_area_image_width_str[] =
2876 "<GSpherical:CroppedAreaImageWidthPixels>";
2877 const char cropped_area_image_height_str[] =
2878 "<GSpherical:CroppedAreaImageHeightPixels>";
2879 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
2880 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
2882 QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
2884 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
2885 (gboolean *) & spherical_metadata->is_spherical);
2886 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
2887 (gboolean *) & spherical_metadata->is_stitched);
2889 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
2890 _get_string_value_from_xml_string (qtdemux, xmlStr,
2891 stitching_software_str, &spherical_metadata->stitching_software);
2892 _get_string_value_from_xml_string (qtdemux, xmlStr,
2893 projection_type_str, &spherical_metadata->projection_type);
2894 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
2895 &spherical_metadata->stereo_mode);
2896 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
2897 &spherical_metadata->source_count);
2898 _get_int_value_from_xml_string (qtdemux, xmlStr,
2899 init_view_heading_str, &spherical_metadata->init_view_heading);
2900 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
2901 &spherical_metadata->init_view_pitch);
2902 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
2903 &spherical_metadata->init_view_roll);
2904 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
2905 &spherical_metadata->timestamp);
2906 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
2907 &spherical_metadata->full_pano_width_pixels);
2908 _get_int_value_from_xml_string (qtdemux, xmlStr,
2909 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
2910 _get_int_value_from_xml_string (qtdemux, xmlStr,
2911 cropped_area_image_width_str,
2912 &spherical_metadata->cropped_area_image_width);
2913 _get_int_value_from_xml_string (qtdemux, xmlStr,
2914 cropped_area_image_height_str,
2915 &spherical_metadata->cropped_area_image_height);
2916 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
2917 &spherical_metadata->cropped_area_left);
2918 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
2919 &spherical_metadata->cropped_area_top);
2926 gst_tag_register_spherical_tags (void) {
2927 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
2930 _("Flag indicating if the video is a spherical video"),
2932 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
2935 _("Flag indicating if the video is stitched"),
2937 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
2939 _("tag-stitching-software"),
2940 _("Software used to stitch the spherical video"),
2942 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
2944 _("tag-projection-type"),
2945 _("Projection type used in the video frames"),
2947 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
2949 _("tag-stereo-mode"),
2950 _("Description of stereoscopic 3D layout"),
2952 gst_tag_register ("source_count", GST_TAG_FLAG_META,
2954 _("tag-source-count"),
2955 _("Number of cameras used to create the spherical video"),
2957 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
2959 _("tag-init-view-heading"),
2960 _("The heading angle of the initial view in degrees"),
2962 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
2964 _("tag-init-view-pitch"),
2965 _("The pitch angle of the initial view in degrees"),
2967 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
2969 _("tag-init-view-roll"),
2970 _("The roll angle of the initial view in degrees"),
2972 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
2975 _("Epoch timestamp of when the first frame in the video was recorded"),
2977 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
2979 _("tag-full-pano-width"),
2980 _("Width of the encoded video frame in pixels"),
2982 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
2984 _("tag-full-pano-height"),
2985 _("Height of the encoded video frame in pixels"),
2987 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
2989 _("tag-cropped-area-image-width"),
2990 _("Width of the video frame to display (e.g. cropping)"),
2992 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
2994 _("tag-cropped-area-image-height"),
2995 _("Height of the video frame to display (e.g. cropping)"),
2997 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
2999 _("tag-cropped-area-left"),
3000 _("Column where the left edge of the image was cropped from the"
3001 " full sized panorama"),
3003 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
3005 _("tag-cropped-area-top"),
3006 _("Row where the top edge of the image was cropped from the"
3007 " full sized panorama"),
3009 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
3011 _("tag-ambisonic-type"),
3012 _("Specifies the type of ambisonic audio represented"),
3014 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
3016 _("tag-ambisonic-format"),
3017 _("Specifies the ambisonic audio format"),
3019 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
3021 _("tag-ambisonic-order"),
3022 _("Specifies the ambisonic audio channel order"),
3029 _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
3031 GstTagList *taglist;
3032 QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
3034 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
3035 spherical_metadata->is_spherical);
3036 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
3037 spherical_metadata->is_stitched);
3038 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
3039 spherical_metadata->stitching_software);
3040 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
3041 spherical_metadata->projection_type);
3042 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
3043 spherical_metadata->stereo_mode);
3044 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
3045 spherical_metadata->source_count);
3046 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
3047 spherical_metadata->init_view_heading);
3048 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
3049 spherical_metadata->init_view_pitch);
3050 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
3051 spherical_metadata->init_view_roll);
3052 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
3053 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
3054 spherical_metadata->full_pano_width_pixels);
3055 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
3056 spherical_metadata->full_pano_height_pixels);
3057 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
3058 spherical_metadata->cropped_area_image_width);
3059 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
3060 spherical_metadata->cropped_area_image_height);
3061 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
3062 spherical_metadata->cropped_area_left);
3063 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
3064 spherical_metadata->cropped_area_top);
3065 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
3066 spherical_metadata->ambisonic_type);
3067 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
3068 spherical_metadata->ambisonic_order);
3069 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
3070 spherical_metadata->ambisonic_format);
3072 taglist = gst_tag_list_new_empty ();
3073 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3074 "is_spherical", spherical_metadata->is_spherical,
3075 "is_stitched", spherical_metadata->is_stitched,
3076 "source_count", spherical_metadata->source_count,
3077 "init_view_heading", spherical_metadata->init_view_heading,
3078 "init_view_pitch", spherical_metadata->init_view_pitch,
3079 "init_view_roll", spherical_metadata->init_view_roll,
3080 "timestamp", spherical_metadata->timestamp,
3081 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
3082 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
3083 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
3084 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
3085 "cropped_area_left", spherical_metadata->cropped_area_left,
3086 "cropped_area_top", spherical_metadata->cropped_area_top,
3087 "ambisonic_type", spherical_metadata->ambisonic_type,
3088 "ambisonic_format", spherical_metadata->ambisonic_format,
3089 "ambisonic_order", spherical_metadata->ambisonic_order,
3092 if (spherical_metadata->stitching_software)
3093 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3094 "stitching_software", spherical_metadata->stitching_software,
3096 if (spherical_metadata->projection_type)
3097 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3098 "projection_type", spherical_metadata->projection_type,
3100 if (spherical_metadata->stereo_mode)
3101 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
3102 "stereo_mode", spherical_metadata->stereo_mode,
3105 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3106 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
3107 gst_tag_list_copy (taglist)));
3109 gst_tag_list_unref(taglist);
3115 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3120 guint8 ambisonic_type = 0;
3121 guint32 ambisonic_order = 0;
3122 guint8 ambisonic_channel_ordering = 0;
3123 guint8 ambisonic_normalization = 0;
3124 guint32 num_channels = 0;
3125 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
3129 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
3131 qtdemux->header_size += length;
3132 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3134 if (length <= offset + 16) {
3135 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
3139 version = QT_UINT8 (buffer + offset);
3140 ambisonic_type = QT_UINT8 (buffer + offset + 1);
3141 ambisonic_order = QT_UINT32 (buffer + offset + 2);
3142 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
3143 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
3144 num_channels = QT_UINT32 (buffer + offset + 8);
3145 for (i = 0; i < num_channels; ++i)
3146 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
3148 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
3149 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
3150 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
3151 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
3152 ambisonic_channel_ordering);
3153 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
3154 ambisonic_normalization);
3155 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
3156 for (i = 0; i < num_channels; ++i)
3157 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
3159 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
3160 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
3161 qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
3163 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
3164 if (num_channels == 4) {
3165 qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
3167 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
3168 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
3169 && (channel_map[0] == 0) && (channel_map[1] == 1)
3170 && (channel_map[2] == 2) && (channel_map[3] == 3))
3171 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
3173 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
3174 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
3175 && (channel_map[0] == 0) && (channel_map[1] == 3)
3176 && (channel_map[2] == 1) && (channel_map[3] == 2))
3177 qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
3184 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3187 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
3195 QtDemuxStream *stream;
3196 GstStructure *structure;
3197 QtDemuxCencSampleSetInfo *ss_info = NULL;
3198 const gchar *system_id;
3199 gboolean uses_sub_sample_encryption = FALSE;
3201 if (qtdemux->n_streams == 0)
3204 stream = qtdemux->streams[0];
3206 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
3207 if (!gst_structure_has_name (structure, "application/x-cenc")) {
3208 GST_WARNING_OBJECT (qtdemux,
3209 "Attempting PIFF box parsing on an unencrypted stream.");
3213 gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
3214 G_TYPE_STRING, &system_id, NULL);
3215 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
3217 stream->protected = TRUE;
3218 stream->protection_scheme_type = FOURCC_cenc;
3220 if (!stream->protection_scheme_info)
3221 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
3223 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3225 if (ss_info->default_properties)
3226 gst_structure_free (ss_info->default_properties);
3228 ss_info->default_properties =
3229 gst_structure_new ("application/x-cenc",
3230 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE, NULL);
3232 if (ss_info->crypto_info) {
3233 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3234 g_ptr_array_free (ss_info->crypto_info, TRUE);
3235 ss_info->crypto_info = NULL;
3239 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
3241 if (!gst_byte_reader_get_uint8 (&br, &version)) {
3242 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
3246 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
3247 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
3251 if ((flags & 0x000001)) {
3252 guint32 algorithm_id = 0;
3255 gboolean is_encrypted = TRUE;
3257 if (!gst_byte_reader_get_uint24_le (&br, &algorithm_id)) {
3258 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
3263 if (algorithm_id == 0) {
3264 is_encrypted = FALSE;
3265 } else if (algorithm_id == 1) {
3266 /* FIXME: maybe store this in properties? */
3267 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
3268 } else if (algorithm_id == 2) {
3269 /* FIXME: maybe store this in properties? */
3270 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
3273 if (!gst_byte_reader_get_uint8 (&br, &iv_size))
3276 if (!gst_byte_reader_get_data (&br, 16, &kid))
3279 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
3280 gst_buffer_fill (kid_buf, 0, kid, 16);
3281 if (ss_info->default_properties)
3282 gst_structure_free (ss_info->default_properties);
3283 ss_info->default_properties =
3284 gst_structure_new ("application/x-cenc",
3285 "iv_size", G_TYPE_UINT, iv_size,
3286 "encrypted", G_TYPE_BOOLEAN, is_encrypted,
3287 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
3288 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
3289 "is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
3290 gst_buffer_unref (kid_buf);
3291 } else if ((flags & 0x000002)) {
3292 uses_sub_sample_encryption = TRUE;
3295 if (!gst_byte_reader_get_uint32_be (&br, &qtdemux->cenc_aux_sample_count)) {
3296 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
3300 ss_info->crypto_info =
3301 g_ptr_array_new_full (qtdemux->cenc_aux_sample_count,
3302 (GDestroyNotify) qtdemux_gst_structure_free);
3304 for (i = 0; i < qtdemux->cenc_aux_sample_count; ++i) {
3305 GstStructure *properties;
3309 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3310 if (properties == NULL) {
3311 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3315 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
3316 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
3317 gst_structure_free (properties);
3320 buf = gst_buffer_new_wrapped (data, iv_size);
3321 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3322 gst_buffer_unref (buf);
3324 if (uses_sub_sample_encryption) {
3325 guint16 n_subsamples;
3327 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
3328 || n_subsamples == 0) {
3329 GST_ERROR_OBJECT (qtdemux,
3330 "failed to get subsample count for sample %u", i);
3331 gst_structure_free (properties);
3334 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3335 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
3336 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3338 gst_structure_free (properties);
3341 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3342 gst_structure_set (properties,
3343 "subsample_count", G_TYPE_UINT, n_subsamples,
3344 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3345 gst_buffer_unref (buf);
3347 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3350 g_ptr_array_add (ss_info->crypto_info, properties);
3355 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3357 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
3358 0x97, 0xA9, 0x42, 0xE8,
3359 0x9C, 0x71, 0x99, 0x94,
3360 0x91, 0xE3, 0xAF, 0xAC
3362 static const guint8 playready_uuid[] = {
3363 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
3364 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
3367 static const guint8 piff_sample_encryption_uuid[] = {
3368 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
3369 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
3372 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3373 static const guint8 spherical_uuid[] = {
3374 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
3375 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
3377 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3381 /* counts as header data */
3382 qtdemux->header_size += length;
3384 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
3386 if (length <= offset + 16) {
3387 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3391 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
3392 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
3393 const char *contents;
3395 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
3396 contents = (char *) (buffer + offset + 16);
3397 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
3399 if (qtdemux->spherical_metadata)
3400 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
3404 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
3406 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3408 GstTagList *taglist;
3410 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
3411 length - offset - 16, NULL);
3412 taglist = gst_tag_list_from_xmp_buffer (buf);
3413 gst_buffer_unref (buf);
3415 /* make sure we have a usable taglist */
3416 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3418 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3420 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3422 const gunichar2 *s_utf16;
3425 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3426 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3427 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3428 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3432 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3433 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3435 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
3436 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3438 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3439 GST_READ_UINT32_LE (buffer + offset),
3440 GST_READ_UINT32_LE (buffer + offset + 4),
3441 GST_READ_UINT32_LE (buffer + offset + 8),
3442 GST_READ_UINT32_LE (buffer + offset + 12));
3447 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3449 GstSidxParser sidx_parser;
3450 GstIsoffParserResult res;
3453 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3456 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3458 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3459 if (res == GST_ISOFF_QT_PARSER_DONE) {
3460 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3462 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3465 /* caller verifies at least 8 bytes in buf */
3467 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3468 guint64 * plength, guint32 * pfourcc)
3473 length = QT_UINT32 (data);
3474 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3475 fourcc = QT_FOURCC (data + 4);
3476 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3479 length = G_MAXUINT64;
3480 } else if (length == 1 && size >= 16) {
3481 /* this means we have an extended size, which is the 64 bit value of
3482 * the next 8 bytes */
3483 length = QT_UINT64 (data + 8);
3484 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3494 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3496 guint32 version = 0;
3497 GstClockTime duration = 0;
3499 if (!gst_byte_reader_get_uint32_be (br, &version))
3504 if (!gst_byte_reader_get_uint64_be (br, &duration))
3509 if (!gst_byte_reader_get_uint32_be (br, &dur))
3514 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3515 qtdemux->duration = duration;
3521 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3527 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3528 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3530 if (!stream->parsed_trex && qtdemux->moov_node) {
3532 GstByteReader trex_data;
3534 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3536 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3539 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3541 /* skip version/flags */
3542 if (!gst_byte_reader_skip (&trex_data, 4))
3544 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3546 if (id != stream->track_id)
3548 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3550 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3552 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3554 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3557 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3558 "duration %d, size %d, flags 0x%x", stream->track_id,
3561 stream->parsed_trex = TRUE;
3562 stream->def_sample_description_index = sdi;
3563 stream->def_sample_duration = dur;
3564 stream->def_sample_size = size;
3565 stream->def_sample_flags = flags;
3568 /* iterate all siblings */
3569 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3575 *ds_duration = stream->def_sample_duration;
3576 *ds_size = stream->def_sample_size;
3577 *ds_flags = stream->def_sample_flags;
3579 /* even then, above values are better than random ... */
3580 if (G_UNLIKELY (!stream->parsed_trex)) {
3581 GST_WARNING_OBJECT (qtdemux,
3582 "failed to find fragment defaults for stream %d", stream->track_id);
3589 /* This method should be called whenever a more accurate duration might
3590 * have been found. It will update all relevant variables if/where needed
3593 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3597 GstClockTime prevdur;
3599 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3601 if (movdur > qtdemux->duration) {
3602 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3603 GST_DEBUG_OBJECT (qtdemux,
3604 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3605 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3606 qtdemux->duration = movdur;
3607 GST_DEBUG_OBJECT (qtdemux,
3608 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3609 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3610 GST_TIME_ARGS (qtdemux->segment.stop));
3611 if (qtdemux->segment.duration == prevdur) {
3612 /* If the current segment has duration/stop identical to previous duration
3613 * update them also (because they were set at that point in time with
3614 * the wrong duration */
3615 /* We convert the value *from* the timescale version to avoid rounding errors */
3616 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3617 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3618 qtdemux->segment.duration = fixeddur;
3619 qtdemux->segment.stop = fixeddur;
3622 for (i = 0; i < qtdemux->n_streams; i++) {
3623 QtDemuxStream *stream = qtdemux->streams[i];
3625 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3626 if (movdur > stream->duration) {
3627 GST_DEBUG_OBJECT (qtdemux,
3628 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3629 GST_TIME_ARGS (duration));
3630 stream->duration = movdur;
3631 if (stream->dummy_segment) {
3632 /* Update all dummy values to new duration */
3633 stream->segments[0].stop_time = duration;
3634 stream->segments[0].duration = duration;
3635 stream->segments[0].media_stop = duration;
3637 /* let downstream know we possibly have a new stop time */
3638 if (stream->segment_index != -1) {
3641 if (qtdemux->segment.rate >= 0) {
3642 pos = stream->segment.start;
3644 pos = stream->segment.stop;
3647 gst_qtdemux_stream_update_segment (qtdemux, stream,
3648 stream->segment_index, pos, NULL, NULL);
3657 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3658 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3659 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3660 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3663 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3665 gint32 data_offset = 0;
3666 guint32 flags = 0, first_flags = 0, samples_count = 0;
3669 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3670 QtDemuxSample *sample;
3671 gboolean ismv = FALSE;
3672 gint64 initial_offset;
3674 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
3675 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3676 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3677 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3679 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3680 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3684 /* presence of stss or not can't really tell us much,
3685 * and flags and so on tend to be marginally reliable in these files */
3686 if (stream->subtype == FOURCC_soun) {
3687 GST_DEBUG_OBJECT (qtdemux,
3688 "sound track in fragmented file; marking all keyframes");
3689 stream->all_keyframe = TRUE;
3692 if (!gst_byte_reader_skip (trun, 1) ||
3693 !gst_byte_reader_get_uint24_be (trun, &flags))
3696 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3699 if (flags & TR_DATA_OFFSET) {
3700 /* note this is really signed */
3701 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3703 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3704 /* default base offset = first byte of moof */
3705 if (*base_offset == -1) {
3706 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3707 *base_offset = moof_offset;
3709 *running_offset = *base_offset + data_offset;
3711 /* if no offset at all, that would mean data starts at moof start,
3712 * which is a bit wrong and is ismv crappy way, so compensate
3713 * assuming data is in mdat following moof */
3714 if (*base_offset == -1) {
3715 *base_offset = moof_offset + moof_length + 8;
3716 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3719 if (*running_offset == -1)
3720 *running_offset = *base_offset;
3723 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3725 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3726 data_offset, flags, samples_count);
3728 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3729 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3730 GST_DEBUG_OBJECT (qtdemux,
3731 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3732 flags ^= TR_FIRST_SAMPLE_FLAGS;
3734 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3736 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3740 /* FIXME ? spec says other bits should also be checked to determine
3741 * entry size (and prefix size for that matter) */
3743 dur_offset = size_offset = 0;
3744 if (flags & TR_SAMPLE_DURATION) {
3745 GST_LOG_OBJECT (qtdemux, "entry duration present");
3746 dur_offset = entry_size;
3749 if (flags & TR_SAMPLE_SIZE) {
3750 GST_LOG_OBJECT (qtdemux, "entry size present");
3751 size_offset = entry_size;
3754 if (flags & TR_SAMPLE_FLAGS) {
3755 GST_LOG_OBJECT (qtdemux, "entry flags present");
3756 flags_offset = entry_size;
3759 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3760 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3761 ct_offset = entry_size;
3765 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3767 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3769 if (stream->n_samples + samples_count >=
3770 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3773 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3774 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3775 (stream->n_samples + samples_count) *
3776 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3778 /* create a new array of samples if it's the first sample parsed */
3779 if (stream->n_samples == 0) {
3780 g_assert (stream->samples == NULL);
3781 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3782 /* or try to reallocate it with space enough to insert the new samples */
3784 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3785 stream->n_samples + samples_count);
3786 if (stream->samples == NULL)
3789 if (qtdemux->fragment_start != -1) {
3790 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3791 qtdemux->fragment_start = -1;
3793 if (stream->n_samples == 0) {
3794 if (decode_ts > 0) {
3795 timestamp = decode_ts;
3796 } else if (stream->pending_seek != NULL) {
3797 /* if we don't have a timestamp from a tfdt box, we'll use the one
3798 * from the mfra seek table */
3799 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3800 GST_TIME_ARGS (stream->pending_seek->ts));
3802 /* FIXME: this is not fully correct, the timestamp refers to the random
3803 * access sample refered to in the tfra entry, which may not necessarily
3804 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3805 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3810 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3811 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3812 GST_TIME_ARGS (gst_ts));
3814 /* subsequent fragments extend stream */
3816 stream->samples[stream->n_samples - 1].timestamp +
3817 stream->samples[stream->n_samples - 1].duration;
3819 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3820 * difference (1 sec.) between decode_ts and timestamp, prefer the
3822 if (has_tfdt && !qtdemux->upstream_format_is_time
3823 && ABSDIFF (decode_ts, timestamp) >
3824 MAX (stream->duration_last_moof / 2,
3825 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3826 GST_INFO_OBJECT (qtdemux,
3827 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3828 ") are significantly different (more than %" GST_TIME_FORMAT
3829 "), using decode_ts",
3830 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3831 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3832 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3833 MAX (stream->duration_last_moof / 2,
3834 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3835 timestamp = decode_ts;
3838 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3839 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3840 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3844 initial_offset = *running_offset;
3846 sample = stream->samples + stream->n_samples;
3847 for (i = 0; i < samples_count; i++) {
3848 guint32 dur, size, sflags, ct;
3850 /* first read sample data */
3851 if (flags & TR_SAMPLE_DURATION) {
3852 dur = QT_UINT32 (data + dur_offset);
3854 dur = d_sample_duration;
3856 if (flags & TR_SAMPLE_SIZE) {
3857 size = QT_UINT32 (data + size_offset);
3859 size = d_sample_size;
3861 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3863 sflags = first_flags;
3865 sflags = d_sample_flags;
3867 } else if (flags & TR_SAMPLE_FLAGS) {
3868 sflags = QT_UINT32 (data + flags_offset);
3870 sflags = d_sample_flags;
3872 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3873 ct = QT_UINT32 (data + ct_offset);
3879 /* fill the sample information */
3880 sample->offset = *running_offset;
3881 sample->pts_offset = ct;
3882 sample->size = size;
3883 sample->timestamp = timestamp;
3884 sample->duration = dur;
3885 /* sample-is-difference-sample */
3886 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3887 * now idea how it relates to bitfield other than massive LE/BE confusion */
3888 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3889 *running_offset += size;
3891 stream->duration_moof += dur;
3895 /* Update total duration if needed */
3896 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3898 /* Pre-emptively figure out size of mdat based on trun information.
3899 * If the [mdat] atom is effectivelly read, it will be replaced by the actual
3900 * size, else we will still be able to use this when dealing with gap'ed
3902 qtdemux->mdatleft = *running_offset - initial_offset;
3903 qtdemux->mdatoffset = initial_offset;
3904 qtdemux->mdatsize = qtdemux->mdatleft;
3906 stream->n_samples += samples_count;
3907 stream->n_samples_moof += samples_count;
3909 if (stream->pending_seek != NULL)
3910 stream->pending_seek = NULL;
3916 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3921 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3927 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3928 "be larger than %uMB (broken file?)", stream->n_samples,
3929 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3934 /* find stream with @id */
3935 static inline QtDemuxStream *
3936 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3938 QtDemuxStream *stream;
3942 if (G_UNLIKELY (!id)) {
3943 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3947 /* try to get it fast and simple */
3948 if (G_LIKELY (id <= qtdemux->n_streams)) {
3949 stream = qtdemux->streams[id - 1];
3950 if (G_LIKELY (stream->track_id == id))
3954 /* linear search otherwise */
3955 for (i = 0; i < qtdemux->n_streams; i++) {
3956 stream = qtdemux->streams[i];
3957 if (stream->track_id == id)
3960 if (qtdemux->mss_mode) {
3961 /* mss should have only 1 stream anyway */
3962 return qtdemux->streams[0];
3969 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3970 guint32 * fragment_number)
3972 if (!gst_byte_reader_skip (mfhd, 4))
3974 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3979 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3985 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3986 QtDemuxStream ** stream, guint32 * default_sample_duration,
3987 guint32 * default_sample_size, guint32 * default_sample_flags,
3988 gint64 * base_offset)
3991 guint32 track_id = 0;
3993 if (!gst_byte_reader_skip (tfhd, 1) ||
3994 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3997 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
4000 *stream = qtdemux_find_stream (qtdemux, track_id);
4001 if (G_UNLIKELY (!*stream))
4002 goto unknown_stream;
4004 if (flags & TF_DEFAULT_BASE_IS_MOOF)
4005 *base_offset = qtdemux->moof_offset;
4007 if (flags & TF_BASE_DATA_OFFSET)
4008 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
4011 /* obtain stream defaults */
4012 qtdemux_parse_trex (qtdemux, *stream,
4013 default_sample_duration, default_sample_size, default_sample_flags);
4015 (*stream)->stsd_sample_description_id =
4016 (*stream)->def_sample_description_index - 1;
4018 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
4019 guint32 sample_description_index;
4020 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
4022 (*stream)->stsd_sample_description_id = sample_description_index - 1;
4025 if (qtdemux->mss_mode) {
4026 /* mss has no stsd entry */
4027 (*stream)->stsd_sample_description_id = 0;
4030 if (flags & TF_DEFAULT_SAMPLE_DURATION)
4031 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
4034 if (flags & TF_DEFAULT_SAMPLE_SIZE)
4035 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
4038 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
4039 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
4046 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
4051 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
4057 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
4058 guint64 * decode_time)
4060 guint32 version = 0;
4062 if (!gst_byte_reader_get_uint32_be (br, &version))
4067 if (!gst_byte_reader_get_uint64_be (br, decode_time))
4070 guint32 dec_time = 0;
4071 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
4073 *decode_time = dec_time;
4076 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
4083 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
4088 /* Returns a pointer to a GstStructure containing the properties of
4089 * the stream sample identified by @sample_index. The caller must unref
4090 * the returned object after use. Returns NULL if unsuccessful. */
4091 static GstStructure *
4092 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
4093 QtDemuxStream * stream, guint sample_index)
4095 QtDemuxCencSampleSetInfo *info = NULL;
4097 g_return_val_if_fail (stream != NULL, NULL);
4098 g_return_val_if_fail (stream->protected, NULL);
4099 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
4101 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4103 /* Currently, cenc properties for groups of samples are not supported, so
4104 * simply return a copy of the default sample properties */
4105 return gst_structure_copy (info->default_properties);
4108 /* Parses the sizes of sample auxiliary information contained within a stream,
4109 * as given in a saiz box. Returns array of sample_count guint8 size values,
4110 * or NULL on failure */
4112 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
4113 GstByteReader * br, guint32 * sample_count)
4117 guint8 default_info_size;
4119 g_return_val_if_fail (qtdemux != NULL, NULL);
4120 g_return_val_if_fail (stream != NULL, NULL);
4121 g_return_val_if_fail (br != NULL, NULL);
4122 g_return_val_if_fail (sample_count != NULL, NULL);
4124 if (!gst_byte_reader_get_uint32_be (br, &flags))
4128 /* aux_info_type and aux_info_type_parameter are ignored */
4129 if (!gst_byte_reader_skip (br, 8))
4133 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
4135 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
4137 if (!gst_byte_reader_get_uint32_be (br, sample_count))
4139 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
4142 if (default_info_size == 0) {
4143 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
4147 info_sizes = g_new (guint8, *sample_count);
4148 memset (info_sizes, default_info_size, *sample_count);
4154 /* Parses the offset of sample auxiliary information contained within a stream,
4155 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
4157 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
4158 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
4163 guint32 aux_info_type = 0;
4164 guint32 aux_info_type_parameter = 0;
4165 guint32 entry_count;
4168 const guint8 *aux_info_type_data = NULL;
4170 g_return_val_if_fail (qtdemux != NULL, FALSE);
4171 g_return_val_if_fail (stream != NULL, FALSE);
4172 g_return_val_if_fail (br != NULL, FALSE);
4173 g_return_val_if_fail (offset != NULL, FALSE);
4175 if (!gst_byte_reader_get_uint8 (br, &version))
4178 if (!gst_byte_reader_get_uint24_be (br, &flags))
4183 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
4185 aux_info_type = QT_FOURCC (aux_info_type_data);
4187 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
4189 } else if (stream->protected) {
4190 aux_info_type = stream->protection_scheme_type;
4192 aux_info_type = CUR_STREAM (stream)->fourcc;
4196 *info_type = aux_info_type;
4197 if (info_type_parameter)
4198 *info_type_parameter = aux_info_type_parameter;
4200 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
4201 "aux_info_type_parameter: %#06x",
4202 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
4204 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
4207 if (entry_count != 1) {
4208 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
4213 if (!gst_byte_reader_get_uint32_be (br, &off_32))
4215 *offset = (guint64) off_32;
4217 if (!gst_byte_reader_get_uint64_be (br, &off_64))
4222 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
4227 qtdemux_gst_structure_free (GstStructure * gststructure)
4230 gst_structure_free (gststructure);
4234 /* Parses auxiliary information relating to samples protected using Common
4235 * Encryption (cenc); the format of this information is defined in
4236 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
4238 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
4239 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
4241 QtDemuxCencSampleSetInfo *ss_info = NULL;
4244 GPtrArray *old_crypto_info = NULL;
4245 guint old_entries = 0;
4247 g_return_val_if_fail (qtdemux != NULL, FALSE);
4248 g_return_val_if_fail (stream != NULL, FALSE);
4249 g_return_val_if_fail (br != NULL, FALSE);
4250 g_return_val_if_fail (stream->protected, FALSE);
4251 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
4253 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4255 if (ss_info->crypto_info) {
4256 old_crypto_info = ss_info->crypto_info;
4257 /* Count number of non-null entries remaining at the tail end */
4258 for (i = old_crypto_info->len - 1; i >= 0; i--) {
4259 if (g_ptr_array_index (old_crypto_info, i) == NULL)
4265 ss_info->crypto_info =
4266 g_ptr_array_new_full (sample_count + old_entries,
4267 (GDestroyNotify) qtdemux_gst_structure_free);
4269 /* We preserve old entries because we parse the next moof in advance
4270 * of consuming all samples from the previous moof, and otherwise
4271 * we'd discard the corresponding crypto info for the samples
4272 * from the previous fragment. */
4274 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
4276 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
4277 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
4279 g_ptr_array_index (old_crypto_info, i) = NULL;
4283 if (old_crypto_info) {
4284 /* Everything now belongs to the new array */
4285 g_ptr_array_free (old_crypto_info, TRUE);
4288 for (i = 0; i < sample_count; ++i) {
4289 GstStructure *properties;
4290 guint16 n_subsamples = 0;
4295 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4296 if (properties == NULL) {
4297 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4300 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
4301 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
4302 gst_structure_free (properties);
4305 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
4306 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
4307 gst_structure_free (properties);
4310 buf = gst_buffer_new_wrapped (data, iv_size);
4311 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4312 gst_buffer_unref (buf);
4313 size = info_sizes[i];
4314 if (size > iv_size) {
4315 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
4316 || !(n_subsamples > 0)) {
4317 gst_structure_free (properties);
4318 GST_ERROR_OBJECT (qtdemux,
4319 "failed to get subsample count for sample %u", i);
4322 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
4323 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
4324 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4326 gst_structure_free (properties);
4329 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
4331 gst_structure_free (properties);
4334 gst_structure_set (properties,
4335 "subsample_count", G_TYPE_UINT, n_subsamples,
4336 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4337 gst_buffer_unref (buf);
4339 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
4341 g_ptr_array_add (ss_info->crypto_info, properties);
4346 /* Converts a UUID in raw byte form to a string representation, as defined in
4347 * RFC 4122. The caller takes ownership of the returned string and is
4348 * responsible for freeing it after use. */
4350 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
4352 const guint8 *uuid = (const guint8 *) uuid_bytes;
4354 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
4355 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4356 uuid[0], uuid[1], uuid[2], uuid[3],
4357 uuid[4], uuid[5], uuid[6], uuid[7],
4358 uuid[8], uuid[9], uuid[10], uuid[11],
4359 uuid[12], uuid[13], uuid[14], uuid[15]);
4362 /* Parses a Protection System Specific Header box (pssh), as defined in the
4363 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4364 * information needed by a specific content protection system in order to
4365 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4368 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
4370 gchar *sysid_string;
4371 guint32 pssh_size = QT_UINT32 (node->data);
4372 GstBuffer *pssh = NULL;
4373 GstEvent *event = NULL;
4374 guint32 parent_box_type;
4377 if (G_UNLIKELY (pssh_size < 32U)) {
4378 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4383 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
4385 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
4387 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
4388 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4389 gst_buffer_get_size (pssh));
4391 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4393 /* Push an event containing the pssh box onto the queues of all streams. */
4394 event = gst_event_new_protection (sysid_string, pssh,
4395 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4396 for (i = 0; i < qtdemux->n_streams; ++i) {
4397 g_queue_push_tail (&qtdemux->streams[i]->protection_scheme_event_queue,
4398 gst_event_ref (event));
4400 g_free (sysid_string);
4401 gst_event_unref (event);
4402 gst_buffer_unref (pssh);
4407 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4408 guint64 moof_offset, QtDemuxStream * stream)
4410 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4412 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4413 GNode *saiz_node, *saio_node, *pssh_node;
4414 GstByteReader saiz_data, saio_data;
4415 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4416 gint64 base_offset, running_offset;
4419 /* NOTE @stream ignored */
4421 moof_node = g_node_new ((guint8 *) buffer);
4422 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4423 qtdemux_node_dump (qtdemux, moof_node);
4425 /* Get fragment number from mfhd and check it's valid */
4427 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4428 if (mfhd_node == NULL)
4430 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4432 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4434 /* unknown base_offset to start with */
4435 base_offset = running_offset = -1;
4436 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4438 guint64 decode_time = 0;
4440 /* Fragment Header node */
4442 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4446 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4447 &ds_size, &ds_flags, &base_offset))
4450 /* The following code assumes at most a single set of sample auxiliary
4451 * data in the fragment (consisting of a saiz box and a corresponding saio
4452 * box); in theory, however, there could be multiple sets of sample
4453 * auxiliary data in a fragment. */
4455 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4458 guint32 info_type = 0;
4460 guint32 info_type_parameter = 0;
4462 g_free (qtdemux->cenc_aux_info_sizes);
4464 qtdemux->cenc_aux_info_sizes =
4465 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4466 &qtdemux->cenc_aux_sample_count);
4467 if (qtdemux->cenc_aux_info_sizes == NULL) {
4468 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4472 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4475 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4476 g_free (qtdemux->cenc_aux_info_sizes);
4477 qtdemux->cenc_aux_info_sizes = NULL;
4481 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4482 &info_type, &info_type_parameter, &offset))) {
4483 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4484 g_free (qtdemux->cenc_aux_info_sizes);
4485 qtdemux->cenc_aux_info_sizes = NULL;
4488 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4489 offset += (guint64) (base_offset - qtdemux->moof_offset);
4490 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
4492 if (offset > length) {
4493 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4494 qtdemux->cenc_aux_info_offset = offset;
4496 gst_byte_reader_init (&br, buffer + offset, length - offset);
4497 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4498 qtdemux->cenc_aux_info_sizes,
4499 qtdemux->cenc_aux_sample_count)) {
4500 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4501 g_free (qtdemux->cenc_aux_info_sizes);
4502 qtdemux->cenc_aux_info_sizes = NULL;
4510 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4513 /* We'll use decode_time to interpolate timestamps
4514 * in case the input timestamps are missing */
4515 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4517 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4518 " (%" GST_TIME_FORMAT ")", decode_time,
4519 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4520 decode_time) : GST_CLOCK_TIME_NONE));
4522 /* Discard the fragment buffer timestamp info to avoid using it.
4523 * Rely on tfdt instead as it is more accurate than the timestamp
4524 * that is fetched from a manifest/playlist and is usually
4526 qtdemux->fragment_start = -1;
4529 if (G_UNLIKELY (!stream)) {
4530 /* we lost track of offset, we'll need to regain it,
4531 * but can delay complaining until later or avoid doing so altogether */
4535 if (G_UNLIKELY (base_offset < -1))
4538 if (qtdemux->upstream_format_is_time)
4539 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
4541 /* initialise moof sample data */
4542 stream->n_samples_moof = 0;
4543 stream->duration_last_moof = stream->duration_moof;
4544 stream->duration_moof = 0;
4546 /* Track Run node */
4548 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4551 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4552 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4553 &running_offset, decode_time, (tfdt_node != NULL));
4554 /* iterate all siblings */
4555 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4559 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4561 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4562 guint32 box_length = QT_UINT32 (uuid_buffer);
4564 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4567 /* if no new base_offset provided for next traf,
4568 * base is end of current traf */
4569 base_offset = running_offset;
4570 running_offset = -1;
4572 if (stream->n_samples_moof && stream->duration_moof)
4573 stream->new_caps = TRUE;
4576 /* iterate all siblings */
4577 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4580 /* parse any protection system info */
4581 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4583 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4584 qtdemux_parse_pssh (qtdemux, pssh_node);
4585 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4588 g_node_destroy (moof_node);
4593 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4598 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4603 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4608 g_node_destroy (moof_node);
4609 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4610 (_("This file is corrupt and cannot be played.")), (NULL));
4616 /* might be used if some day we actually use mfra & co
4617 * for random access to fragments,
4618 * but that will require quite some modifications and much less relying
4619 * on a sample array */
4623 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4625 QtDemuxStream *stream;
4626 guint32 ver_flags, track_id, len, num_entries, i;
4627 guint value_size, traf_size, trun_size, sample_size;
4628 guint64 time = 0, moof_offset = 0;
4630 GstBuffer *buf = NULL;
4635 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4637 if (!gst_byte_reader_skip (&tfra, 8))
4640 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4643 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4644 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4645 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4648 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4650 stream = qtdemux_find_stream (qtdemux, track_id);
4652 goto unknown_trackid;
4654 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4655 sample_size = (len & 3) + 1;
4656 trun_size = ((len & 12) >> 2) + 1;
4657 traf_size = ((len & 48) >> 4) + 1;
4659 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4660 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4662 if (num_entries == 0)
4665 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4666 value_size + value_size + traf_size + trun_size + sample_size))
4669 g_free (stream->ra_entries);
4670 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4671 stream->n_ra_entries = num_entries;
4673 for (i = 0; i < num_entries; i++) {
4674 qt_atom_parser_get_offset (&tfra, value_size, &time);
4675 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4676 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4677 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4678 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4680 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4682 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4683 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4685 stream->ra_entries[i].ts = time;
4686 stream->ra_entries[i].moof_offset = moof_offset;
4688 /* don't want to go through the entire file and read all moofs at startup */
4690 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4691 if (ret != GST_FLOW_OK)
4693 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4694 moof_offset, stream);
4695 gst_buffer_unref (buf);
4699 check_update_duration (qtdemux, time);
4706 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4711 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4716 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4722 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4724 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4725 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4726 GstBuffer *mfro = NULL, *mfra = NULL;
4728 gboolean ret = FALSE;
4729 GNode *mfra_node, *tfra_node;
4730 guint64 mfra_offset = 0;
4731 guint32 fourcc, mfra_size;
4734 /* query upstream size in bytes */
4735 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4736 goto size_query_failed;
4738 /* mfro box should be at the very end of the file */
4739 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4740 if (flow != GST_FLOW_OK)
4743 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4745 fourcc = QT_FOURCC (mfro_map.data + 4);
4746 if (fourcc != FOURCC_mfro)
4749 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4750 if (mfro_map.size < 16)
4751 goto invalid_mfro_size;
4753 mfra_size = QT_UINT32 (mfro_map.data + 12);
4754 if (mfra_size >= len)
4755 goto invalid_mfra_size;
4757 mfra_offset = len - mfra_size;
4759 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4760 mfra_offset, mfra_size);
4762 /* now get and parse mfra box */
4763 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4764 if (flow != GST_FLOW_OK)
4767 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4769 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4770 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4772 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4775 qtdemux_parse_tfra (qtdemux, tfra_node);
4776 /* iterate all siblings */
4777 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4779 g_node_destroy (mfra_node);
4781 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4787 if (mfro_map.memory != NULL)
4788 gst_buffer_unmap (mfro, &mfro_map);
4789 gst_buffer_unref (mfro);
4792 if (mfra_map.memory != NULL)
4793 gst_buffer_unmap (mfra, &mfra_map);
4794 gst_buffer_unref (mfra);
4801 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4806 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4811 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4816 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4822 add_offset (guint64 offset, guint64 advance)
4824 /* Avoid 64-bit overflow by clamping */
4825 if (offset > G_MAXUINT64 - advance)
4827 return offset + advance;
4830 static GstFlowReturn
4831 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4835 GstBuffer *buf = NULL;
4836 GstFlowReturn ret = GST_FLOW_OK;
4837 guint64 cur_offset = qtdemux->offset;
4840 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4841 if (G_UNLIKELY (ret != GST_FLOW_OK))
4843 gst_buffer_map (buf, &map, GST_MAP_READ);
4844 if (G_LIKELY (map.size >= 8))
4845 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4846 gst_buffer_unmap (buf, &map);
4847 gst_buffer_unref (buf);
4849 /* maybe we already got most we needed, so only consider this eof */
4850 if (G_UNLIKELY (length == 0)) {
4851 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4852 (_("Invalid atom size.")),
4853 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4854 GST_FOURCC_ARGS (fourcc)));
4861 /* record for later parsing when needed */
4862 if (!qtdemux->moof_offset) {
4863 qtdemux->moof_offset = qtdemux->offset;
4865 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4868 qtdemux->offset += length; /* skip moof and keep going */
4870 if (qtdemux->got_moov) {
4871 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4882 GST_LOG_OBJECT (qtdemux,
4883 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4884 GST_FOURCC_ARGS (fourcc), cur_offset);
4885 qtdemux->offset = add_offset (qtdemux->offset, length);
4890 GstBuffer *moov = NULL;
4892 if (qtdemux->got_moov) {
4893 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4894 qtdemux->offset = add_offset (qtdemux->offset, length);
4898 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4899 if (ret != GST_FLOW_OK)
4901 gst_buffer_map (moov, &map, GST_MAP_READ);
4903 if (length != map.size) {
4904 /* Some files have a 'moov' atom at the end of the file which contains
4905 * a terminal 'free' atom where the body of the atom is missing.
4906 * Check for, and permit, this special case.
4908 if (map.size >= 8) {
4909 guint8 *final_data = map.data + (map.size - 8);
4910 guint32 final_length = QT_UINT32 (final_data);
4911 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4913 if (final_fourcc == FOURCC_free
4914 && map.size + final_length - 8 == length) {
4915 /* Ok, we've found that special case. Allocate a new buffer with
4916 * that free atom actually present. */
4917 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4918 gst_buffer_fill (newmoov, 0, map.data, map.size);
4919 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4920 gst_buffer_unmap (moov, &map);
4921 gst_buffer_unref (moov);
4923 gst_buffer_map (moov, &map, GST_MAP_READ);
4928 if (length != map.size) {
4929 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4930 (_("This file is incomplete and cannot be played.")),
4931 ("We got less than expected (received %" G_GSIZE_FORMAT
4932 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4933 (guint) length, cur_offset));
4934 gst_buffer_unmap (moov, &map);
4935 gst_buffer_unref (moov);
4936 ret = GST_FLOW_ERROR;
4939 qtdemux->offset += length;
4941 qtdemux_parse_moov (qtdemux, map.data, length);
4942 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4944 qtdemux_parse_tree (qtdemux);
4945 if (qtdemux->moov_node_compressed) {
4946 g_node_destroy (qtdemux->moov_node_compressed);
4947 g_free (qtdemux->moov_node->data);
4949 qtdemux->moov_node_compressed = NULL;
4950 g_node_destroy (qtdemux->moov_node);
4951 qtdemux->moov_node = NULL;
4952 gst_buffer_unmap (moov, &map);
4953 gst_buffer_unref (moov);
4954 qtdemux->got_moov = TRUE;
4960 GstBuffer *ftyp = NULL;
4962 /* extract major brand; might come in handy for ISO vs QT issues */
4963 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4964 if (ret != GST_FLOW_OK)
4966 qtdemux->offset += length;
4967 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4968 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4969 gst_buffer_unmap (ftyp, &map);
4970 gst_buffer_unref (ftyp);
4975 GstBuffer *uuid = NULL;
4977 /* uuid are extension atoms */
4978 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4979 if (ret != GST_FLOW_OK)
4981 qtdemux->offset += length;
4982 gst_buffer_map (uuid, &map, GST_MAP_READ);
4983 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4984 gst_buffer_unmap (uuid, &map);
4985 gst_buffer_unref (uuid);
4990 GstBuffer *sidx = NULL;
4991 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4992 if (ret != GST_FLOW_OK)
4994 qtdemux->offset += length;
4995 gst_buffer_map (sidx, &map, GST_MAP_READ);
4996 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4997 gst_buffer_unmap (sidx, &map);
4998 gst_buffer_unref (sidx);
5003 GstBuffer *unknown = NULL;
5005 GST_LOG_OBJECT (qtdemux,
5006 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
5007 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
5009 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
5010 if (ret != GST_FLOW_OK)
5012 gst_buffer_map (unknown, &map, GST_MAP_READ);
5013 GST_MEMDUMP ("Unknown tag", map.data, map.size);
5014 gst_buffer_unmap (unknown, &map);
5015 gst_buffer_unref (unknown);
5016 qtdemux->offset += length;
5022 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
5023 /* digested all data, show what we have */
5024 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
5025 if (qtdemux->spherical_metadata)
5026 _send_spherical_metadata_msg_to_bus (qtdemux);
5027 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
5028 qtdemux_prepare_streams (qtdemux);
5029 ret = qtdemux_expose_streams (qtdemux);
5031 qtdemux->state = QTDEMUX_STATE_MOVIE;
5032 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
5039 /* Seeks to the previous keyframe of the indexed stream and
5040 * aligns other streams with respect to the keyframe timestamp
5041 * of indexed stream. Only called in case of Reverse Playback
5043 static GstFlowReturn
5044 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
5047 guint32 seg_idx = 0, k_index = 0;
5048 guint32 ref_seg_idx, ref_k_index;
5049 GstClockTime k_pos = 0, last_stop = 0;
5050 QtDemuxSegment *seg = NULL;
5051 QtDemuxStream *ref_str = NULL;
5052 guint64 seg_media_start_mov; /* segment media start time in mov format */
5055 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
5056 * and finally align all the other streams on that timestamp with their
5057 * respective keyframes */
5058 for (n = 0; n < qtdemux->n_streams; n++) {
5059 QtDemuxStream *str = qtdemux->streams[n];
5061 /* No candidate yet, take the first stream */
5067 /* So that stream has a segment, we prefer video streams */
5068 if (str->subtype == FOURCC_vide) {
5074 if (G_UNLIKELY (!ref_str)) {
5075 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
5079 if (G_UNLIKELY (!ref_str->from_sample)) {
5080 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
5084 /* So that stream has been playing from from_sample to to_sample. We will
5085 * get the timestamp of the previous sample and search for a keyframe before
5086 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
5087 if (ref_str->subtype == FOURCC_vide) {
5088 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
5089 ref_str->from_sample - 1, FALSE);
5091 if (ref_str->from_sample >= 10)
5092 k_index = ref_str->from_sample - 10;
5098 ref_str->samples[k_index].timestamp +
5099 ref_str->samples[k_index].pts_offset;
5101 /* get current segment for that stream */
5102 seg = &ref_str->segments[ref_str->segment_index];
5103 /* Use segment start in original timescale for comparisons */
5104 seg_media_start_mov = seg->trak_media_start;
5106 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
5107 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT "\n",
5108 k_index, target_ts, seg_media_start_mov,
5109 GST_TIME_ARGS (seg->media_start));
5111 /* Crawl back through segments to find the one containing this I frame */
5112 while (target_ts < seg_media_start_mov) {
5113 GST_DEBUG_OBJECT (qtdemux,
5114 "keyframe position (sample %u) is out of segment %u " " target %"
5115 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
5116 ref_str->segment_index, target_ts, seg_media_start_mov);
5118 if (G_UNLIKELY (!ref_str->segment_index)) {
5119 /* Reached first segment, let's consider it's EOS */
5122 ref_str->segment_index--;
5123 seg = &ref_str->segments[ref_str->segment_index];
5124 /* Use segment start in original timescale for comparisons */
5125 seg_media_start_mov = seg->trak_media_start;
5127 /* Calculate time position of the keyframe and where we should stop */
5129 QTSTREAMTIME_TO_GSTTIME (ref_str,
5130 target_ts - seg->trak_media_start) + seg->time;
5132 QTSTREAMTIME_TO_GSTTIME (ref_str,
5133 ref_str->samples[ref_str->from_sample].timestamp -
5134 seg->trak_media_start) + seg->time;
5136 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
5137 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
5138 k_index, GST_TIME_ARGS (k_pos));
5140 /* Set last_stop with the keyframe timestamp we pushed of that stream */
5141 qtdemux->segment.position = last_stop;
5142 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
5143 GST_TIME_ARGS (last_stop));
5145 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5146 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5150 ref_seg_idx = ref_str->segment_index;
5151 ref_k_index = k_index;
5153 /* Align them all on this */
5154 for (n = 0; n < qtdemux->n_streams; n++) {
5156 GstClockTime seg_time = 0;
5157 QtDemuxStream *str = qtdemux->streams[n];
5159 /* aligning reference stream again might lead to backing up to yet another
5160 * keyframe (due to timestamp rounding issues),
5161 * potentially putting more load on downstream; so let's try to avoid */
5162 if (str == ref_str) {
5163 seg_idx = ref_seg_idx;
5164 seg = &str->segments[seg_idx];
5165 k_index = ref_k_index;
5166 GST_DEBUG_OBJECT (qtdemux, "reference stream %d segment %d, "
5167 "sample at index %d", n, ref_str->segment_index, k_index);
5169 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
5170 GST_DEBUG_OBJECT (qtdemux,
5171 "stream %d align segment %d for keyframe pos %" GST_TIME_FORMAT, n,
5172 seg_idx, GST_TIME_ARGS (k_pos));
5174 /* get segment and time in the segment */
5175 seg = &str->segments[seg_idx];
5176 seg_time = k_pos - seg->time;
5178 /* get the media time in the segment.
5179 * No adjustment for empty "filler" segments */
5180 if (seg->media_start != GST_CLOCK_TIME_NONE)
5181 seg_time += seg->media_start;
5183 /* get the index of the sample with media time */
5184 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
5185 GST_DEBUG_OBJECT (qtdemux,
5186 "stream %d sample for %" GST_TIME_FORMAT " at %u", n,
5187 GST_TIME_ARGS (seg_time), index);
5189 /* find previous keyframe */
5190 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
5193 /* Remember until where we want to go */
5194 str->to_sample = str->from_sample - 1;
5195 /* Define our time position */
5197 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
5198 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
5199 if (seg->media_start != GST_CLOCK_TIME_NONE)
5200 str->time_position -= seg->media_start;
5202 /* Now seek back in time */
5203 gst_qtdemux_move_stream (qtdemux, str, k_index);
5204 GST_DEBUG_OBJECT (qtdemux, "stream %d keyframe at %u, time position %"
5205 GST_TIME_FORMAT " playing from sample %u to %u", n, k_index,
5206 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5212 return GST_FLOW_EOS;
5216 * Gets the current qt segment start, stop and position for the
5217 * given time offset. This is used in update_segment()
5220 gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
5221 QtDemuxStream * stream, GstClockTime offset,
5222 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5224 GstClockTime seg_time;
5225 GstClockTime start, stop, time;
5226 QtDemuxSegment *segment;
5228 segment = &stream->segments[stream->segment_index];
5230 /* get time in this segment */
5231 seg_time = (offset - segment->time) * segment->rate;
5233 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5234 GST_TIME_ARGS (seg_time));
5236 if (G_UNLIKELY (seg_time > segment->duration)) {
5237 GST_LOG_OBJECT (stream->pad,
5238 "seg_time > segment->duration %" GST_TIME_FORMAT,
5239 GST_TIME_ARGS (segment->duration));
5240 seg_time = segment->duration;
5243 /* qtdemux->segment.stop is in outside-time-realm, whereas
5244 * segment->media_stop is in track-time-realm.
5246 * In order to compare the two, we need to bring segment.stop
5247 * into the track-time-realm
5249 * FIXME - does this comment still hold? Don't see any conversion here */
5251 stop = qtdemux->segment.stop;
5252 if (stop == GST_CLOCK_TIME_NONE)
5253 stop = qtdemux->segment.duration;
5254 if (stop == GST_CLOCK_TIME_NONE)
5255 stop = segment->media_stop;
5258 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5260 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5261 start = segment->time + seg_time;
5263 stop = start - seg_time + segment->duration;
5264 } else if (qtdemux->segment.rate >= 0) {
5265 start = MIN (segment->media_start + seg_time, stop);
5268 if (segment->media_start >= qtdemux->segment.start) {
5269 time = segment->time;
5271 time = segment->time + (qtdemux->segment.start - segment->media_start);
5274 start = MAX (segment->media_start, qtdemux->segment.start);
5275 stop = MIN (segment->media_start + seg_time, stop);
5284 * Updates the qt segment used for the stream and pushes a new segment event
5285 * downstream on this stream's pad.
5288 gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5289 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5290 GstClockTime * _stop)
5292 QtDemuxSegment *segment;
5293 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5297 /* update the current segment */
5298 stream->segment_index = seg_idx;
5300 /* get the segment */
5301 segment = &stream->segments[seg_idx];
5303 if (G_UNLIKELY (offset < segment->time)) {
5304 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5305 GST_TIME_ARGS (segment->time));
5309 /* segment lies beyond total indicated duration */
5310 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5311 segment->time > qtdemux->segment.duration)) {
5312 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5313 " < segment->time %" GST_TIME_FORMAT,
5314 GST_TIME_ARGS (qtdemux->segment.duration),
5315 GST_TIME_ARGS (segment->time));
5319 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
5320 &start, &stop, &time);
5322 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5323 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5324 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5326 /* combine global rate with that of the segment */
5327 rate = segment->rate * qtdemux->segment.rate;
5329 /* Copy flags from main segment */
5330 stream->segment.flags = qtdemux->segment.flags;
5332 /* update the segment values used for clipping */
5333 stream->segment.offset = qtdemux->segment.offset;
5334 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5335 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5336 stream->segment.rate = rate;
5337 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5338 stream->cslg_shift);
5339 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5340 stream->cslg_shift);
5341 stream->segment.time = time;
5342 stream->segment.position = stream->segment.start;
5344 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5347 /* now prepare and send the segment */
5349 event = gst_event_new_segment (&stream->segment);
5350 if (qtdemux->segment_seqnum) {
5351 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5353 gst_pad_push_event (stream->pad, event);
5354 /* assume we can send more data now */
5355 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5356 /* clear to send tags on this pad now */
5357 gst_qtdemux_push_tags (qtdemux, stream);
5368 /* activate the given segment number @seg_idx of @stream at time @offset.
5369 * @offset is an absolute global position over all the segments.
5371 * This will push out a NEWSEGMENT event with the right values and
5372 * position the stream index to the first decodable sample before
5376 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5377 guint32 seg_idx, GstClockTime offset)
5379 QtDemuxSegment *segment;
5380 guint32 index, kf_index;
5381 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5383 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5384 seg_idx, GST_TIME_ARGS (offset));
5386 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5390 segment = &stream->segments[stream->segment_index];
5392 /* in the fragmented case, we pick a fragment that starts before our
5393 * desired position and rely on downstream to wait for a keyframe
5394 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5395 * tfra entries tells us which trun/sample the key unit is in, but we don't
5396 * make use of this additional information at the moment) */
5397 if (qtdemux->fragmented) {
5398 stream->to_sample = G_MAXUINT32;
5402 /* We don't need to look for a sample in push-based */
5403 if (!qtdemux->pullbased)
5406 /* and move to the keyframe before the indicated media time of the
5408 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5409 if (qtdemux->segment.rate >= 0) {
5410 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5411 stream->to_sample = G_MAXUINT32;
5412 GST_DEBUG_OBJECT (stream->pad,
5413 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5414 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5415 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5417 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5418 stream->to_sample = index;
5419 GST_DEBUG_OBJECT (stream->pad,
5420 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5421 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5422 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5425 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5426 "this is an empty segment");
5430 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5431 * encountered an error and printed a message so we return appropriately */
5435 /* we're at the right spot */
5436 if (index == stream->sample_index) {
5437 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5441 /* find keyframe of the target index */
5442 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5445 /* indent does stupid stuff with stream->samples[].timestamp */
5447 /* if we move forwards, we don't have to go back to the previous
5448 * keyframe since we already sent that. We can also just jump to
5449 * the keyframe right before the target index if there is one. */
5450 if (index > stream->sample_index) {
5451 /* moving forwards check if we move past a keyframe */
5452 if (kf_index > stream->sample_index) {
5453 GST_DEBUG_OBJECT (stream->pad,
5454 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5455 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5456 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5457 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5459 GST_DEBUG_OBJECT (stream->pad,
5460 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
5461 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5462 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5465 GST_DEBUG_OBJECT (stream->pad,
5466 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
5467 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
5468 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
5469 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5477 /* prepare to get the current sample of @stream, getting essential values.
5479 * This function will also prepare and send the segment when needed.
5481 * Return FALSE if the stream is EOS.
5486 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5487 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5488 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5489 gboolean * keyframe)
5491 QtDemuxSample *sample;
5492 GstClockTime time_position;
5495 g_return_val_if_fail (stream != NULL, FALSE);
5497 time_position = stream->time_position;
5498 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5501 seg_idx = stream->segment_index;
5502 if (G_UNLIKELY (seg_idx == -1)) {
5503 /* find segment corresponding to time_position if we are looking
5505 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5508 /* different segment, activate it, sample_index will be set. */
5509 if (G_UNLIKELY (stream->segment_index != seg_idx))
5510 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5512 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream->
5514 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5516 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5517 " prepare empty sample");
5520 *pts = *dts = time_position;
5521 *duration = seg->duration - (time_position - seg->time);
5528 if (stream->sample_index == -1)
5529 stream->sample_index = 0;
5531 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5532 stream->sample_index, stream->n_samples);
5534 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5535 if (!qtdemux->fragmented)
5538 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5542 GST_OBJECT_LOCK (qtdemux);
5543 flow = qtdemux_add_fragmented_samples (qtdemux);
5544 GST_OBJECT_UNLOCK (qtdemux);
5546 if (flow != GST_FLOW_OK)
5549 while (stream->sample_index >= stream->n_samples);
5552 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5553 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5554 stream->sample_index);
5558 /* now get the info for the sample we're at */
5559 sample = &stream->samples[stream->sample_index];
5561 *dts = QTSAMPLE_DTS (stream, sample);
5562 *pts = QTSAMPLE_PTS (stream, sample);
5563 *offset = sample->offset;
5564 *size = sample->size;
5565 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5566 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5573 stream->time_position = GST_CLOCK_TIME_NONE;
5578 /* move to the next sample in @stream.
5580 * Moves to the next segment when needed.
5583 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5585 QtDemuxSample *sample;
5586 QtDemuxSegment *segment;
5588 /* get current segment */
5589 segment = &stream->segments[stream->segment_index];
5591 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5592 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5596 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5597 /* Mark the stream as EOS */
5598 GST_DEBUG_OBJECT (qtdemux,
5599 "reached max allowed sample %u, mark EOS", stream->to_sample);
5600 stream->time_position = GST_CLOCK_TIME_NONE;
5604 /* move to next sample */
5605 stream->sample_index++;
5606 stream->offset_in_sample = 0;
5608 /* reached the last sample, we need the next segment */
5609 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5612 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5613 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5614 stream->sample_index);
5618 /* get next sample */
5619 sample = &stream->samples[stream->sample_index];
5621 /* see if we are past the segment */
5622 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5625 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5626 /* inside the segment, update time_position, looks very familiar to
5627 * GStreamer segments, doesn't it? */
5628 stream->time_position =
5629 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5631 /* not yet in segment, time does not yet increment. This means
5632 * that we are still prerolling keyframes to the decoder so it can
5633 * decode the first sample of the segment. */
5634 stream->time_position = segment->time;
5638 /* move to the next segment */
5641 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5643 if (stream->segment_index == stream->n_segments - 1) {
5644 /* are we at the end of the last segment, we're EOS */
5645 stream->time_position = GST_CLOCK_TIME_NONE;
5647 /* else we're only at the end of the current segment */
5648 stream->time_position = segment->stop_time;
5650 /* make sure we select a new segment */
5652 /* accumulate previous segments */
5653 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5654 stream->accumulated_base +=
5655 (stream->segment.stop -
5656 stream->segment.start) / ABS (stream->segment.rate);
5658 stream->segment_index = -1;
5663 gst_qtdemux_sync_streams (GstQTDemux * demux)
5667 if (demux->n_streams <= 1)
5670 for (i = 0; i < demux->n_streams; i++) {
5671 QtDemuxStream *stream;
5672 GstClockTime end_time;
5674 stream = demux->streams[i];
5679 /* TODO advance time on subtitle streams here, if any some day */
5681 /* some clips/trailers may have unbalanced streams at the end,
5682 * so send EOS on shorter stream to prevent stalling others */
5684 /* do not mess with EOS if SEGMENT seeking */
5685 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5688 if (demux->pullbased) {
5689 /* loop mode is sample time based */
5690 if (!STREAM_IS_EOS (stream))
5693 /* push mode is byte position based */
5694 if (stream->n_samples &&
5695 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5699 if (stream->sent_eos)
5702 /* only act if some gap */
5703 end_time = stream->segments[stream->n_segments - 1].stop_time;
5704 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5705 ", stream end: %" GST_TIME_FORMAT,
5706 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5707 if (GST_CLOCK_TIME_IS_VALID (end_time)
5708 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5711 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5712 GST_PAD_NAME (stream->pad));
5713 stream->sent_eos = TRUE;
5714 event = gst_event_new_eos ();
5715 if (demux->segment_seqnum)
5716 gst_event_set_seqnum (event, demux->segment_seqnum);
5717 gst_pad_push_event (stream->pad, event);
5722 /* EOS and NOT_LINKED need to be combined. This means that we return:
5724 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5725 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5727 static GstFlowReturn
5728 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5731 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5734 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5737 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5739 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5743 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5744 * completely clipped
5746 * Should be used only with raw buffers */
5748 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5751 guint64 start, stop, cstart, cstop, diff;
5752 GstClockTime pts, duration;
5754 gint num_rate, denom_rate;
5759 osize = size = gst_buffer_get_size (buf);
5762 /* depending on the type, setup the clip parameters */
5763 if (stream->subtype == FOURCC_soun) {
5764 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5765 num_rate = GST_SECOND;
5766 denom_rate = (gint) CUR_STREAM (stream)->rate;
5768 } else if (stream->subtype == FOURCC_vide) {
5770 num_rate = CUR_STREAM (stream)->fps_n;
5771 denom_rate = CUR_STREAM (stream)->fps_d;
5776 if (frame_size <= 0)
5777 goto bad_frame_size;
5779 /* we can only clip if we have a valid pts */
5780 pts = GST_BUFFER_PTS (buf);
5781 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5784 duration = GST_BUFFER_DURATION (buf);
5786 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5788 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5792 stop = start + duration;
5794 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5795 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5798 /* see if some clipping happened */
5799 diff = cstart - start;
5805 /* bring clipped time to samples and to bytes */
5806 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5809 GST_DEBUG_OBJECT (qtdemux,
5810 "clipping start to %" GST_TIME_FORMAT " %"
5811 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5817 diff = stop - cstop;
5822 /* bring clipped time to samples and then to bytes */
5823 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5825 GST_DEBUG_OBJECT (qtdemux,
5826 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5827 " bytes", GST_TIME_ARGS (cstop), diff);
5832 if (offset != 0 || size != osize)
5833 gst_buffer_resize (buf, offset, size);
5835 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5836 GST_BUFFER_PTS (buf) = pts;
5837 GST_BUFFER_DURATION (buf) = duration;
5841 /* dropped buffer */
5844 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5849 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5854 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5859 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5860 gst_buffer_unref (buf);
5866 gst_qtdemux_align_buffer (GstQTDemux * demux,
5867 GstBuffer * buffer, gsize alignment)
5871 gst_buffer_map (buffer, &map, GST_MAP_READ);
5873 if (map.size < sizeof (guintptr)) {
5874 gst_buffer_unmap (buffer, &map);
5878 if (((guintptr) map.data) & (alignment - 1)) {
5879 GstBuffer *new_buffer;
5880 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5882 new_buffer = gst_buffer_new_allocate (NULL,
5883 gst_buffer_get_size (buffer), ¶ms);
5885 /* Copy data "by hand", so ensure alignment is kept: */
5886 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5888 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5889 GST_DEBUG_OBJECT (demux,
5890 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5893 gst_buffer_unmap (buffer, &map);
5894 gst_buffer_unref (buffer);
5899 gst_buffer_unmap (buffer, &map);
5903 /* the input buffer metadata must be writable,
5904 * but time/duration etc not yet set and need not be preserved */
5906 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5913 /* not many cases for now */
5914 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_mp4s)) {
5915 /* send a one time dvd clut event */
5916 if (stream->pending_event && stream->pad)
5917 gst_pad_push_event (stream->pad, stream->pending_event);
5918 stream->pending_event = NULL;
5921 if (G_UNLIKELY (stream->subtype != FOURCC_text
5922 && stream->subtype != FOURCC_sbtl &&
5923 stream->subtype != FOURCC_subp)) {
5927 gst_buffer_map (buf, &map, GST_MAP_READ);
5929 /* empty buffer is sent to terminate previous subtitle */
5930 if (map.size <= 2) {
5931 gst_buffer_unmap (buf, &map);
5932 gst_buffer_unref (buf);
5935 if (stream->subtype == FOURCC_subp) {
5936 /* That's all the processing needed for subpictures */
5937 gst_buffer_unmap (buf, &map);
5941 nsize = GST_READ_UINT16_BE (map.data);
5942 nsize = MIN (nsize, map.size - 2);
5944 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5947 /* takes care of UTF-8 validation or UTF-16 recognition,
5948 * no other encoding expected */
5949 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5950 gst_buffer_unmap (buf, &map);
5952 gst_buffer_unref (buf);
5953 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5955 /* this should not really happen unless the subtitle is corrupted */
5956 gst_buffer_unref (buf);
5960 /* FIXME ? convert optional subsequent style info to markup */
5965 /* Sets a buffer's attributes properly and pushes it downstream.
5966 * Also checks for additional actions and custom processing that may
5967 * need to be done first.
5969 static GstFlowReturn
5970 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
5971 QtDemuxStream * stream, GstBuffer * buf,
5972 GstClockTime dts, GstClockTime pts, GstClockTime duration,
5973 gboolean keyframe, GstClockTime position, guint64 byte_position)
5975 GstFlowReturn ret = GST_FLOW_OK;
5977 /* offset the timestamps according to the edit list */
5979 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
5983 gst_buffer_map (buf, &map, GST_MAP_READ);
5984 url = g_strndup ((gchar *) map.data, map.size);
5985 gst_buffer_unmap (buf, &map);
5986 if (url != NULL && strlen (url) != 0) {
5987 /* we have RTSP redirect now */
5988 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5989 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
5990 gst_structure_new ("redirect",
5991 "new-location", G_TYPE_STRING, url, NULL)));
5992 qtdemux->posted_redirect = TRUE;
5994 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6000 /* position reporting */
6001 if (qtdemux->segment.rate >= 0) {
6002 qtdemux->segment.position = position;
6003 gst_qtdemux_sync_streams (qtdemux);
6006 if (G_UNLIKELY (!stream->pad)) {
6007 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6008 gst_buffer_unref (buf);
6012 /* send out pending buffers */
6013 while (stream->buffers) {
6014 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6016 if (G_UNLIKELY (stream->discont)) {
6017 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6018 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6019 stream->discont = FALSE;
6021 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6024 if (stream->alignment > 1)
6025 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6026 gst_pad_push (stream->pad, buffer);
6028 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6031 /* we're going to modify the metadata */
6032 buf = gst_buffer_make_writable (buf);
6034 if (G_UNLIKELY (stream->need_process))
6035 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
6041 GST_BUFFER_DTS (buf) = dts;
6042 GST_BUFFER_PTS (buf) = pts;
6043 GST_BUFFER_DURATION (buf) = duration;
6044 GST_BUFFER_OFFSET (buf) = -1;
6045 GST_BUFFER_OFFSET_END (buf) = -1;
6047 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6048 gst_buffer_append_memory (buf,
6049 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6051 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6052 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6055 if (G_UNLIKELY (qtdemux->element_index)) {
6056 GstClockTime stream_time;
6059 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6061 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6062 GST_LOG_OBJECT (qtdemux,
6063 "adding association %" GST_TIME_FORMAT "-> %"
6064 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6065 gst_index_add_association (qtdemux->element_index,
6067 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6068 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6069 GST_FORMAT_BYTES, byte_position, NULL);
6074 if (stream->need_clip)
6075 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
6077 if (G_UNLIKELY (buf == NULL))
6080 if (G_UNLIKELY (stream->discont)) {
6081 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6082 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6083 stream->discont = FALSE;
6085 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6089 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6090 stream->on_keyframe = FALSE;
6092 stream->on_keyframe = TRUE;
6096 GST_LOG_OBJECT (qtdemux,
6097 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6098 ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
6099 GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
6100 GST_PAD_NAME (stream->pad));
6102 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
6103 GstStructure *crypto_info;
6104 QtDemuxCencSampleSetInfo *info =
6105 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6109 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6110 gst_pad_push_event (stream->pad, event);
6113 if (info->crypto_info == NULL) {
6114 GST_DEBUG_OBJECT (qtdemux, "cenc metadata hasn't been parsed yet");
6115 gst_buffer_unref (buf);
6119 /* The end of the crypto_info array matches our n_samples position,
6120 * so count backward from there */
6121 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6122 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6123 /* steal structure from array */
6124 crypto_info = g_ptr_array_index (info->crypto_info, index);
6125 g_ptr_array_index (info->crypto_info, index) = NULL;
6126 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6127 info->crypto_info->len);
6128 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6129 GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
6131 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6132 index, stream->sample_index);
6136 if (stream->alignment > 1)
6137 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6139 ret = gst_pad_push (stream->pad, buf);
6141 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6142 /* mark position in stream, we'll need this to know when to send GAP event */
6143 stream->segment.position = pts + duration;
6150 static const QtDemuxRandomAccessEntry *
6151 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6152 GstClockTime pos, gboolean after)
6154 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6155 guint n_entries = stream->n_ra_entries;
6158 /* we assume the table is sorted */
6159 for (i = 0; i < n_entries; ++i) {
6160 if (entries[i].ts > pos)
6164 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6165 * probably okay to assume that the index lists the very first fragment */
6172 return &entries[i - 1];
6176 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6178 const QtDemuxRandomAccessEntry *best_entry = NULL;
6181 GST_OBJECT_LOCK (qtdemux);
6183 g_assert (qtdemux->n_streams > 0);
6185 for (i = 0; i < qtdemux->n_streams; i++) {
6186 const QtDemuxRandomAccessEntry *entry;
6187 QtDemuxStream *stream;
6188 gboolean is_audio_or_video;
6190 stream = qtdemux->streams[i];
6192 g_free (stream->samples);
6193 stream->samples = NULL;
6194 stream->n_samples = 0;
6195 stream->stbl_index = -1; /* no samples have yet been parsed */
6196 stream->sample_index = -1;
6198 if (stream->protection_scheme_info) {
6199 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6200 if (stream->protection_scheme_type == FOURCC_cenc) {
6201 QtDemuxCencSampleSetInfo *info =
6202 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6203 if (info->crypto_info) {
6204 g_ptr_array_free (info->crypto_info, TRUE);
6205 info->crypto_info = NULL;
6210 if (stream->ra_entries == NULL)
6213 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6214 is_audio_or_video = TRUE;
6216 is_audio_or_video = FALSE;
6219 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6220 stream->time_position, !is_audio_or_video);
6222 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6223 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6225 stream->pending_seek = entry;
6227 /* decide position to jump to just based on audio/video tracks, not subs */
6228 if (!is_audio_or_video)
6231 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6235 if (best_entry == NULL) {
6236 GST_OBJECT_UNLOCK (qtdemux);
6240 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6241 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6242 GST_TIME_ARGS (qtdemux->streams[0]->time_position),
6243 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6245 qtdemux->moof_offset = best_entry->moof_offset;
6247 qtdemux_add_fragmented_samples (qtdemux);
6249 GST_OBJECT_UNLOCK (qtdemux);
6253 static GstFlowReturn
6254 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6256 GstFlowReturn ret = GST_FLOW_OK;
6257 GstBuffer *buf = NULL;
6258 QtDemuxStream *stream;
6259 GstClockTime min_time;
6261 GstClockTime dts = GST_CLOCK_TIME_NONE;
6262 GstClockTime pts = GST_CLOCK_TIME_NONE;
6263 GstClockTime duration = 0;
6264 gboolean keyframe = FALSE;
6265 guint sample_size = 0;
6271 gst_qtdemux_push_pending_newsegment (qtdemux);
6273 if (qtdemux->fragmented_seek_pending) {
6274 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6275 gst_qtdemux_do_fragmented_seek (qtdemux);
6276 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6277 qtdemux->fragmented_seek_pending = FALSE;
6280 /* Figure out the next stream sample to output, min_time is expressed in
6281 * global time and runs over the edit list segments. */
6282 min_time = G_MAXUINT64;
6284 for (i = 0; i < qtdemux->n_streams; i++) {
6285 GstClockTime position;
6287 stream = qtdemux->streams[i];
6288 position = stream->time_position;
6290 /* position of -1 is EOS */
6291 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
6292 min_time = position;
6297 if (G_UNLIKELY (index == -1)) {
6298 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6302 /* check for segment end */
6303 if (G_UNLIKELY (qtdemux->segment.stop != -1
6304 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
6305 || (qtdemux->segment.rate < 0
6306 && qtdemux->segment.start > min_time))
6307 && qtdemux->streams[index]->on_keyframe)) {
6308 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6309 qtdemux->streams[index]->time_position = GST_CLOCK_TIME_NONE;
6313 /* gap events for subtitle streams */
6314 for (i = 0; i < qtdemux->n_streams; i++) {
6315 stream = qtdemux->streams[i];
6316 if (stream->pad && (stream->subtype == FOURCC_subp
6317 || stream->subtype == FOURCC_text
6318 || stream->subtype == FOURCC_sbtl)) {
6319 /* send one second gap events until the stream catches up */
6320 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6321 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6322 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6323 stream->segment.position + GST_SECOND < min_time) {
6325 gst_event_new_gap (stream->segment.position, GST_SECOND);
6326 gst_pad_push_event (stream->pad, gap);
6327 stream->segment.position += GST_SECOND;
6332 stream = qtdemux->streams[index];
6333 /* fetch info for the current sample of this stream */
6334 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6335 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6338 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6339 if (stream->new_caps) {
6340 gst_qtdemux_configure_stream (qtdemux, stream);
6341 qtdemux_do_allocation (qtdemux, stream);
6344 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6345 if (G_UNLIKELY (qtdemux->
6346 segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6347 if (stream->subtype == FOURCC_vide && !keyframe) {
6348 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on stream %d", index);
6353 GST_DEBUG_OBJECT (qtdemux,
6354 "pushing from stream %d, empty %d offset %" G_GUINT64_FORMAT
6355 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6356 ", duration %" GST_TIME_FORMAT, index, empty, offset, sample_size,
6357 GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
6359 if (G_UNLIKELY (empty)) {
6360 /* empty segment, push a gap and move to the next one */
6361 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6362 stream->segment.position = pts + duration;
6366 /* hmm, empty sample, skip and move to next sample */
6367 if (G_UNLIKELY (sample_size <= 0))
6370 /* last pushed sample was out of boundary, goto next sample */
6371 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6374 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
6377 GST_DEBUG_OBJECT (qtdemux,
6378 "size %d larger than stream max_buffer_size %d, trimming",
6379 sample_size, stream->max_buffer_size);
6381 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6384 if (qtdemux->cenc_aux_info_offset > 0) {
6387 GstBuffer *aux_info = NULL;
6389 /* pull the data stored before the sample */
6391 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6392 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6393 if (G_UNLIKELY (ret != GST_FLOW_OK))
6395 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6396 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6397 gst_byte_reader_init (&br, map.data + 8, map.size);
6398 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6399 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6400 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6401 gst_buffer_unmap (aux_info, &map);
6402 gst_buffer_unref (aux_info);
6403 ret = GST_FLOW_ERROR;
6406 gst_buffer_unmap (aux_info, &map);
6407 gst_buffer_unref (aux_info);
6410 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6413 if (stream->use_allocator) {
6414 /* if we have a per-stream allocator, use it */
6415 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6418 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6420 if (G_UNLIKELY (ret != GST_FLOW_OK))
6423 if (size != sample_size) {
6424 pts += gst_util_uint64_scale_int (GST_SECOND,
6425 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6428 gst_util_uint64_scale_int (GST_SECOND,
6429 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6432 gst_util_uint64_scale_int (GST_SECOND,
6433 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6436 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6437 dts, pts, duration, keyframe, min_time, offset);
6439 if (size != sample_size) {
6440 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6441 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6443 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6445 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6446 if (time_position >= segment->media_start) {
6447 /* inside the segment, update time_position, looks very familiar to
6448 * GStreamer segments, doesn't it? */
6449 stream->time_position = (time_position - segment->media_start) +
6452 /* not yet in segment, time does not yet increment. This means
6453 * that we are still prerolling keyframes to the decoder so it can
6454 * decode the first sample of the segment. */
6455 stream->time_position = segment->time;
6460 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6461 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6462 * we have no more data for the pad to push */
6463 if (ret == GST_FLOW_EOS)
6466 stream->offset_in_sample += size;
6467 if (stream->offset_in_sample >= sample_size) {
6468 gst_qtdemux_advance_sample (qtdemux, stream);
6473 gst_qtdemux_advance_sample (qtdemux, stream);
6481 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6487 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6488 /* EOS will be raised if all are EOS */
6495 gst_qtdemux_loop (GstPad * pad)
6497 GstQTDemux *qtdemux;
6501 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6503 cur_offset = qtdemux->offset;
6504 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6505 cur_offset, qt_demux_state_string (qtdemux->state));
6507 switch (qtdemux->state) {
6508 case QTDEMUX_STATE_INITIAL:
6509 case QTDEMUX_STATE_HEADER:
6510 ret = gst_qtdemux_loop_state_header (qtdemux);
6512 case QTDEMUX_STATE_MOVIE:
6513 ret = gst_qtdemux_loop_state_movie (qtdemux);
6514 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6515 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6523 /* if something went wrong, pause */
6524 if (ret != GST_FLOW_OK)
6528 gst_object_unref (qtdemux);
6534 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6535 (NULL), ("streaming stopped, invalid state"));
6536 gst_pad_pause_task (pad);
6537 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6542 const gchar *reason = gst_flow_get_name (ret);
6544 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6546 gst_pad_pause_task (pad);
6548 /* fatal errors need special actions */
6550 if (ret == GST_FLOW_EOS) {
6551 if (qtdemux->n_streams == 0) {
6552 /* we have no streams, post an error */
6553 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6555 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6558 if ((stop = qtdemux->segment.stop) == -1)
6559 stop = qtdemux->segment.duration;
6561 if (qtdemux->segment.rate >= 0) {
6562 GstMessage *message;
6565 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6566 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6567 GST_FORMAT_TIME, stop);
6568 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6569 if (qtdemux->segment_seqnum) {
6570 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6571 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6573 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6574 gst_qtdemux_push_event (qtdemux, event);
6576 GstMessage *message;
6579 /* For Reverse Playback */
6580 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6581 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6582 GST_FORMAT_TIME, qtdemux->segment.start);
6583 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6584 qtdemux->segment.start);
6585 if (qtdemux->segment_seqnum) {
6586 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6587 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6589 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6590 gst_qtdemux_push_event (qtdemux, event);
6595 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6596 event = gst_event_new_eos ();
6597 if (qtdemux->segment_seqnum)
6598 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6599 gst_qtdemux_push_event (qtdemux, event);
6601 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6602 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6603 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6612 * Returns if there are samples to be played.
6615 has_next_entry (GstQTDemux * demux)
6617 QtDemuxStream *stream;
6620 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6622 for (i = 0; i < demux->n_streams; i++) {
6623 stream = demux->streams[i];
6625 if (stream->sample_index == -1) {
6626 stream->sample_index = 0;
6627 stream->offset_in_sample = 0;
6630 if (stream->sample_index >= stream->n_samples) {
6631 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
6634 GST_DEBUG_OBJECT (demux, "Found a sample");
6638 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6645 * Returns the size of the first entry at the current offset.
6646 * If -1, there are none (which means EOS or empty file).
6649 next_entry_size (GstQTDemux * demux)
6651 QtDemuxStream *stream;
6654 guint64 smalloffs = (guint64) - 1;
6655 QtDemuxSample *sample;
6657 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6660 for (i = 0; i < demux->n_streams; i++) {
6661 stream = demux->streams[i];
6663 if (stream->sample_index == -1) {
6664 stream->sample_index = 0;
6665 stream->offset_in_sample = 0;
6668 if (stream->sample_index >= stream->n_samples) {
6669 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
6673 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6674 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6675 stream->sample_index);
6679 sample = &stream->samples[stream->sample_index];
6681 GST_LOG_OBJECT (demux,
6682 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
6683 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
6684 sample->offset, sample->size);
6686 if (((smalloffs == -1)
6687 || (sample->offset < smalloffs)) && (sample->size)) {
6689 smalloffs = sample->offset;
6693 GST_LOG_OBJECT (demux,
6694 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
6695 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
6700 stream = demux->streams[smallidx];
6701 sample = &stream->samples[stream->sample_index];
6703 if (sample->offset >= demux->offset) {
6704 demux->todrop = sample->offset - demux->offset;
6705 return sample->size + demux->todrop;
6708 GST_DEBUG_OBJECT (demux,
6709 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6714 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6716 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6718 gst_element_post_message (GST_ELEMENT_CAST (demux),
6719 gst_message_new_element (GST_OBJECT_CAST (demux),
6720 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6724 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6729 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6732 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6733 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6734 GST_SEEK_TYPE_NONE, -1);
6736 /* store seqnum to drop flush events, they don't need to reach downstream */
6737 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6738 res = gst_pad_push_event (demux->sinkpad, event);
6739 demux->offset_seek_seqnum = 0;
6744 /* check for seekable upstream, above and beyond a mere query */
6746 gst_qtdemux_check_seekability (GstQTDemux * demux)
6749 gboolean seekable = FALSE;
6750 gint64 start = -1, stop = -1;
6752 if (demux->upstream_size)
6755 if (demux->upstream_format_is_time)
6758 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6759 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6760 GST_DEBUG_OBJECT (demux, "seeking query failed");
6764 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6766 /* try harder to query upstream size if we didn't get it the first time */
6767 if (seekable && stop == -1) {
6768 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6769 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6772 /* if upstream doesn't know the size, it's likely that it's not seekable in
6773 * practice even if it technically may be seekable */
6774 if (seekable && (start != 0 || stop <= start)) {
6775 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6780 gst_query_unref (query);
6782 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6783 G_GUINT64_FORMAT ")", seekable, start, stop);
6784 demux->upstream_seekable = seekable;
6785 demux->upstream_size = seekable ? stop : -1;
6789 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6791 g_return_if_fail (bytes <= demux->todrop);
6793 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6794 gst_adapter_flush (demux->adapter, bytes);
6795 demux->neededbytes -= bytes;
6796 demux->offset += bytes;
6797 demux->todrop -= bytes;
6801 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
6803 if (G_UNLIKELY (demux->pending_newsegment)) {
6806 gst_qtdemux_push_pending_newsegment (demux);
6807 /* clear to send tags on all streams */
6808 for (i = 0; i < demux->n_streams; i++) {
6809 QtDemuxStream *stream;
6810 stream = demux->streams[i];
6811 gst_qtdemux_push_tags (demux, stream);
6812 if (CUR_STREAM (stream)->sparse) {
6813 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6814 gst_pad_push_event (stream->pad,
6815 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
6822 gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
6823 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
6825 GstClockTime ts, dur;
6830 stream->segments[segment_index].duration - (pos -
6831 stream->segments[segment_index].time);
6832 gap = gst_event_new_gap (ts, dur);
6833 stream->time_position += dur;
6835 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
6836 "segment: %" GST_PTR_FORMAT, gap);
6837 gst_pad_push_event (stream->pad, gap);
6841 gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
6842 QtDemuxStream * stream)
6846 /* Push any initial gap segments before proceeding to the
6848 for (i = 0; i < stream->n_segments; i++) {
6849 gst_qtdemux_activate_segment (demux, stream, i, stream->time_position);
6851 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
6852 gst_qtdemux_send_gap_for_segment (demux, stream, i,
6853 stream->time_position);
6855 /* Only support empty segment at the beginning followed by
6856 * one non-empty segment, this was checked when parsing the
6857 * edts atom, arriving here is unexpected */
6858 g_assert (i + 1 == stream->n_segments);
6864 static GstFlowReturn
6865 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6869 demux = GST_QTDEMUX (parent);
6871 GST_DEBUG_OBJECT (demux,
6872 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
6873 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
6874 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
6875 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
6876 gst_buffer_get_size (inbuf), demux->offset);
6878 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6879 gboolean is_gap_input = FALSE;
6882 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6884 for (i = 0; i < demux->n_streams; i++) {
6885 demux->streams[i]->discont = TRUE;
6888 /* Check if we can land back on our feet in the case where upstream is
6889 * handling the seeking/pushing of samples with gaps in between (like
6890 * in the case of trick-mode DASH for example) */
6891 if (demux->upstream_format_is_time
6892 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
6894 for (i = 0; i < demux->n_streams; i++) {
6896 GST_LOG_OBJECT (demux,
6897 "Stream #%d , checking if offset %" G_GUINT64_FORMAT
6898 " is a sample start", i, GST_BUFFER_OFFSET (inbuf));
6900 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
6901 demux->streams[i], GST_BUFFER_OFFSET (inbuf));
6903 QtDemuxSample *sample = &demux->streams[i]->samples[res];
6904 GST_LOG_OBJECT (demux,
6905 "Checking if sample %d from stream %d is valid (offset:%"
6906 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res, i,
6907 sample->offset, sample->size);
6908 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
6909 GST_LOG_OBJECT (demux,
6910 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
6912 is_gap_input = TRUE;
6913 /* We can go back to standard playback mode */
6914 demux->state = QTDEMUX_STATE_MOVIE;
6915 /* Remember which sample this stream is at */
6916 demux->streams[i]->sample_index = res;
6917 /* Finally update all push-based values to the expected values */
6918 demux->neededbytes = demux->streams[i]->samples[res].size;
6919 demux->offset = GST_BUFFER_OFFSET (inbuf);
6921 demux->mdatsize - demux->offset + demux->mdatoffset;
6926 if (!is_gap_input) {
6927 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
6928 /* Reset state if it's a real discont */
6929 demux->neededbytes = 16;
6930 demux->state = QTDEMUX_STATE_INITIAL;
6931 demux->offset = GST_BUFFER_OFFSET (inbuf);
6932 gst_adapter_clear (demux->adapter);
6935 /* Reverse fragmented playback, need to flush all we have before
6936 * consuming a new fragment.
6937 * The samples array have the timestamps calculated by accumulating the
6938 * durations but this won't work for reverse playback of fragments as
6939 * the timestamps of a subsequent fragment should be smaller than the
6940 * previously received one. */
6941 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
6942 gst_qtdemux_process_adapter (demux, TRUE);
6943 for (i = 0; i < demux->n_streams; i++)
6944 gst_qtdemux_stream_flush_samples_data (demux, demux->streams[i]);
6948 gst_adapter_push (demux->adapter, inbuf);
6950 GST_DEBUG_OBJECT (demux,
6951 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6952 demux->neededbytes, gst_adapter_available (demux->adapter));
6954 return gst_qtdemux_process_adapter (demux, FALSE);
6957 static GstFlowReturn
6958 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6960 GstFlowReturn ret = GST_FLOW_OK;
6962 /* we never really mean to buffer that much */
6963 if (demux->neededbytes == -1) {
6967 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6968 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6970 #ifndef GST_DISABLE_GST_DEBUG
6972 guint64 discont_offset, distance_from_discont;
6974 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
6975 distance_from_discont =
6976 gst_adapter_distance_from_discont (demux->adapter);
6978 GST_DEBUG_OBJECT (demux,
6979 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
6980 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
6981 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
6982 demux->offset, discont_offset, distance_from_discont);
6986 switch (demux->state) {
6987 case QTDEMUX_STATE_INITIAL:{
6992 gst_qtdemux_check_seekability (demux);
6994 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6996 /* get fourcc/length, set neededbytes */
6997 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6999 gst_adapter_unmap (demux->adapter);
7001 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7002 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7004 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7005 (_("This file is invalid and cannot be played.")),
7006 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7007 GST_FOURCC_ARGS (fourcc)));
7008 ret = GST_FLOW_ERROR;
7011 if (fourcc == FOURCC_mdat) {
7012 gint next_entry = next_entry_size (demux);
7013 if (demux->n_streams > 0 && (next_entry != -1 || !demux->fragmented)) {
7014 /* we have the headers, start playback */
7015 demux->state = QTDEMUX_STATE_MOVIE;
7016 demux->neededbytes = next_entry;
7017 demux->mdatleft = size;
7018 demux->mdatsize = demux->mdatleft;
7020 /* no headers yet, try to get them */
7023 guint64 old, target;
7026 old = demux->offset;
7027 target = old + size;
7029 /* try to jump over the atom with a seek */
7030 /* only bother if it seems worth doing so,
7031 * and avoids possible upstream/server problems */
7032 if (demux->upstream_seekable &&
7033 demux->upstream_size > 4 * (1 << 20)) {
7034 res = qtdemux_seek_offset (demux, target);
7036 GST_DEBUG_OBJECT (demux, "skipping seek");
7041 GST_DEBUG_OBJECT (demux, "seek success");
7042 /* remember the offset fo the first mdat so we can seek back to it
7043 * after we have the headers */
7044 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7045 demux->first_mdat = old;
7046 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7049 /* seek worked, continue reading */
7050 demux->offset = target;
7051 demux->neededbytes = 16;
7052 demux->state = QTDEMUX_STATE_INITIAL;
7054 /* seek failed, need to buffer */
7055 demux->offset = old;
7056 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7057 /* there may be multiple mdat (or alike) buffers */
7059 if (demux->mdatbuffer)
7060 bs = gst_buffer_get_size (demux->mdatbuffer);
7063 if (size + bs > 10 * (1 << 20))
7065 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7066 demux->neededbytes = size;
7067 if (!demux->mdatbuffer)
7068 demux->mdatoffset = demux->offset;
7071 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7072 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7073 (_("This file is invalid and cannot be played.")),
7074 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7075 GST_FOURCC_ARGS (fourcc), size));
7076 ret = GST_FLOW_ERROR;
7079 /* this means we already started buffering and still no moov header,
7080 * let's continue buffering everything till we get moov */
7081 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7082 || fourcc == FOURCC_moof))
7084 demux->neededbytes = size;
7085 demux->state = QTDEMUX_STATE_HEADER;
7089 case QTDEMUX_STATE_HEADER:{
7093 GST_DEBUG_OBJECT (demux, "In header");
7095 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7097 /* parse the header */
7098 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7100 if (fourcc == FOURCC_moov) {
7103 /* in usual fragmented setup we could try to scan for more
7104 * and end up at the the moov (after mdat) again */
7105 if (demux->got_moov && demux->n_streams > 0 &&
7107 || demux->last_moov_offset == demux->offset)) {
7108 GST_DEBUG_OBJECT (demux,
7109 "Skipping moov atom as we have (this) one already");
7111 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7113 if (demux->got_moov && demux->fragmented) {
7114 GST_DEBUG_OBJECT (demux,
7115 "Got a second moov, clean up data from old one");
7116 if (demux->moov_node_compressed) {
7117 g_node_destroy (demux->moov_node_compressed);
7118 if (demux->moov_node)
7119 g_free (demux->moov_node->data);
7121 demux->moov_node_compressed = NULL;
7122 if (demux->moov_node)
7123 g_node_destroy (demux->moov_node);
7124 demux->moov_node = NULL;
7126 /* prepare newsegment to send when streaming actually starts */
7127 if (!demux->pending_newsegment) {
7128 demux->pending_newsegment =
7129 gst_event_new_segment (&demux->segment);
7130 if (demux->segment_seqnum)
7131 gst_event_set_seqnum (demux->pending_newsegment,
7132 demux->segment_seqnum);
7136 demux->last_moov_offset = demux->offset;
7138 qtdemux_parse_moov (demux, data, demux->neededbytes);
7139 qtdemux_node_dump (demux, demux->moov_node);
7140 qtdemux_parse_tree (demux);
7141 qtdemux_prepare_streams (demux);
7142 if (!demux->got_moov)
7143 qtdemux_expose_streams (demux);
7146 for (n = 0; n < demux->n_streams; n++) {
7147 QtDemuxStream *stream = demux->streams[n];
7149 gst_qtdemux_configure_stream (demux, stream);
7153 demux->got_moov = TRUE;
7154 gst_qtdemux_check_send_pending_segment (demux);
7156 /* fragmented streams headers shouldn't contain edts atoms */
7157 if (!demux->fragmented) {
7158 for (n = 0; n < demux->n_streams; n++) {
7159 gst_qtdemux_stream_send_initial_gap_segments (demux,
7164 if (demux->moov_node_compressed) {
7165 g_node_destroy (demux->moov_node_compressed);
7166 g_free (demux->moov_node->data);
7168 demux->moov_node_compressed = NULL;
7169 g_node_destroy (demux->moov_node);
7170 demux->moov_node = NULL;
7171 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7173 } else if (fourcc == FOURCC_moof) {
7174 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7176 GstClockTime prev_pts;
7177 guint64 prev_offset;
7178 guint64 adapter_discont_offset, adapter_discont_dist;
7180 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7183 * The timestamp of the moof buffer is relevant as some scenarios
7184 * won't have the initial timestamp in the atoms. Whenever a new
7185 * buffer has started, we get that buffer's PTS and use it as a base
7186 * timestamp for the trun entries.
7188 * To keep track of the current buffer timestamp and starting point
7189 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7190 * from the beggining of the buffer, with the distance and demux->offset
7191 * we know if it is still the same buffer or not.
7193 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7194 prev_offset = demux->offset - dist;
7195 if (demux->fragment_start_offset == -1
7196 || prev_offset > demux->fragment_start_offset) {
7197 demux->fragment_start_offset = prev_offset;
7198 demux->fragment_start = prev_pts;
7199 GST_DEBUG_OBJECT (demux,
7200 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7201 GST_TIME_FORMAT, demux->fragment_start_offset,
7202 GST_TIME_ARGS (demux->fragment_start));
7205 /* We can't use prev_offset() here because this would require
7206 * upstream to set consistent and correct offsets on all buffers
7207 * since the discont. Nothing ever did that in the past and we
7208 * would break backwards compatibility here then.
7209 * Instead take the offset we had at the last discont and count
7210 * the bytes from there. This works with old code as there would
7211 * be no discont between moov and moof, and also works with
7212 * adaptivedemux which correctly sets offset and will set the
7213 * DISCONT flag accordingly when needed.
7215 * We also only do this for upstream TIME segments as otherwise
7216 * there are potential backwards compatibility problems with
7217 * seeking in PUSH mode and upstream providing inconsistent
7219 adapter_discont_offset =
7220 gst_adapter_offset_at_discont (demux->adapter);
7221 adapter_discont_dist =
7222 gst_adapter_distance_from_discont (demux->adapter);
7224 GST_DEBUG_OBJECT (demux,
7225 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7226 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7227 demux->offset, adapter_discont_offset, adapter_discont_dist);
7229 if (demux->upstream_format_is_time) {
7230 demux->moof_offset = adapter_discont_offset;
7231 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7232 demux->moof_offset += adapter_discont_dist;
7233 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7234 demux->moof_offset = demux->offset;
7236 demux->moof_offset = demux->offset;
7239 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7240 demux->moof_offset, NULL)) {
7241 gst_adapter_unmap (demux->adapter);
7242 ret = GST_FLOW_ERROR;
7245 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7246 if (demux->mss_mode && !demux->exposed) {
7247 if (!demux->pending_newsegment) {
7248 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
7249 demux->pending_newsegment =
7250 gst_event_new_segment (&demux->segment);
7251 if (demux->segment_seqnum)
7252 gst_event_set_seqnum (demux->pending_newsegment,
7253 demux->segment_seqnum);
7255 qtdemux_expose_streams (demux);
7258 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7260 } else if (fourcc == FOURCC_ftyp) {
7261 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7262 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7263 } else if (fourcc == FOURCC_uuid) {
7264 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7265 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7266 } else if (fourcc == FOURCC_sidx) {
7267 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7268 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7272 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7275 /* [free] is a padding atom */
7276 GST_DEBUG_OBJECT (demux,
7277 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7278 GST_FOURCC_ARGS (fourcc));
7281 GST_WARNING_OBJECT (demux,
7282 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7283 GST_FOURCC_ARGS (fourcc));
7284 /* Let's jump that one and go back to initial state */
7288 gst_adapter_unmap (demux->adapter);
7291 if (demux->mdatbuffer && demux->n_streams) {
7292 gsize remaining_data_size = 0;
7294 /* the mdat was before the header */
7295 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7296 demux->n_streams, demux->mdatbuffer);
7297 /* restore our adapter/offset view of things with upstream;
7298 * put preceding buffered data ahead of current moov data.
7299 * This should also handle evil mdat, moov, mdat cases and alike */
7300 gst_adapter_flush (demux->adapter, demux->neededbytes);
7302 /* Store any remaining data after the mdat for later usage */
7303 remaining_data_size = gst_adapter_available (demux->adapter);
7304 if (remaining_data_size > 0) {
7305 g_assert (demux->restoredata_buffer == NULL);
7306 demux->restoredata_buffer =
7307 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7308 demux->restoredata_offset = demux->offset + demux->neededbytes;
7309 GST_DEBUG_OBJECT (demux,
7310 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7311 G_GUINT64_FORMAT, remaining_data_size,
7312 demux->restoredata_offset);
7315 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7316 demux->mdatbuffer = NULL;
7317 demux->offset = demux->mdatoffset;
7318 demux->neededbytes = next_entry_size (demux);
7319 demux->state = QTDEMUX_STATE_MOVIE;
7320 demux->mdatleft = gst_adapter_available (demux->adapter);
7321 demux->mdatsize = demux->mdatleft;
7323 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7324 gst_adapter_flush (demux->adapter, demux->neededbytes);
7326 /* only go back to the mdat if there are samples to play */
7327 if (demux->got_moov && demux->first_mdat != -1
7328 && has_next_entry (demux)) {
7331 /* we need to seek back */
7332 res = qtdemux_seek_offset (demux, demux->first_mdat);
7334 demux->offset = demux->first_mdat;
7336 GST_DEBUG_OBJECT (demux, "Seek back failed");
7339 demux->offset += demux->neededbytes;
7341 demux->neededbytes = 16;
7342 demux->state = QTDEMUX_STATE_INITIAL;
7347 case QTDEMUX_STATE_BUFFER_MDAT:{
7351 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7353 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7354 gst_buffer_extract (buf, 0, fourcc, 4);
7355 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7356 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7357 if (demux->mdatbuffer)
7358 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7360 demux->mdatbuffer = buf;
7361 demux->offset += demux->neededbytes;
7362 demux->neededbytes = 16;
7363 demux->state = QTDEMUX_STATE_INITIAL;
7364 gst_qtdemux_post_progress (demux, 1, 1);
7368 case QTDEMUX_STATE_MOVIE:{
7369 QtDemuxStream *stream = NULL;
7370 QtDemuxSample *sample;
7372 GstClockTime dts, pts, duration;
7375 GST_DEBUG_OBJECT (demux,
7376 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7378 if (demux->fragmented) {
7379 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7381 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7382 /* if needed data starts within this atom,
7383 * then it should not exceed this atom */
7384 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7385 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7386 (_("This file is invalid and cannot be played.")),
7387 ("sample data crosses atom boundary"));
7388 ret = GST_FLOW_ERROR;
7391 demux->mdatleft -= demux->neededbytes;
7393 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7394 /* so we are dropping more than left in this atom */
7395 gst_qtdemux_drop_data (demux, demux->mdatleft);
7396 demux->mdatleft = 0;
7398 /* need to resume atom parsing so we do not miss any other pieces */
7399 demux->state = QTDEMUX_STATE_INITIAL;
7400 demux->neededbytes = 16;
7402 /* check if there was any stored post mdat data from previous buffers */
7403 if (demux->restoredata_buffer) {
7404 g_assert (gst_adapter_available (demux->adapter) == 0);
7406 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7407 demux->restoredata_buffer = NULL;
7408 demux->offset = demux->restoredata_offset;
7415 if (demux->todrop) {
7416 if (demux->cenc_aux_info_offset > 0) {
7420 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7421 data = gst_adapter_map (demux->adapter, demux->todrop);
7422 gst_byte_reader_init (&br, data + 8, demux->todrop);
7423 if (!qtdemux_parse_cenc_aux_info (demux, demux->streams[0], &br,
7424 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7425 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7426 ret = GST_FLOW_ERROR;
7427 gst_adapter_unmap (demux->adapter);
7428 g_free (demux->cenc_aux_info_sizes);
7429 demux->cenc_aux_info_sizes = NULL;
7432 demux->cenc_aux_info_offset = 0;
7433 g_free (demux->cenc_aux_info_sizes);
7434 demux->cenc_aux_info_sizes = NULL;
7435 gst_adapter_unmap (demux->adapter);
7437 gst_qtdemux_drop_data (demux, demux->todrop);
7441 /* initial newsegment sent here after having added pads,
7442 * possible others in sink_event */
7443 gst_qtdemux_check_send_pending_segment (demux);
7445 /* Figure out which stream this packet belongs to */
7446 for (i = 0; i < demux->n_streams; i++) {
7447 stream = demux->streams[i];
7448 if (stream->sample_index >= stream->n_samples)
7450 GST_LOG_OBJECT (demux,
7451 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
7452 " / size:%d)", i, stream->sample_index,
7453 stream->samples[stream->sample_index].offset,
7454 stream->samples[stream->sample_index].size);
7456 if (stream->samples[stream->sample_index].offset == demux->offset)
7460 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
7461 goto unknown_stream;
7463 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7465 if (stream->new_caps) {
7466 gst_qtdemux_configure_stream (demux, stream);
7469 /* Put data in a buffer, set timestamps, caps, ... */
7470 sample = &stream->samples[stream->sample_index];
7472 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7473 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7474 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7476 dts = QTSAMPLE_DTS (stream, sample);
7477 pts = QTSAMPLE_PTS (stream, sample);
7478 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7479 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7481 /* check for segment end */
7482 if (G_UNLIKELY (demux->segment.stop != -1
7483 && demux->segment.stop <= pts && stream->on_keyframe)) {
7484 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7485 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7487 /* skip this data, stream is EOS */
7488 gst_adapter_flush (demux->adapter, demux->neededbytes);
7489 demux->offset += demux->neededbytes;
7491 /* check if all streams are eos */
7493 for (i = 0; i < demux->n_streams; i++) {
7494 if (!STREAM_IS_EOS (demux->streams[i])) {
7503 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7505 /* FIXME: should either be an assert or a plain check */
7506 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7508 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7509 dts, pts, duration, keyframe, dts, demux->offset);
7513 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7515 /* skip this data, stream is EOS */
7516 gst_adapter_flush (demux->adapter, demux->neededbytes);
7519 stream->sample_index++;
7520 stream->offset_in_sample = 0;
7522 /* update current offset and figure out size of next buffer */
7523 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7524 demux->offset, demux->neededbytes);
7525 demux->offset += demux->neededbytes;
7526 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7530 if (ret == GST_FLOW_EOS) {
7531 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7532 demux->neededbytes = -1;
7536 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7537 if (demux->fragmented) {
7538 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7539 /* there may be more to follow, only finish this atom */
7540 demux->todrop = demux->mdatleft;
7541 demux->neededbytes = demux->todrop;
7546 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7547 goto non_ok_unlinked_flow;
7556 /* when buffering movie data, at least show user something is happening */
7557 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7558 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7559 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7560 demux->neededbytes);
7567 non_ok_unlinked_flow:
7569 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7570 gst_flow_get_name (ret));
7575 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7576 ret = GST_FLOW_ERROR;
7581 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7587 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7588 (NULL), ("qtdemuxer invalid state %d", demux->state));
7589 ret = GST_FLOW_ERROR;
7594 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7595 (NULL), ("no 'moov' atom within the first 10 MB"));
7596 ret = GST_FLOW_ERROR;
7602 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7607 query = gst_query_new_scheduling ();
7609 if (!gst_pad_peer_query (sinkpad, query)) {
7610 gst_query_unref (query);
7614 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7615 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7616 gst_query_unref (query);
7621 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7622 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7626 GST_DEBUG_OBJECT (sinkpad, "activating push");
7627 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7632 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7633 GstPadMode mode, gboolean active)
7636 GstQTDemux *demux = GST_QTDEMUX (parent);
7639 case GST_PAD_MODE_PUSH:
7640 demux->pullbased = FALSE;
7643 case GST_PAD_MODE_PULL:
7645 demux->pullbased = TRUE;
7646 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7649 res = gst_pad_stop_task (sinkpad);
7661 qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7667 memset (&z, 0, sizeof (z));
7672 if ((ret = inflateInit (&z)) != Z_OK) {
7673 GST_ERROR ("inflateInit() returned %d", ret);
7677 z.next_in = z_buffer;
7678 z.avail_in = z_length;
7680 buffer = (guint8 *) g_malloc (*length);
7681 z.avail_out = *length;
7682 z.next_out = (Bytef *) buffer;
7684 ret = inflate (&z, Z_NO_FLUSH);
7685 if (ret == Z_STREAM_END) {
7687 } else if (ret != Z_OK) {
7688 GST_WARNING ("inflate() returned %d", ret);
7693 buffer = (guint8 *) g_realloc (buffer, *length);
7694 z.next_out = (Bytef *) (buffer + z.total_out);
7695 z.avail_out += 4096;
7696 } while (z.avail_in > 0);
7698 if (ret != Z_STREAM_END) {
7703 *length = z.total_out;
7710 #endif /* HAVE_ZLIB */
7713 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7717 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7719 /* counts as header data */
7720 qtdemux->header_size += length;
7722 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7723 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7725 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7732 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7733 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7734 if (dcom == NULL || cmvd == NULL)
7735 goto invalid_compression;
7737 dcom_len = QT_UINT32 (dcom->data);
7739 goto invalid_compression;
7741 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7745 guint uncompressed_length;
7746 guint compressed_length;
7750 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7752 goto invalid_compression;
7754 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
7755 compressed_length = cmvd_len - 12;
7756 GST_LOG ("length = %u", uncompressed_length);
7759 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
7760 compressed_length, &uncompressed_length);
7763 qtdemux->moov_node_compressed = qtdemux->moov_node;
7764 qtdemux->moov_node = g_node_new (buf);
7766 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
7767 uncompressed_length);
7771 #endif /* HAVE_ZLIB */
7773 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
7774 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
7781 invalid_compression:
7783 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
7789 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
7792 while (G_UNLIKELY (buf < end)) {
7796 if (G_UNLIKELY (buf + 4 > end)) {
7797 GST_LOG_OBJECT (qtdemux, "buffer overrun");
7800 len = QT_UINT32 (buf);
7801 if (G_UNLIKELY (len == 0)) {
7802 GST_LOG_OBJECT (qtdemux, "empty container");
7805 if (G_UNLIKELY (len < 8)) {
7806 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
7809 if (G_UNLIKELY (len > (end - buf))) {
7810 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
7811 (gint) (end - buf));
7815 child = g_node_new ((guint8 *) buf);
7816 g_node_append (node, child);
7817 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
7818 qtdemux_parse_node (qtdemux, child, buf, len);
7826 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
7829 int len = QT_UINT32 (xdxt->data);
7830 guint8 *buf = xdxt->data;
7831 guint8 *end = buf + len;
7834 /* skip size and type */
7842 size = QT_UINT32 (buf);
7843 type = QT_FOURCC (buf + 4);
7845 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
7847 if (buf + size > end || size <= 0)
7853 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
7854 GST_FOURCC_ARGS (type));
7858 buffer = gst_buffer_new_and_alloc (size);
7859 gst_buffer_fill (buffer, 0, buf, size);
7860 stream->buffers = g_slist_append (stream->buffers, buffer);
7861 GST_LOG_OBJECT (qtdemux, "parsing theora header");
7864 buffer = gst_buffer_new_and_alloc (size);
7865 gst_buffer_fill (buffer, 0, buf, size);
7866 stream->buffers = g_slist_append (stream->buffers, buffer);
7867 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
7870 buffer = gst_buffer_new_and_alloc (size);
7871 gst_buffer_fill (buffer, 0, buf, size);
7872 stream->buffers = g_slist_append (stream->buffers, buffer);
7873 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
7876 GST_WARNING_OBJECT (qtdemux,
7877 "unknown theora cookie %" GST_FOURCC_FORMAT,
7878 GST_FOURCC_ARGS (type));
7887 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
7891 guint32 node_length = 0;
7892 const QtNodeType *type;
7895 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
7897 if (G_UNLIKELY (length < 8))
7898 goto not_enough_data;
7900 node_length = QT_UINT32 (buffer);
7901 fourcc = QT_FOURCC (buffer + 4);
7903 /* ignore empty nodes */
7904 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
7907 type = qtdemux_type_get (fourcc);
7909 end = buffer + length;
7911 GST_LOG_OBJECT (qtdemux,
7912 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
7913 GST_FOURCC_ARGS (fourcc), node_length, type->name);
7915 if (node_length > length)
7916 goto broken_atom_size;
7918 if (type->flags & QT_FLAG_CONTAINER) {
7919 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
7924 if (node_length < 20) {
7925 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
7928 GST_DEBUG_OBJECT (qtdemux,
7929 "parsing stsd (sample table, sample description) atom");
7930 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
7931 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7942 /* also read alac (or whatever) in stead of mp4a in the following,
7943 * since a similar layout is used in other cases as well */
7944 if (fourcc == FOURCC_mp4a)
7946 else if (fourcc == FOURCC_fLaC)
7951 /* There are two things we might encounter here: a true mp4a atom, and
7952 an mp4a entry in an stsd atom. The latter is what we're interested
7953 in, and it looks like an atom, but isn't really one. The true mp4a
7954 atom is short, so we detect it based on length here. */
7955 if (length < min_size) {
7956 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
7957 GST_FOURCC_ARGS (fourcc));
7961 /* 'version' here is the sound sample description version. Types 0 and
7962 1 are documented in the QTFF reference, but type 2 is not: it's
7963 described in Apple header files instead (struct SoundDescriptionV2
7965 version = QT_UINT16 (buffer + 16);
7967 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7968 GST_FOURCC_ARGS (fourcc), version);
7970 /* parse any esds descriptors */
7982 GST_WARNING_OBJECT (qtdemux,
7983 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7984 GST_FOURCC_ARGS (fourcc), version);
7989 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8015 /* codec_data is contained inside these atoms, which all have
8016 * the same format. */
8017 /* video sample description size is 86 bytes without extension.
8018 * node_length have to be bigger than 86 bytes because video sample
8019 * description can include extenstions such as esds, fiel, glbl, etc. */
8020 if (node_length < 86) {
8021 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8022 " sample description length too short (%u < 86)",
8023 GST_FOURCC_ARGS (fourcc), node_length);
8027 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8028 GST_FOURCC_ARGS (fourcc));
8030 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8032 * revision level (2 bytes) : must be set to 0. */
8033 version = QT_UINT32 (buffer + 16);
8034 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8036 /* compressor name : PASCAL string and informative purposes
8037 * first byte : the number of bytes to be displayed.
8038 * it has to be less than 32 because it is reserved
8039 * space of 32 bytes total including itself. */
8040 str_len = QT_UINT8 (buffer + 50);
8042 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8043 (char *) buffer + 51);
8045 GST_WARNING_OBJECT (qtdemux,
8046 "compressorname length too big (%u > 31)", str_len);
8048 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8050 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8055 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8056 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8061 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8062 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8063 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8072 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8073 GST_FOURCC_ARGS (fourcc));
8077 version = QT_UINT32 (buffer + 12);
8078 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8085 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8090 if (length < offset) {
8091 GST_WARNING_OBJECT (qtdemux,
8092 "skipping too small %" GST_FOURCC_FORMAT " box",
8093 GST_FOURCC_ARGS (fourcc));
8096 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8102 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8107 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8112 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8115 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
8118 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
8121 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
8123 if (!strcmp (type->name, "unknown"))
8124 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8128 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8129 GST_FOURCC_ARGS (fourcc));
8135 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8136 (_("This file is corrupt and cannot be played.")),
8137 ("Not enough data for an atom header, got only %u bytes", length));
8142 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8143 (_("This file is corrupt and cannot be played.")),
8144 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8145 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8152 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
8156 guint32 child_fourcc;
8158 for (child = g_node_first_child (node); child;
8159 child = g_node_next_sibling (child)) {
8160 buffer = (guint8 *) child->data;
8162 child_fourcc = QT_FOURCC (buffer + 4);
8164 if (G_UNLIKELY (child_fourcc == fourcc)) {
8172 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
8173 GstByteReader * parser)
8177 guint32 child_fourcc, child_len;
8179 for (child = g_node_first_child (node); child;
8180 child = g_node_next_sibling (child)) {
8181 buffer = (guint8 *) child->data;
8183 child_len = QT_UINT32 (buffer);
8184 child_fourcc = QT_FOURCC (buffer + 4);
8186 if (G_UNLIKELY (child_fourcc == fourcc)) {
8187 if (G_UNLIKELY (child_len < (4 + 4)))
8189 /* FIXME: must verify if atom length < parent atom length */
8190 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8198 qtdemux_tree_get_child_by_index (GNode * node, guint index)
8200 return g_node_nth_child (node, index);
8204 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
8205 GstByteReader * parser)
8209 guint32 child_fourcc, child_len;
8211 for (child = g_node_next_sibling (node); child;
8212 child = g_node_next_sibling (child)) {
8213 buffer = (guint8 *) child->data;
8215 child_fourcc = QT_FOURCC (buffer + 4);
8217 if (child_fourcc == fourcc) {
8219 child_len = QT_UINT32 (buffer);
8220 if (G_UNLIKELY (child_len < (4 + 4)))
8222 /* FIXME: must verify if atom length < parent atom length */
8223 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
8232 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
8234 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
8238 qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
8240 /* FIXME: This can only reliably work if demuxers have a
8241 * separate streaming thread per srcpad. This should be
8242 * done in a demuxer base class, which integrates parts
8245 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8250 query = gst_query_new_allocation (stream->caps, FALSE);
8252 if (!gst_pad_peer_query (stream->pad, query)) {
8253 /* not a problem, just debug a little */
8254 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8257 if (stream->allocator)
8258 gst_object_unref (stream->allocator);
8260 if (gst_query_get_n_allocation_params (query) > 0) {
8261 /* try the allocator */
8262 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8264 stream->use_allocator = TRUE;
8266 stream->allocator = NULL;
8267 gst_allocation_params_init (&stream->params);
8268 stream->use_allocator = FALSE;
8270 gst_query_unref (query);
8275 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8276 QtDemuxStream * stream)
8279 const gchar *selected_system;
8281 g_return_val_if_fail (qtdemux != NULL, FALSE);
8282 g_return_val_if_fail (stream != NULL, FALSE);
8283 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8286 if (stream->protection_scheme_type != FOURCC_cenc) {
8287 GST_ERROR_OBJECT (qtdemux, "unsupported protection scheme");
8290 if (qtdemux->protection_system_ids == NULL) {
8291 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
8292 "cenc protection system information has been found");
8295 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8296 selected_system = gst_protection_select_system ((const gchar **)
8297 qtdemux->protection_system_ids->pdata);
8298 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8299 qtdemux->protection_system_ids->len - 1);
8300 if (!selected_system) {
8301 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8302 "suitable decryptor element has been found");
8306 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8307 if (!gst_structure_has_name (s, "application/x-cenc")) {
8308 gst_structure_set (s,
8309 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8310 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8312 gst_structure_set_name (s, "application/x-cenc");
8318 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8320 if (stream->subtype == FOURCC_vide) {
8321 /* fps is calculated base on the duration of the average framerate since
8322 * qt does not have a fixed framerate. */
8323 gboolean fps_available = TRUE;
8325 if ((stream->n_samples == 1) && (stream->first_duration == 0)) {
8327 CUR_STREAM (stream)->fps_n = 0;
8328 CUR_STREAM (stream)->fps_d = 1;
8330 if (stream->duration == 0 || stream->n_samples < 2) {
8331 CUR_STREAM (stream)->fps_n = stream->timescale;
8332 CUR_STREAM (stream)->fps_d = 1;
8333 fps_available = FALSE;
8335 GstClockTime avg_duration;
8339 /* duration and n_samples can be updated for fragmented format
8340 * so, framerate of fragmented format is calculated using data in a moof */
8341 if (qtdemux->fragmented && stream->n_samples_moof > 0
8342 && stream->duration_moof > 0) {
8343 n_samples = stream->n_samples_moof;
8344 duration = stream->duration_moof;
8346 n_samples = stream->n_samples;
8347 duration = stream->duration;
8350 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8351 /* stream->duration is guint64, timescale, n_samples are guint32 */
8353 gst_util_uint64_scale_round (duration -
8354 stream->first_duration, GST_SECOND,
8355 (guint64) (stream->timescale) * (n_samples - 1));
8357 GST_LOG_OBJECT (qtdemux,
8358 "Calculating avg sample duration based on stream (or moof) duration %"
8360 " minus first sample %u, leaving %d samples gives %"
8361 GST_TIME_FORMAT, duration, stream->first_duration,
8362 n_samples - 1, GST_TIME_ARGS (avg_duration));
8364 gst_video_guess_framerate (avg_duration, &CUR_STREAM (stream)->fps_n,
8365 &CUR_STREAM (stream)->fps_d);
8367 GST_DEBUG_OBJECT (qtdemux,
8368 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8369 stream->timescale, CUR_STREAM (stream)->fps_n,
8370 CUR_STREAM (stream)->fps_d);
8374 if (CUR_STREAM (stream)->caps) {
8375 CUR_STREAM (stream)->caps =
8376 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8378 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8379 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8380 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8382 /* set framerate if calculated framerate is reliable */
8383 if (fps_available) {
8384 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8385 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8386 CUR_STREAM (stream)->fps_d, NULL);
8389 /* calculate pixel-aspect-ratio using display width and height */
8390 GST_DEBUG_OBJECT (qtdemux,
8391 "video size %dx%d, target display size %dx%d",
8392 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8393 stream->display_width, stream->display_height);
8394 /* qt file might have pasp atom */
8395 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8396 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8397 CUR_STREAM (stream)->par_h);
8398 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8399 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8400 CUR_STREAM (stream)->par_h, NULL);
8401 } else if (stream->display_width > 0 && stream->display_height > 0
8402 && CUR_STREAM (stream)->width > 0
8403 && CUR_STREAM (stream)->height > 0) {
8406 /* calculate the pixel aspect ratio using the display and pixel w/h */
8407 n = stream->display_width * CUR_STREAM (stream)->height;
8408 d = stream->display_height * CUR_STREAM (stream)->width;
8411 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8412 CUR_STREAM (stream)->par_w = n;
8413 CUR_STREAM (stream)->par_h = d;
8414 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8415 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8416 CUR_STREAM (stream)->par_h, NULL);
8419 if (CUR_STREAM (stream)->interlace_mode > 0) {
8420 if (CUR_STREAM (stream)->interlace_mode == 1) {
8421 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8422 G_TYPE_STRING, "progressive", NULL);
8423 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8424 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8425 G_TYPE_STRING, "interleaved", NULL);
8426 if (CUR_STREAM (stream)->field_order == 9) {
8427 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8428 G_TYPE_STRING, "top-field-first", NULL);
8429 } else if (CUR_STREAM (stream)->field_order == 14) {
8430 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8431 G_TYPE_STRING, "bottom-field-first", NULL);
8436 /* Create incomplete colorimetry here if needed */
8437 if (CUR_STREAM (stream)->colorimetry.range ||
8438 CUR_STREAM (stream)->colorimetry.matrix ||
8439 CUR_STREAM (stream)->colorimetry.transfer
8440 || CUR_STREAM (stream)->colorimetry.primaries) {
8441 gchar *colorimetry =
8442 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8443 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8444 G_TYPE_STRING, colorimetry, NULL);
8445 g_free (colorimetry);
8448 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8449 guint par_w = 1, par_h = 1;
8451 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8452 par_w = CUR_STREAM (stream)->par_w;
8453 par_h = CUR_STREAM (stream)->par_h;
8456 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8457 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8459 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8462 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8463 "multiview-mode", G_TYPE_STRING,
8464 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8465 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8466 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8471 else if (stream->subtype == FOURCC_soun) {
8472 if (CUR_STREAM (stream)->caps) {
8473 CUR_STREAM (stream)->caps =
8474 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8475 if (CUR_STREAM (stream)->rate > 0)
8476 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8477 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8478 if (CUR_STREAM (stream)->n_channels > 0)
8479 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8480 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8481 if (CUR_STREAM (stream)->n_channels > 2) {
8482 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8483 * correctly; this is just the minimum we can do - assume
8484 * we don't actually have any channel positions. */
8485 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8486 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8492 GstCaps *prev_caps = NULL;
8494 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8495 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8496 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8497 gst_pad_set_active (stream->pad, TRUE);
8499 gst_pad_use_fixed_caps (stream->pad);
8501 if (stream->protected) {
8502 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8503 GST_ERROR_OBJECT (qtdemux,
8504 "Failed to configure protected stream caps.");
8509 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8510 CUR_STREAM (stream)->caps);
8511 if (stream->new_stream) {
8514 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8517 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8520 gst_event_parse_stream_flags (event, &stream_flags);
8521 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8522 qtdemux->have_group_id = TRUE;
8524 qtdemux->have_group_id = FALSE;
8525 gst_event_unref (event);
8526 } else if (!qtdemux->have_group_id) {
8527 qtdemux->have_group_id = TRUE;
8528 qtdemux->group_id = gst_util_group_id_next ();
8531 stream->new_stream = FALSE;
8533 gst_pad_create_stream_id_printf (stream->pad,
8534 GST_ELEMENT_CAST (qtdemux), "%03u", stream->track_id);
8535 event = gst_event_new_stream_start (stream_id);
8536 if (qtdemux->have_group_id)
8537 gst_event_set_group_id (event, qtdemux->group_id);
8538 if (stream->disabled)
8539 stream_flags |= GST_STREAM_FLAG_UNSELECT;
8540 if (CUR_STREAM (stream)->sparse) {
8541 stream_flags |= GST_STREAM_FLAG_SPARSE;
8543 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
8545 gst_event_set_stream_flags (event, stream_flags);
8546 gst_pad_push_event (stream->pad, event);
8550 prev_caps = gst_pad_get_current_caps (stream->pad);
8552 if (CUR_STREAM (stream)->caps) {
8554 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
8555 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8556 CUR_STREAM (stream)->caps);
8557 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
8559 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
8562 GST_WARNING_OBJECT (qtdemux, "stream without caps");
8566 gst_caps_unref (prev_caps);
8567 stream->new_caps = FALSE;
8573 gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
8574 QtDemuxStream * stream)
8576 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
8579 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
8580 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
8581 if (G_UNLIKELY (stream->stsd_sample_description_id >=
8582 stream->stsd_entries_length)) {
8583 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
8584 (_("This file is invalid and cannot be played.")),
8585 ("New sample description id is out of bounds (%d >= %d)",
8586 stream->stsd_sample_description_id, stream->stsd_entries_length));
8588 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
8589 stream->new_caps = TRUE;
8594 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
8595 QtDemuxStream * stream, GstTagList * list)
8597 gboolean ret = TRUE;
8598 /* consistent default for push based mode */
8599 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
8601 if (stream->subtype == FOURCC_vide) {
8602 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8605 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8608 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8609 gst_object_unref (stream->pad);
8615 qtdemux->n_video_streams++;
8616 } else if (stream->subtype == FOURCC_soun) {
8617 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
8620 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
8622 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8623 gst_object_unref (stream->pad);
8628 qtdemux->n_audio_streams++;
8629 } else if (stream->subtype == FOURCC_strm) {
8630 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
8631 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
8632 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
8633 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
8636 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
8638 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8639 gst_object_unref (stream->pad);
8644 qtdemux->n_sub_streams++;
8645 } else if (CUR_STREAM (stream)->caps) {
8646 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
8649 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
8651 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
8652 gst_object_unref (stream->pad);
8657 qtdemux->n_video_streams++;
8659 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
8666 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
8667 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
8668 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
8669 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
8671 if (stream->stream_tags)
8672 gst_tag_list_unref (stream->stream_tags);
8673 stream->stream_tags = list;
8675 /* global tags go on each pad anyway */
8676 stream->send_global_tags = TRUE;
8677 /* send upstream GST_EVENT_PROTECTION events that were received before
8678 this source pad was created */
8679 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
8680 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
8684 gst_tag_list_unref (list);
8688 /* find next atom with @fourcc starting at @offset */
8689 static GstFlowReturn
8690 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
8691 guint64 * length, guint32 fourcc)
8697 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
8698 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
8704 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
8705 if (G_UNLIKELY (ret != GST_FLOW_OK))
8707 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
8710 gst_buffer_unref (buf);
8713 gst_buffer_map (buf, &map, GST_MAP_READ);
8714 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
8715 gst_buffer_unmap (buf, &map);
8716 gst_buffer_unref (buf);
8718 if (G_UNLIKELY (*length == 0)) {
8719 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
8720 ret = GST_FLOW_ERROR;
8724 if (lfourcc == fourcc) {
8725 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
8729 GST_LOG_OBJECT (qtdemux,
8730 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
8731 GST_FOURCC_ARGS (fourcc), *offset);
8740 /* might simply have had last one */
8741 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
8746 /* should only do something in pull mode */
8747 /* call with OBJECT lock */
8748 static GstFlowReturn
8749 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
8751 guint64 length, offset;
8752 GstBuffer *buf = NULL;
8753 GstFlowReturn ret = GST_FLOW_OK;
8754 GstFlowReturn res = GST_FLOW_OK;
8757 offset = qtdemux->moof_offset;
8758 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
8761 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8762 return GST_FLOW_EOS;
8765 /* best not do pull etc with lock held */
8766 GST_OBJECT_UNLOCK (qtdemux);
8768 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8769 if (ret != GST_FLOW_OK)
8772 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
8773 if (G_UNLIKELY (ret != GST_FLOW_OK))
8775 gst_buffer_map (buf, &map, GST_MAP_READ);
8776 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
8777 gst_buffer_unmap (buf, &map);
8778 gst_buffer_unref (buf);
8783 gst_buffer_unmap (buf, &map);
8784 gst_buffer_unref (buf);
8788 /* look for next moof */
8789 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
8790 if (G_UNLIKELY (ret != GST_FLOW_OK))
8794 GST_OBJECT_LOCK (qtdemux);
8796 qtdemux->moof_offset = offset;
8802 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
8804 res = GST_FLOW_ERROR;
8809 /* maybe upstream temporarily flushing */
8810 if (ret != GST_FLOW_FLUSHING) {
8811 GST_DEBUG_OBJECT (qtdemux, "no next moof");
8814 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
8815 /* resume at current position next time */
8822 /* initialise bytereaders for stbl sub-atoms */
8824 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
8826 stream->stbl_index = -1; /* no samples have yet been parsed */
8827 stream->sample_index = -1;
8829 /* time-to-sample atom */
8830 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
8833 /* copy atom data into a new buffer for later use */
8834 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
8836 /* skip version + flags */
8837 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
8838 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
8840 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
8842 /* make sure there's enough data */
8843 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
8844 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
8845 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
8846 stream->n_sample_times);
8847 if (!stream->n_sample_times)
8851 /* sync sample atom */
8852 stream->stps_present = FALSE;
8853 if ((stream->stss_present =
8854 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
8855 &stream->stss) ? TRUE : FALSE) == TRUE) {
8856 /* copy atom data into a new buffer for later use */
8857 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
8859 /* skip version + flags */
8860 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
8861 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
8864 if (stream->n_sample_syncs) {
8865 /* make sure there's enough data */
8866 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
8870 /* partial sync sample atom */
8871 if ((stream->stps_present =
8872 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
8873 &stream->stps) ? TRUE : FALSE) == TRUE) {
8874 /* copy atom data into a new buffer for later use */
8875 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
8877 /* skip version + flags */
8878 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
8879 !gst_byte_reader_get_uint32_be (&stream->stps,
8880 &stream->n_sample_partial_syncs))
8883 /* if there are no entries, the stss table contains the real
8885 if (stream->n_sample_partial_syncs) {
8886 /* make sure there's enough data */
8887 if (!qt_atom_parser_has_chunks (&stream->stps,
8888 stream->n_sample_partial_syncs, 4))
8895 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
8898 /* copy atom data into a new buffer for later use */
8899 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
8901 /* skip version + flags */
8902 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
8903 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
8906 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
8909 if (!stream->n_samples)
8912 /* sample-to-chunk atom */
8913 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
8916 /* copy atom data into a new buffer for later use */
8917 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
8919 /* skip version + flags */
8920 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
8921 !gst_byte_reader_get_uint32_be (&stream->stsc,
8922 &stream->n_samples_per_chunk))
8925 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
8926 stream->n_samples_per_chunk);
8928 /* make sure there's enough data */
8929 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
8935 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
8936 stream->co_size = sizeof (guint32);
8937 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
8939 stream->co_size = sizeof (guint64);
8943 /* copy atom data into a new buffer for later use */
8944 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
8946 /* skip version + flags */
8947 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
8950 /* chunks_are_samples == TRUE means treat chunks as samples */
8951 stream->chunks_are_samples = stream->sample_size
8952 && !CUR_STREAM (stream)->sampled;
8953 if (stream->chunks_are_samples) {
8954 /* treat chunks as samples */
8955 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
8958 /* skip number of entries */
8959 if (!gst_byte_reader_skip (&stream->stco, 4))
8962 /* make sure there are enough data in the stsz atom */
8963 if (!stream->sample_size) {
8964 /* different sizes for each sample */
8965 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
8970 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
8971 stream->n_samples, (guint) sizeof (QtDemuxSample),
8972 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
8974 if (stream->n_samples >=
8975 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
8976 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
8977 "be larger than %uMB (broken file?)", stream->n_samples,
8978 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
8982 g_assert (stream->samples == NULL);
8983 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
8984 if (!stream->samples) {
8985 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
8990 /* composition time-to-sample */
8991 if ((stream->ctts_present =
8992 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
8993 &stream->ctts) ? TRUE : FALSE) == TRUE) {
8994 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
8996 /* copy atom data into a new buffer for later use */
8997 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
8999 /* skip version + flags */
9000 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
9001 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9002 &stream->n_composition_times))
9005 /* make sure there's enough data */
9006 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9010 /* This is optional, if missing we iterate the ctts */
9011 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9012 if (!gst_byte_reader_skip (&cslg, 1 + 3)
9013 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
9014 g_free ((gpointer) cslg.data);
9018 gint32 cslg_least = 0;
9019 guint num_entries, pos;
9022 pos = gst_byte_reader_get_pos (&stream->ctts);
9023 num_entries = stream->n_composition_times;
9025 stream->cslg_shift = 0;
9027 for (i = 0; i < num_entries; i++) {
9030 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9031 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9033 if (offset < cslg_least)
9034 cslg_least = offset;
9038 stream->cslg_shift = ABS (cslg_least);
9040 stream->cslg_shift = 0;
9042 /* reset the reader so we can generate sample table */
9043 gst_byte_reader_set_pos (&stream->ctts, pos);
9046 /* Ensure the cslg_shift value is consistent so we can use it
9047 * unconditionnally to produce TS and Segment */
9048 stream->cslg_shift = 0;
9055 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9056 (_("This file is corrupt and cannot be played.")), (NULL));
9061 gst_qtdemux_stbl_free (stream);
9062 if (!qtdemux->fragmented) {
9063 /* not quite good */
9064 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9067 /* may pick up samples elsewhere */
9073 /* collect samples from the next sample to be parsed up to sample @n for @stream
9074 * by reading the info from @stbl
9076 * This code can be executed from both the streaming thread and the seeking
9077 * thread so it takes the object lock to protect itself
9080 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9083 QtDemuxSample *samples, *first, *cur, *last;
9084 guint32 n_samples_per_chunk;
9087 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9088 GST_FOURCC_FORMAT ", pad %s",
9089 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9090 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9092 n_samples = stream->n_samples;
9095 goto out_of_samples;
9097 GST_OBJECT_LOCK (qtdemux);
9098 if (n <= stream->stbl_index)
9099 goto already_parsed;
9101 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9103 if (!stream->stsz.data) {
9104 /* so we already parsed and passed all the moov samples;
9105 * onto fragmented ones */
9106 g_assert (qtdemux->fragmented);
9110 /* pointer to the sample table */
9111 samples = stream->samples;
9113 /* starts from -1, moves to the next sample index to parse */
9114 stream->stbl_index++;
9116 /* keep track of the first and last sample to fill */
9117 first = &samples[stream->stbl_index];
9120 if (!stream->chunks_are_samples) {
9121 /* set the sample sizes */
9122 if (stream->sample_size == 0) {
9123 /* different sizes for each sample */
9124 for (cur = first; cur <= last; cur++) {
9125 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9126 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9127 (guint) (cur - samples), cur->size);
9130 /* samples have the same size */
9131 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9132 for (cur = first; cur <= last; cur++)
9133 cur->size = stream->sample_size;
9137 n_samples_per_chunk = stream->n_samples_per_chunk;
9140 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9143 if (stream->stsc_chunk_index >= stream->last_chunk
9144 || stream->stsc_chunk_index < stream->first_chunk) {
9145 stream->first_chunk =
9146 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9147 stream->samples_per_chunk =
9148 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9150 stream->stsd_sample_description_id =
9151 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9153 /* chunk numbers are counted from 1 it seems */
9154 if (G_UNLIKELY (stream->first_chunk == 0))
9157 --stream->first_chunk;
9159 /* the last chunk of each entry is calculated by taking the first chunk
9160 * of the next entry; except if there is no next, where we fake it with
9162 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9163 stream->last_chunk = G_MAXUINT32;
9165 stream->last_chunk =
9166 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9167 if (G_UNLIKELY (stream->last_chunk == 0))
9170 --stream->last_chunk;
9173 GST_LOG_OBJECT (qtdemux,
9174 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9175 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9176 stream->samples_per_chunk, stream->stsd_sample_description_id);
9178 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9181 if (stream->last_chunk != G_MAXUINT32) {
9182 if (!qt_atom_parser_peek_sub (&stream->stco,
9183 stream->first_chunk * stream->co_size,
9184 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9189 stream->co_chunk = stream->stco;
9190 if (!gst_byte_reader_skip (&stream->co_chunk,
9191 stream->first_chunk * stream->co_size))
9195 stream->stsc_chunk_index = stream->first_chunk;
9198 last_chunk = stream->last_chunk;
9200 if (stream->chunks_are_samples) {
9201 cur = &samples[stream->stsc_chunk_index];
9203 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9206 stream->stsc_chunk_index = j;
9211 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9214 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9215 "%" G_GUINT64_FORMAT, j, cur->offset);
9217 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9218 CUR_STREAM (stream)->bytes_per_frame > 0) {
9220 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9221 CUR_STREAM (stream)->samples_per_frame *
9222 CUR_STREAM (stream)->bytes_per_frame;
9224 cur->size = stream->samples_per_chunk;
9227 GST_DEBUG_OBJECT (qtdemux,
9228 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9229 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9230 stream->stco_sample_index)), cur->size);
9232 cur->timestamp = stream->stco_sample_index;
9233 cur->duration = stream->samples_per_chunk;
9234 cur->keyframe = TRUE;
9237 stream->stco_sample_index += stream->samples_per_chunk;
9239 stream->stsc_chunk_index = j;
9241 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9242 guint32 samples_per_chunk;
9243 guint64 chunk_offset;
9245 if (!stream->stsc_sample_index
9246 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9247 &stream->chunk_offset))
9250 samples_per_chunk = stream->samples_per_chunk;
9251 chunk_offset = stream->chunk_offset;
9253 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9254 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9255 G_GUINT64_FORMAT " and size %d",
9256 (guint) (cur - samples), chunk_offset, cur->size);
9258 cur->offset = chunk_offset;
9259 chunk_offset += cur->size;
9262 if (G_UNLIKELY (cur > last)) {
9264 stream->stsc_sample_index = k + 1;
9265 stream->chunk_offset = chunk_offset;
9266 stream->stsc_chunk_index = j;
9270 stream->stsc_sample_index = 0;
9272 stream->stsc_chunk_index = j;
9274 stream->stsc_index++;
9277 if (stream->chunks_are_samples)
9281 guint32 n_sample_times;
9283 n_sample_times = stream->n_sample_times;
9286 for (i = stream->stts_index; i < n_sample_times; i++) {
9287 guint32 stts_samples;
9288 gint32 stts_duration;
9291 if (stream->stts_sample_index >= stream->stts_samples
9292 || !stream->stts_sample_index) {
9294 stream->stts_samples =
9295 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9296 stream->stts_duration =
9297 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9299 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9300 i, stream->stts_samples, stream->stts_duration);
9302 stream->stts_sample_index = 0;
9305 stts_samples = stream->stts_samples;
9306 stts_duration = stream->stts_duration;
9307 stts_time = stream->stts_time;
9309 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9310 GST_DEBUG_OBJECT (qtdemux,
9311 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9312 (guint) (cur - samples), j,
9313 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
9315 cur->timestamp = stts_time;
9316 cur->duration = stts_duration;
9318 /* avoid 32-bit wrap-around,
9319 * but still mind possible 'negative' duration */
9320 stts_time += (gint64) stts_duration;
9323 if (G_UNLIKELY (cur > last)) {
9325 stream->stts_time = stts_time;
9326 stream->stts_sample_index = j + 1;
9327 if (stream->stts_sample_index >= stream->stts_samples)
9328 stream->stts_index++;
9332 stream->stts_sample_index = 0;
9333 stream->stts_time = stts_time;
9334 stream->stts_index++;
9336 /* fill up empty timestamps with the last timestamp, this can happen when
9337 * the last samples do not decode and so we don't have timestamps for them.
9338 * We however look at the last timestamp to estimate the track length so we
9339 * need something in here. */
9340 for (; cur < last; cur++) {
9341 GST_DEBUG_OBJECT (qtdemux,
9342 "fill sample %d: timestamp %" GST_TIME_FORMAT,
9343 (guint) (cur - samples),
9344 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
9345 cur->timestamp = stream->stts_time;
9351 /* sample sync, can be NULL */
9352 if (stream->stss_present == TRUE) {
9353 guint32 n_sample_syncs;
9355 n_sample_syncs = stream->n_sample_syncs;
9357 if (!n_sample_syncs) {
9358 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
9359 stream->all_keyframe = TRUE;
9361 for (i = stream->stss_index; i < n_sample_syncs; i++) {
9362 /* note that the first sample is index 1, not 0 */
9365 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
9367 if (G_LIKELY (index > 0 && index <= n_samples)) {
9369 samples[index].keyframe = TRUE;
9370 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9371 /* and exit if we have enough samples */
9372 if (G_UNLIKELY (index >= n)) {
9379 stream->stss_index = i;
9382 /* stps marks partial sync frames like open GOP I-Frames */
9383 if (stream->stps_present == TRUE) {
9384 guint32 n_sample_partial_syncs;
9386 n_sample_partial_syncs = stream->n_sample_partial_syncs;
9388 /* if there are no entries, the stss table contains the real
9390 if (n_sample_partial_syncs) {
9391 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
9392 /* note that the first sample is index 1, not 0 */
9395 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
9397 if (G_LIKELY (index > 0 && index <= n_samples)) {
9399 samples[index].keyframe = TRUE;
9400 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
9401 /* and exit if we have enough samples */
9402 if (G_UNLIKELY (index >= n)) {
9409 stream->stps_index = i;
9413 /* no stss, all samples are keyframes */
9414 stream->all_keyframe = TRUE;
9415 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
9420 /* composition time to sample */
9421 if (stream->ctts_present == TRUE) {
9422 guint32 n_composition_times;
9424 gint32 ctts_soffset;
9426 /* Fill in the pts_offsets */
9428 n_composition_times = stream->n_composition_times;
9430 for (i = stream->ctts_index; i < n_composition_times; i++) {
9431 if (stream->ctts_sample_index >= stream->ctts_count
9432 || !stream->ctts_sample_index) {
9433 stream->ctts_count =
9434 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
9435 stream->ctts_soffset =
9436 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9437 stream->ctts_sample_index = 0;
9440 ctts_count = stream->ctts_count;
9441 ctts_soffset = stream->ctts_soffset;
9443 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
9444 cur->pts_offset = ctts_soffset;
9447 if (G_UNLIKELY (cur > last)) {
9449 stream->ctts_sample_index = j + 1;
9453 stream->ctts_sample_index = 0;
9454 stream->ctts_index++;
9458 stream->stbl_index = n;
9459 /* if index has been completely parsed, free data that is no-longer needed */
9460 if (n + 1 == stream->n_samples) {
9461 gst_qtdemux_stbl_free (stream);
9462 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
9463 if (qtdemux->pullbased) {
9464 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
9465 while (n + 1 == stream->n_samples)
9466 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
9470 GST_OBJECT_UNLOCK (qtdemux);
9477 GST_LOG_OBJECT (qtdemux,
9478 "Tried to parse up to sample %u but this sample has already been parsed",
9480 /* if fragmented, there may be more */
9481 if (qtdemux->fragmented && n == stream->stbl_index)
9483 GST_OBJECT_UNLOCK (qtdemux);
9489 GST_LOG_OBJECT (qtdemux,
9490 "Tried to parse up to sample %u but there are only %u samples", n + 1,
9492 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9493 (_("This file is corrupt and cannot be played.")), (NULL));
9498 GST_OBJECT_UNLOCK (qtdemux);
9499 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9500 (_("This file is corrupt and cannot be played.")), (NULL));
9505 /* collect all segment info for @stream.
9508 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
9512 /* accept edts if they contain gaps at start and there is only
9513 * one media segment */
9514 gboolean allow_pushbased_edts = TRUE;
9515 gint media_segments_count = 0;
9517 /* parse and prepare segment info from the edit list */
9518 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
9519 stream->n_segments = 0;
9520 stream->segments = NULL;
9521 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
9524 gint i, count, entry_size;
9527 const guint8 *buffer;
9531 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
9532 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
9535 buffer = elst->data;
9537 size = QT_UINT32 (buffer);
9538 /* version, flags, n_segments */
9540 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9543 version = QT_UINT8 (buffer + 8);
9544 entry_size = (version == 1) ? 20 : 12;
9546 n_segments = QT_UINT32 (buffer + 12);
9548 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
9549 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
9553 /* we might allocate a bit too much, at least allocate 1 segment */
9554 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
9556 /* segments always start from 0 */
9561 for (i = 0; i < n_segments; i++) {
9564 gboolean time_valid = TRUE;
9565 QtDemuxSegment *segment;
9567 GstClockTime media_start = GST_CLOCK_TIME_NONE;
9570 media_time = QT_UINT64 (buffer + 8);
9571 duration = QT_UINT64 (buffer);
9572 if (media_time == G_MAXUINT64)
9575 media_time = QT_UINT32 (buffer + 4);
9576 duration = QT_UINT32 (buffer);
9577 if (media_time == G_MAXUINT32)
9582 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
9584 segment = &stream->segments[count++];
9586 /* time and duration expressed in global timescale */
9587 segment->time = stime;
9588 /* add non scaled values so we don't cause roundoff errors */
9589 if (duration || media_start == GST_CLOCK_TIME_NONE) {
9591 stime = QTTIME_TO_GSTTIME (qtdemux, time);
9592 segment->duration = stime - segment->time;
9594 /* zero duration does not imply media_start == media_stop
9595 * but, only specify media_start.*/
9596 stime = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
9597 if (GST_CLOCK_TIME_IS_VALID (stime) && time_valid
9598 && stime >= media_start) {
9599 segment->duration = stime - media_start;
9601 segment->duration = GST_CLOCK_TIME_NONE;
9604 segment->stop_time = stime;
9606 segment->trak_media_start = media_time;
9607 /* media_time expressed in stream timescale */
9609 segment->media_start = media_start;
9610 segment->media_stop = segment->media_start + segment->duration;
9611 media_segments_count++;
9613 segment->media_start = GST_CLOCK_TIME_NONE;
9614 segment->media_stop = GST_CLOCK_TIME_NONE;
9616 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
9618 if (rate_int <= 1) {
9619 /* 0 is not allowed, some programs write 1 instead of the floating point
9621 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
9625 segment->rate = rate_int / 65536.0;
9628 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
9629 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
9630 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
9631 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
9632 i, GST_TIME_ARGS (segment->time),
9633 GST_TIME_ARGS (segment->duration),
9634 GST_TIME_ARGS (segment->media_start), media_time,
9635 GST_TIME_ARGS (segment->media_stop),
9636 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
9638 if (segment->stop_time > qtdemux->segment.stop) {
9639 GST_WARNING_OBJECT (qtdemux, "Segment %d "
9640 " extends to %" GST_TIME_FORMAT
9641 " past the end of the file duration %" GST_TIME_FORMAT
9642 " it will be truncated", i, GST_TIME_ARGS (segment->stop_time),
9643 GST_TIME_ARGS (qtdemux->segment.stop));
9644 qtdemux->segment.stop = segment->stop_time;
9647 buffer += entry_size;
9649 GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
9650 stream->n_segments = count;
9651 if (media_segments_count != 1)
9652 allow_pushbased_edts = FALSE;
9656 /* push based does not handle segments, so act accordingly here,
9657 * and warn if applicable */
9658 if (!qtdemux->pullbased && !allow_pushbased_edts) {
9659 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
9660 /* remove and use default one below, we stream like it anyway */
9661 g_free (stream->segments);
9662 stream->segments = NULL;
9663 stream->n_segments = 0;
9666 /* no segments, create one to play the complete trak */
9667 if (stream->n_segments == 0) {
9668 GstClockTime stream_duration =
9669 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
9671 if (stream->segments == NULL)
9672 stream->segments = g_new (QtDemuxSegment, 1);
9674 /* represent unknown our way */
9675 if (stream_duration == 0)
9676 stream_duration = GST_CLOCK_TIME_NONE;
9678 stream->segments[0].time = 0;
9679 stream->segments[0].stop_time = stream_duration;
9680 stream->segments[0].duration = stream_duration;
9681 stream->segments[0].media_start = 0;
9682 stream->segments[0].media_stop = stream_duration;
9683 stream->segments[0].rate = 1.0;
9684 stream->segments[0].trak_media_start = 0;
9686 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
9687 GST_TIME_ARGS (stream_duration));
9688 stream->n_segments = 1;
9689 stream->dummy_segment = TRUE;
9691 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
9697 * Parses the stsd atom of a svq3 trak looking for
9698 * the SMI and gama atoms.
9701 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
9702 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
9704 const guint8 *_gamma = NULL;
9705 GstBuffer *_seqh = NULL;
9706 const guint8 *stsd_data = stsd_entry_data;
9707 guint32 length = QT_UINT32 (stsd_data);
9711 GST_WARNING_OBJECT (qtdemux, "stsd too short");
9717 version = QT_UINT16 (stsd_data);
9722 while (length > 8) {
9723 guint32 fourcc, size;
9725 size = QT_UINT32 (stsd_data);
9726 fourcc = QT_FOURCC (stsd_data + 4);
9727 data = stsd_data + 8;
9730 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
9731 "svq3 atom parsing");
9740 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
9741 " for gama atom, expected 12", size);
9746 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
9748 if (_seqh != NULL) {
9749 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
9750 " found, ignoring");
9752 seqh_size = QT_UINT32 (data + 4);
9753 if (seqh_size > 0) {
9754 _seqh = gst_buffer_new_and_alloc (seqh_size);
9755 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
9762 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
9763 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
9767 if (size <= length) {
9773 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
9776 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
9777 G_GUINT16_FORMAT, version);
9788 gst_buffer_unref (_seqh);
9793 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
9800 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
9801 * atom that might contain a 'data' atom with the rtsp uri.
9802 * This case was reported in bug #597497, some info about
9803 * the hndl atom can be found in TN1195
9805 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
9806 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
9809 guint32 dref_num_entries = 0;
9810 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
9811 gst_byte_reader_skip (&dref, 4) &&
9812 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
9815 /* search dref entries for hndl atom */
9816 for (i = 0; i < dref_num_entries; i++) {
9817 guint32 size = 0, type;
9818 guint8 string_len = 0;
9819 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
9820 qt_atom_parser_get_fourcc (&dref, &type)) {
9821 if (type == FOURCC_hndl) {
9822 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
9824 /* skip data reference handle bytes and the
9825 * following pascal string and some extra 4
9826 * bytes I have no idea what are */
9827 if (!gst_byte_reader_skip (&dref, 4) ||
9828 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
9829 !gst_byte_reader_skip (&dref, string_len + 4)) {
9830 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
9834 /* iterate over the atoms to find the data atom */
9835 while (gst_byte_reader_get_remaining (&dref) >= 8) {
9839 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
9840 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
9841 if (atom_type == FOURCC_data) {
9842 const guint8 *uri_aux = NULL;
9844 /* found the data atom that might contain the rtsp uri */
9845 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
9846 "hndl atom, interpreting it as an URI");
9847 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
9849 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
9850 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
9852 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
9853 "didn't contain a rtsp address");
9855 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
9860 /* skipping to the next entry */
9861 if (!gst_byte_reader_skip (&dref, atom_size - 8))
9864 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
9871 /* skip to the next entry */
9872 if (!gst_byte_reader_skip (&dref, size - 8))
9875 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
9878 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
9884 #define AMR_NB_ALL_MODES 0x81ff
9885 #define AMR_WB_ALL_MODES 0x83ff
9887 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
9889 /* The 'damr' atom is of the form:
9891 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
9892 * 32 b 8 b 16 b 8 b 8 b
9894 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
9895 * represents the highest mode used in the stream (and thus the maximum
9896 * bitrate), with a couple of special cases as seen below.
9899 /* Map of frame type ID -> bitrate */
9900 static const guint nb_bitrates[] = {
9901 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
9903 static const guint wb_bitrates[] = {
9904 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
9910 gst_buffer_map (buf, &map, GST_MAP_READ);
9912 if (map.size != 0x11) {
9913 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
9917 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
9918 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
9919 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
9923 mode_set = QT_UINT16 (map.data + 13);
9925 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
9926 max_mode = 7 + (wb ? 1 : 0);
9928 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
9929 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
9931 if (max_mode == -1) {
9932 GST_DEBUG ("No mode indication was found (mode set) = %x",
9937 gst_buffer_unmap (buf, &map);
9938 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
9941 gst_buffer_unmap (buf, &map);
9946 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
9947 GstByteReader * reader, guint32 * matrix, const gchar * atom)
9950 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
9956 if (gst_byte_reader_get_remaining (reader) < 36)
9959 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
9960 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
9961 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
9962 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
9963 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
9964 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
9965 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
9966 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
9967 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
9969 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
9970 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
9971 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
9973 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
9974 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
9976 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
9977 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
9984 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
9985 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
9992 * This macro will only compare value abdegh, it expects cfi to have already
9995 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
9996 (m)[3] == (d << 16) && (m)[4] == (e << 16))
9998 /* only handle the cases where the last column has standard values */
9999 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10000 const gchar *rotation_tag = NULL;
10002 /* no rotation needed */
10003 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10005 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10006 rotation_tag = "rotate-90";
10007 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10008 rotation_tag = "rotate-180";
10009 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10010 rotation_tag = "rotate-270";
10012 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10015 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10017 if (rotation_tag != NULL) {
10018 if (*taglist == NULL)
10019 *taglist = gst_tag_list_new_empty ();
10020 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10021 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10024 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10028 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10029 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10030 * Common Encryption (cenc), the function will also parse the tenc box (defined
10031 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10032 * (typically an enc[v|a|t|s] sample entry); the function will set
10033 * @original_fmt to the fourcc of the original unencrypted stream format.
10034 * Returns TRUE if successful; FALSE otherwise. */
10036 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10037 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10044 g_return_val_if_fail (qtdemux != NULL, FALSE);
10045 g_return_val_if_fail (stream != NULL, FALSE);
10046 g_return_val_if_fail (container != NULL, FALSE);
10047 g_return_val_if_fail (original_fmt != NULL, FALSE);
10049 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10050 if (G_UNLIKELY (!sinf)) {
10051 if (stream->protection_scheme_type == FOURCC_cenc) {
10052 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10053 "mandatory for Common Encryption");
10059 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10060 if (G_UNLIKELY (!frma)) {
10061 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10065 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10066 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10067 GST_FOURCC_ARGS (*original_fmt));
10069 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10071 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10074 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10075 stream->protection_scheme_version =
10076 QT_UINT32 ((const guint8 *) schm->data + 16);
10078 GST_DEBUG_OBJECT (qtdemux,
10079 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10080 "protection_scheme_version: %#010x",
10081 GST_FOURCC_ARGS (stream->protection_scheme_type),
10082 stream->protection_scheme_version);
10084 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10086 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10089 if (stream->protection_scheme_type == FOURCC_cenc) {
10090 QtDemuxCencSampleSetInfo *info;
10092 const guint8 *tenc_data;
10093 guint32 isEncrypted;
10095 const guint8 *default_kid;
10096 GstBuffer *kid_buf;
10098 if (G_UNLIKELY (!stream->protection_scheme_info))
10099 stream->protection_scheme_info =
10100 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10102 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10104 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10106 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10107 "which is mandatory for Common Encryption");
10110 tenc_data = (const guint8 *) tenc->data + 12;
10111 isEncrypted = QT_UINT24 (tenc_data);
10112 iv_size = QT_UINT8 (tenc_data + 3);
10113 default_kid = (tenc_data + 4);
10114 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
10115 gst_buffer_fill (kid_buf, 0, default_kid, 16);
10116 if (info->default_properties)
10117 gst_structure_free (info->default_properties);
10118 info->default_properties =
10119 gst_structure_new ("application/x-cenc",
10120 "iv_size", G_TYPE_UINT, iv_size,
10121 "encrypted", G_TYPE_BOOLEAN, (isEncrypted == 1),
10122 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
10123 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
10124 "is_encrypted=%u, iv_size=%u", isEncrypted, iv_size);
10125 gst_buffer_unref (kid_buf);
10130 /* parse the traks.
10131 * With each track we associate a new QtDemuxStream that contains all the info
10133 * traks that do not decode to something (like strm traks) will not have a pad.
10136 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
10138 GstByteReader tkhd;
10157 QtDemuxStream *stream = NULL;
10158 gboolean new_stream = FALSE;
10159 gchar *codec = NULL;
10160 const guint8 *stsd_data;
10161 const guint8 *stsd_entry_data;
10162 guint remaining_stsd_len;
10163 guint stsd_entry_count;
10165 guint16 lang_code; /* quicktime lang code or packed iso code */
10167 guint32 tkhd_flags = 0;
10168 guint8 tkhd_version = 0;
10169 guint32 w = 0, h = 0;
10171 guint value_size, stsd_len, len;
10175 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
10177 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
10178 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
10179 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
10182 /* pick between 64 or 32 bits */
10183 value_size = tkhd_version == 1 ? 8 : 4;
10184 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
10185 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
10188 if (!qtdemux->got_moov) {
10189 if (qtdemux_find_stream (qtdemux, track_id))
10190 goto existing_stream;
10191 stream = _create_stream ();
10192 stream->track_id = track_id;
10195 stream = qtdemux_find_stream (qtdemux, track_id);
10197 GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
10201 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
10203 /* flush samples data from this track from previous moov */
10204 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
10205 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
10207 /* need defaults for fragments */
10208 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10210 if ((tkhd_flags & 1) == 0)
10211 stream->disabled = TRUE;
10213 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
10214 tkhd_version, tkhd_flags, stream->track_id);
10216 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
10219 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
10220 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
10221 if (qtdemux->major_brand != FOURCC_mjp2 ||
10222 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
10226 len = QT_UINT32 ((guint8 *) mdhd->data);
10227 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
10228 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
10229 if (version == 0x01000000) {
10232 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
10233 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
10234 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
10238 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
10239 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
10240 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
10243 if (lang_code < 0x400) {
10244 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
10245 } else if (lang_code == 0x7fff) {
10246 stream->lang_id[0] = 0; /* unspecified */
10248 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
10249 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
10250 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
10251 stream->lang_id[3] = 0;
10254 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
10255 stream->timescale);
10256 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
10258 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
10259 lang_code, stream->lang_id);
10261 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
10264 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
10265 /* chapters track reference */
10266 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
10268 gsize length = GST_READ_UINT32_BE (chap->data);
10269 if (qtdemux->chapters_track_id)
10270 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
10272 if (length >= 12) {
10273 qtdemux->chapters_track_id =
10274 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
10279 /* fragmented files may have bogus duration in moov */
10280 if (!qtdemux->fragmented &&
10281 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
10282 guint64 tdur1, tdur2;
10284 /* don't overflow */
10285 tdur1 = stream->timescale * (guint64) qtdemux->duration;
10286 tdur2 = qtdemux->timescale * (guint64) stream->duration;
10289 * some of those trailers, nowadays, have prologue images that are
10290 * themselves video tracks as well. I haven't really found a way to
10291 * identify those yet, except for just looking at their duration. */
10292 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
10293 GST_WARNING_OBJECT (qtdemux,
10294 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
10295 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
10296 "found, assuming preview image or something; skipping track",
10297 stream->duration, stream->timescale, qtdemux->duration,
10298 qtdemux->timescale);
10300 gst_qtdemux_stream_free (qtdemux, stream);
10305 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
10308 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
10309 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
10311 len = QT_UINT32 ((guint8 *) hdlr->data);
10313 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
10314 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
10315 GST_FOURCC_ARGS (stream->subtype));
10317 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
10320 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
10323 /*parse svmi header if existing */
10324 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
10326 len = QT_UINT32 ((guint8 *) svmi->data);
10327 version = QT_UINT32 ((guint8 *) svmi->data + 8);
10329 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
10330 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
10331 guint8 frame_type, frame_layout;
10333 /* MPEG-A stereo video */
10334 if (qtdemux->major_brand == FOURCC_ss02)
10335 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
10337 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
10338 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
10339 switch (frame_type) {
10341 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
10344 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
10347 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
10350 /* mode 3 is primary/secondary view sequence, ie
10351 * left/right views in separate tracks. See section 7.2
10352 * of ISO/IEC 23000-11:2009 */
10353 GST_FIXME_OBJECT (qtdemux,
10354 "Implement stereo video in separate streams");
10357 if ((frame_layout & 0x1) == 0)
10358 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
10360 GST_LOG_OBJECT (qtdemux,
10361 "StereoVideo: composition type: %u, is_left_first: %u",
10362 frame_type, frame_layout);
10363 stream->multiview_mode = mode;
10364 stream->multiview_flags = flags;
10368 /* parse rest of tkhd */
10369 if (stream->subtype == FOURCC_vide) {
10372 /* version 1 uses some 64-bit ints */
10373 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
10376 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
10379 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
10380 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
10383 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
10384 &stream->stream_tags);
10388 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
10390 stsd_data = (const guint8 *) stsd->data;
10392 /* stsd should at least have one entry */
10393 stsd_len = QT_UINT32 (stsd_data);
10394 if (stsd_len < 24) {
10395 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
10396 if (stream->subtype == FOURCC_vivo) {
10398 gst_qtdemux_stream_free (qtdemux, stream);
10405 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
10406 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
10407 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
10408 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
10410 stsd_entry_data = stsd_data + 16;
10411 remaining_stsd_len = stsd_len - 16;
10412 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
10413 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
10415 /* and that entry should fit within stsd */
10416 len = QT_UINT32 (stsd_entry_data);
10417 if (len > remaining_stsd_len)
10420 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
10421 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
10422 GST_FOURCC_ARGS (entry->fourcc));
10423 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
10425 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
10426 goto error_encrypted;
10428 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
10429 /* FIXME this looks wrong, there might be multiple children
10430 * with the same type */
10431 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
10432 stream->protected = TRUE;
10433 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
10434 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
10437 if (stream->subtype == FOURCC_vide) {
10439 gint depth, palette_size, palette_count;
10440 guint32 *palette_data = NULL;
10442 entry->sampled = TRUE;
10444 stream->display_width = w >> 16;
10445 stream->display_height = h >> 16;
10448 if (len < 86) /* TODO verify */
10451 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
10452 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
10453 entry->fps_n = 0; /* this is filled in later */
10454 entry->fps_d = 0; /* this is filled in later */
10455 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
10456 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
10458 /* if color_table_id is 0, ctab atom must follow; however some files
10459 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
10460 * if color table is not present we'll correct the value */
10461 if (entry->color_table_id == 0 &&
10463 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
10464 entry->color_table_id = -1;
10467 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
10468 entry->width, entry->height, entry->bits_per_sample,
10469 entry->color_table_id);
10471 depth = entry->bits_per_sample;
10473 /* more than 32 bits means grayscale */
10474 gray = (depth > 32);
10475 /* low 32 bits specify the depth */
10478 /* different number of palette entries is determined by depth. */
10480 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
10481 palette_count = (1 << depth);
10482 palette_size = palette_count * 4;
10484 if (entry->color_table_id) {
10485 switch (palette_count) {
10489 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
10492 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
10497 g_memdup (ff_qt_grayscale_palette_16, palette_size);
10499 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
10504 g_memdup (ff_qt_grayscale_palette_256, palette_size);
10506 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
10509 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10510 (_("The video in this file might not play correctly.")),
10511 ("unsupported palette depth %d", depth));
10515 gint i, j, start, end;
10521 start = QT_UINT32 (stsd_entry_data + offset + 70);
10522 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
10523 end = QT_UINT16 (stsd_entry_data + offset + 76);
10525 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
10526 start, end, palette_count);
10533 if (len < 94 + (end - start) * 8)
10536 /* palette is always the same size */
10537 palette_data = g_malloc0 (256 * 4);
10538 palette_size = 256 * 4;
10540 for (j = 0, i = start; i <= end; j++, i++) {
10541 guint32 a, r, g, b;
10543 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
10544 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
10545 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
10546 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
10548 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
10549 (g & 0xff00) | (b >> 8);
10554 gst_caps_unref (entry->caps);
10557 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
10559 if (G_UNLIKELY (!entry->caps)) {
10560 g_free (palette_data);
10561 goto unknown_stream;
10565 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
10566 GST_TAG_VIDEO_CODEC, codec, NULL);
10571 if (palette_data) {
10574 if (entry->rgb8_palette)
10575 gst_memory_unref (entry->rgb8_palette);
10576 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
10577 palette_data, palette_size, 0, palette_size, palette_data, g_free);
10579 s = gst_caps_get_structure (entry->caps, 0);
10581 /* non-raw video has a palette_data property. raw video has the palette as
10582 * an extra plane that we append to the output buffers before we push
10584 if (!gst_structure_has_name (s, "video/x-raw")) {
10585 GstBuffer *palette;
10587 palette = gst_buffer_new ();
10588 gst_buffer_append_memory (palette, entry->rgb8_palette);
10589 entry->rgb8_palette = NULL;
10591 gst_caps_set_simple (entry->caps, "palette_data",
10592 GST_TYPE_BUFFER, palette, NULL);
10593 gst_buffer_unref (palette);
10595 } else if (palette_count != 0) {
10596 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
10597 (NULL), ("Unsupported palette depth %d", depth));
10600 GST_LOG_OBJECT (qtdemux, "frame count: %u",
10601 QT_UINT16 (stsd_entry_data + offset + 32));
10607 /* pick 'the' stsd child */
10608 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10609 if (!stream->protected) {
10610 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
10614 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) {
10620 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
10621 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
10622 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
10623 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
10627 const guint8 *pasp_data = (const guint8 *) pasp->data;
10628 gint len = QT_UINT32 (pasp_data);
10631 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
10632 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
10634 CUR_STREAM (stream)->par_w = 0;
10635 CUR_STREAM (stream)->par_h = 0;
10638 CUR_STREAM (stream)->par_w = 0;
10639 CUR_STREAM (stream)->par_h = 0;
10643 const guint8 *fiel_data = (const guint8 *) fiel->data;
10644 gint len = QT_UINT32 (fiel_data);
10647 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
10648 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
10653 const guint8 *colr_data = (const guint8 *) colr->data;
10654 gint len = QT_UINT32 (colr_data);
10656 if (len == 19 || len == 18) {
10657 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
10659 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
10660 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
10661 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
10662 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
10663 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
10665 switch (primaries) {
10667 CUR_STREAM (stream)->colorimetry.primaries =
10668 GST_VIDEO_COLOR_PRIMARIES_BT709;
10671 CUR_STREAM (stream)->colorimetry.primaries =
10672 GST_VIDEO_COLOR_PRIMARIES_BT470BG;
10675 CUR_STREAM (stream)->colorimetry.primaries =
10676 GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
10679 CUR_STREAM (stream)->colorimetry.primaries =
10680 GST_VIDEO_COLOR_PRIMARIES_BT2020;
10686 switch (transfer_function) {
10688 CUR_STREAM (stream)->colorimetry.transfer =
10689 GST_VIDEO_TRANSFER_BT709;
10692 CUR_STREAM (stream)->colorimetry.transfer =
10693 GST_VIDEO_TRANSFER_SMPTE240M;
10701 CUR_STREAM (stream)->colorimetry.matrix =
10702 GST_VIDEO_COLOR_MATRIX_BT709;
10705 CUR_STREAM (stream)->colorimetry.matrix =
10706 GST_VIDEO_COLOR_MATRIX_BT601;
10709 CUR_STREAM (stream)->colorimetry.matrix =
10710 GST_VIDEO_COLOR_MATRIX_SMPTE240M;
10713 CUR_STREAM (stream)->colorimetry.matrix =
10714 GST_VIDEO_COLOR_MATRIX_BT2020;
10720 CUR_STREAM (stream)->colorimetry.range =
10721 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
10722 GST_VIDEO_COLOR_RANGE_16_235;
10724 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
10727 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
10732 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
10733 stream->stream_tags);
10740 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10741 const guint8 *avc_data = stsd_entry_data + 0x56;
10744 while (len >= 0x8) {
10747 if (QT_UINT32 (avc_data) <= len)
10748 size = QT_UINT32 (avc_data) - 0x8;
10753 /* No real data, so break out */
10756 switch (QT_FOURCC (avc_data + 0x4)) {
10759 /* parse, if found */
10762 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10764 /* First 4 bytes are the length of the atom, the next 4 bytes
10765 * are the fourcc, the next 1 byte is the version, and the
10766 * subsequent bytes are profile_tier_level structure like data. */
10767 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
10768 avc_data + 8 + 1, size - 1);
10769 buf = gst_buffer_new_and_alloc (size);
10770 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
10771 gst_caps_set_simple (entry->caps,
10772 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10773 gst_buffer_unref (buf);
10781 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
10783 /* First 4 bytes are the length of the atom, the next 4 bytes
10784 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
10785 * next 1 byte is the version, and the
10786 * subsequent bytes are sequence parameter set like data. */
10788 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
10790 gst_codec_utils_h264_caps_set_level_and_profile
10791 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
10793 buf = gst_buffer_new_and_alloc (size);
10794 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
10795 gst_caps_set_simple (entry->caps,
10796 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10797 gst_buffer_unref (buf);
10803 guint avg_bitrate, max_bitrate;
10805 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
10809 max_bitrate = QT_UINT32 (avc_data + 0xc);
10810 avg_bitrate = QT_UINT32 (avc_data + 0x10);
10812 if (!max_bitrate && !avg_bitrate)
10815 /* Some muxers seem to swap the average and maximum bitrates
10816 * (I'm looking at you, YouTube), so we swap for sanity. */
10817 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
10818 guint temp = avg_bitrate;
10820 avg_bitrate = max_bitrate;
10821 max_bitrate = temp;
10824 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
10825 gst_tag_list_add (stream->stream_tags,
10826 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
10827 max_bitrate, NULL);
10829 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
10830 gst_tag_list_add (stream->stream_tags,
10831 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
10843 avc_data += size + 8;
10852 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
10853 const guint8 *hevc_data = stsd_entry_data + 0x56;
10856 while (len >= 0x8) {
10859 if (QT_UINT32 (hevc_data) <= len)
10860 size = QT_UINT32 (hevc_data) - 0x8;
10865 /* No real data, so break out */
10868 switch (QT_FOURCC (hevc_data + 0x4)) {
10871 /* parse, if found */
10874 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
10876 /* First 4 bytes are the length of the atom, the next 4 bytes
10877 * are the fourcc, the next 1 byte is the version, and the
10878 * subsequent bytes are sequence parameter set like data. */
10879 gst_codec_utils_h265_caps_set_level_tier_and_profile
10880 (entry->caps, hevc_data + 8 + 1, size - 1);
10882 buf = gst_buffer_new_and_alloc (size);
10883 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
10884 gst_caps_set_simple (entry->caps,
10885 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10886 gst_buffer_unref (buf);
10893 hevc_data += size + 8;
10906 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
10907 GST_FOURCC_ARGS (fourcc));
10909 /* codec data might be in glbl extension atom */
10911 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
10917 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
10919 len = QT_UINT32 (data);
10922 buf = gst_buffer_new_and_alloc (len);
10923 gst_buffer_fill (buf, 0, data + 8, len);
10924 gst_caps_set_simple (entry->caps,
10925 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10926 gst_buffer_unref (buf);
10933 /* see annex I of the jpeg2000 spec */
10934 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
10935 const guint8 *data;
10936 const gchar *colorspace = NULL;
10938 guint32 ncomp_map = 0;
10939 gint32 *comp_map = NULL;
10940 guint32 nchan_def = 0;
10941 gint32 *chan_def = NULL;
10943 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
10944 /* some required atoms */
10945 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
10948 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
10952 /* number of components; redundant with info in codestream, but useful
10954 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
10955 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
10957 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
10959 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
10962 GST_DEBUG_OBJECT (qtdemux, "found colr");
10963 /* extract colour space info */
10964 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
10965 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
10967 colorspace = "sRGB";
10970 colorspace = "GRAY";
10973 colorspace = "sYUV";
10981 /* colr is required, and only values 16, 17, and 18 are specified,
10982 so error if we have no colorspace */
10985 /* extract component mapping */
10986 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
10988 guint32 cmap_len = 0;
10990 cmap_len = QT_UINT32 (cmap->data);
10991 if (cmap_len >= 8) {
10992 /* normal box, subtract off header */
10994 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
10995 if (cmap_len % 4 == 0) {
10996 ncomp_map = (cmap_len / 4);
10997 comp_map = g_new0 (gint32, ncomp_map);
10998 for (i = 0; i < ncomp_map; i++) {
11001 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
11002 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
11003 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
11004 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
11009 /* extract channel definitions */
11010 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
11012 guint32 cdef_len = 0;
11014 cdef_len = QT_UINT32 (cdef->data);
11015 if (cdef_len >= 10) {
11016 /* normal box, subtract off header and len */
11018 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
11019 if (cdef_len % 6 == 0) {
11020 nchan_def = (cdef_len / 6);
11021 chan_def = g_new0 (gint32, nchan_def);
11022 for (i = 0; i < nchan_def; i++)
11024 for (i = 0; i < nchan_def; i++) {
11025 guint16 cn, typ, asoc;
11026 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
11027 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
11028 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
11029 if (cn < nchan_def) {
11032 chan_def[cn] = asoc;
11035 chan_def[cn] = 0; /* alpha */
11038 chan_def[cn] = -typ;
11046 gst_caps_set_simple (entry->caps,
11047 "num-components", G_TYPE_INT, ncomp, NULL);
11048 gst_caps_set_simple (entry->caps,
11049 "colorspace", G_TYPE_STRING, colorspace, NULL);
11052 GValue arr = { 0, };
11053 GValue elt = { 0, };
11055 g_value_init (&arr, GST_TYPE_ARRAY);
11056 g_value_init (&elt, G_TYPE_INT);
11057 for (i = 0; i < ncomp_map; i++) {
11058 g_value_set_int (&elt, comp_map[i]);
11059 gst_value_array_append_value (&arr, &elt);
11061 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11062 "component-map", &arr);
11063 g_value_unset (&elt);
11064 g_value_unset (&arr);
11069 GValue arr = { 0, };
11070 GValue elt = { 0, };
11072 g_value_init (&arr, GST_TYPE_ARRAY);
11073 g_value_init (&elt, G_TYPE_INT);
11074 for (i = 0; i < nchan_def; i++) {
11075 g_value_set_int (&elt, chan_def[i]);
11076 gst_value_array_append_value (&arr, &elt);
11078 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
11079 "channel-definitions", &arr);
11080 g_value_unset (&elt);
11081 g_value_unset (&arr);
11085 /* some optional atoms */
11086 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
11087 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
11089 /* indicate possible fields in caps */
11091 data = (guint8 *) field->data + 8;
11093 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
11094 (gint) * data, NULL);
11096 /* add codec_data if provided */
11101 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
11102 data = prefix->data;
11103 len = QT_UINT32 (data);
11106 buf = gst_buffer_new_and_alloc (len);
11107 gst_buffer_fill (buf, 0, data + 8, len);
11108 gst_caps_set_simple (entry->caps,
11109 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11110 gst_buffer_unref (buf);
11119 GstBuffer *seqh = NULL;
11120 const guint8 *gamma_data = NULL;
11121 gint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
11123 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
11126 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
11127 QT_FP32 (gamma_data), NULL);
11130 /* sorry for the bad name, but we don't know what this is, other
11131 * than its own fourcc */
11132 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
11134 gst_buffer_unref (seqh);
11137 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
11138 buf = gst_buffer_new_and_alloc (len);
11139 gst_buffer_fill (buf, 0, stsd_data, len);
11140 gst_caps_set_simple (entry->caps,
11141 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11142 gst_buffer_unref (buf);
11147 /* https://developer.apple.com/standards/qtff-2001.pdf,
11148 * page 92, "Video Sample Description", under table 3.1 */
11151 const gint compressor_offset =
11152 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
11153 const gint min_size = compressor_offset + 32 + 2 + 2;
11156 guint16 color_table_id = 0;
11159 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
11161 /* recover information on interlaced/progressive */
11162 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
11166 len = QT_UINT32 (jpeg->data);
11167 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
11169 if (len >= min_size) {
11170 gst_byte_reader_init (&br, jpeg->data, len);
11172 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
11173 gst_byte_reader_get_uint16_le (&br, &color_table_id);
11174 if (color_table_id != 0) {
11175 /* the spec says there can be concatenated chunks in the data, and we want
11176 * to find one called field. Walk through them. */
11177 gint offset = min_size;
11178 while (offset + 8 < len) {
11179 guint32 size = 0, tag;
11180 ok = gst_byte_reader_get_uint32_le (&br, &size);
11181 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
11182 if (!ok || size < 8) {
11183 GST_WARNING_OBJECT (qtdemux,
11184 "Failed to walk optional chunk list");
11187 GST_DEBUG_OBJECT (qtdemux,
11188 "Found optional %4.4s chunk, size %u",
11189 (const char *) &tag, size);
11190 if (tag == FOURCC_fiel) {
11191 guint8 n_fields = 0, ordering = 0;
11192 gst_byte_reader_get_uint8 (&br, &n_fields);
11193 gst_byte_reader_get_uint8 (&br, &ordering);
11194 if (n_fields == 1 || n_fields == 2) {
11195 GST_DEBUG_OBJECT (qtdemux,
11196 "Found fiel tag with %u fields, ordering %u",
11197 n_fields, ordering);
11199 gst_caps_set_simple (CUR_STREAM (stream)->caps,
11200 "interlace-mode", G_TYPE_STRING, "interleaved",
11203 GST_WARNING_OBJECT (qtdemux,
11204 "Found fiel tag with invalid fields (%u)", n_fields);
11210 GST_DEBUG_OBJECT (qtdemux,
11211 "Color table ID is 0, not trying to get interlacedness");
11214 GST_WARNING_OBJECT (qtdemux,
11215 "Length of jpeg chunk is too small, not trying to get interlacedness");
11223 gst_caps_set_simple (entry->caps,
11224 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
11230 GNode *xith, *xdxt;
11232 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
11233 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11237 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
11241 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
11242 /* collect the headers and store them in a stream list so that we can
11243 * send them out first */
11244 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
11254 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
11255 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11258 ovc1_data = ovc1->data;
11259 ovc1_len = QT_UINT32 (ovc1_data);
11260 if (ovc1_len <= 198) {
11261 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
11264 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
11265 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
11266 gst_caps_set_simple (entry->caps,
11267 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11268 gst_buffer_unref (buf);
11273 gint len = QT_UINT32 (stsd_entry_data) - 0x56;
11274 const guint8 *vc1_data = stsd_entry_data + 0x56;
11280 if (QT_UINT32 (vc1_data) <= len)
11281 size = QT_UINT32 (vc1_data) - 8;
11286 /* No real data, so break out */
11289 switch (QT_FOURCC (vc1_data + 0x4)) {
11290 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
11294 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
11295 buf = gst_buffer_new_and_alloc (size);
11296 gst_buffer_fill (buf, 0, vc1_data + 8, size);
11297 gst_caps_set_simple (entry->caps,
11298 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11299 gst_buffer_unref (buf);
11306 vc1_data += size + 8;
11315 GST_INFO_OBJECT (qtdemux,
11316 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
11317 GST_FOURCC_ARGS (fourcc), entry->caps);
11319 } else if (stream->subtype == FOURCC_soun) {
11320 int version, samplesize;
11321 guint16 compression_id;
11322 gboolean amrwb = FALSE;
11325 /* sample description entry (16) + sound sample description v0 (20) */
11329 version = QT_UINT32 (stsd_entry_data + offset);
11330 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
11331 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
11332 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
11333 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
11335 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
11336 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
11337 QT_UINT32 (stsd_entry_data + offset + 4));
11338 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11339 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
11340 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
11341 GST_LOG_OBJECT (qtdemux, "packet size: %d",
11342 QT_UINT16 (stsd_entry_data + offset + 14));
11343 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11345 if (compression_id == 0xfffe)
11346 entry->sampled = TRUE;
11348 /* first assume uncompressed audio */
11349 entry->bytes_per_sample = samplesize / 8;
11350 entry->samples_per_frame = entry->n_channels;
11351 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
11352 entry->samples_per_packet = entry->samples_per_frame;
11353 entry->bytes_per_packet = entry->bytes_per_sample;
11357 /* Yes, these have to be hard-coded */
11360 entry->samples_per_packet = 6;
11361 entry->bytes_per_packet = 1;
11362 entry->bytes_per_frame = 1 * entry->n_channels;
11363 entry->bytes_per_sample = 1;
11364 entry->samples_per_frame = 6 * entry->n_channels;
11369 entry->samples_per_packet = 3;
11370 entry->bytes_per_packet = 1;
11371 entry->bytes_per_frame = 1 * entry->n_channels;
11372 entry->bytes_per_sample = 1;
11373 entry->samples_per_frame = 3 * entry->n_channels;
11378 entry->samples_per_packet = 64;
11379 entry->bytes_per_packet = 34;
11380 entry->bytes_per_frame = 34 * entry->n_channels;
11381 entry->bytes_per_sample = 2;
11382 entry->samples_per_frame = 64 * entry->n_channels;
11388 entry->samples_per_packet = 1;
11389 entry->bytes_per_packet = 1;
11390 entry->bytes_per_frame = 1 * entry->n_channels;
11391 entry->bytes_per_sample = 1;
11392 entry->samples_per_frame = 1 * entry->n_channels;
11397 entry->samples_per_packet = 160;
11398 entry->bytes_per_packet = 33;
11399 entry->bytes_per_frame = 33 * entry->n_channels;
11400 entry->bytes_per_sample = 2;
11401 entry->samples_per_frame = 160 * entry->n_channels;
11408 if (version == 0x00010000) {
11409 /* sample description entry (16) + sound sample description v1 (20+16) */
11420 /* only parse extra decoding config for non-pcm audio */
11421 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
11422 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
11423 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
11424 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
11426 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
11427 entry->samples_per_packet);
11428 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11429 entry->bytes_per_packet);
11430 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
11431 entry->bytes_per_frame);
11432 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
11433 entry->bytes_per_sample);
11435 if (!entry->sampled && entry->bytes_per_packet) {
11436 entry->samples_per_frame = (entry->bytes_per_frame /
11437 entry->bytes_per_packet) * entry->samples_per_packet;
11438 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
11439 entry->samples_per_frame);
11444 } else if (version == 0x00020000) {
11451 /* sample description entry (16) + sound sample description v2 (56) */
11455 qtfp.val = QT_UINT64 (stsd_entry_data + offset + 4);
11456 entry->rate = qtfp.fp;
11457 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
11459 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
11460 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
11461 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
11462 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
11463 QT_UINT32 (stsd_entry_data + offset + 20));
11464 GST_LOG_OBJECT (qtdemux, "format flags: %X",
11465 QT_UINT32 (stsd_entry_data + offset + 24));
11466 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
11467 QT_UINT32 (stsd_entry_data + offset + 28));
11468 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
11469 QT_UINT32 (stsd_entry_data + offset + 32));
11470 } else if (version != 0x00000) {
11471 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
11476 gst_caps_unref (entry->caps);
11478 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
11479 stsd_entry_data + 32, len - 16, &codec);
11487 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
11489 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
11491 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
11493 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
11496 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
11497 gst_caps_set_simple (entry->caps,
11498 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE",
11505 const guint8 *owma_data;
11506 const gchar *codec_name = NULL;
11510 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11511 /* FIXME this should also be gst_riff_strf_auds,
11512 * but the latter one is actually missing bits-per-sample :( */
11517 gint32 nSamplesPerSec;
11518 gint32 nAvgBytesPerSec;
11519 gint16 nBlockAlign;
11520 gint16 wBitsPerSample;
11523 WAVEFORMATEX *wfex;
11525 GST_DEBUG_OBJECT (qtdemux, "parse owma");
11526 owma_data = stsd_entry_data;
11527 owma_len = QT_UINT32 (owma_data);
11528 if (owma_len <= 54) {
11529 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
11532 wfex = (WAVEFORMATEX *) (owma_data + 36);
11533 buf = gst_buffer_new_and_alloc (owma_len - 54);
11534 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
11535 if (wfex->wFormatTag == 0x0161) {
11536 codec_name = "Windows Media Audio";
11538 } else if (wfex->wFormatTag == 0x0162) {
11539 codec_name = "Windows Media Audio 9 Pro";
11541 } else if (wfex->wFormatTag == 0x0163) {
11542 codec_name = "Windows Media Audio 9 Lossless";
11543 /* is that correct? gstffmpegcodecmap.c is missing it, but
11544 * fluendo codec seems to support it */
11548 gst_caps_set_simple (entry->caps,
11549 "codec_data", GST_TYPE_BUFFER, buf,
11550 "wmaversion", G_TYPE_INT, version,
11551 "block_align", G_TYPE_INT,
11552 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
11553 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
11554 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
11555 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
11556 gst_buffer_unref (buf);
11560 codec = g_strdup (codec_name);
11566 gint len = QT_UINT32 (stsd_entry_data) - offset;
11567 const guint8 *wfex_data = stsd_entry_data + offset;
11568 const gchar *codec_name = NULL;
11570 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
11571 /* FIXME this should also be gst_riff_strf_auds,
11572 * but the latter one is actually missing bits-per-sample :( */
11577 gint32 nSamplesPerSec;
11578 gint32 nAvgBytesPerSec;
11579 gint16 nBlockAlign;
11580 gint16 wBitsPerSample;
11585 /* FIXME: unify with similar wavformatex parsing code above */
11586 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
11592 if (QT_UINT32 (wfex_data) <= len)
11593 size = QT_UINT32 (wfex_data) - 8;
11598 /* No real data, so break out */
11601 switch (QT_FOURCC (wfex_data + 4)) {
11602 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
11604 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
11609 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
11610 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
11611 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
11612 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
11613 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
11614 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
11615 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
11617 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
11618 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
11619 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
11620 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
11621 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
11622 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
11624 if (wfex.wFormatTag == 0x0161) {
11625 codec_name = "Windows Media Audio";
11627 } else if (wfex.wFormatTag == 0x0162) {
11628 codec_name = "Windows Media Audio 9 Pro";
11630 } else if (wfex.wFormatTag == 0x0163) {
11631 codec_name = "Windows Media Audio 9 Lossless";
11632 /* is that correct? gstffmpegcodecmap.c is missing it, but
11633 * fluendo codec seems to support it */
11637 gst_caps_set_simple (entry->caps,
11638 "wmaversion", G_TYPE_INT, version,
11639 "block_align", G_TYPE_INT, wfex.nBlockAlign,
11640 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
11641 "width", G_TYPE_INT, wfex.wBitsPerSample,
11642 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
11644 if (size > wfex.cbSize) {
11647 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
11648 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
11649 size - wfex.cbSize);
11650 gst_caps_set_simple (entry->caps,
11651 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11652 gst_buffer_unref (buf);
11654 GST_WARNING_OBJECT (qtdemux, "no codec data");
11659 codec = g_strdup (codec_name);
11667 wfex_data += size + 8;
11673 const guint8 *opus_data;
11674 guint8 *channel_mapping = NULL;
11677 guint8 channel_mapping_family;
11678 guint8 stream_count;
11679 guint8 coupled_count;
11682 opus_data = stsd_entry_data;
11684 channels = GST_READ_UINT8 (opus_data + 45);
11685 rate = GST_READ_UINT32_LE (opus_data + 48);
11686 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
11687 stream_count = GST_READ_UINT8 (opus_data + 55);
11688 coupled_count = GST_READ_UINT8 (opus_data + 56);
11690 if (channels > 0) {
11691 channel_mapping = g_malloc (channels * sizeof (guint8));
11692 for (i = 0; i < channels; i++)
11693 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
11696 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
11697 channel_mapping_family, stream_count, coupled_count,
11709 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11710 GST_TAG_AUDIO_CODEC, codec, NULL);
11714 /* some bitrate info may have ended up in caps */
11715 s = gst_caps_get_structure (entry->caps, 0);
11716 gst_structure_get_int (s, "bitrate", &bitrate);
11718 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11719 GST_TAG_BITRATE, bitrate, NULL);
11722 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11723 if (!stream->protected) {
11725 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv) {
11729 if (stream->protected && fourcc == FOURCC_mp4a) {
11730 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
11734 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_mp4a) {
11742 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
11744 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
11746 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
11750 /* If the fourcc's bottom 16 bits gives 'sm', then the top
11751 16 bits is a byte-swapped wave-style codec identifier,
11752 and we can find a WAVE header internally to a 'wave' atom here.
11753 This can more clearly be thought of as 'ms' as the top 16 bits, and a
11754 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
11757 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
11758 if (len < offset + 20) {
11759 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
11761 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
11762 const guint8 *data = stsd_entry_data + offset + 16;
11764 GNode *waveheadernode;
11766 wavenode = g_node_new ((guint8 *) data);
11767 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
11768 const guint8 *waveheader;
11771 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
11772 if (waveheadernode) {
11773 waveheader = (const guint8 *) waveheadernode->data;
11774 headerlen = QT_UINT32 (waveheader);
11776 if (headerlen > 8) {
11777 gst_riff_strf_auds *header = NULL;
11778 GstBuffer *headerbuf;
11784 headerbuf = gst_buffer_new_and_alloc (headerlen);
11785 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
11787 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
11788 headerbuf, &header, &extra)) {
11789 gst_caps_unref (entry->caps);
11790 /* FIXME: Need to do something with the channel reorder map */
11792 gst_riff_create_audio_caps (header->format, NULL, header,
11793 extra, NULL, NULL, NULL);
11796 gst_buffer_unref (extra);
11801 GST_DEBUG ("Didn't find waveheadernode for this codec");
11803 g_node_destroy (wavenode);
11806 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11807 stream->stream_tags);
11811 /* FIXME: what is in the chunk? */
11814 gint len = QT_UINT32 (stsd_data);
11816 /* seems to be always = 116 = 0x74 */
11822 gint len = QT_UINT32 (stsd_entry_data);
11825 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
11827 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
11828 gst_caps_set_simple (entry->caps,
11829 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11830 gst_buffer_unref (buf);
11832 gst_caps_set_simple (entry->caps,
11833 "samplesize", G_TYPE_INT, samplesize, NULL);
11838 GNode *alac, *wave = NULL;
11840 /* apparently, m4a has this atom appended directly in the stsd entry,
11841 * while mov has it in a wave atom */
11842 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
11844 /* alac now refers to stsd entry atom */
11845 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
11847 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
11849 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
11852 const guint8 *alac_data = alac->data;
11853 gint len = QT_UINT32 (alac->data);
11857 GST_DEBUG_OBJECT (qtdemux,
11858 "discarding alac atom with unexpected len %d", len);
11860 /* codec-data contains alac atom size and prefix,
11861 * ffmpeg likes it that way, not quite gst-ish though ...*/
11862 buf = gst_buffer_new_and_alloc (len);
11863 gst_buffer_fill (buf, 0, alac->data, len);
11864 gst_caps_set_simple (entry->caps,
11865 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11866 gst_buffer_unref (buf);
11868 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
11869 entry->n_channels = QT_UINT8 (alac_data + 21);
11870 entry->rate = QT_UINT32 (alac_data + 32);
11873 gst_caps_set_simple (entry->caps,
11874 "samplesize", G_TYPE_INT, samplesize, NULL);
11879 /* The codingname of the sample entry is 'fLaC' */
11880 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
11883 /* The 'dfLa' box is added to the sample entry to convey
11884 initializing information for the decoder. */
11885 const GNode *dfla =
11886 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
11889 const guint32 len = QT_UINT32 (dfla->data);
11891 /* Must contain at least dfLa box header (12),
11892 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
11894 GST_DEBUG_OBJECT (qtdemux,
11895 "discarding dfla atom with unexpected len %d", len);
11897 /* skip dfLa header to get the METADATA_BLOCKs */
11898 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
11899 const guint32 metadata_blocks_len = len - 12;
11901 gchar *stream_marker = g_strdup ("fLaC");
11902 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
11903 strlen (stream_marker));
11906 guint32 remainder = 0;
11907 guint32 block_size = 0;
11908 gboolean is_last = FALSE;
11910 GValue array = G_VALUE_INIT;
11911 GValue value = G_VALUE_INIT;
11913 g_value_init (&array, GST_TYPE_ARRAY);
11914 g_value_init (&value, GST_TYPE_BUFFER);
11916 gst_value_set_buffer (&value, block);
11917 gst_value_array_append_value (&array, &value);
11918 g_value_reset (&value);
11920 gst_buffer_unref (block);
11922 /* check there's at least one METADATA_BLOCK_HEADER's worth
11923 * of data, and we haven't already finished parsing */
11924 while (!is_last && ((index + 3) < metadata_blocks_len)) {
11925 remainder = metadata_blocks_len - index;
11927 /* add the METADATA_BLOCK_HEADER size to the signalled size */
11929 (metadata_blocks[index + 1] << 16) +
11930 (metadata_blocks[index + 2] << 8) +
11931 metadata_blocks[index + 3];
11933 /* be careful not to read off end of box */
11934 if (block_size > remainder) {
11938 is_last = metadata_blocks[index] >> 7;
11940 block = gst_buffer_new_and_alloc (block_size);
11942 gst_buffer_fill (block, 0, &metadata_blocks[index],
11945 gst_value_set_buffer (&value, block);
11946 gst_value_array_append_value (&array, &value);
11947 g_value_reset (&value);
11949 gst_buffer_unref (block);
11951 index += block_size;
11954 /* only append the metadata if we successfully read all of it */
11956 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
11957 (stream)->caps, 0), "streamheader", &array);
11959 GST_WARNING_OBJECT (qtdemux,
11960 "discarding all METADATA_BLOCKs due to invalid "
11961 "block_size %d at idx %d, rem %d", block_size, index,
11965 g_value_unset (&value);
11966 g_value_unset (&array);
11968 /* The sample rate obtained from the stsd may not be accurate
11969 * since it cannot represent rates greater than 65535Hz, so
11970 * override that value with the sample rate from the
11971 * METADATA_BLOCK_STREAMINFO block */
11972 CUR_STREAM (stream)->rate =
11973 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
11984 gint len = QT_UINT32 (stsd_entry_data);
11987 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
11990 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
11992 /* If we have enough data, let's try to get the 'damr' atom. See
11993 * the 3GPP container spec (26.244) for more details. */
11994 if ((len - 0x34) > 8 &&
11995 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
11996 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11997 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
12000 gst_caps_set_simple (entry->caps,
12001 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12002 gst_buffer_unref (buf);
12008 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
12009 gint len = QT_UINT32 (stsd_entry_data);
12012 guint16 sound_version = QT_UINT16 (stsd_entry_data + 16);
12014 if (sound_version == 1) {
12015 guint16 channels = QT_UINT16 (stsd_entry_data + 24);
12016 guint32 time_scale = QT_UINT32 (stsd_entry_data + 30);
12017 guint8 codec_data[2];
12019 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
12021 gint sample_rate_index =
12022 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
12024 /* build AAC codec data */
12025 codec_data[0] = profile << 3;
12026 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
12027 codec_data[1] = (sample_rate_index & 0x01) << 7;
12028 codec_data[1] |= (channels & 0xF) << 3;
12030 buf = gst_buffer_new_and_alloc (2);
12031 gst_buffer_fill (buf, 0, codec_data, 2);
12032 gst_caps_set_simple (entry->caps,
12033 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12034 gst_buffer_unref (buf);
12040 GST_INFO_OBJECT (qtdemux,
12041 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12045 GST_INFO_OBJECT (qtdemux,
12046 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12047 GST_FOURCC_ARGS (fourcc), entry->caps);
12049 } else if (stream->subtype == FOURCC_strm) {
12050 if (fourcc == FOURCC_rtsp) {
12051 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
12053 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
12054 GST_FOURCC_ARGS (fourcc));
12055 goto unknown_stream;
12057 entry->sampled = TRUE;
12058 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
12059 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
12061 entry->sampled = TRUE;
12062 entry->sparse = TRUE;
12065 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12068 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12069 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12074 /* hunt for sort-of codec data */
12078 GNode *mp4s = NULL;
12079 GNode *esds = NULL;
12081 /* look for palette in a stsd->mp4s->esds sub-atom */
12082 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
12084 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
12085 if (esds == NULL) {
12087 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
12091 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
12092 stream->stream_tags);
12096 GST_INFO_OBJECT (qtdemux,
12097 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12100 GST_INFO_OBJECT (qtdemux,
12101 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12102 GST_FOURCC_ARGS (fourcc), entry->caps);
12104 /* everything in 1 sample */
12105 entry->sampled = TRUE;
12108 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
12111 if (entry->caps == NULL)
12112 goto unknown_stream;
12115 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12116 GST_TAG_SUBTITLE_CODEC, codec, NULL);
12122 /* promote to sampled format */
12123 if (entry->fourcc == FOURCC_samr) {
12124 /* force mono 8000 Hz for AMR */
12125 entry->sampled = TRUE;
12126 entry->n_channels = 1;
12127 entry->rate = 8000;
12128 } else if (entry->fourcc == FOURCC_sawb) {
12129 /* force mono 16000 Hz for AMR-WB */
12130 entry->sampled = TRUE;
12131 entry->n_channels = 1;
12132 entry->rate = 16000;
12133 } else if (entry->fourcc == FOURCC_mp4a) {
12134 entry->sampled = TRUE;
12138 stsd_entry_data += len;
12139 remaining_stsd_len -= len;
12143 /* collect sample information */
12144 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
12145 goto samples_failed;
12147 if (qtdemux->fragmented) {
12150 /* need all moov samples as basis; probably not many if any at all */
12151 /* prevent moof parsing taking of at this time */
12152 offset = qtdemux->moof_offset;
12153 qtdemux->moof_offset = 0;
12154 if (stream->n_samples &&
12155 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
12156 qtdemux->moof_offset = offset;
12157 goto samples_failed;
12159 qtdemux->moof_offset = 0;
12160 /* movie duration more reliable in this case (e.g. mehd) */
12161 if (qtdemux->segment.duration &&
12162 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
12164 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
12167 /* configure segments */
12168 if (!qtdemux_parse_segments (qtdemux, stream, trak))
12169 goto segments_failed;
12171 /* add some language tag, if useful */
12172 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
12173 strcmp (stream->lang_id, "und")) {
12174 const gchar *lang_code;
12176 /* convert ISO 639-2 code to ISO 639-1 */
12177 lang_code = gst_tag_get_language_code (stream->lang_id);
12178 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12179 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
12182 /* Check for UDTA tags */
12183 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
12184 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
12187 /* now we are ready to add the stream */
12188 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
12189 goto too_many_streams;
12191 if (!qtdemux->got_moov) {
12192 qtdemux->streams[qtdemux->n_streams] = stream;
12193 qtdemux->n_streams++;
12194 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
12202 GST_INFO_OBJECT (qtdemux, "skip disabled track");
12204 gst_qtdemux_stream_free (qtdemux, stream);
12209 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
12210 (_("This file is corrupt and cannot be played.")), (NULL));
12212 gst_qtdemux_stream_free (qtdemux, stream);
12217 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
12219 gst_qtdemux_stream_free (qtdemux, stream);
12225 /* we posted an error already */
12226 /* free stbl sub-atoms */
12227 gst_qtdemux_stbl_free (stream);
12229 gst_qtdemux_stream_free (qtdemux, stream);
12234 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
12237 gst_qtdemux_stream_free (qtdemux, stream);
12242 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
12243 GST_FOURCC_ARGS (stream->subtype));
12245 gst_qtdemux_stream_free (qtdemux, stream);
12250 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
12251 (_("This file contains too many streams. Only playing first %d"),
12252 GST_QTDEMUX_MAX_STREAMS), (NULL));
12257 /* If we can estimate the overall bitrate, and don't have information about the
12258 * stream bitrate for exactly one stream, this guesses the stream bitrate as
12259 * the overall bitrate minus the sum of the bitrates of all other streams. This
12260 * should be useful for the common case where we have one audio and one video
12261 * stream and can estimate the bitrate of one, but not the other. */
12263 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
12265 QtDemuxStream *stream = NULL;
12266 gint64 size, sys_bitrate, sum_bitrate = 0;
12267 GstClockTime duration;
12271 if (qtdemux->fragmented)
12274 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
12276 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
12278 GST_DEBUG_OBJECT (qtdemux,
12279 "Size in bytes of the stream not known - bailing");
12283 /* Subtract the header size */
12284 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
12285 size, qtdemux->header_size);
12287 if (size < qtdemux->header_size)
12290 size = size - qtdemux->header_size;
12292 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
12293 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
12297 for (i = 0; i < qtdemux->n_streams; i++) {
12298 switch (qtdemux->streams[i]->subtype) {
12301 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
12302 CUR_STREAM (qtdemux->streams[i])->caps);
12303 /* retrieve bitrate, prefer avg then max */
12305 if (qtdemux->streams[i]->stream_tags) {
12306 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12307 GST_TAG_MAXIMUM_BITRATE, &bitrate);
12308 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
12309 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12310 GST_TAG_NOMINAL_BITRATE, &bitrate);
12311 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
12312 gst_tag_list_get_uint (qtdemux->streams[i]->stream_tags,
12313 GST_TAG_BITRATE, &bitrate);
12314 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
12317 sum_bitrate += bitrate;
12320 GST_DEBUG_OBJECT (qtdemux,
12321 ">1 stream with unknown bitrate - bailing");
12324 stream = qtdemux->streams[i];
12328 /* For other subtypes, we assume no significant impact on bitrate */
12334 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
12338 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
12340 if (sys_bitrate < sum_bitrate) {
12341 /* This can happen, since sum_bitrate might be derived from maximum
12342 * bitrates and not average bitrates */
12343 GST_DEBUG_OBJECT (qtdemux,
12344 "System bitrate less than sum bitrate - bailing");
12348 bitrate = sys_bitrate - sum_bitrate;
12349 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
12350 ", Stream bitrate = %u", sys_bitrate, bitrate);
12352 if (!stream->stream_tags)
12353 stream->stream_tags = gst_tag_list_new_empty ();
12355 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
12357 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12358 GST_TAG_BITRATE, bitrate, NULL);
12361 static GstFlowReturn
12362 qtdemux_prepare_streams (GstQTDemux * qtdemux)
12365 GstFlowReturn ret = GST_FLOW_OK;
12367 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
12369 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
12370 QtDemuxStream *stream = qtdemux->streams[i];
12371 guint32 sample_num = 0;
12373 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
12374 i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12376 if (qtdemux->fragmented) {
12377 /* need all moov samples first */
12378 GST_OBJECT_LOCK (qtdemux);
12379 while (stream->n_samples == 0)
12380 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
12382 GST_OBJECT_UNLOCK (qtdemux);
12384 /* discard any stray moof */
12385 qtdemux->moof_offset = 0;
12388 /* prepare braking */
12389 if (ret != GST_FLOW_ERROR)
12392 /* in pull mode, we should have parsed some sample info by now;
12393 * and quite some code will not handle no samples.
12394 * in push mode, we'll just have to deal with it */
12395 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
12396 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
12397 gst_qtdemux_remove_stream (qtdemux, i);
12402 /* parse the initial sample for use in setting the frame rate cap */
12403 while (sample_num == 0 && sample_num < stream->n_samples) {
12404 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
12408 if (stream->n_samples > 0 && stream->stbl_index >= 0) {
12409 stream->first_duration = stream->samples[0].duration;
12410 GST_LOG_OBJECT (qtdemux, "stream %d first duration %u",
12411 stream->track_id, stream->first_duration);
12418 static GstFlowReturn
12419 qtdemux_expose_streams (GstQTDemux * qtdemux)
12422 GSList *oldpads = NULL;
12425 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
12427 for (i = 0; i < qtdemux->n_streams; i++) {
12428 QtDemuxStream *stream = qtdemux->streams[i];
12429 GstPad *oldpad = stream->pad;
12432 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
12433 i, stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
12435 if ((stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) &&
12436 stream->track_id == qtdemux->chapters_track_id) {
12437 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
12438 so that it doesn't look like a subtitle track */
12439 gst_qtdemux_remove_stream (qtdemux, i);
12444 /* now we have all info and can expose */
12445 list = stream->stream_tags;
12446 stream->stream_tags = NULL;
12448 oldpads = g_slist_prepend (oldpads, oldpad);
12449 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
12450 return GST_FLOW_ERROR;
12453 gst_qtdemux_guess_bitrate (qtdemux);
12455 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
12457 for (iter = oldpads; iter; iter = g_slist_next (iter)) {
12458 GstPad *oldpad = iter->data;
12461 event = gst_event_new_eos ();
12462 if (qtdemux->segment_seqnum)
12463 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
12465 gst_pad_push_event (oldpad, event);
12466 gst_pad_set_active (oldpad, FALSE);
12467 gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad);
12468 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, oldpad);
12469 gst_object_unref (oldpad);
12472 /* check if we should post a redirect in case there is a single trak
12473 * and it is a redirecting trak */
12474 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
12477 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
12478 "an external content");
12479 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
12480 gst_structure_new ("redirect",
12481 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
12483 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
12484 qtdemux->posted_redirect = TRUE;
12487 for (i = 0; i < qtdemux->n_streams; i++) {
12488 QtDemuxStream *stream = qtdemux->streams[i];
12490 qtdemux_do_allocation (qtdemux, stream);
12493 qtdemux->exposed = TRUE;
12494 return GST_FLOW_OK;
12497 /* check if major or compatible brand is 3GP */
12498 static inline gboolean
12499 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
12502 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12504 } else if (qtdemux->comp_brands != NULL) {
12508 gboolean res = FALSE;
12510 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
12513 while (size >= 4) {
12514 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
12519 gst_buffer_unmap (qtdemux->comp_brands, &map);
12526 /* check if tag is a spec'ed 3GP tag keyword storing a string */
12527 static inline gboolean
12528 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
12530 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
12531 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
12532 || fourcc == FOURCC_albm;
12536 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
12537 const char *tag, const char *dummy, GNode * node)
12539 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12543 gdouble longitude, latitude, altitude;
12546 len = QT_UINT32 (node->data);
12553 /* TODO: language code skipped */
12555 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
12558 /* do not alarm in trivial case, but bail out otherwise */
12559 if (*(data + offset) != 0) {
12560 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
12564 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12565 GST_TAG_GEO_LOCATION_NAME, name, NULL);
12566 offset += strlen (name);
12570 if (len < offset + 2 + 4 + 4 + 4)
12573 /* +1 +1 = skip null-terminator and location role byte */
12575 /* table in spec says unsigned, semantics say negative has meaning ... */
12576 longitude = QT_SFP32 (data + offset);
12579 latitude = QT_SFP32 (data + offset);
12582 altitude = QT_SFP32 (data + offset);
12584 /* one invalid means all are invalid */
12585 if (longitude >= -180.0 && longitude <= 180.0 &&
12586 latitude >= -90.0 && latitude <= 90.0) {
12587 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
12588 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
12589 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
12590 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
12593 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
12600 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
12607 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
12608 const char *tag, const char *dummy, GNode * node)
12614 len = QT_UINT32 (node->data);
12618 y = QT_UINT16 ((guint8 *) node->data + 12);
12620 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
12623 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
12625 date = g_date_new_dmy (1, 1, y);
12626 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
12627 g_date_free (date);
12631 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
12632 const char *tag, const char *dummy, GNode * node)
12635 char *tag_str = NULL;
12640 len = QT_UINT32 (node->data);
12645 entity = (guint8 *) node->data + offset;
12646 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
12647 GST_DEBUG_OBJECT (qtdemux,
12648 "classification info: %c%c%c%c invalid classification entity",
12649 entity[0], entity[1], entity[2], entity[3]);
12654 table = QT_UINT16 ((guint8 *) node->data + offset);
12656 /* Language code skipped */
12660 /* Tag format: "XXXX://Y[YYYY]/classification info string"
12661 * XXXX: classification entity, fixed length 4 chars.
12662 * Y[YYYY]: classification table, max 5 chars.
12664 tag_str = g_strdup_printf ("----://%u/%s",
12665 table, (char *) node->data + offset);
12667 /* memcpy To be sure we're preserving byte order */
12668 memcpy (tag_str, entity, 4);
12669 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
12671 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
12680 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
12686 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
12687 const char *tag, const char *dummy, GNode * node)
12689 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12695 gboolean ret = TRUE;
12696 const gchar *charset = NULL;
12698 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12700 len = QT_UINT32 (data->data);
12701 type = QT_UINT32 ((guint8 *) data->data + 8);
12702 if (type == 0x00000001 && len > 16) {
12703 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
12706 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
12707 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
12710 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
12714 len = QT_UINT32 (node->data);
12715 type = QT_UINT32 ((guint8 *) node->data + 4);
12716 if ((type >> 24) == 0xa9 && len > 8 + 4) {
12720 /* Type starts with the (C) symbol, so the next data is a list
12721 * of (string size(16), language code(16), string) */
12723 str_len = QT_UINT16 ((guint8 *) node->data + 8);
12724 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
12726 /* the string + fourcc + size + 2 16bit fields,
12727 * means that there are more tags in this atom */
12728 if (len > str_len + 8 + 4) {
12729 /* TODO how to represent the same tag in different languages? */
12730 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
12731 "text alternatives, reading only first one");
12735 len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
12736 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
12738 if (lang_code < 0x800) { /* MAC encoded string */
12741 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
12742 QT_FOURCC ((guint8 *) node->data + 4))) {
12743 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
12745 /* we go for 3GP style encoding if major brands claims so,
12746 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
12747 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
12748 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
12749 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
12751 /* 16-bit Language code is ignored here as well */
12752 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
12759 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
12760 ret = FALSE; /* may have to fallback */
12763 GError *err = NULL;
12765 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
12766 charset, NULL, NULL, &err);
12768 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
12769 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
12771 g_error_free (err);
12774 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
12775 len - offset, env_vars);
12778 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
12779 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
12783 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
12790 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
12791 const char *tag, const char *dummy, GNode * node)
12793 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
12797 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
12798 const char *tag, const char *dummy, GNode * node)
12800 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
12802 char *s, *t, *k = NULL;
12807 /* first try normal string tag if major brand not 3GP */
12808 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
12809 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
12810 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
12811 * let's try it 3gpp way after minor safety check */
12813 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
12819 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
12823 len = QT_UINT32 (data);
12827 count = QT_UINT8 (data + 14);
12829 for (; count; count--) {
12832 if (offset + 1 > len)
12834 slen = QT_UINT8 (data + offset);
12836 if (offset + slen > len)
12838 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
12841 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
12843 t = g_strjoin (",", k, s, NULL);
12851 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
12858 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
12859 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
12868 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
12874 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
12875 const char *tag1, const char *tag2, GNode * node)
12882 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12884 len = QT_UINT32 (data->data);
12885 type = QT_UINT32 ((guint8 *) data->data + 8);
12886 if (type == 0x00000000 && len >= 22) {
12887 n1 = QT_UINT16 ((guint8 *) data->data + 18);
12888 n2 = QT_UINT16 ((guint8 *) data->data + 20);
12890 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
12891 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
12894 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
12895 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
12902 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
12903 const char *tag1, const char *dummy, GNode * node)
12910 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12912 len = QT_UINT32 (data->data);
12913 type = QT_UINT32 ((guint8 *) data->data + 8);
12914 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
12915 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
12916 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
12917 n1 = QT_UINT16 ((guint8 *) data->data + 16);
12919 /* do not add bpm=0 */
12920 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
12921 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
12929 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
12930 const char *tag1, const char *dummy, GNode * node)
12937 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12939 len = QT_UINT32 (data->data);
12940 type = QT_UINT32 ((guint8 *) data->data + 8);
12941 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
12942 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
12943 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
12944 num = QT_UINT32 ((guint8 *) data->data + 16);
12946 /* do not add num=0 */
12947 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
12948 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
12955 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
12956 const char *tag1, const char *dummy, GNode * node)
12963 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12965 len = QT_UINT32 (data->data);
12966 type = QT_UINT32 ((guint8 *) data->data + 8);
12967 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
12968 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
12969 GstTagImageType image_type;
12971 if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
12972 image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
12974 image_type = GST_TAG_IMAGE_TYPE_NONE;
12977 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
12978 len - 16, image_type))) {
12979 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
12980 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
12981 gst_sample_unref (sample);
12988 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
12989 const char *tag, const char *dummy, GNode * node)
12996 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
12998 len = QT_UINT32 (data->data);
12999 type = QT_UINT32 ((guint8 *) data->data + 8);
13000 if (type == 0x00000001 && len > 16) {
13001 guint y, m = 1, d = 1;
13004 s = g_strndup ((char *) data->data + 16, len - 16);
13005 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
13006 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
13007 if (ret >= 1 && y > 1500 && y < 3000) {
13010 date = g_date_new_dmy (d, m, y);
13011 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
13012 g_date_free (date);
13014 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
13022 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
13023 const char *tag, const char *dummy, GNode * node)
13027 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13029 /* re-route to normal string tag if major brand says so
13030 * or no data atom and compatible brand suggests so */
13031 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
13032 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
13033 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
13038 guint len, type, n;
13040 len = QT_UINT32 (data->data);
13041 type = QT_UINT32 ((guint8 *) data->data + 8);
13042 if (type == 0x00000000 && len >= 18) {
13043 n = QT_UINT16 ((guint8 *) data->data + 16);
13045 const gchar *genre;
13047 genre = gst_tag_id3_genre_get (n - 1);
13048 if (genre != NULL) {
13049 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
13050 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
13058 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
13059 const gchar * tag, guint8 * data, guint32 datasize)
13064 /* make a copy to have \0 at the end */
13065 datacopy = g_strndup ((gchar *) data, datasize);
13067 /* convert the str to double */
13068 if (sscanf (datacopy, "%lf", &value) == 1) {
13069 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
13070 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
13072 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
13080 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
13081 const char *tag, const char *tag_bis, GNode * node)
13090 const gchar *meanstr;
13091 const gchar *namestr;
13093 /* checking the whole ---- atom size for consistency */
13094 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
13095 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
13099 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
13101 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
13105 meansize = QT_UINT32 (mean->data);
13106 if (meansize <= 12) {
13107 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
13110 meanstr = ((gchar *) mean->data) + 12;
13113 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
13115 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
13119 namesize = QT_UINT32 (name->data);
13120 if (namesize <= 12) {
13121 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
13124 namestr = ((gchar *) name->data) + 12;
13132 * uint24 - data type
13136 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
13138 GST_WARNING_OBJECT (demux, "No data atom in this tag");
13141 datasize = QT_UINT32 (data->data);
13142 if (datasize <= 16) {
13143 GST_WARNING_OBJECT (demux, "Data atom too small");
13146 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
13148 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
13149 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
13150 static const struct
13152 const gchar name[28];
13153 const gchar tag[28];
13156 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
13157 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
13158 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
13159 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
13160 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
13161 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
13162 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
13163 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
13167 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
13168 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
13169 switch (gst_tag_get_type (tags[i].tag)) {
13170 case G_TYPE_DOUBLE:
13171 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
13172 ((guint8 *) data->data) + 16, datasize - 16);
13174 case G_TYPE_STRING:
13175 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
13184 if (i == G_N_ELEMENTS (tags))
13194 #ifndef GST_DISABLE_GST_DEBUG
13196 gchar *namestr_dbg;
13197 gchar *meanstr_dbg;
13199 meanstr_dbg = g_strndup (meanstr, meansize);
13200 namestr_dbg = g_strndup (namestr, namesize);
13202 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
13203 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
13205 g_free (namestr_dbg);
13206 g_free (meanstr_dbg);
13213 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
13214 const char *tag_bis, GNode * node)
13219 GstTagList *id32_taglist = NULL;
13221 GST_LOG_OBJECT (demux, "parsing ID32");
13224 len = GST_READ_UINT32_BE (data);
13226 /* need at least full box and language tag */
13230 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
13231 gst_buffer_fill (buf, 0, data + 14, len - 14);
13233 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
13234 if (id32_taglist) {
13235 GST_LOG_OBJECT (demux, "parsing ok");
13236 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
13237 gst_tag_list_unref (id32_taglist);
13239 GST_LOG_OBJECT (demux, "parsing failed");
13242 gst_buffer_unref (buf);
13245 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
13246 const char *tag, const char *tag_bis, GNode * node);
13249 FOURCC_pcst -> if media is a podcast -> bool
13250 FOURCC_cpil -> if media is part of a compilation -> bool
13251 FOURCC_pgap -> if media is part of a gapless context -> bool
13252 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
13255 static const struct
13258 const gchar *gst_tag;
13259 const gchar *gst_tag_bis;
13260 const GstQTDemuxAddTagFunc func;
13263 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13264 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
13265 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
13266 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13267 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13268 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
13269 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
13270 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
13271 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13272 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
13273 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13274 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
13275 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13276 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13277 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13278 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
13279 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
13280 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
13281 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
13282 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13283 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
13284 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
13285 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13286 qtdemux_tag_add_num}, {
13287 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
13288 qtdemux_tag_add_num}, {
13289 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
13290 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
13291 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
13292 FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
13293 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
13294 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
13295 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13296 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
13297 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
13298 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
13299 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
13300 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13301 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
13302 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
13303 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
13304 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
13305 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
13306 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
13307 qtdemux_tag_add_classification}, {
13308 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
13309 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
13310 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
13312 /* This is a special case, some tags are stored in this
13313 * 'reverse dns naming', according to:
13314 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
13317 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
13318 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
13319 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
13322 struct _GstQtDemuxTagList
13325 GstTagList *taglist;
13327 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
13330 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
13336 const gchar *style;
13341 GstQTDemux *demux = qtdemuxtaglist->demux;
13342 GstTagList *taglist = qtdemuxtaglist->taglist;
13345 len = QT_UINT32 (data);
13346 buf = gst_buffer_new_and_alloc (len);
13347 gst_buffer_fill (buf, 0, data, len);
13349 /* heuristic to determine style of tag */
13350 if (QT_FOURCC (data + 4) == FOURCC_____ ||
13351 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
13353 else if (demux->major_brand == FOURCC_qt__)
13354 style = "quicktime";
13355 /* fall back to assuming iso/3gp tag style */
13359 /* santize the name for the caps. */
13360 for (i = 0; i < 4; i++) {
13361 guint8 d = data[4 + i];
13362 if (g_ascii_isalnum (d))
13363 ndata[i] = g_ascii_tolower (d);
13368 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
13369 ndata[0], ndata[1], ndata[2], ndata[3]);
13370 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
13372 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
13373 sample = gst_sample_new (buf, NULL, NULL, s);
13374 gst_buffer_unref (buf);
13375 g_free (media_type);
13377 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
13380 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
13381 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
13383 gst_sample_unref (sample);
13387 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
13394 GstQtDemuxTagList demuxtaglist;
13396 demuxtaglist.demux = qtdemux;
13397 demuxtaglist.taglist = taglist;
13399 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
13400 if (meta != NULL) {
13401 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
13402 if (ilst == NULL) {
13403 GST_LOG_OBJECT (qtdemux, "no ilst");
13408 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
13412 while (i < G_N_ELEMENTS (add_funcs)) {
13413 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
13417 len = QT_UINT32 (node->data);
13419 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
13420 GST_FOURCC_ARGS (add_funcs[i].fourcc));
13422 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
13423 add_funcs[i].gst_tag_bis, node);
13425 g_node_destroy (node);
13431 /* parsed nodes have been removed, pass along remainder as blob */
13432 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
13433 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
13435 /* parse up XMP_ node if existing */
13436 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
13437 if (xmp_ != NULL) {
13439 GstTagList *xmptaglist;
13441 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
13442 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
13443 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
13444 gst_buffer_unref (buf);
13446 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
13448 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
13454 GstStructure *structure; /* helper for sort function */
13456 guint min_req_bitrate;
13457 guint min_req_qt_version;
13461 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
13463 GstQtReference *ref_a = (GstQtReference *) a;
13464 GstQtReference *ref_b = (GstQtReference *) b;
13466 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
13467 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
13469 /* known bitrates go before unknown; higher bitrates go first */
13470 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
13473 /* sort the redirects and post a message for the application.
13476 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
13478 GstQtReference *best;
13481 GValue list_val = { 0, };
13484 g_assert (references != NULL);
13486 references = g_list_sort (references, qtdemux_redirects_sort_func);
13488 best = (GstQtReference *) references->data;
13490 g_value_init (&list_val, GST_TYPE_LIST);
13492 for (l = references; l != NULL; l = l->next) {
13493 GstQtReference *ref = (GstQtReference *) l->data;
13494 GValue struct_val = { 0, };
13496 ref->structure = gst_structure_new ("redirect",
13497 "new-location", G_TYPE_STRING, ref->location, NULL);
13499 if (ref->min_req_bitrate > 0) {
13500 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
13501 ref->min_req_bitrate, NULL);
13504 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
13505 g_value_set_boxed (&struct_val, ref->structure);
13506 gst_value_list_append_value (&list_val, &struct_val);
13507 g_value_unset (&struct_val);
13508 /* don't free anything here yet, since we need best->structure below */
13511 g_assert (best != NULL);
13512 s = gst_structure_copy (best->structure);
13514 if (g_list_length (references) > 1) {
13515 gst_structure_set_value (s, "locations", &list_val);
13518 g_value_unset (&list_val);
13520 for (l = references; l != NULL; l = l->next) {
13521 GstQtReference *ref = (GstQtReference *) l->data;
13523 gst_structure_free (ref->structure);
13524 g_free (ref->location);
13527 g_list_free (references);
13529 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
13530 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
13531 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
13532 qtdemux->posted_redirect = TRUE;
13535 /* look for redirect nodes, collect all redirect information and
13539 qtdemux_parse_redirects (GstQTDemux * qtdemux)
13541 GNode *rmra, *rmda, *rdrf;
13543 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
13545 GList *redirects = NULL;
13547 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
13549 GstQtReference ref = { NULL, NULL, 0, 0 };
13550 GNode *rmdr, *rmvc;
13552 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
13553 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
13554 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
13555 ref.min_req_bitrate);
13558 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
13559 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
13560 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
13562 #ifndef GST_DISABLE_GST_DEBUG
13563 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
13565 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
13567 GST_LOG_OBJECT (qtdemux,
13568 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
13569 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
13570 bitmask, check_type);
13571 if (package == FOURCC_qtim && check_type == 0) {
13572 ref.min_req_qt_version = version;
13576 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
13582 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
13583 if (ref_len > 20) {
13584 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
13585 ref_data = (guint8 *) rdrf->data + 20;
13586 if (ref_type == FOURCC_alis) {
13587 guint record_len, record_version, fn_len;
13589 if (ref_len > 70) {
13590 /* MacOSX alias record, google for alias-layout.txt */
13591 record_len = QT_UINT16 (ref_data + 4);
13592 record_version = QT_UINT16 (ref_data + 4 + 2);
13593 fn_len = QT_UINT8 (ref_data + 50);
13594 if (record_len > 50 && record_version == 2 && fn_len > 0) {
13595 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
13598 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
13601 } else if (ref_type == FOURCC_url_) {
13602 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
13604 GST_DEBUG_OBJECT (qtdemux,
13605 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
13606 GST_FOURCC_ARGS (ref_type));
13608 if (ref.location != NULL) {
13609 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
13611 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
13613 GST_WARNING_OBJECT (qtdemux,
13614 "Failed to extract redirect location from rdrf atom");
13617 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
13621 /* look for others */
13622 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
13625 if (redirects != NULL) {
13626 qtdemux_process_redirects (qtdemux, redirects);
13632 static GstTagList *
13633 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
13637 if (tags == NULL) {
13638 tags = gst_tag_list_new_empty ();
13639 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
13642 if (qtdemux->major_brand == FOURCC_mjp2)
13643 fmt = "Motion JPEG 2000";
13644 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
13646 else if (qtdemux->major_brand == FOURCC_qt__)
13648 else if (qtdemux->fragmented)
13651 fmt = "ISO MP4/M4A";
13653 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
13654 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
13656 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
13662 /* we have read the complete moov node now.
13663 * This function parses all of the relevant info, creates the traks and
13664 * prepares all data structures for playback
13667 qtdemux_parse_tree (GstQTDemux * qtdemux)
13673 GstClockTime duration;
13675 guint64 creation_time;
13676 GstDateTime *datetime = NULL;
13679 /* make sure we have a usable taglist */
13680 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13682 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
13683 if (mvhd == NULL) {
13684 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
13685 return qtdemux_parse_redirects (qtdemux);
13688 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
13689 if (version == 1) {
13690 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
13691 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
13692 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
13693 } else if (version == 0) {
13694 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
13695 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
13696 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
13698 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
13702 /* Moving qt creation time (secs since 1904) to unix time */
13703 if (creation_time != 0) {
13704 /* Try to use epoch first as it should be faster and more commonly found */
13705 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
13708 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
13709 /* some data cleansing sanity */
13710 g_get_current_time (&now);
13711 if (now.tv_sec + 24 * 3600 < creation_time) {
13712 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
13714 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
13717 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
13718 GDateTime *dt, *dt_local;
13720 dt = g_date_time_add_seconds (base_dt, creation_time);
13721 dt_local = g_date_time_to_local (dt);
13722 datetime = gst_date_time_new_from_g_date_time (dt_local);
13724 g_date_time_unref (base_dt);
13725 g_date_time_unref (dt);
13729 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
13730 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
13732 gst_date_time_unref (datetime);
13735 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
13736 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
13738 /* check for fragmented file and get some (default) data */
13739 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
13742 GstByteReader mehd_data;
13744 /* let track parsing or anyone know weird stuff might happen ... */
13745 qtdemux->fragmented = TRUE;
13747 /* compensate for total duration */
13748 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
13750 qtdemux_parse_mehd (qtdemux, &mehd_data);
13753 /* set duration in the segment info */
13754 gst_qtdemux_get_duration (qtdemux, &duration);
13756 qtdemux->segment.duration = duration;
13757 /* also do not exceed duration; stop is set that way post seek anyway,
13758 * and segment activation falls back to duration,
13759 * whereas loop only checks stop, so let's align this here as well */
13760 qtdemux->segment.stop = duration;
13763 /* parse all traks */
13764 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
13766 qtdemux_parse_trak (qtdemux, trak);
13767 /* iterate all siblings */
13768 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
13771 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
13774 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
13776 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13778 GST_LOG_OBJECT (qtdemux, "No udta node found.");
13781 /* maybe also some tags in meta box */
13782 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
13784 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
13785 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
13787 GST_LOG_OBJECT (qtdemux, "No meta node found.");
13790 /* parse any protection system info */
13791 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
13793 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
13794 qtdemux_parse_pssh (qtdemux, pssh);
13795 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
13798 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
13803 /* taken from ffmpeg */
13805 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
13817 len = (len << 7) | (c & 0x7f);
13825 /* this can change the codec originally present in @list */
13827 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
13828 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
13830 int len = QT_UINT32 (esds->data);
13831 guint8 *ptr = esds->data;
13832 guint8 *end = ptr + len;
13834 guint8 *data_ptr = NULL;
13836 guint8 object_type_id = 0;
13837 const char *codec_name = NULL;
13838 GstCaps *caps = NULL;
13840 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
13842 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
13844 while (ptr + 1 < end) {
13845 tag = QT_UINT8 (ptr);
13846 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
13848 len = read_descr_size (ptr, end, &ptr);
13849 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
13851 /* Check the stated amount of data is available for reading */
13852 if (len < 0 || ptr + len > end)
13856 case ES_DESCRIPTOR_TAG:
13857 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
13858 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
13861 case DECODER_CONFIG_DESC_TAG:{
13862 guint max_bitrate, avg_bitrate;
13864 object_type_id = QT_UINT8 (ptr);
13865 max_bitrate = QT_UINT32 (ptr + 5);
13866 avg_bitrate = QT_UINT32 (ptr + 9);
13867 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
13868 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
13869 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
13870 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
13871 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
13872 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
13873 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
13874 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
13876 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
13877 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
13878 avg_bitrate, NULL);
13883 case DECODER_SPECIFIC_INFO_TAG:
13884 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
13885 if (object_type_id == 0xe0 && len == 0x40) {
13891 GST_DEBUG_OBJECT (qtdemux,
13892 "Have VOBSUB palette. Creating palette event");
13893 /* move to decConfigDescr data and read palette */
13895 for (i = 0; i < 16; i++) {
13896 clut[i] = QT_UINT32 (data);
13900 s = gst_structure_new ("application/x-gst-dvd", "event",
13901 G_TYPE_STRING, "dvd-spu-clut-change",
13902 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
13903 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
13904 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
13905 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
13906 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
13907 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
13908 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
13909 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
13912 /* store event and trigger custom processing */
13913 stream->pending_event =
13914 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
13916 /* Generic codec_data handler puts it on the caps */
13923 case SL_CONFIG_DESC_TAG:
13924 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
13928 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
13930 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
13936 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
13937 * in use, and should also be used to override some other parameters for some
13939 switch (object_type_id) {
13940 case 0x20: /* MPEG-4 */
13941 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
13942 * profile_and_level_indication */
13943 if (data_ptr != NULL && data_len >= 5 &&
13944 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
13945 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
13946 data_ptr + 4, data_len - 4);
13948 break; /* Nothing special needed here */
13949 case 0x21: /* H.264 */
13950 codec_name = "H.264 / AVC";
13951 caps = gst_caps_new_simple ("video/x-h264",
13952 "stream-format", G_TYPE_STRING, "avc",
13953 "alignment", G_TYPE_STRING, "au", NULL);
13955 case 0x40: /* AAC (any) */
13956 case 0x66: /* AAC Main */
13957 case 0x67: /* AAC LC */
13958 case 0x68: /* AAC SSR */
13959 /* Override channels and rate based on the codec_data, as it's often
13961 /* Only do so for basic setup without HE-AAC extension */
13962 if (data_ptr && data_len == 2) {
13963 guint channels, rate;
13965 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
13967 entry->n_channels = channels;
13969 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
13971 entry->rate = rate;
13974 /* Set level and profile if possible */
13975 if (data_ptr != NULL && data_len >= 2) {
13976 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
13977 data_ptr, data_len);
13979 const gchar *profile_str = NULL;
13982 guint8 *codec_data;
13983 gint rate_idx, profile;
13985 /* No codec_data, let's invent something.
13986 * FIXME: This is wrong for SBR! */
13988 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
13990 buffer = gst_buffer_new_and_alloc (2);
13991 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
13992 codec_data = map.data;
13995 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
13998 switch (object_type_id) {
14000 profile_str = "main";
14004 profile_str = "lc";
14008 profile_str = "ssr";
14016 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14018 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14020 gst_buffer_unmap (buffer, &map);
14021 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14022 GST_TYPE_BUFFER, buffer, NULL);
14023 gst_buffer_unref (buffer);
14026 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14027 G_TYPE_STRING, profile_str, NULL);
14031 case 0x60: /* MPEG-2, various profiles */
14037 codec_name = "MPEG-2 video";
14038 caps = gst_caps_new_simple ("video/mpeg",
14039 "mpegversion", G_TYPE_INT, 2,
14040 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14042 case 0x69: /* MPEG-2 BC audio */
14043 case 0x6B: /* MPEG-1 audio */
14044 caps = gst_caps_new_simple ("audio/mpeg",
14045 "mpegversion", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
14046 codec_name = "MPEG-1 audio";
14048 case 0x6A: /* MPEG-1 */
14049 codec_name = "MPEG-1 video";
14050 caps = gst_caps_new_simple ("video/mpeg",
14051 "mpegversion", G_TYPE_INT, 1,
14052 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14054 case 0x6C: /* MJPEG */
14056 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14058 codec_name = "Motion-JPEG";
14060 case 0x6D: /* PNG */
14062 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14064 codec_name = "PNG still images";
14066 case 0x6E: /* JPEG2000 */
14067 codec_name = "JPEG-2000";
14068 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14070 case 0xA4: /* Dirac */
14071 codec_name = "Dirac";
14072 caps = gst_caps_new_empty_simple ("video/x-dirac");
14074 case 0xA5: /* AC3 */
14075 codec_name = "AC-3 audio";
14076 caps = gst_caps_new_simple ("audio/x-ac3",
14077 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14079 case 0xA9: /* AC3 */
14080 codec_name = "DTS audio";
14081 caps = gst_caps_new_simple ("audio/x-dts",
14082 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14084 case 0xE1: /* QCELP */
14085 /* QCELP, the codec_data is a riff tag (little endian) with
14086 * 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). */
14087 caps = gst_caps_new_empty_simple ("audio/qcelp");
14088 codec_name = "QCELP";
14094 /* If we have a replacement caps, then change our caps for this stream */
14096 gst_caps_unref (entry->caps);
14097 entry->caps = caps;
14100 if (codec_name && list)
14101 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14102 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14104 /* Add the codec_data attribute to caps, if we have it */
14108 buffer = gst_buffer_new_and_alloc (data_len);
14109 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14111 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14112 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14114 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14116 gst_buffer_unref (buffer);
14121 static inline GstCaps *
14122 _get_unknown_codec_name (const gchar * type, guint32 fourcc)
14126 char *s, fourstr[5];
14128 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14129 for (i = 0; i < 4; i++) {
14130 if (!g_ascii_isalnum (fourstr[i]))
14133 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14134 caps = gst_caps_new_empty_simple (s);
14139 #define _codec(name) \
14141 if (codec_name) { \
14142 *codec_name = g_strdup (name); \
14147 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14148 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14149 const guint8 * stsd_entry_data, gchar ** codec_name)
14151 GstCaps *caps = NULL;
14152 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14155 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
14156 _codec ("PNG still images");
14157 caps = gst_caps_new_empty_simple ("image/png");
14160 _codec ("JPEG still images");
14162 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14165 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14166 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14167 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14168 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14169 _codec ("Motion-JPEG");
14171 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14174 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14175 _codec ("Motion-JPEG format B");
14176 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14179 _codec ("JPEG-2000");
14180 /* override to what it should be according to spec, avoid palette_data */
14181 entry->bits_per_sample = 24;
14182 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14185 _codec ("Sorensen video v.3");
14186 caps = gst_caps_new_simple ("video/x-svq",
14187 "svqversion", G_TYPE_INT, 3, NULL);
14189 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14190 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14191 _codec ("Sorensen video v.1");
14192 caps = gst_caps_new_simple ("video/x-svq",
14193 "svqversion", G_TYPE_INT, 1, NULL);
14195 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14196 caps = gst_caps_new_empty_simple ("video/x-raw");
14197 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14198 _codec ("Windows Raw RGB");
14199 stream->alignment = 32;
14205 bps = QT_UINT16 (stsd_entry_data + 82);
14208 format = GST_VIDEO_FORMAT_RGB15;
14211 format = GST_VIDEO_FORMAT_RGB16;
14214 format = GST_VIDEO_FORMAT_RGB;
14217 format = GST_VIDEO_FORMAT_ARGB;
14225 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14226 format = GST_VIDEO_FORMAT_I420;
14228 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14229 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14230 format = GST_VIDEO_FORMAT_I420;
14233 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14234 format = GST_VIDEO_FORMAT_UYVY;
14236 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14237 format = GST_VIDEO_FORMAT_v308;
14239 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14240 format = GST_VIDEO_FORMAT_v216;
14243 format = GST_VIDEO_FORMAT_v210;
14245 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14246 format = GST_VIDEO_FORMAT_r210;
14248 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14249 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14250 format = GST_VIDEO_FORMAT_v410;
14253 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14254 * but different order than AYUV
14255 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14256 format = GST_VIDEO_FORMAT_v408;
14259 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14260 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14261 _codec ("MPEG-1 video");
14262 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14263 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14265 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14266 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14267 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14268 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14269 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14270 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14271 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14272 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14273 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14274 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14275 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14276 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14277 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14278 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14279 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14280 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14281 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
14282 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
14283 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
14284 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
14285 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
14286 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
14287 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
14288 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
14289 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
14290 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
14291 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
14292 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
14293 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
14294 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
14295 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
14296 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
14297 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
14298 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
14299 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
14300 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
14301 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14302 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
14303 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
14304 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
14305 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
14306 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
14307 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
14308 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
14309 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
14310 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
14311 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
14312 _codec ("MPEG-2 video");
14313 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
14314 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14316 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
14317 _codec ("GIF still images");
14318 caps = gst_caps_new_empty_simple ("image/gif");
14321 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
14323 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
14325 /* ffmpeg uses the height/width props, don't know why */
14326 caps = gst_caps_new_simple ("video/x-h263",
14327 "variant", G_TYPE_STRING, "itu", NULL);
14331 _codec ("MPEG-4 video");
14332 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14333 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14335 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
14336 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
14337 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
14338 caps = gst_caps_new_simple ("video/x-msmpeg",
14339 "msmpegversion", G_TYPE_INT, 43, NULL);
14341 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
14343 caps = gst_caps_new_simple ("video/x-divx",
14344 "divxversion", G_TYPE_INT, 3, NULL);
14346 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
14347 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
14349 caps = gst_caps_new_simple ("video/x-divx",
14350 "divxversion", G_TYPE_INT, 4, NULL);
14352 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
14354 caps = gst_caps_new_simple ("video/x-divx",
14355 "divxversion", G_TYPE_INT, 5, NULL);
14358 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
14360 caps = gst_caps_new_simple ("video/x-ffv",
14361 "ffvversion", G_TYPE_INT, 1, NULL);
14364 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
14365 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
14370 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
14371 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
14372 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14376 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
14377 _codec ("Cinepak");
14378 caps = gst_caps_new_empty_simple ("video/x-cinepak");
14380 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
14381 _codec ("Apple QuickDraw");
14382 caps = gst_caps_new_empty_simple ("video/x-qdrw");
14384 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
14385 _codec ("Apple video");
14386 caps = gst_caps_new_empty_simple ("video/x-apple-video");
14390 _codec ("H.264 / AVC");
14391 caps = gst_caps_new_simple ("video/x-h264",
14392 "stream-format", G_TYPE_STRING, "avc",
14393 "alignment", G_TYPE_STRING, "au", NULL);
14396 _codec ("H.264 / AVC");
14397 caps = gst_caps_new_simple ("video/x-h264",
14398 "stream-format", G_TYPE_STRING, "avc3",
14399 "alignment", G_TYPE_STRING, "au", NULL);
14403 _codec ("H.265 / HEVC");
14404 caps = gst_caps_new_simple ("video/x-h265",
14405 "stream-format", G_TYPE_STRING, "hvc1",
14406 "alignment", G_TYPE_STRING, "au", NULL);
14409 _codec ("H.265 / HEVC");
14410 caps = gst_caps_new_simple ("video/x-h265",
14411 "stream-format", G_TYPE_STRING, "hev1",
14412 "alignment", G_TYPE_STRING, "au", NULL);
14415 _codec ("Run-length encoding");
14416 caps = gst_caps_new_simple ("video/x-rle",
14417 "layout", G_TYPE_STRING, "quicktime", NULL);
14420 _codec ("Run-length encoding");
14421 caps = gst_caps_new_simple ("video/x-rle",
14422 "layout", G_TYPE_STRING, "microsoft", NULL);
14424 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
14425 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
14426 _codec ("Indeo Video 3");
14427 caps = gst_caps_new_simple ("video/x-indeo",
14428 "indeoversion", G_TYPE_INT, 3, NULL);
14430 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
14431 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
14432 _codec ("Intel Video 4");
14433 caps = gst_caps_new_simple ("video/x-indeo",
14434 "indeoversion", G_TYPE_INT, 4, NULL);
14438 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
14439 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
14440 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
14441 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
14442 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
14443 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
14444 _codec ("DV Video");
14445 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
14446 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14448 case FOURCC_dv5n: /* DVCPRO50 NTSC */
14449 case FOURCC_dv5p: /* DVCPRO50 PAL */
14450 _codec ("DVCPro50 Video");
14451 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
14452 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14454 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
14455 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
14456 _codec ("DVCProHD Video");
14457 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
14458 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14460 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
14461 _codec ("Apple Graphics (SMC)");
14462 caps = gst_caps_new_empty_simple ("video/x-smc");
14464 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
14466 caps = gst_caps_new_empty_simple ("video/x-vp3");
14468 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
14469 _codec ("VP6 Flash");
14470 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
14474 caps = gst_caps_new_empty_simple ("video/x-theora");
14475 /* theora uses one byte of padding in the data stream because it does not
14476 * allow 0 sized packets while theora does */
14477 entry->padding = 1;
14481 caps = gst_caps_new_empty_simple ("video/x-dirac");
14483 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
14484 _codec ("TIFF still images");
14485 caps = gst_caps_new_empty_simple ("image/tiff");
14487 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
14488 _codec ("Apple Intermediate Codec");
14489 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
14491 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
14492 _codec ("AVID DNxHD");
14493 caps = gst_caps_from_string ("video/x-dnxhd");
14496 _codec ("On2 VP8");
14497 caps = gst_caps_from_string ("video/x-vp8");
14500 _codec ("Apple ProRes LT");
14502 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
14506 _codec ("Apple ProRes HQ");
14508 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
14512 _codec ("Apple ProRes");
14514 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14518 _codec ("Apple ProRes Proxy");
14520 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14524 _codec ("Apple ProRes 4444");
14526 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14530 _codec ("Apple ProRes 4444 XQ");
14532 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
14536 _codec ("GoPro CineForm");
14537 caps = gst_caps_from_string ("video/x-cineform");
14542 caps = gst_caps_new_simple ("video/x-wmv",
14543 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
14545 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
14548 caps = _get_unknown_codec_name ("video", fourcc);
14553 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
14556 gst_video_info_init (&info);
14557 gst_video_info_set_format (&info, format, entry->width, entry->height);
14559 caps = gst_video_info_to_caps (&info);
14560 *codec_name = gst_pb_utils_get_codec_description (caps);
14562 /* enable clipping for raw video streams */
14563 stream->need_clip = TRUE;
14564 stream->alignment = 32;
14571 round_up_pow2 (guint n)
14583 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14584 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
14585 int len, gchar ** codec_name)
14588 const GstStructure *s;
14591 GstAudioFormat format = 0;
14594 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14596 depth = entry->bytes_per_packet * 8;
14599 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
14601 /* 8-bit audio is unsigned */
14603 format = GST_AUDIO_FORMAT_U8;
14604 /* otherwise it's signed and big-endian just like 'twos' */
14606 endian = G_BIG_ENDIAN;
14613 endian = G_LITTLE_ENDIAN;
14616 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
14618 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
14622 caps = gst_caps_new_simple ("audio/x-raw",
14623 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14624 "layout", G_TYPE_STRING, "interleaved", NULL);
14625 stream->alignment = GST_ROUND_UP_8 (depth);
14626 stream->alignment = round_up_pow2 (stream->alignment);
14629 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
14630 _codec ("Raw 64-bit floating-point audio");
14631 caps = gst_caps_new_simple ("audio/x-raw",
14632 "format", G_TYPE_STRING, "F64BE",
14633 "layout", G_TYPE_STRING, "interleaved", NULL);
14634 stream->alignment = 8;
14636 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
14637 _codec ("Raw 32-bit floating-point audio");
14638 caps = gst_caps_new_simple ("audio/x-raw",
14639 "format", G_TYPE_STRING, "F32BE",
14640 "layout", G_TYPE_STRING, "interleaved", NULL);
14641 stream->alignment = 4;
14644 _codec ("Raw 24-bit PCM audio");
14645 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
14647 caps = gst_caps_new_simple ("audio/x-raw",
14648 "format", G_TYPE_STRING, "S24BE",
14649 "layout", G_TYPE_STRING, "interleaved", NULL);
14650 stream->alignment = 4;
14652 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
14653 _codec ("Raw 32-bit PCM audio");
14654 caps = gst_caps_new_simple ("audio/x-raw",
14655 "format", G_TYPE_STRING, "S32BE",
14656 "layout", G_TYPE_STRING, "interleaved", NULL);
14657 stream->alignment = 4;
14659 case GST_MAKE_FOURCC ('s', '1', '6', 'l'):
14660 _codec ("Raw 16-bit PCM audio");
14661 caps = gst_caps_new_simple ("audio/x-raw",
14662 "format", G_TYPE_STRING, "S16LE",
14663 "layout", G_TYPE_STRING, "interleaved", NULL);
14664 stream->alignment = 2;
14667 _codec ("Mu-law audio");
14668 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
14671 _codec ("A-law audio");
14672 caps = gst_caps_new_empty_simple ("audio/x-alaw");
14676 _codec ("Microsoft ADPCM");
14677 /* Microsoft ADPCM-ACM code 2 */
14678 caps = gst_caps_new_simple ("audio/x-adpcm",
14679 "layout", G_TYPE_STRING, "microsoft", NULL);
14683 _codec ("DVI/IMA ADPCM");
14684 caps = gst_caps_new_simple ("audio/x-adpcm",
14685 "layout", G_TYPE_STRING, "dvi", NULL);
14689 _codec ("DVI/Intel IMA ADPCM");
14690 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
14691 caps = gst_caps_new_simple ("audio/x-adpcm",
14692 "layout", G_TYPE_STRING, "quicktime", NULL);
14696 /* MPEG layer 3, CBR only (pre QT4.1) */
14698 _codec ("MPEG-1 layer 3");
14699 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
14700 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
14701 "mpegversion", G_TYPE_INT, 1, NULL);
14703 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
14704 _codec ("MPEG-1 layer 2");
14706 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
14707 "mpegversion", G_TYPE_INT, 1, NULL);
14710 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
14711 _codec ("EAC-3 audio");
14712 caps = gst_caps_new_simple ("audio/x-eac3",
14713 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14714 entry->sampled = TRUE;
14716 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
14718 _codec ("AC-3 audio");
14719 caps = gst_caps_new_simple ("audio/x-ac3",
14720 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14721 entry->sampled = TRUE;
14723 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
14724 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
14725 _codec ("DTS audio");
14726 caps = gst_caps_new_simple ("audio/x-dts",
14727 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14728 entry->sampled = TRUE;
14730 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
14731 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
14732 _codec ("DTS-HD audio");
14733 caps = gst_caps_new_simple ("audio/x-dts",
14734 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14735 entry->sampled = TRUE;
14739 caps = gst_caps_new_simple ("audio/x-mace",
14740 "maceversion", G_TYPE_INT, 3, NULL);
14744 caps = gst_caps_new_simple ("audio/x-mace",
14745 "maceversion", G_TYPE_INT, 6, NULL);
14747 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
14749 caps = gst_caps_new_empty_simple ("application/ogg");
14751 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
14752 _codec ("DV audio");
14753 caps = gst_caps_new_empty_simple ("audio/x-dv");
14756 _codec ("MPEG-4 AAC audio");
14757 caps = gst_caps_new_simple ("audio/mpeg",
14758 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
14759 "stream-format", G_TYPE_STRING, "raw", NULL);
14761 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
14762 _codec ("QDesign Music");
14763 caps = gst_caps_new_empty_simple ("audio/x-qdm");
14766 _codec ("QDesign Music v.2");
14767 /* FIXME: QDesign music version 2 (no constant) */
14768 if (FALSE && data) {
14769 caps = gst_caps_new_simple ("audio/x-qdm2",
14770 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
14771 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
14772 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
14774 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
14778 _codec ("GSM audio");
14779 caps = gst_caps_new_empty_simple ("audio/x-gsm");
14782 _codec ("AMR audio");
14783 caps = gst_caps_new_empty_simple ("audio/AMR");
14786 _codec ("AMR-WB audio");
14787 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
14790 _codec ("Quicktime IMA ADPCM");
14791 caps = gst_caps_new_simple ("audio/x-adpcm",
14792 "layout", G_TYPE_STRING, "quicktime", NULL);
14795 _codec ("Apple lossless audio");
14796 caps = gst_caps_new_empty_simple ("audio/x-alac");
14799 _codec ("Free Lossless Audio Codec");
14800 caps = gst_caps_new_simple ("audio/x-flac",
14801 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14803 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
14804 _codec ("QualComm PureVoice");
14805 caps = gst_caps_from_string ("audio/qcelp");
14810 caps = gst_caps_new_empty_simple ("audio/x-wma");
14814 caps = gst_caps_new_empty_simple ("audio/x-opus");
14816 case GST_MAKE_FOURCC ('l', 'p', 'c', 'm'):
14821 GstAudioFormat format;
14824 FLAG_IS_FLOAT = 0x1,
14825 FLAG_IS_BIG_ENDIAN = 0x2,
14826 FLAG_IS_SIGNED = 0x4,
14827 FLAG_IS_PACKED = 0x8,
14828 FLAG_IS_ALIGNED_HIGH = 0x10,
14829 FLAG_IS_NON_INTERLEAVED = 0x20
14831 _codec ("Raw LPCM audio");
14833 if (data && len >= 56) {
14834 depth = QT_UINT32 (data + 40);
14835 flags = QT_UINT32 (data + 44);
14836 width = QT_UINT32 (data + 48) * 8 / entry->n_channels;
14838 if ((flags & FLAG_IS_FLOAT) == 0) {
14843 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
14844 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
14845 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
14846 caps = gst_caps_new_simple ("audio/x-raw",
14847 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14848 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14849 "non-interleaved" : "interleaved", NULL);
14850 stream->alignment = GST_ROUND_UP_8 (depth);
14851 stream->alignment = round_up_pow2 (stream->alignment);
14856 if (flags & FLAG_IS_BIG_ENDIAN)
14857 format = GST_AUDIO_FORMAT_F64BE;
14859 format = GST_AUDIO_FORMAT_F64LE;
14861 if (flags & FLAG_IS_BIG_ENDIAN)
14862 format = GST_AUDIO_FORMAT_F32BE;
14864 format = GST_AUDIO_FORMAT_F32LE;
14866 caps = gst_caps_new_simple ("audio/x-raw",
14867 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
14868 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
14869 "non-interleaved" : "interleaved", NULL);
14870 stream->alignment = width / 8;
14874 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
14878 caps = _get_unknown_codec_name ("audio", fourcc);
14884 GstCaps *templ_caps =
14885 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
14886 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
14887 gst_caps_unref (caps);
14888 gst_caps_unref (templ_caps);
14889 caps = intersection;
14892 /* enable clipping for raw audio streams */
14893 s = gst_caps_get_structure (caps, 0);
14894 name = gst_structure_get_name (s);
14895 if (g_str_has_prefix (name, "audio/x-raw")) {
14896 stream->need_clip = TRUE;
14897 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
14898 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
14904 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14905 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14906 const guint8 * stsd_entry_data, gchar ** codec_name)
14910 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
14914 _codec ("DVD subtitle");
14915 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
14916 stream->need_process = TRUE;
14919 _codec ("Quicktime timed text");
14922 _codec ("3GPP timed text");
14924 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
14926 /* actual text piece needs to be extracted */
14927 stream->need_process = TRUE;
14930 _codec ("XML subtitles");
14931 caps = gst_caps_new_empty_simple ("application/ttml+xml");
14935 caps = _get_unknown_codec_name ("text", fourcc);
14943 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14944 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14945 const guint8 * stsd_entry_data, gchar ** codec_name)
14951 _codec ("MPEG 1 video");
14952 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14953 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14963 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
14964 const gchar * system_id)
14968 if (!qtdemux->protection_system_ids)
14969 qtdemux->protection_system_ids =
14970 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
14971 /* Check whether we already have an entry for this system ID. */
14972 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
14973 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
14974 if (g_ascii_strcasecmp (system_id, id) == 0) {
14978 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
14979 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,