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.
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
23 * You should have received a copy of the GNU Library General Public
24 * License along with this library; if not, write to the
25 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
30 * SECTION:element-qtdemux
32 * Demuxes a .mov file into raw or compressed audio and/or video streams.
34 * This element supports both push and pull-based scheduling, depending on the
35 * capabilities of the upstream elements.
38 * <title>Example launch line</title>
40 * 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
41 * ]| Play (parse and decode) a .mov file and try to output it to
42 * an automatically detected soundcard and videosink. If the MOV file contains
43 * compressed audio or video data, this will only work if you have the
44 * right decoder elements/plugins installed.
52 #include "gst/gst-i18n-plugin.h"
54 #include <glib/gprintf.h>
55 #include <gst/tag/tag.h>
56 #include <gst/audio/audio.h>
57 #include <gst/video/video.h>
59 #include "qtatomparser.h"
60 #include "qtdemux_types.h"
61 #include "qtdemux_dump.h"
63 #include "descriptors.h"
64 #include "qtdemux_lang.h"
66 #include "qtpalette.h"
68 #include "gst/riff/riff-media.h"
69 #include "gst/riff/riff-read.h"
71 #include <gst/pbutils/pbutils.h>
78 #include <gst/math-compat.h>
84 /* max. size considered 'sane' for non-mdat atoms */
85 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
87 /* if the sample index is larger than this, something is likely wrong */
88 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
90 /* For converting qt creation times to unix epoch times */
91 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
92 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
93 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
94 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
96 #define STREAM_IS_EOS(s) (s->time_position == GST_CLOCK_TIME_NONE)
98 GST_DEBUG_CATEGORY (qtdemux_debug);
100 /*typedef struct _QtNode QtNode; */
101 typedef struct _QtDemuxSegment QtDemuxSegment;
102 typedef struct _QtDemuxSample QtDemuxSample;
104 typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
113 struct _QtDemuxSample
116 gint32 pts_offset; /* Add this value to timestamp to get the pts */
118 guint64 timestamp; /* DTS In mov time */
119 guint32 duration; /* In mov time */
120 gboolean keyframe; /* TRUE when this packet is a keyframe */
123 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
124 typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
126 struct _QtDemuxSphericalMetadata
128 gboolean is_spherical;
129 gboolean is_stitched;
130 char *stitching_software;
131 char *projection_type;
134 int init_view_heading;
138 int full_pano_width_pixels;
139 int full_pano_height_pixels;
140 int cropped_area_image_width;
141 int cropped_area_image_height;
142 int cropped_area_left;
143 int cropped_area_top;
144 QTDEMUX_AMBISONIC_TYPE ambisonic_type;
145 QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
146 QTDEMUX_AMBISONIC_ORDER ambisonic_order;
149 QtDemuxSphericalMetadata *spherical_metadata;
150 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
152 /* Macros for converting to/from timescale */
153 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
154 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
156 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
157 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
159 /* timestamp is the DTS */
160 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
161 /* timestamp + offset is the PTS */
162 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
163 /* timestamp + duration - dts is the duration */
164 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
166 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
169 * Quicktime has tracks and segments. A track is a continuous piece of
170 * multimedia content. The track is not always played from start to finish but
171 * instead, pieces of the track are 'cut out' and played in sequence. This is
172 * what the segments do.
174 * Inside the track we have keyframes (K) and delta frames. The track has its
175 * own timing, which starts from 0 and extends to end. The position in the track
176 * is called the media_time.
178 * The segments now describe the pieces that should be played from this track
179 * and are basically tuples of media_time/duration/rate entries. We can have
180 * multiple segments and they are all played after one another. An example:
182 * segment 1: media_time: 1 second, duration: 1 second, rate 1
183 * segment 2: media_time: 3 second, duration: 2 second, rate 2
185 * To correctly play back this track, one must play: 1 second of media starting
186 * from media_time 1 followed by 2 seconds of media starting from media_time 3
189 * Each of the segments will be played at a specific time, the first segment at
190 * time 0, the second one after the duration of the first one, etc.. Note that
191 * the time in resulting playback is not identical to the media_time of the
194 * Visually, assuming the track has 4 second of media_time:
197 * .-----------------------------------------------------------.
198 * track: | K.....K.........K........K.......K.......K...........K... |
199 * '-----------------------------------------------------------'
201 * .------------^ ^ .----------^ ^
202 * / .-------------' / .------------------'
204 * .--------------. .--------------.
205 * | segment 1 | | segment 2 |
206 * '--------------' '--------------'
208 * The challenge here is to cut out the right pieces of the track for each of
209 * the playback segments. This fortunately can easily be done with the SEGMENT
210 * events of GStreamer.
212 * For playback of segment 1, we need to provide the decoder with the keyframe
213 * (a), in the above figure, but we must instruct it only to output the decoded
214 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
215 * position set to the time of the segment: 0.
217 * We then proceed to push data from keyframe (a) to frame (b). The decoder
218 * decodes but clips all before media_time 1.
220 * After finishing a segment, we push out a new SEGMENT event with the clipping
221 * boundaries of the new data.
223 * This is a good usecase for the GStreamer accumulated SEGMENT events.
226 struct _QtDemuxSegment
228 /* global time and duration, all gst time */
230 GstClockTime stop_time;
231 GstClockTime duration;
232 /* media time of trak, all gst time */
233 GstClockTime media_start;
234 GstClockTime media_stop;
236 /* Media start time in trak timescale units */
237 guint32 trak_media_start;
240 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
242 /* Used with fragmented MP4 files (mfra atom) */
247 } QtDemuxRandomAccessEntry;
249 struct _QtDemuxStream
260 gboolean new_stream; /* signals that a stream_start is required */
261 gboolean on_keyframe; /* if this stream last pushed buffer was a
262 * keyframe. This is important to identify
263 * where to stop pushing buffers after a
264 * segment stop time */
266 /* if the stream has a redirect URI in its headers, we store it here */
273 guint64 duration; /* in timescale */
277 gchar lang_id[4]; /* ISO 639-2T language code */
281 QtDemuxSample *samples;
282 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
283 guint32 first_duration; /* duration in timescale of first sample, used for figuring out
284 the framerate, in timescale units */
285 guint32 offset_in_sample;
286 guint32 max_buffer_size;
288 /* if we use chunks or samples */
300 /* Numerator/denominator framerate */
303 guint16 bits_per_sample;
304 guint16 color_table_id;
305 GstMemory *rgb8_palette;
310 guint samples_per_packet;
311 guint samples_per_frame;
312 guint bytes_per_packet;
313 guint bytes_per_sample;
314 guint bytes_per_frame;
318 gboolean use_allocator;
319 GstAllocator *allocator;
320 GstAllocationParams params;
322 /* when a discontinuity is pending */
325 /* list of buffers to push first */
328 /* if we need to clip this buffer. This is only needed for uncompressed
332 /* buffer needs some custom processing, e.g. subtitles */
333 gboolean need_process;
335 /* current position */
336 guint32 segment_index;
337 guint32 sample_index;
338 GstClockTime time_position; /* in gst time */
339 guint64 accumulated_base;
341 /* the Gst segment we are processing out, used for clipping */
343 guint32 segment_seqnum; /* segment event seqnum obtained from seek */
345 /* quicktime segments */
347 QtDemuxSegment *segments;
348 gboolean dummy_segment;
353 GstTagList *pending_tags;
354 gboolean send_global_tags;
356 GstEvent *pending_event;
366 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
370 GstByteReader co_chunk;
372 guint32 current_chunk;
374 guint32 samples_per_chunk;
375 guint32 stco_sample_index;
377 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
380 guint32 n_samples_per_chunk;
381 guint32 stsc_chunk_index;
382 guint32 stsc_sample_index;
383 guint64 chunk_offset;
386 guint32 stts_samples;
387 guint32 n_sample_times;
388 guint32 stts_sample_index;
390 guint32 stts_duration;
392 gboolean stss_present;
393 guint32 n_sample_syncs;
396 gboolean stps_present;
397 guint32 n_sample_partial_syncs;
399 QtDemuxRandomAccessEntry *ra_entries;
402 const QtDemuxRandomAccessEntry *pending_seek;
405 gboolean ctts_present;
406 guint32 n_composition_times;
408 guint32 ctts_sample_index;
416 gboolean parsed_trex;
417 guint32 def_sample_duration;
418 guint32 def_sample_size;
419 guint32 def_sample_flags;
423 /* stereoscopic video streams */
424 GstVideoMultiviewMode multiview_mode;
425 GstVideoMultiviewFlags multiview_flags;
427 /* protected streams */
429 guint32 protection_scheme_type;
430 guint32 protection_scheme_version;
431 gpointer protection_scheme_info; /* specific to the protection scheme */
432 GQueue protection_scheme_event_queue;
435 /* Contains properties and cryptographic info for a set of samples from a
436 * track protected using Common Encryption (cenc) */
437 struct _QtDemuxCencSampleSetInfo
439 GstStructure *default_properties;
441 /* @crypto_info holds one GstStructure per sample */
442 GPtrArray *crypto_info;
447 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
448 QTDEMUX_STATE_HEADER, /* Parsing the header */
449 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
450 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
453 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
454 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
455 guint32 fourcc, GstByteReader * parser);
456 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
457 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
458 guint32 fourcc, GstByteReader * parser);
460 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
462 static GstStaticPadTemplate gst_qtdemux_sink_template =
463 GST_STATIC_PAD_TEMPLATE ("sink",
466 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
470 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
471 GST_STATIC_PAD_TEMPLATE ("video_%u",
474 GST_STATIC_CAPS_ANY);
476 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
477 GST_STATIC_PAD_TEMPLATE ("audio_%u",
480 GST_STATIC_CAPS_ANY);
482 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
483 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
486 GST_STATIC_CAPS_ANY);
488 #define gst_qtdemux_parent_class parent_class
489 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
491 static void gst_qtdemux_dispose (GObject * object);
494 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
495 GstClockTime media_time);
497 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
498 QtDemuxStream * str, gint64 media_offset);
501 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
502 static GstIndex *gst_qtdemux_get_index (GstElement * element);
504 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
505 GstStateChange transition);
506 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
507 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
508 GstObject * parent, GstPadMode mode, gboolean active);
510 static void gst_qtdemux_loop (GstPad * pad);
511 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
513 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
515 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
516 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
517 QtDemuxStream * stream);
518 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
521 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
522 const guint8 * buffer, guint length);
523 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
524 const guint8 * buffer, guint length);
525 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
526 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
529 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
530 QtDemuxStream * stream, GNode * esds, GstTagList * list);
531 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
532 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
533 gchar ** codec_name);
534 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
535 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
536 gchar ** codec_name);
537 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
538 QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
539 gchar ** codec_name);
540 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
541 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
542 gchar ** codec_name);
544 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
545 QtDemuxStream * stream, guint32 n);
546 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
547 static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
548 QtDemuxStream * stream);
549 static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux,
550 QtDemuxStream * stream);
551 static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index);
552 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
553 static void qtdemux_do_allocation (GstQTDemux * qtdemux,
554 QtDemuxStream * stream);
556 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
557 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
559 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
562 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
563 static void gst_tag_register_spherical_tags (void);
564 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
567 gst_qtdemux_class_init (GstQTDemuxClass * klass)
569 GObjectClass *gobject_class;
570 GstElementClass *gstelement_class;
572 gobject_class = (GObjectClass *) klass;
573 gstelement_class = (GstElementClass *) klass;
575 parent_class = g_type_class_peek_parent (klass);
577 gobject_class->dispose = gst_qtdemux_dispose;
579 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
581 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
582 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
585 gst_tag_register_musicbrainz_tags ();
587 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
588 gst_tag_register_spherical_tags ();
589 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
591 gst_element_class_add_pad_template (gstelement_class,
592 gst_static_pad_template_get (&gst_qtdemux_sink_template));
593 gst_element_class_add_pad_template (gstelement_class,
594 gst_static_pad_template_get (&gst_qtdemux_videosrc_template));
595 gst_element_class_add_pad_template (gstelement_class,
596 gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
597 gst_element_class_add_pad_template (gstelement_class,
598 gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
599 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
601 "Demultiplex a QuickTime file into audio and video streams",
602 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
604 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
609 gst_qtdemux_init (GstQTDemux * qtdemux)
612 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
613 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
614 gst_pad_set_activatemode_function (qtdemux->sinkpad,
615 qtdemux_sink_activate_mode);
616 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
617 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
618 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
620 qtdemux->state = QTDEMUX_STATE_INITIAL;
621 qtdemux->pullbased = FALSE;
622 qtdemux->posted_redirect = FALSE;
623 qtdemux->neededbytes = 16;
625 qtdemux->adapter = gst_adapter_new ();
627 qtdemux->first_mdat = -1;
628 qtdemux->got_moov = FALSE;
629 qtdemux->mdatoffset = -1;
630 qtdemux->mdatbuffer = NULL;
631 qtdemux->restoredata_buffer = NULL;
632 qtdemux->restoredata_offset = -1;
633 qtdemux->fragment_start = -1;
634 qtdemux->fragment_start_offset = -1;
635 qtdemux->media_caps = NULL;
636 qtdemux->exposed = FALSE;
637 qtdemux->mss_mode = FALSE;
638 qtdemux->pending_newsegment = NULL;
639 qtdemux->upstream_format_is_time = FALSE;
640 qtdemux->have_group_id = FALSE;
641 qtdemux->group_id = G_MAXUINT;
642 qtdemux->protection_system_ids = NULL;
643 g_queue_init (&qtdemux->protection_event_queue);
644 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
645 qtdemux->flowcombiner = gst_flow_combiner_new ();
647 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
648 spherical_metadata = (QtDemuxSphericalMetadata *)
649 malloc (sizeof (QtDemuxSphericalMetadata));
651 if (spherical_metadata) {
652 spherical_metadata->is_spherical = FALSE;
653 spherical_metadata->is_stitched = FALSE;
654 spherical_metadata->stitching_software = NULL;
655 spherical_metadata->projection_type = NULL;
656 spherical_metadata->stereo_mode = NULL;
657 spherical_metadata->source_count = 0;
658 spherical_metadata->init_view_heading = 0;
659 spherical_metadata->init_view_pitch = 0;
660 spherical_metadata->init_view_roll = 0;
661 spherical_metadata->timestamp = 0;
662 spherical_metadata->full_pano_width_pixels = 0;
663 spherical_metadata->full_pano_height_pixels = 0;
664 spherical_metadata->cropped_area_image_width = 0;
665 spherical_metadata->cropped_area_image_height = 0;
666 spherical_metadata->cropped_area_left = 0;
667 spherical_metadata->cropped_area_top = 0;
668 spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
669 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
670 spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
672 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
674 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
678 gst_qtdemux_dispose (GObject * object)
680 GstQTDemux *qtdemux = GST_QTDEMUX (object);
682 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
683 if (spherical_metadata->stitching_software)
684 free(spherical_metadata->stitching_software);
685 if (spherical_metadata->projection_type)
686 free(spherical_metadata->projection_type);
687 if (spherical_metadata->stereo_mode)
688 free(spherical_metadata->stereo_mode);
689 if (spherical_metadata)
690 free(spherical_metadata);
691 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
693 if (qtdemux->adapter) {
694 g_object_unref (G_OBJECT (qtdemux->adapter));
695 qtdemux->adapter = NULL;
697 gst_flow_combiner_free (qtdemux->flowcombiner);
698 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
700 g_queue_clear (&qtdemux->protection_event_queue);
702 G_OBJECT_CLASS (parent_class)->dispose (object);
706 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
708 if (qtdemux->posted_redirect) {
709 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
710 (_("This file contains no playable streams.")),
711 ("no known streams found, a redirect message has been posted"));
713 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
714 (_("This file contains no playable streams.")),
715 ("no known streams found"));
720 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
722 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
723 mem, size, 0, size, mem, free_func);
727 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
734 if (G_UNLIKELY (size == 0)) {
736 GstBuffer *tmp = NULL;
738 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
739 if (ret != GST_FLOW_OK)
742 gst_buffer_map (tmp, &map, GST_MAP_READ);
743 size = QT_UINT32 (map.data);
744 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
746 gst_buffer_unmap (tmp, &map);
747 gst_buffer_unref (tmp);
750 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
751 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
752 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
753 /* we're pulling header but already got most interesting bits,
754 * so never mind the rest (e.g. tags) (that much) */
755 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
759 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
760 (_("This file is invalid and cannot be played.")),
761 ("atom has bogus size %" G_GUINT64_FORMAT, size));
762 return GST_FLOW_ERROR;
766 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
768 if (G_UNLIKELY (flow != GST_FLOW_OK))
771 bsize = gst_buffer_get_size (*buf);
772 /* Catch short reads - we don't want any partial atoms */
773 if (G_UNLIKELY (bsize < size)) {
774 GST_WARNING_OBJECT (qtdemux,
775 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
776 gst_buffer_unref (*buf);
786 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
787 GstFormat dest_format, gint64 * dest_value)
790 QtDemuxStream *stream = gst_pad_get_element_private (pad);
791 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
794 if (stream->subtype != FOURCC_vide) {
799 switch (src_format) {
800 case GST_FORMAT_TIME:
801 switch (dest_format) {
802 case GST_FORMAT_BYTES:{
803 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
807 *dest_value = stream->samples[index].offset;
809 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
810 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
811 GST_TIME_ARGS (src_value), *dest_value);
819 case GST_FORMAT_BYTES:
820 switch (dest_format) {
821 case GST_FORMAT_TIME:{
823 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
830 QTSTREAMTIME_TO_GSTTIME (stream,
831 stream->samples[index].timestamp);
832 GST_DEBUG_OBJECT (qtdemux,
833 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
834 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
847 gst_object_unref (qtdemux);
854 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
858 *duration = GST_CLOCK_TIME_NONE;
860 if (qtdemux->duration != 0) {
861 if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
862 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
869 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
872 gboolean res = FALSE;
873 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
875 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
877 switch (GST_QUERY_TYPE (query)) {
878 case GST_QUERY_POSITION:{
881 gst_query_parse_position (query, &fmt, NULL);
882 if (fmt == GST_FORMAT_TIME
883 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
884 gst_query_set_position (query, GST_FORMAT_TIME,
885 qtdemux->segment.position);
890 case GST_QUERY_DURATION:{
893 gst_query_parse_duration (query, &fmt, NULL);
894 if (fmt == GST_FORMAT_TIME) {
895 /* First try to query upstream */
896 res = gst_pad_query_default (pad, parent, query);
899 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
900 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
907 case GST_QUERY_CONVERT:{
908 GstFormat src_fmt, dest_fmt;
909 gint64 src_value, dest_value = 0;
911 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
913 res = gst_qtdemux_src_convert (pad,
914 src_fmt, src_value, dest_fmt, &dest_value);
916 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
921 case GST_QUERY_FORMATS:
922 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
925 case GST_QUERY_SEEKING:{
929 /* try upstream first */
930 res = gst_pad_query_default (pad, parent, query);
933 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
934 if (fmt == GST_FORMAT_TIME) {
935 GstClockTime duration = GST_CLOCK_TIME_NONE;
937 gst_qtdemux_get_duration (qtdemux, &duration);
939 if (!qtdemux->pullbased) {
942 /* we might be able with help from upstream */
944 q = gst_query_new_seeking (GST_FORMAT_BYTES);
945 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
946 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
947 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
951 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
957 case GST_QUERY_SEGMENT:
962 format = qtdemux->segment.format;
965 gst_segment_to_stream_time (&qtdemux->segment, format,
966 qtdemux->segment.start);
967 if ((stop = qtdemux->segment.stop) == -1)
968 stop = qtdemux->segment.duration;
970 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
972 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
977 res = gst_pad_query_default (pad, parent, query);
985 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
987 if (G_LIKELY (stream->pad)) {
988 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
989 GST_DEBUG_PAD_NAME (stream->pad));
991 if (G_UNLIKELY (stream->pending_tags)) {
992 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
993 stream->pending_tags);
994 gst_pad_push_event (stream->pad,
995 gst_event_new_tag (stream->pending_tags));
996 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
997 /* post message qtdemux tag (for early recive application) */
998 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
999 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
1000 gst_tag_list_copy (stream->pending_tags)));
1002 stream->pending_tags = NULL;
1005 if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
1006 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
1008 gst_pad_push_event (stream->pad,
1009 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
1010 stream->send_global_tags = FALSE;
1015 /* push event on all source pads; takes ownership of the event */
1017 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1020 gboolean has_valid_stream = FALSE;
1021 GstEventType etype = GST_EVENT_TYPE (event);
1023 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
1024 GST_EVENT_TYPE_NAME (event));
1026 for (n = 0; n < qtdemux->n_streams; n++) {
1028 QtDemuxStream *stream = qtdemux->streams[n];
1029 GST_DEBUG_OBJECT (qtdemux, "pushing on pad %i", n);
1031 if ((pad = stream->pad)) {
1032 has_valid_stream = TRUE;
1034 if (etype == GST_EVENT_EOS) {
1035 /* let's not send twice */
1036 if (stream->sent_eos)
1038 stream->sent_eos = TRUE;
1041 gst_pad_push_event (pad, gst_event_ref (event));
1045 gst_event_unref (event);
1047 /* if it is EOS and there are no pads, post an error */
1048 if (!has_valid_stream && etype == GST_EVENT_EOS) {
1049 gst_qtdemux_post_no_playable_stream_error (qtdemux);
1053 /* push a pending newsegment event, if any from the streaming thread */
1055 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
1057 if (qtdemux->pending_newsegment) {
1058 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
1059 qtdemux->pending_newsegment = NULL;
1069 find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data)
1071 if (s1->timestamp + s1->pts_offset > *media_time)
1077 /* find the index of the sample that includes the data for @media_time using a
1078 * binary search. Only to be called in optimized cases of linear search below.
1080 * Returns the index of the sample.
1083 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1086 QtDemuxSample *result;
1089 /* convert media_time to mov format */
1091 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1093 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1094 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1095 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1097 if (G_LIKELY (result))
1098 index = result - str->samples;
1107 /* find the index of the sample that includes the data for @media_offset using a
1110 * Returns the index of the sample.
1113 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1114 QtDemuxStream * str, gint64 media_offset)
1116 QtDemuxSample *result = str->samples;
1119 if (result == NULL || str->n_samples == 0)
1122 if (media_offset == result->offset)
1126 while (index < str->n_samples - 1) {
1127 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1130 if (media_offset < result->offset)
1141 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1146 /* find the index of the sample that includes the data for @media_time using a
1147 * linear search, and keeping in mind that not all samples may have been parsed
1148 * yet. If possible, it will delegate to binary search.
1150 * Returns the index of the sample.
1153 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1154 GstClockTime media_time)
1158 QtDemuxSample *sample;
1160 /* convert media_time to mov format */
1162 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1164 sample = str->samples;
1165 if (mov_time == sample->timestamp + sample->pts_offset)
1168 /* use faster search if requested time in already parsed range */
1169 sample = str->samples + str->stbl_index;
1170 if (str->stbl_index >= 0 &&
1171 mov_time <= (sample->timestamp + sample->pts_offset))
1172 return gst_qtdemux_find_index (qtdemux, str, media_time);
1174 while (index < str->n_samples - 1) {
1175 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1178 sample = str->samples + index + 1;
1179 if (mov_time < (sample->timestamp + sample->pts_offset))
1189 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1194 /* find the index of the keyframe needed to decode the sample at @index
1197 * Returns the index of the keyframe.
1200 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1203 guint32 new_index = index;
1205 if (index >= str->n_samples) {
1206 new_index = str->n_samples;
1210 /* all keyframes, return index */
1211 if (str->all_keyframe) {
1216 /* else go back until we have a keyframe */
1218 if (str->samples[new_index].keyframe)
1228 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1229 "gave %u", index, new_index);
1234 /* find the segment for @time_position for @stream
1236 * Returns the index of the segment containing @time_position.
1237 * Returns the last segment and sets the @eos variable to TRUE
1238 * if the time is beyond the end. @eos may be NULL
1241 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1242 GstClockTime time_position)
1247 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1248 GST_TIME_ARGS (time_position));
1251 for (i = 0; i < stream->n_segments; i++) {
1252 QtDemuxSegment *segment = &stream->segments[i];
1254 GST_LOG_OBJECT (stream->pad,
1255 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1256 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1258 /* For the last segment we include stop_time in the last segment */
1259 if (i < stream->n_segments - 1) {
1260 if (segment->time <= time_position && time_position < segment->stop_time) {
1261 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1266 /* Last segment always matches */
1274 /* move the stream @str to the sample position @index.
1276 * Updates @str->sample_index and marks discontinuity if needed.
1279 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1282 /* no change needed */
1283 if (index == str->sample_index)
1286 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1289 /* position changed, we have a discont */
1290 str->sample_index = index;
1291 str->offset_in_sample = 0;
1292 /* Each time we move in the stream we store the position where we are
1294 str->from_sample = index;
1295 str->discont = TRUE;
1299 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1300 gboolean use_sparse, gint64 * key_time, gint64 * key_offset)
1303 gint64 min_byte_offset = -1;
1306 min_offset = desired_time;
1308 /* for each stream, find the index of the sample in the segment
1309 * and move back to the previous keyframe. */
1310 for (n = 0; n < qtdemux->n_streams; n++) {
1312 guint32 index, kindex;
1314 GstClockTime media_start;
1315 GstClockTime media_time;
1316 GstClockTime seg_time;
1317 QtDemuxSegment *seg;
1319 str = qtdemux->streams[n];
1321 if (str->sparse && !use_sparse)
1324 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1325 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1327 /* get segment and time in the segment */
1328 seg = &str->segments[seg_idx];
1329 seg_time = desired_time - seg->time;
1331 /* get the media time in the segment */
1332 media_start = seg->media_start + seg_time;
1334 /* get the index of the sample with media time */
1335 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1336 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1337 " at offset %" G_GUINT64_FORMAT,
1338 GST_TIME_ARGS (media_start), index, str->samples[index].offset);
1340 /* find previous keyframe */
1341 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);
1343 /* if the keyframe is at a different position, we need to update the
1344 * requested seek time */
1345 if (index != kindex) {
1348 /* get timestamp of keyframe */
1349 media_time = QTSAMPLE_DTS (str, &str->samples[kindex]);
1350 GST_DEBUG_OBJECT (qtdemux,
1351 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1352 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1353 str->samples[kindex].offset);
1355 /* keyframes in the segment get a chance to change the
1356 * desired_offset. keyframes out of the segment are
1358 if (media_time >= seg->media_start) {
1359 GstClockTime seg_time;
1361 /* this keyframe is inside the segment, convert back to
1363 seg_time = (media_time - seg->media_start) + seg->time;
1364 if (seg_time < min_offset)
1365 min_offset = seg_time;
1369 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1370 min_byte_offset = str->samples[index].offset;
1374 *key_time = min_offset;
1376 *key_offset = min_byte_offset;
1380 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1381 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1385 g_return_val_if_fail (format != NULL, FALSE);
1386 g_return_val_if_fail (cur != NULL, FALSE);
1387 g_return_val_if_fail (stop != NULL, FALSE);
1389 if (*format == GST_FORMAT_TIME)
1393 if (cur_type != GST_SEEK_TYPE_NONE)
1394 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1395 if (res && stop_type != GST_SEEK_TYPE_NONE)
1396 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1399 *format = GST_FORMAT_TIME;
1404 /* perform seek in push based mode:
1405 find BYTE position to move to based on time and delegate to upstream
1408 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1413 GstSeekType cur_type, stop_type;
1414 gint64 cur, stop, key_cur;
1417 gint64 original_stop;
1420 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1422 gst_event_parse_seek (event, &rate, &format, &flags,
1423 &cur_type, &cur, &stop_type, &stop);
1424 seqnum = gst_event_get_seqnum (event);
1426 /* only forward streaming and seeking is possible */
1428 goto unsupported_seek;
1430 /* convert to TIME if needed and possible */
1431 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1435 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1436 * the original stop position to use when upstream pushes the new segment
1438 original_stop = stop;
1441 /* find reasonable corresponding BYTE position,
1442 * also try to mind about keyframes, since we can not go back a bit for them
1444 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, &key_cur, &byte_cur);
1449 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1450 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1453 GST_OBJECT_LOCK (qtdemux);
1454 qtdemux->seek_offset = byte_cur;
1455 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1456 qtdemux->push_seek_start = cur;
1458 qtdemux->push_seek_start = key_cur;
1461 if (stop_type == GST_SEEK_TYPE_NONE) {
1462 qtdemux->push_seek_stop = qtdemux->segment.stop;
1464 qtdemux->push_seek_stop = original_stop;
1466 GST_OBJECT_UNLOCK (qtdemux);
1468 /* BYTE seek event */
1469 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1471 gst_event_set_seqnum (event, seqnum);
1472 res = gst_pad_push_event (qtdemux->sinkpad, event);
1479 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1485 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1490 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1495 /* perform the seek.
1497 * We set all segment_indexes in the streams to unknown and
1498 * adjust the time_position to the desired position. this is enough
1499 * to trigger a segment switch in the streaming thread to start
1500 * streaming from the desired position.
1502 * Keyframe seeking is a little more complicated when dealing with
1503 * segments. Ideally we want to move to the previous keyframe in
1504 * the segment but there might not be a keyframe in the segment. In
1505 * fact, none of the segments could contain a keyframe. We take a
1506 * practical approach: seek to the previous keyframe in the segment,
1507 * if there is none, seek to the beginning of the segment.
1509 * Called with STREAM_LOCK
1512 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1513 guint32 seqnum, GstSeekFlags flags)
1515 gint64 desired_offset;
1518 desired_offset = segment->position;
1520 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1521 GST_TIME_ARGS (desired_offset));
1523 /* may not have enough fragmented info to do this adjustment,
1524 * and we can't scan (and probably should not) at this time with
1525 * possibly flushing upstream */
1526 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1529 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, &min_offset, NULL);
1530 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1531 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1532 desired_offset = min_offset;
1535 /* and set all streams to the final position */
1536 gst_flow_combiner_reset (qtdemux->flowcombiner);
1537 for (n = 0; n < qtdemux->n_streams; n++) {
1538 QtDemuxStream *stream = qtdemux->streams[n];
1540 stream->time_position = desired_offset;
1541 stream->accumulated_base = 0;
1542 stream->sample_index = -1;
1543 stream->offset_in_sample = 0;
1544 stream->segment_index = -1;
1545 stream->sent_eos = FALSE;
1546 stream->segment_seqnum = seqnum;
1548 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1549 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1551 segment->position = desired_offset;
1552 segment->time = desired_offset;
1553 if (segment->rate >= 0) {
1554 segment->start = desired_offset;
1556 /* we stop at the end */
1557 if (segment->stop == -1)
1558 segment->stop = segment->duration;
1560 segment->stop = desired_offset;
1563 if (qtdemux->fragmented)
1564 qtdemux->fragmented_seek_pending = TRUE;
1569 /* do a seek in pull based mode */
1571 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1576 GstSeekType cur_type, stop_type;
1580 GstSegment seeksegment;
1582 GstEvent *flush_event;
1585 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1587 gst_event_parse_seek (event, &rate, &format, &flags,
1588 &cur_type, &cur, &stop_type, &stop);
1589 seqnum = gst_event_get_seqnum (event);
1591 /* we have to have a format as the segment format. Try to convert
1593 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1597 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1599 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1603 flush = flags & GST_SEEK_FLAG_FLUSH;
1605 /* stop streaming, either by flushing or by pausing the task */
1607 flush_event = gst_event_new_flush_start ();
1609 gst_event_set_seqnum (flush_event, seqnum);
1610 /* unlock upstream pull_range */
1611 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1612 /* make sure out loop function exits */
1613 gst_qtdemux_push_event (qtdemux, flush_event);
1615 /* non flushing seek, pause the task */
1616 gst_pad_pause_task (qtdemux->sinkpad);
1619 /* wait for streaming to finish */
1620 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1622 /* copy segment, we need this because we still need the old
1623 * segment when we close the current segment. */
1624 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1627 /* configure the segment with the seek variables */
1628 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1629 gst_segment_do_seek (&seeksegment, rate, format, flags,
1630 cur_type, cur, stop_type, stop, &update);
1633 /* now do the seek, this actually never returns FALSE */
1634 gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1636 /* prepare for streaming again */
1638 flush_event = gst_event_new_flush_stop (TRUE);
1640 gst_event_set_seqnum (flush_event, seqnum);
1642 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1643 gst_qtdemux_push_event (qtdemux, flush_event);
1646 /* commit the new segment */
1647 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1649 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1650 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1651 qtdemux->segment.format, qtdemux->segment.position);
1653 gst_message_set_seqnum (msg, seqnum);
1654 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1657 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1658 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1659 qtdemux->sinkpad, NULL);
1661 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1668 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1674 qtdemux_ensure_index (GstQTDemux * qtdemux)
1678 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1680 /* Build complete index */
1681 for (i = 0; i < qtdemux->n_streams; i++) {
1682 QtDemuxStream *stream = qtdemux->streams[i];
1684 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1692 GST_LOG_OBJECT (qtdemux,
1693 "Building complete index of stream %u for seeking failed!", i);
1699 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1702 gboolean res = TRUE;
1703 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1705 switch (GST_EVENT_TYPE (event)) {
1706 case GST_EVENT_SEEK:
1708 #ifndef GST_DISABLE_GST_DEBUG
1709 GstClockTime ts = gst_util_get_timestamp ();
1712 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1713 /* seek should be handled by upstream, we might need to re-download fragments */
1714 GST_DEBUG_OBJECT (qtdemux,
1715 "let upstream handle seek for fragmented playback");
1719 /* Build complete index for seeking;
1720 * if not a fragmented file at least */
1721 if (!qtdemux->fragmented)
1722 if (!qtdemux_ensure_index (qtdemux))
1724 #ifndef GST_DISABLE_GST_DEBUG
1725 ts = gst_util_get_timestamp () - ts;
1726 GST_INFO_OBJECT (qtdemux,
1727 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1730 if (qtdemux->pullbased) {
1731 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1732 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1733 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1735 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
1736 && !qtdemux->fragmented) {
1737 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1739 GST_DEBUG_OBJECT (qtdemux,
1740 "ignoring seek in push mode in current state");
1743 gst_event_unref (event);
1746 case GST_EVENT_NAVIGATION:
1748 gst_event_unref (event);
1752 res = gst_pad_event_default (pad, parent, event);
1762 GST_ERROR_OBJECT (qtdemux, "Index failed");
1763 gst_event_unref (event);
1769 /* stream/index return sample that is min/max w.r.t. byte position,
1770 * time is min/max w.r.t. time of samples,
1771 * the latter need not be time of the former sample */
1773 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1774 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1777 gint64 time, min_time;
1778 QtDemuxStream *stream;
1784 for (n = 0; n < qtdemux->n_streams; ++n) {
1787 gboolean set_sample;
1789 str = qtdemux->streams[n];
1796 i = str->n_samples - 1;
1800 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1801 if (str->samples[i].size == 0)
1804 if (fw && (str->samples[i].offset < byte_pos))
1807 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1810 /* move stream to first available sample */
1812 gst_qtdemux_move_stream (qtdemux, str, i);
1816 /* avoid index from sparse streams since they might be far away */
1818 /* determine min/max time */
1819 time = QTSAMPLE_PTS (str, &str->samples[i]);
1820 if (min_time == -1 || (!fw && time > min_time) ||
1821 (fw && time < min_time)) {
1825 /* determine stream with leading sample, to get its position */
1827 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1828 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1836 /* no sample for this stream, mark eos */
1838 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1849 static QtDemuxStream *
1850 _create_stream (void)
1852 QtDemuxStream *stream;
1854 stream = g_new0 (QtDemuxStream, 1);
1855 /* new streams always need a discont */
1856 stream->discont = TRUE;
1857 /* we enable clipping for raw audio/video streams */
1858 stream->need_clip = FALSE;
1859 stream->need_process = FALSE;
1860 stream->segment_index = -1;
1861 stream->time_position = 0;
1862 stream->sample_index = -1;
1863 stream->offset_in_sample = 0;
1864 stream->new_stream = TRUE;
1865 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1866 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1867 stream->protected = FALSE;
1868 stream->protection_scheme_type = 0;
1869 stream->protection_scheme_version = 0;
1870 stream->protection_scheme_info = NULL;
1871 g_queue_init (&stream->protection_scheme_event_queue);
1876 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1878 GstStructure *structure;
1879 const gchar *variant;
1880 const GstCaps *mediacaps = NULL;
1882 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1884 structure = gst_caps_get_structure (caps, 0);
1885 variant = gst_structure_get_string (structure, "variant");
1887 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1888 QtDemuxStream *stream;
1889 const GValue *value;
1891 demux->fragmented = TRUE;
1892 demux->mss_mode = TRUE;
1894 if (demux->n_streams > 1) {
1895 /* can't do this, we can only renegotiate for another mss format */
1899 value = gst_structure_get_value (structure, "media-caps");
1902 const GValue *timescale_v;
1904 /* TODO update when stream changes during playback */
1906 if (demux->n_streams == 0) {
1907 stream = _create_stream ();
1908 demux->streams[demux->n_streams] = stream;
1909 demux->n_streams = 1;
1911 stream = demux->streams[0];
1914 timescale_v = gst_structure_get_value (structure, "timescale");
1916 stream->timescale = g_value_get_uint64 (timescale_v);
1918 /* default mss timescale */
1919 stream->timescale = 10000000;
1921 demux->timescale = stream->timescale;
1923 mediacaps = gst_value_get_caps (value);
1924 if (!stream->caps || !gst_caps_is_equal_fixed (mediacaps, stream->caps)) {
1925 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1927 stream->new_caps = TRUE;
1929 gst_caps_replace (&stream->caps, (GstCaps *) mediacaps);
1930 structure = gst_caps_get_structure (mediacaps, 0);
1931 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
1932 stream->subtype = FOURCC_vide;
1934 gst_structure_get_int (structure, "width", &stream->width);
1935 gst_structure_get_int (structure, "height", &stream->height);
1936 gst_structure_get_fraction (structure, "framerate", &stream->fps_n,
1938 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1940 stream->subtype = FOURCC_soun;
1941 gst_structure_get_int (structure, "channels", &stream->n_channels);
1942 gst_structure_get_int (structure, "rate", &rate);
1943 stream->rate = rate;
1946 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1948 demux->mss_mode = FALSE;
1955 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
1959 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
1960 gst_pad_stop_task (qtdemux->sinkpad);
1962 if (hard || qtdemux->upstream_format_is_time) {
1963 qtdemux->state = QTDEMUX_STATE_INITIAL;
1964 qtdemux->neededbytes = 16;
1965 qtdemux->todrop = 0;
1966 qtdemux->pullbased = FALSE;
1967 qtdemux->posted_redirect = FALSE;
1968 qtdemux->first_mdat = -1;
1969 qtdemux->header_size = 0;
1970 qtdemux->mdatoffset = -1;
1971 qtdemux->restoredata_offset = -1;
1972 if (qtdemux->mdatbuffer)
1973 gst_buffer_unref (qtdemux->mdatbuffer);
1974 if (qtdemux->restoredata_buffer)
1975 gst_buffer_unref (qtdemux->restoredata_buffer);
1976 qtdemux->mdatbuffer = NULL;
1977 qtdemux->restoredata_buffer = NULL;
1978 qtdemux->mdatleft = 0;
1979 if (qtdemux->comp_brands)
1980 gst_buffer_unref (qtdemux->comp_brands);
1981 qtdemux->comp_brands = NULL;
1982 qtdemux->last_moov_offset = -1;
1983 if (qtdemux->moov_node)
1984 g_node_destroy (qtdemux->moov_node);
1985 qtdemux->moov_node = NULL;
1986 qtdemux->moov_node_compressed = NULL;
1987 if (qtdemux->tag_list)
1988 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
1989 qtdemux->tag_list = NULL;
1991 if (qtdemux->element_index)
1992 gst_object_unref (qtdemux->element_index);
1993 qtdemux->element_index = NULL;
1995 qtdemux->major_brand = 0;
1996 if (qtdemux->pending_newsegment)
1997 gst_event_unref (qtdemux->pending_newsegment);
1998 qtdemux->pending_newsegment = NULL;
1999 qtdemux->upstream_format_is_time = FALSE;
2000 qtdemux->upstream_seekable = FALSE;
2001 qtdemux->upstream_size = 0;
2003 qtdemux->fragment_start = -1;
2004 qtdemux->fragment_start_offset = -1;
2005 qtdemux->duration = 0;
2006 qtdemux->moof_offset = 0;
2007 qtdemux->chapters_track_id = 0;
2008 qtdemux->have_group_id = FALSE;
2009 qtdemux->group_id = G_MAXUINT;
2011 if (qtdemux->protection_system_ids) {
2012 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2013 qtdemux->protection_system_ids = NULL;
2015 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2017 g_queue_clear (&qtdemux->protection_event_queue);
2019 qtdemux->offset = 0;
2020 gst_adapter_clear (qtdemux->adapter);
2021 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2024 for (n = 0; n < qtdemux->n_streams; n++) {
2025 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2026 qtdemux->streams[n] = NULL;
2028 qtdemux->n_streams = 0;
2029 qtdemux->n_video_streams = 0;
2030 qtdemux->n_audio_streams = 0;
2031 qtdemux->n_sub_streams = 0;
2032 qtdemux->exposed = FALSE;
2033 qtdemux->fragmented = FALSE;
2034 qtdemux->mss_mode = FALSE;
2035 gst_caps_replace (&qtdemux->media_caps, NULL);
2036 qtdemux->timescale = 0;
2037 qtdemux->got_moov = FALSE;
2038 } else if (qtdemux->mss_mode) {
2039 gst_flow_combiner_reset (qtdemux->flowcombiner);
2040 for (n = 0; n < qtdemux->n_streams; n++)
2041 gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]);
2043 gst_flow_combiner_reset (qtdemux->flowcombiner);
2044 for (n = 0; n < qtdemux->n_streams; n++) {
2045 qtdemux->streams[n]->sent_eos = FALSE;
2046 qtdemux->streams[n]->segment_seqnum = 0;
2047 qtdemux->streams[n]->time_position = 0;
2048 qtdemux->streams[n]->accumulated_base = 0;
2054 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2057 GstQTDemux *demux = GST_QTDEMUX (parent);
2058 gboolean res = TRUE;
2060 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2062 switch (GST_EVENT_TYPE (event)) {
2063 case GST_EVENT_SEGMENT:
2066 QtDemuxStream *stream;
2069 GstEvent *segment_event;
2071 /* some debug output */
2072 gst_event_copy_segment (event, &segment);
2073 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2076 if (segment.format == GST_FORMAT_TIME) {
2077 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
2078 gst_event_replace (&demux->pending_newsegment, event);
2079 demux->upstream_format_is_time = TRUE;
2081 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2082 "not in time format");
2084 /* chain will send initial newsegment after pads have been added */
2085 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
2086 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2091 /* check if this matches a time seek we received previously
2092 * FIXME for backwards compatibility reasons we use the
2093 * seek_offset here to compare. In the future we might want to
2094 * change this to use the seqnum as it uniquely should identify
2095 * the segment that corresponds to the seek. */
2096 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2097 ", received segment offset %" G_GINT64_FORMAT,
2098 demux->seek_offset, segment.start);
2099 if (segment.format == GST_FORMAT_BYTES
2100 && demux->seek_offset == segment.start) {
2101 GST_OBJECT_LOCK (demux);
2102 offset = segment.start;
2104 segment.format = GST_FORMAT_TIME;
2105 segment.start = demux->push_seek_start;
2106 segment.stop = demux->push_seek_stop;
2107 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2108 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2109 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2110 GST_OBJECT_UNLOCK (demux);
2113 /* we only expect a BYTE segment, e.g. following a seek */
2114 if (segment.format == GST_FORMAT_BYTES) {
2115 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2116 offset = segment.start;
2118 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2119 NULL, (gint64 *) & segment.start);
2120 if ((gint64) segment.start < 0)
2123 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2124 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2125 NULL, (gint64 *) & segment.stop);
2126 /* keyframe seeking should already arrange for start >= stop,
2127 * but make sure in other rare cases */
2128 segment.stop = MAX (segment.stop, segment.start);
2130 } else if (segment.format == GST_FORMAT_TIME) {
2131 /* push all data on the adapter before starting this
2133 gst_qtdemux_process_adapter (demux, TRUE);
2135 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2139 /* accept upstream's notion of segment and distribute along */
2140 segment.format = GST_FORMAT_TIME;
2141 segment.position = segment.time = segment.start;
2142 segment.duration = demux->segment.duration;
2143 segment.base = gst_segment_to_running_time (&demux->segment,
2144 GST_FORMAT_TIME, demux->segment.position);
2146 gst_segment_copy_into (&segment, &demux->segment);
2147 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2148 segment_event = gst_event_new_segment (&segment);
2149 gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event));
2150 gst_qtdemux_push_event (demux, segment_event);
2152 /* clear leftover in current segment, if any */
2153 gst_adapter_clear (demux->adapter);
2155 /* set up streaming thread */
2156 demux->offset = offset;
2157 if (demux->upstream_format_is_time) {
2158 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2159 "set values to restart reading from a new atom");
2160 demux->neededbytes = 16;
2163 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2166 demux->todrop = stream->samples[idx].offset - offset;
2167 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2169 /* set up for EOS */
2170 demux->neededbytes = -1;
2175 gst_event_unref (event);
2179 case GST_EVENT_FLUSH_STOP:
2183 dur = demux->segment.duration;
2184 gst_qtdemux_reset (demux, FALSE);
2185 demux->segment.duration = dur;
2189 /* If we are in push mode, and get an EOS before we've seen any streams,
2190 * then error out - we have nowhere to send the EOS */
2191 if (!demux->pullbased) {
2193 gboolean has_valid_stream = FALSE;
2194 for (i = 0; i < demux->n_streams; i++) {
2195 if (demux->streams[i]->pad != NULL) {
2196 has_valid_stream = TRUE;
2200 if (!has_valid_stream)
2201 gst_qtdemux_post_no_playable_stream_error (demux);
2203 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2204 (guint) gst_adapter_available (demux->adapter));
2205 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2211 case GST_EVENT_CAPS:{
2212 GstCaps *caps = NULL;
2214 gst_event_parse_caps (event, &caps);
2215 gst_qtdemux_setcaps (demux, caps);
2217 gst_event_unref (event);
2220 case GST_EVENT_PROTECTION:
2222 const gchar *system_id = NULL;
2224 gst_event_parse_protection (event, &system_id, NULL, NULL);
2225 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2227 gst_qtdemux_append_protection_system_id (demux, system_id);
2228 /* save the event for later, for source pads that have not been created */
2229 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2230 /* send it to all pads that already exist */
2231 gst_qtdemux_push_event (demux, event);
2239 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2247 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2249 GstQTDemux *demux = GST_QTDEMUX (element);
2251 GST_OBJECT_LOCK (demux);
2252 if (demux->element_index)
2253 gst_object_unref (demux->element_index);
2255 demux->element_index = gst_object_ref (index);
2257 demux->element_index = NULL;
2259 GST_OBJECT_UNLOCK (demux);
2260 /* object lock might be taken again */
2262 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2263 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2264 demux->element_index, demux->index_id);
2268 gst_qtdemux_get_index (GstElement * element)
2270 GstIndex *result = NULL;
2271 GstQTDemux *demux = GST_QTDEMUX (element);
2273 GST_OBJECT_LOCK (demux);
2274 if (demux->element_index)
2275 result = gst_object_ref (demux->element_index);
2276 GST_OBJECT_UNLOCK (demux);
2278 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2285 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2287 g_free ((gpointer) stream->stco.data);
2288 stream->stco.data = NULL;
2289 g_free ((gpointer) stream->stsz.data);
2290 stream->stsz.data = NULL;
2291 g_free ((gpointer) stream->stsc.data);
2292 stream->stsc.data = NULL;
2293 g_free ((gpointer) stream->stts.data);
2294 stream->stts.data = NULL;
2295 g_free ((gpointer) stream->stss.data);
2296 stream->stss.data = NULL;
2297 g_free ((gpointer) stream->stps.data);
2298 stream->stps.data = NULL;
2299 g_free ((gpointer) stream->ctts.data);
2300 stream->ctts.data = NULL;
2304 gst_qtdemux_stream_flush_segments_data (GstQTDemux * qtdemux,
2305 QtDemuxStream * stream)
2307 g_free (stream->segments);
2308 stream->segments = NULL;
2309 stream->segment_index = -1;
2310 stream->accumulated_base = 0;
2314 gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux,
2315 QtDemuxStream * stream)
2317 g_free (stream->samples);
2318 stream->samples = NULL;
2319 gst_qtdemux_stbl_free (stream);
2322 g_free (stream->ra_entries);
2323 stream->ra_entries = NULL;
2324 stream->n_ra_entries = 0;
2326 stream->sample_index = -1;
2327 stream->stbl_index = -1;
2328 stream->n_samples = 0;
2329 stream->time_position = 0;
2333 gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream)
2335 if (stream->allocator)
2336 gst_object_unref (stream->allocator);
2337 while (stream->buffers) {
2338 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2339 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2341 if (stream->rgb8_palette) {
2342 gst_memory_unref (stream->rgb8_palette);
2343 stream->rgb8_palette = NULL;
2346 if (stream->pending_tags)
2347 gst_tag_list_unref (stream->pending_tags);
2348 stream->pending_tags = NULL;
2349 g_free (stream->redirect_uri);
2350 stream->redirect_uri = NULL;
2351 stream->sent_eos = FALSE;
2352 stream->sparse = FALSE;
2353 stream->protected = FALSE;
2354 if (stream->protection_scheme_info) {
2355 if (stream->protection_scheme_type == FOURCC_cenc) {
2356 QtDemuxCencSampleSetInfo *info =
2357 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2358 if (info->default_properties)
2359 gst_structure_free (info->default_properties);
2360 if (info->crypto_info)
2361 g_ptr_array_free (info->crypto_info, TRUE);
2363 g_free (stream->protection_scheme_info);
2364 stream->protection_scheme_info = NULL;
2366 stream->protection_scheme_type = 0;
2367 stream->protection_scheme_version = 0;
2368 g_queue_foreach (&stream->protection_scheme_event_queue,
2369 (GFunc) gst_event_unref, NULL);
2370 g_queue_clear (&stream->protection_scheme_event_queue);
2371 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
2372 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
2376 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
2378 gst_qtdemux_stream_clear (qtdemux, stream);
2380 gst_caps_unref (stream->caps);
2381 stream->caps = NULL;
2383 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
2384 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
2390 gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int i)
2392 g_assert (i >= 0 && i < qtdemux->n_streams && qtdemux->streams[i] != NULL);
2394 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[i]);
2395 qtdemux->streams[i] = qtdemux->streams[qtdemux->n_streams - 1];
2396 qtdemux->streams[qtdemux->n_streams - 1] = NULL;
2397 qtdemux->n_streams--;
2400 static GstStateChangeReturn
2401 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2403 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2404 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2406 switch (transition) {
2407 case GST_STATE_CHANGE_PAUSED_TO_READY:
2413 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2415 switch (transition) {
2416 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2417 gst_qtdemux_reset (qtdemux, TRUE);
2428 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2430 /* counts as header data */
2431 qtdemux->header_size += length;
2433 /* only consider at least a sufficiently complete ftyp atom */
2437 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2438 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2439 GST_FOURCC_ARGS (qtdemux->major_brand));
2440 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2441 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2446 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2447 GstTagList * xmptaglist)
2449 /* Strip out bogus fields */
2451 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2452 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2453 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2455 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2458 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2460 /* prioritize native tags using _KEEP mode */
2461 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2462 gst_tag_list_unref (xmptaglist);
2466 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2468 _get_int_value_from_xml_string (GstQTDemux * qtdemux,
2469 const char *xml_str, const char *param_name, int *value)
2471 char *value_start, *value_end, *endptr;
2472 const short value_length_max = 12;
2473 char init_view_ret[12];
2474 int value_length = 0;
2477 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2480 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2485 value_start += strlen (param_name);
2486 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2489 value_end = strchr (value_start, '<');
2491 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2495 value_length = value_end - value_start;
2496 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2497 || (value_start[value_length - 1] == '\t')))
2500 if (value_start[i] == '+' || value_start[i] == '-')
2502 while (i < value_length) {
2503 if (value_start[i] < '0' || value_start[i] > '9') {
2504 GST_ERROR_OBJECT (qtdemux,
2505 "error: incorrect value, integer was expected\n");
2511 if (value_length >= value_length_max || value_length < 1) {
2512 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2516 strncpy (init_view_ret, value_start, value_length_max);
2517 init_view_ret[value_length] = '\0';
2519 *value = strtol (init_view_ret, &endptr, 10);
2520 if (endptr == init_view_ret) {
2521 GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
2529 _get_string_value_from_xml_string (GstQTDemux * qtdemux,
2530 const char *xml_str, const char *param_name, char **value)
2532 char *value_start, *value_end;
2533 const short value_length_max = 256;
2534 int value_length = 0;
2536 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2539 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2544 value_start += strlen (param_name);
2545 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2548 value_end = strchr (value_start, '<');
2550 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2554 value_length = value_end - value_start;
2555 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2556 || (value_start[value_length - 1] == '\t')))
2559 if (value_length >= value_length_max || value_length < 1) {
2560 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2564 *value = (char *) malloc (value_length);
2566 if (*value == NULL) {
2567 GST_ERROR_OBJECT (qtdemux, "error: malloc failed\n");
2571 strncpy (*value, value_start, value_length);
2577 _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
2578 const char *xml_str, const char *param_name, gboolean * value)
2580 char *value_start, *value_end;
2581 int value_length = 0;
2583 value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
2586 GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
2591 value_start += strlen (param_name);
2592 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
2595 value_end = strchr (value_start, '<');
2597 GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
2601 value_length = value_end - value_start;
2602 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
2603 || (value_start[value_length - 1] == '\t')))
2606 if (value_length < 1) {
2607 GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
2611 *value = strstr (value_start, "true") ? TRUE : FALSE;
2617 _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux,
2618 const char *xmlStr, QtDemuxSphericalMetadata * spherical_metadata)
2620 const char is_spherical_str[] = "<GSpherical:Spherical>";
2621 const char is_stitched_str[] = "<GSpherical:Stitched>";
2622 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
2623 const char projection_type_str[] = "<GSpherical:ProjectionType>";
2624 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
2625 const char source_count_str[] = "<GSpherical:SourceCount>";
2626 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
2627 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
2628 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
2629 const char timestamp_str[] = "<GSpherical:Timestamp>";
2630 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
2631 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
2632 const char cropped_area_image_width_str[] =
2633 "<GSpherical:CroppedAreaImageWidthPixels>";
2634 const char cropped_area_image_height_str[] =
2635 "<GSpherical:CroppedAreaImageHeightPixels>";
2636 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
2637 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
2639 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
2640 (gboolean *) & spherical_metadata->is_spherical);
2641 _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
2642 (gboolean *) & spherical_metadata->is_stitched);
2644 if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
2645 _get_string_value_from_xml_string (qtdemux, xmlStr,
2646 stitching_software_str, &spherical_metadata->stitching_software);
2647 _get_string_value_from_xml_string (qtdemux, xmlStr,
2648 projection_type_str, &spherical_metadata->projection_type);
2649 _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
2650 &spherical_metadata->stereo_mode);
2651 _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
2652 &spherical_metadata->source_count);
2653 _get_int_value_from_xml_string (qtdemux, xmlStr,
2654 init_view_heading_str, &spherical_metadata->init_view_heading);
2655 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
2656 &spherical_metadata->init_view_pitch);
2657 _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
2658 &spherical_metadata->init_view_roll);
2659 _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
2660 &spherical_metadata->timestamp);
2661 _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
2662 &spherical_metadata->full_pano_width_pixels);
2663 _get_int_value_from_xml_string (qtdemux, xmlStr,
2664 full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
2665 _get_int_value_from_xml_string (qtdemux, xmlStr,
2666 cropped_area_image_width_str,
2667 &spherical_metadata->cropped_area_image_width);
2668 _get_int_value_from_xml_string (qtdemux, xmlStr,
2669 cropped_area_image_height_str,
2670 &spherical_metadata->cropped_area_image_height);
2671 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
2672 &spherical_metadata->cropped_area_left);
2673 _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
2674 &spherical_metadata->cropped_area_top);
2681 gst_tag_register_spherical_tags (void) {
2682 gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
2685 _("Flag indicating if the video is a spherical video"),
2687 gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
2690 _("Flag indicating if the video is stitched"),
2692 gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
2694 _("tag-stitching-software"),
2695 _("Software used to stitch the spherical video"),
2697 gst_tag_register ("projection_type", GST_TAG_FLAG_META,
2699 _("tag-projection-type"),
2700 _("Projection type used in the video frames"),
2702 gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
2704 _("tag-stereo-mode"),
2705 _("Description of stereoscopic 3D layout"),
2707 gst_tag_register ("source_count", GST_TAG_FLAG_META,
2709 _("tag-source-count"),
2710 _("Number of cameras used to create the spherical video"),
2712 gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
2714 _("tag-init-view-heading"),
2715 _("The heading angle of the initial view in degrees"),
2717 gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
2719 _("tag-init-view-pitch"),
2720 _("The pitch angle of the initial view in degrees"),
2722 gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
2724 _("tag-init-view-roll"),
2725 _("The roll angle of the initial view in degrees"),
2727 gst_tag_register ("timestamp", GST_TAG_FLAG_META,
2730 _("Epoch timestamp of when the first frame in the video was recorded"),
2732 gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
2734 _("tag-full-pano-width"),
2735 _("Width of the encoded video frame in pixels"),
2737 gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
2739 _("tag-full-pano-height"),
2740 _("Height of the encoded video frame in pixels"),
2742 gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
2744 _("tag-cropped-area-image-width"),
2745 _("Width of the video frame to display (e.g. cropping)"),
2747 gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
2749 _("tag-cropped-area-image-height"),
2750 _("Height of the video frame to display (e.g. cropping)"),
2752 gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
2754 _("tag-cropped-area-left"),
2755 _("Column where the left edge of the image was cropped from the"
2756 " full sized panorama"),
2758 gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
2760 _("tag-cropped-area-top"),
2761 _("Row where the top edge of the image was cropped from the"
2762 " full sized panorama"),
2764 gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
2766 _("tag-ambisonic-type"),
2767 _("Specifies the type of ambisonic audio represented"),
2769 gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
2771 _("tag-ambisonic-format"),
2772 _("Specifies the ambisonic audio format"),
2774 gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
2776 _("tag-ambisonic-order"),
2777 _("Specifies the ambisonic audio channel order"),
2784 _send_message_to_bus (GstQTDemux * qtdemux,
2785 QtDemuxSphericalMetadata * spherical_metadata)
2787 GstTagList *taglist;
2789 GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
2790 spherical_metadata->is_spherical);
2791 GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
2792 spherical_metadata->is_stitched);
2793 GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
2794 spherical_metadata->stitching_software);
2795 GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
2796 spherical_metadata->projection_type);
2797 GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
2798 spherical_metadata->stereo_mode);
2799 GST_DEBUG_OBJECT (qtdemux, "source_count %d",
2800 spherical_metadata->source_count);
2801 GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
2802 spherical_metadata->init_view_heading);
2803 GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
2804 spherical_metadata->init_view_pitch);
2805 GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
2806 spherical_metadata->init_view_roll);
2807 GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
2808 GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
2809 spherical_metadata->full_pano_width_pixels);
2810 GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
2811 spherical_metadata->full_pano_height_pixels);
2812 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
2813 spherical_metadata->cropped_area_image_width);
2814 GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
2815 spherical_metadata->cropped_area_image_height);
2816 GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
2817 spherical_metadata->cropped_area_left);
2818 GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
2819 spherical_metadata->cropped_area_top);
2820 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
2821 spherical_metadata->ambisonic_type);
2822 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
2823 spherical_metadata->ambisonic_order);
2824 GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
2825 spherical_metadata->ambisonic_format);
2827 taglist = gst_tag_list_new_empty ();
2828 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
2829 "is_spherical", spherical_metadata->is_spherical,
2830 "is_stitched", spherical_metadata->is_stitched,
2831 "source_count", spherical_metadata->source_count,
2832 "init_view_heading", spherical_metadata->init_view_heading,
2833 "init_view_pitch", spherical_metadata->init_view_pitch,
2834 "init_view_roll", spherical_metadata->init_view_roll,
2835 "timestamp", spherical_metadata->timestamp,
2836 "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
2837 "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
2838 "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
2839 "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
2840 "cropped_area_left", spherical_metadata->cropped_area_left,
2841 "cropped_area_top", spherical_metadata->cropped_area_top,
2842 "ambisonic_type", spherical_metadata->ambisonic_type,
2843 "ambisonic_format", spherical_metadata->ambisonic_format,
2844 "ambisonic_order", spherical_metadata->ambisonic_order,
2847 if (spherical_metadata->stitching_software)
2848 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
2849 "stitching_software", spherical_metadata->stitching_software,
2851 if (spherical_metadata->projection_type)
2852 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
2853 "projection_type", spherical_metadata->projection_type,
2855 if (spherical_metadata->stereo_mode)
2856 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
2857 "stereo_mode", spherical_metadata->stereo_mode,
2860 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
2861 gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
2862 gst_tag_list_copy (taglist)));
2864 gst_tag_list_unref(taglist);
2870 qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2875 guint8 ambisonic_type = 0;
2876 guint32 ambisonic_order = 0;
2877 guint8 ambisonic_channel_ordering = 0;
2878 guint8 ambisonic_normalization = 0;
2879 guint32 num_channels = 0;
2880 guint32 channel_map[49] = { 0 }; /* Up to 6th order */
2884 GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
2886 qtdemux->header_size += length;
2887 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2889 if (length <= offset + 16) {
2890 GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
2894 version = QT_UINT8 (buffer + offset);
2895 ambisonic_type = QT_UINT8 (buffer + offset + 1);
2896 ambisonic_order = QT_UINT32 (buffer + offset + 2);
2897 ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
2898 ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
2899 num_channels = QT_UINT32 (buffer + offset + 8);
2900 for (i = 0; i < num_channels; ++i)
2901 channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
2903 GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
2904 GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
2905 GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
2906 GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
2907 ambisonic_channel_ordering);
2908 GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
2909 ambisonic_normalization);
2910 GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
2911 for (i = 0; i < num_channels; ++i)
2912 GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
2914 if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
2915 if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
2916 spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
2918 if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
2919 if (num_channels == 4) {
2920 spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
2922 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
2923 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
2924 && (channel_map[0] == 0) && (channel_map[1] == 1)
2925 && (channel_map[2] == 2) && (channel_map[3] == 3))
2926 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
2928 if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
2929 && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
2930 && (channel_map[0] == 0) && (channel_map[1] == 3)
2931 && (channel_map[2] == 1) && (channel_map[3] == 2))
2932 spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
2939 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
2942 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2944 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2945 0x97, 0xA9, 0x42, 0xE8,
2946 0x9C, 0x71, 0x99, 0x94,
2947 0x91, 0xE3, 0xAF, 0xAC
2949 static const guint8 playready_uuid[] = {
2950 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2951 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2954 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2955 static const guint8 spherical_uuid[] = {
2956 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
2957 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
2959 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
2963 /* counts as header data */
2964 qtdemux->header_size += length;
2966 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2968 if (length <= offset + 16) {
2969 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2973 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
2974 if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
2975 const char *contents;
2977 GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
2978 contents = (char *) (buffer + offset + 16);
2979 GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
2981 if (spherical_metadata)
2982 _parse_spatial_video_metadata_from_xml_string (qtdemux, contents,
2983 spherical_metadata);
2987 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
2989 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2991 GstTagList *taglist;
2993 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
2994 length - offset - 16, NULL);
2995 taglist = gst_tag_list_from_xmp_buffer (buf);
2996 gst_buffer_unref (buf);
2998 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
3000 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3002 const gunichar2 *s_utf16;
3005 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3006 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3007 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3008 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3012 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3013 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3016 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3017 GST_READ_UINT32_LE (buffer + offset),
3018 GST_READ_UINT32_LE (buffer + offset + 4),
3019 GST_READ_UINT32_LE (buffer + offset + 8),
3020 GST_READ_UINT32_LE (buffer + offset + 12));
3025 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3027 GstSidxParser sidx_parser;
3028 GstIsoffParserResult res;
3031 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3034 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3036 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3037 if (res == GST_ISOFF_QT_PARSER_DONE) {
3038 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3040 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3043 /* caller verifies at least 8 bytes in buf */
3045 extract_initial_length_and_fourcc (const guint8 * data, guint size,
3046 guint64 * plength, guint32 * pfourcc)
3051 length = QT_UINT32 (data);
3052 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3053 fourcc = QT_FOURCC (data + 4);
3054 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3057 length = G_MAXUINT64;
3058 } else if (length == 1 && size >= 16) {
3059 /* this means we have an extended size, which is the 64 bit value of
3060 * the next 8 bytes */
3061 length = QT_UINT64 (data + 8);
3062 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3072 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3074 guint32 version = 0;
3075 GstClockTime duration = 0;
3077 if (!gst_byte_reader_get_uint32_be (br, &version))
3082 if (!gst_byte_reader_get_uint64_be (br, &duration))
3087 if (!gst_byte_reader_get_uint32_be (br, &dur))
3092 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3093 qtdemux->duration = duration;
3099 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3105 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3106 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3108 if (!stream->parsed_trex && qtdemux->moov_node) {
3110 GstByteReader trex_data;
3112 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3114 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3117 guint32 id = 0, dur = 0, size = 0, flags = 0, dummy = 0;
3119 /* skip version/flags */
3120 if (!gst_byte_reader_skip (&trex_data, 4))
3122 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3124 if (id != stream->track_id)
3126 /* sample description index; ignore */
3127 if (!gst_byte_reader_get_uint32_be (&trex_data, &dummy))
3129 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3131 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3133 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3136 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3137 "duration %d, size %d, flags 0x%x", stream->track_id,
3140 stream->parsed_trex = TRUE;
3141 stream->def_sample_duration = dur;
3142 stream->def_sample_size = size;
3143 stream->def_sample_flags = flags;
3146 /* iterate all siblings */
3147 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3153 *ds_duration = stream->def_sample_duration;
3154 *ds_size = stream->def_sample_size;
3155 *ds_flags = stream->def_sample_flags;
3157 /* even then, above values are better than random ... */
3158 if (G_UNLIKELY (!stream->parsed_trex)) {
3159 GST_WARNING_OBJECT (qtdemux,
3160 "failed to find fragment defaults for stream %d", stream->track_id);
3167 /* This method should be called whenever a more accurate duration might
3168 * have been found. It will update all relevant variables if/where needed
3171 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3175 GstClockTime prevdur;
3177 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3179 if (movdur > qtdemux->duration) {
3180 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3181 GST_DEBUG_OBJECT (qtdemux,
3182 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3183 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3184 qtdemux->duration = movdur;
3185 GST_DEBUG_OBJECT (qtdemux,
3186 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3187 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3188 GST_TIME_ARGS (qtdemux->segment.stop));
3189 if (qtdemux->segment.duration == prevdur) {
3190 /* If the current segment has duration/stop identical to previous duration
3191 * update them also (because they were set at that point in time with
3192 * the wrong duration */
3193 /* We convert the value *from* the timescale version to avoid rounding errors */
3194 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3195 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3196 qtdemux->segment.duration = fixeddur;
3197 qtdemux->segment.stop = fixeddur;
3200 for (i = 0; i < qtdemux->n_streams; i++) {
3201 QtDemuxStream *stream = qtdemux->streams[i];
3203 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3204 if (movdur > stream->duration) {
3205 GST_DEBUG_OBJECT (qtdemux,
3206 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3207 GST_TIME_ARGS (duration));
3208 stream->duration = movdur;
3209 if (stream->dummy_segment) {
3210 /* Update all dummy values to new duration */
3211 stream->segments[0].stop_time = duration;
3212 stream->segments[0].duration = duration;
3213 stream->segments[0].media_stop = duration;
3221 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3222 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3223 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3224 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts)
3226 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3228 gint32 data_offset = 0;
3229 guint32 flags = 0, first_flags = 0, samples_count = 0;
3232 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3233 QtDemuxSample *sample;
3234 gboolean ismv = FALSE;
3236 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
3237 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3238 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3239 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3241 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3242 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3246 /* presence of stss or not can't really tell us much,
3247 * and flags and so on tend to be marginally reliable in these files */
3248 if (stream->subtype == FOURCC_soun) {
3249 GST_DEBUG_OBJECT (qtdemux,
3250 "sound track in fragmented file; marking all keyframes");
3251 stream->all_keyframe = TRUE;
3254 if (!gst_byte_reader_skip (trun, 1) ||
3255 !gst_byte_reader_get_uint24_be (trun, &flags))
3258 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3261 if (flags & TR_DATA_OFFSET) {
3262 /* note this is really signed */
3263 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3265 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3266 /* default base offset = first byte of moof */
3267 if (*base_offset == -1) {
3268 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3269 *base_offset = moof_offset;
3271 *running_offset = *base_offset + data_offset;
3273 /* if no offset at all, that would mean data starts at moof start,
3274 * which is a bit wrong and is ismv crappy way, so compensate
3275 * assuming data is in mdat following moof */
3276 if (*base_offset == -1) {
3277 *base_offset = moof_offset + moof_length + 8;
3278 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3281 if (*running_offset == -1)
3282 *running_offset = *base_offset;
3285 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3287 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3288 data_offset, flags, samples_count);
3290 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3291 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3292 GST_DEBUG_OBJECT (qtdemux,
3293 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3294 flags ^= TR_FIRST_SAMPLE_FLAGS;
3296 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3298 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
3302 /* FIXME ? spec says other bits should also be checked to determine
3303 * entry size (and prefix size for that matter) */
3305 dur_offset = size_offset = 0;
3306 if (flags & TR_SAMPLE_DURATION) {
3307 GST_LOG_OBJECT (qtdemux, "entry duration present");
3308 dur_offset = entry_size;
3311 if (flags & TR_SAMPLE_SIZE) {
3312 GST_LOG_OBJECT (qtdemux, "entry size present");
3313 size_offset = entry_size;
3316 if (flags & TR_SAMPLE_FLAGS) {
3317 GST_LOG_OBJECT (qtdemux, "entry flags present");
3318 flags_offset = entry_size;
3321 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3322 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3323 ct_offset = entry_size;
3327 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3329 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3331 if (stream->n_samples + samples_count >=
3332 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3335 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3336 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3337 (stream->n_samples + samples_count) *
3338 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3340 /* create a new array of samples if it's the first sample parsed */
3341 if (stream->n_samples == 0) {
3342 g_assert (stream->samples == NULL);
3343 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3344 /* or try to reallocate it with space enough to insert the new samples */
3346 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3347 stream->n_samples + samples_count);
3348 if (stream->samples == NULL)
3351 if (qtdemux->fragment_start != -1) {
3352 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3353 qtdemux->fragment_start = -1;
3355 if (stream->n_samples == 0) {
3356 if (decode_ts > 0) {
3357 timestamp = decode_ts;
3358 } else if (stream->pending_seek != NULL) {
3359 /* if we don't have a timestamp from a tfdt box, we'll use the one
3360 * from the mfra seek table */
3361 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3362 GST_TIME_ARGS (stream->pending_seek->ts));
3364 /* FIXME: this is not fully correct, the timestamp refers to the random
3365 * access sample refered to in the tfra entry, which may not necessarily
3366 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3367 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3372 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3373 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3374 GST_TIME_ARGS (gst_ts));
3376 /* subsequent fragments extend stream */
3378 stream->samples[stream->n_samples - 1].timestamp +
3379 stream->samples[stream->n_samples - 1].duration;
3381 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3382 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3383 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3387 sample = stream->samples + stream->n_samples;
3388 for (i = 0; i < samples_count; i++) {
3389 guint32 dur, size, sflags, ct;
3391 /* first read sample data */
3392 if (flags & TR_SAMPLE_DURATION) {
3393 dur = QT_UINT32 (data + dur_offset);
3395 dur = d_sample_duration;
3397 if (flags & TR_SAMPLE_SIZE) {
3398 size = QT_UINT32 (data + size_offset);
3400 size = d_sample_size;
3402 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3404 sflags = first_flags;
3406 sflags = d_sample_flags;
3408 } else if (flags & TR_SAMPLE_FLAGS) {
3409 sflags = QT_UINT32 (data + flags_offset);
3411 sflags = d_sample_flags;
3413 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3414 ct = QT_UINT32 (data + ct_offset);
3420 /* fill the sample information */
3421 sample->offset = *running_offset;
3422 sample->pts_offset = ct;
3423 sample->size = size;
3424 sample->timestamp = timestamp;
3425 sample->duration = dur;
3426 /* sample-is-difference-sample */
3427 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3428 * now idea how it relates to bitfield other than massive LE/BE confusion */
3429 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3430 *running_offset += size;
3435 /* Update total duration if needed */
3436 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3438 stream->n_samples += samples_count;
3440 if (stream->pending_seek != NULL)
3441 stream->pending_seek = NULL;
3447 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3452 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3458 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3459 "be larger than %uMB (broken file?)", stream->n_samples,
3460 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3465 /* find stream with @id */
3466 static inline QtDemuxStream *
3467 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3469 QtDemuxStream *stream;
3473 if (G_UNLIKELY (!id)) {
3474 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3478 /* try to get it fast and simple */
3479 if (G_LIKELY (id <= qtdemux->n_streams)) {
3480 stream = qtdemux->streams[id - 1];
3481 if (G_LIKELY (stream->track_id == id))
3485 /* linear search otherwise */
3486 for (i = 0; i < qtdemux->n_streams; i++) {
3487 stream = qtdemux->streams[i];
3488 if (stream->track_id == id)
3491 if (qtdemux->mss_mode) {
3492 /* mss should have only 1 stream anyway */
3493 return qtdemux->streams[0];
3500 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3501 guint32 * fragment_number)
3503 if (!gst_byte_reader_skip (mfhd, 4))
3505 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3510 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3516 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3517 QtDemuxStream ** stream, guint32 * default_sample_duration,
3518 guint32 * default_sample_size, guint32 * default_sample_flags,
3519 gint64 * base_offset)
3522 guint32 track_id = 0;
3524 if (!gst_byte_reader_skip (tfhd, 1) ||
3525 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3528 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3531 *stream = qtdemux_find_stream (qtdemux, track_id);
3532 if (G_UNLIKELY (!*stream))
3533 goto unknown_stream;
3535 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3536 *base_offset = qtdemux->moof_offset;
3538 if (flags & TF_BASE_DATA_OFFSET)
3539 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3542 /* obtain stream defaults */
3543 qtdemux_parse_trex (qtdemux, *stream,
3544 default_sample_duration, default_sample_size, default_sample_flags);
3546 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
3547 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
3548 if (!gst_byte_reader_skip (tfhd, 4))
3551 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3552 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3555 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3556 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3559 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3560 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3567 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3572 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
3578 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3579 guint64 * decode_time)
3581 guint32 version = 0;
3583 if (!gst_byte_reader_get_uint32_be (br, &version))
3588 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3591 guint32 dec_time = 0;
3592 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3594 *decode_time = dec_time;
3597 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3604 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3609 /* Returns a pointer to a GstStructure containing the properties of
3610 * the stream sample identified by @sample_index. The caller must unref
3611 * the returned object after use. Returns NULL if unsuccessful. */
3612 static GstStructure *
3613 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3614 QtDemuxStream * stream, guint sample_index)
3616 QtDemuxCencSampleSetInfo *info = NULL;
3618 g_return_val_if_fail (stream != NULL, NULL);
3619 g_return_val_if_fail (stream->protected, NULL);
3620 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3622 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3624 /* Currently, cenc properties for groups of samples are not supported, so
3625 * simply return a copy of the default sample properties */
3626 return gst_structure_copy (info->default_properties);
3629 /* Parses the sizes of sample auxiliary information contained within a stream,
3630 * as given in a saiz box. Returns array of sample_count guint8 size values,
3631 * or NULL on failure */
3633 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3634 GstByteReader * br, guint32 * sample_count)
3638 guint8 default_info_size;
3640 g_return_val_if_fail (qtdemux != NULL, NULL);
3641 g_return_val_if_fail (stream != NULL, NULL);
3642 g_return_val_if_fail (br != NULL, NULL);
3643 g_return_val_if_fail (sample_count != NULL, NULL);
3645 if (!gst_byte_reader_get_uint32_be (br, &flags))
3649 /* aux_info_type and aux_info_type_parameter are ignored */
3650 if (!gst_byte_reader_skip (br, 8))
3654 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3656 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3658 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3660 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3663 if (default_info_size == 0) {
3664 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3668 info_sizes = g_new (guint8, *sample_count);
3669 memset (info_sizes, default_info_size, *sample_count);
3675 /* Parses the offset of sample auxiliary information contained within a stream,
3676 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3678 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3679 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3684 guint32 aux_info_type = 0;
3685 guint32 aux_info_type_parameter = 0;
3686 guint32 entry_count;
3690 g_return_val_if_fail (qtdemux != NULL, FALSE);
3691 g_return_val_if_fail (stream != NULL, FALSE);
3692 g_return_val_if_fail (br != NULL, FALSE);
3693 g_return_val_if_fail (offset != NULL, FALSE);
3695 if (!gst_byte_reader_get_uint8 (br, &version))
3698 if (!gst_byte_reader_get_uint24_be (br, &flags))
3702 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type))
3704 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3706 } else if (stream->protected) {
3707 aux_info_type = stream->protection_scheme_type;
3709 aux_info_type = stream->fourcc;
3713 *info_type = aux_info_type;
3714 if (info_type_parameter)
3715 *info_type_parameter = aux_info_type_parameter;
3717 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3718 "aux_info_type_parameter: %#06x",
3719 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3721 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3724 if (entry_count != 1) {
3725 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3730 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3732 *offset = (guint64) off_32;
3734 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3739 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3744 qtdemux_gst_structure_free (GstStructure * gststructure)
3747 gst_structure_free (gststructure);
3751 /* Parses auxiliary information relating to samples protected using Common
3752 * Encryption (cenc); the format of this information is defined in
3753 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
3755 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3756 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3758 QtDemuxCencSampleSetInfo *ss_info = NULL;
3762 g_return_val_if_fail (qtdemux != NULL, FALSE);
3763 g_return_val_if_fail (stream != NULL, FALSE);
3764 g_return_val_if_fail (br != NULL, FALSE);
3765 g_return_val_if_fail (stream->protected, FALSE);
3766 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3768 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3770 if (ss_info->crypto_info) {
3771 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3772 g_ptr_array_free (ss_info->crypto_info, TRUE);
3775 ss_info->crypto_info =
3776 g_ptr_array_new_full (sample_count,
3777 (GDestroyNotify) qtdemux_gst_structure_free);
3779 for (i = 0; i < sample_count; ++i) {
3780 GstStructure *properties;
3781 guint16 n_subsamples;
3786 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3787 if (properties == NULL) {
3788 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3791 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3792 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3793 gst_structure_free (properties);
3796 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
3797 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3798 gst_structure_free (properties);
3801 buf = gst_buffer_new_wrapped (data, iv_size);
3802 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3803 size = info_sizes[i];
3804 if (size > iv_size) {
3805 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3806 || !(n_subsamples > 0)) {
3807 gst_structure_free (properties);
3808 GST_ERROR_OBJECT (qtdemux,
3809 "failed to get subsample count for sample %u", i);
3812 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3813 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3814 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3816 gst_structure_free (properties);
3819 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3821 gst_structure_free (properties);
3824 gst_structure_set (properties,
3825 "subsample_count", G_TYPE_UINT, n_subsamples,
3826 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3828 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3830 g_ptr_array_add (ss_info->crypto_info, properties);
3835 /* Converts a UUID in raw byte form to a string representation, as defined in
3836 * RFC 4122. The caller takes ownership of the returned string and is
3837 * responsible for freeing it after use. */
3839 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
3841 const guint8 *uuid = (const guint8 *) uuid_bytes;
3843 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3844 "%02x%02x-%02x%02x%02x%02x%02x%02x",
3845 uuid[0], uuid[1], uuid[2], uuid[3],
3846 uuid[4], uuid[5], uuid[6], uuid[7],
3847 uuid[8], uuid[9], uuid[10], uuid[11],
3848 uuid[12], uuid[13], uuid[14], uuid[15]);
3851 /* Parses a Protection System Specific Header box (pssh), as defined in the
3852 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
3853 * information needed by a specific content protection system in order to
3854 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
3857 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
3859 gchar *sysid_string;
3860 guint32 pssh_size = QT_UINT32 (node->data);
3861 GstBuffer *pssh = NULL;
3862 GstEvent *event = NULL;
3863 guint32 parent_box_type;
3866 if (G_UNLIKELY (pssh_size < 32U)) {
3867 GST_ERROR_OBJECT (qtdemux, "invalid box size");
3872 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
3874 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
3876 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
3877 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
3878 gst_buffer_get_size (pssh));
3880 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
3882 /* Push an event containing the pssh box onto the queues of all streams. */
3883 event = gst_event_new_protection (sysid_string, pssh,
3884 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
3885 for (i = 0; i < qtdemux->n_streams; ++i) {
3886 g_queue_push_tail (&qtdemux->streams[i]->protection_scheme_event_queue,
3887 gst_event_ref (event));
3889 g_free (sysid_string);
3890 gst_event_unref (event);
3891 gst_buffer_unref (pssh);
3896 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
3897 guint64 moof_offset, QtDemuxStream * stream)
3899 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
3900 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
3901 GNode *saiz_node, *saio_node, *pssh_node;
3902 GstByteReader saiz_data, saio_data;
3903 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
3904 gint64 base_offset, running_offset;
3907 /* NOTE @stream ignored */
3909 moof_node = g_node_new ((guint8 *) buffer);
3910 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
3911 qtdemux_node_dump (qtdemux, moof_node);
3913 /* Get fragment number from mfhd and check it's valid */
3915 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
3916 if (mfhd_node == NULL)
3918 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
3920 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
3922 /* unknown base_offset to start with */
3923 base_offset = running_offset = -1;
3924 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
3926 guint64 decode_time = 0;
3928 /* Fragment Header node */
3930 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
3934 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
3935 &ds_size, &ds_flags, &base_offset))
3938 /* The following code assumes at most a single set of sample auxiliary
3939 * data in the fragment (consisting of a saiz box and a corresponding saio
3940 * box); in theory, however, there could be multiple sets of sample
3941 * auxiliary data in a fragment. */
3943 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
3947 guint32 sample_count;
3948 guint32 info_type = 0;
3950 guint32 info_type_parameter = 0;
3952 info_sizes = qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
3954 if (G_UNLIKELY (info_sizes == NULL)) {
3955 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
3959 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
3962 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
3966 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
3967 &info_type, &info_type_parameter, &offset))) {
3968 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
3969 g_free (info_sizes);
3972 if (base_offset > qtdemux->moof_offset)
3973 offset += (guint64) (base_offset - qtdemux->moof_offset);
3974 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
3976 if (offset > length) {
3977 GST_ERROR_OBJECT (qtdemux, "cenc auxiliary info outside moof "
3978 "boxes is not supported");
3979 g_free (info_sizes);
3982 gst_byte_reader_init (&br, buffer + offset, length - offset);
3983 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
3984 info_sizes, sample_count)) {
3985 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
3989 g_free (info_sizes);
3993 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
3996 GstClockTime decode_time_ts;
3998 /* We'll use decode_time to interpolate timestamps
3999 * in case the input timestamps are missing */
4000 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4002 decode_time_ts = QTSTREAMTIME_TO_GSTTIME (stream, decode_time);
4004 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4005 " (%" GST_TIME_FORMAT ")", decode_time,
4006 GST_TIME_ARGS (decode_time_ts));
4008 /* Discard the fragment buffer timestamp info to avoid using it.
4009 * Rely on tfdt instead as it is more accurate than the timestamp
4010 * that is fetched from a manifest/playlist and is usually
4012 qtdemux->fragment_start = -1;
4015 if (G_UNLIKELY (!stream)) {
4016 /* we lost track of offset, we'll need to regain it,
4017 * but can delay complaining until later or avoid doing so altogether */
4021 if (G_UNLIKELY (base_offset < -1))
4024 if (qtdemux->upstream_format_is_time)
4025 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
4027 /* Track Run node */
4029 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4032 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4033 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4034 &running_offset, decode_time);
4035 /* iterate all siblings */
4036 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4039 /* if no new base_offset provided for next traf,
4040 * base is end of current traf */
4041 base_offset = running_offset;
4042 running_offset = -1;
4044 /* iterate all siblings */
4045 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4048 /* parse any protection system info */
4049 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4051 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4052 qtdemux_parse_pssh (qtdemux, pssh_node);
4053 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4056 g_node_destroy (moof_node);
4061 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4066 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4071 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4076 g_node_destroy (moof_node);
4077 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4078 (_("This file is corrupt and cannot be played.")), (NULL));
4084 /* might be used if some day we actually use mfra & co
4085 * for random access to fragments,
4086 * but that will require quite some modifications and much less relying
4087 * on a sample array */
4091 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4093 QtDemuxStream *stream;
4094 guint32 ver_flags, track_id, len, num_entries, i;
4095 guint value_size, traf_size, trun_size, sample_size;
4096 guint64 time = 0, moof_offset = 0;
4098 GstBuffer *buf = NULL;
4103 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4105 if (!gst_byte_reader_skip (&tfra, 8))
4108 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4111 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4112 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4113 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4116 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4118 stream = qtdemux_find_stream (qtdemux, track_id);
4120 goto unknown_trackid;
4122 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4123 sample_size = (len & 3) + 1;
4124 trun_size = ((len & 12) >> 2) + 1;
4125 traf_size = ((len & 48) >> 4) + 1;
4127 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4128 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4130 if (num_entries == 0)
4133 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4134 value_size + value_size + traf_size + trun_size + sample_size))
4137 g_free (stream->ra_entries);
4138 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4139 stream->n_ra_entries = num_entries;
4141 for (i = 0; i < num_entries; i++) {
4142 qt_atom_parser_get_offset (&tfra, value_size, &time);
4143 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4144 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4145 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4146 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4148 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4150 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4151 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4153 stream->ra_entries[i].ts = time;
4154 stream->ra_entries[i].moof_offset = moof_offset;
4156 /* don't want to go through the entire file and read all moofs at startup */
4158 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4159 if (ret != GST_FLOW_OK)
4161 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4162 moof_offset, stream);
4163 gst_buffer_unref (buf);
4167 check_update_duration (qtdemux, time);
4174 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4179 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4184 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4190 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4192 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4193 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4194 GstBuffer *mfro = NULL, *mfra = NULL;
4196 gboolean ret = FALSE;
4197 GNode *mfra_node, *tfra_node;
4198 guint64 mfra_offset = 0;
4199 guint32 fourcc, mfra_size;
4202 /* query upstream size in bytes */
4203 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4204 goto size_query_failed;
4206 /* mfro box should be at the very end of the file */
4207 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4208 if (flow != GST_FLOW_OK)
4211 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4213 fourcc = QT_FOURCC (mfro_map.data + 4);
4214 if (fourcc != FOURCC_mfro)
4217 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4218 if (mfro_map.size < 16)
4219 goto invalid_mfro_size;
4221 mfra_size = QT_UINT32 (mfro_map.data + 12);
4222 if (mfra_size >= len)
4223 goto invalid_mfra_size;
4225 mfra_offset = len - mfra_size;
4227 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4228 mfra_offset, mfra_size);
4230 /* now get and parse mfra box */
4231 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4232 if (flow != GST_FLOW_OK)
4235 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4237 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4238 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4240 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4243 qtdemux_parse_tfra (qtdemux, tfra_node);
4244 /* iterate all siblings */
4245 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4247 g_node_destroy (mfra_node);
4249 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4255 if (mfro_map.memory != NULL)
4256 gst_buffer_unmap (mfro, &mfro_map);
4257 gst_buffer_unref (mfro);
4260 if (mfra_map.memory != NULL)
4261 gst_buffer_unmap (mfra, &mfra_map);
4262 gst_buffer_unref (mfra);
4269 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4274 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4279 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4284 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4290 add_offset (guint64 offset, guint64 advance)
4292 /* Avoid 64-bit overflow by clamping */
4293 if (offset > G_MAXUINT64 - advance)
4295 return offset + advance;
4298 static GstFlowReturn
4299 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4303 GstBuffer *buf = NULL;
4304 GstFlowReturn ret = GST_FLOW_OK;
4305 guint64 cur_offset = qtdemux->offset;
4308 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4309 if (G_UNLIKELY (ret != GST_FLOW_OK))
4311 gst_buffer_map (buf, &map, GST_MAP_READ);
4312 if (G_LIKELY (map.size >= 8))
4313 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4314 gst_buffer_unmap (buf, &map);
4315 gst_buffer_unref (buf);
4317 /* maybe we already got most we needed, so only consider this eof */
4318 if (G_UNLIKELY (length == 0)) {
4319 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4320 (_("Invalid atom size.")),
4321 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4322 GST_FOURCC_ARGS (fourcc)));
4329 /* record for later parsing when needed */
4330 if (!qtdemux->moof_offset) {
4331 qtdemux->moof_offset = qtdemux->offset;
4333 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4336 qtdemux->offset += length; /* skip moof and keep going */
4338 if (qtdemux->got_moov) {
4339 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4350 GST_LOG_OBJECT (qtdemux,
4351 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4352 GST_FOURCC_ARGS (fourcc), cur_offset);
4353 qtdemux->offset = add_offset (qtdemux->offset, length);
4358 GstBuffer *moov = NULL;
4360 if (qtdemux->got_moov) {
4361 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4362 qtdemux->offset = add_offset (qtdemux->offset, length);
4366 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4367 if (ret != GST_FLOW_OK)
4369 gst_buffer_map (moov, &map, GST_MAP_READ);
4371 if (length != map.size) {
4372 /* Some files have a 'moov' atom at the end of the file which contains
4373 * a terminal 'free' atom where the body of the atom is missing.
4374 * Check for, and permit, this special case.
4376 if (map.size >= 8) {
4377 guint8 *final_data = map.data + (map.size - 8);
4378 guint32 final_length = QT_UINT32 (final_data);
4379 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4381 if (final_fourcc == FOURCC_free
4382 && map.size + final_length - 8 == length) {
4383 /* Ok, we've found that special case. Allocate a new buffer with
4384 * that free atom actually present. */
4385 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4386 gst_buffer_fill (newmoov, 0, map.data, map.size);
4387 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4388 gst_buffer_unmap (moov, &map);
4389 gst_buffer_unref (moov);
4391 gst_buffer_map (moov, &map, GST_MAP_READ);
4396 if (length != map.size) {
4397 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4398 (_("This file is incomplete and cannot be played.")),
4399 ("We got less than expected (received %" G_GSIZE_FORMAT
4400 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4401 (guint) length, cur_offset));
4402 gst_buffer_unmap (moov, &map);
4403 gst_buffer_unref (moov);
4404 ret = GST_FLOW_ERROR;
4407 qtdemux->offset += length;
4409 qtdemux_parse_moov (qtdemux, map.data, length);
4410 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4412 qtdemux_parse_tree (qtdemux);
4413 g_node_destroy (qtdemux->moov_node);
4414 gst_buffer_unmap (moov, &map);
4415 gst_buffer_unref (moov);
4416 qtdemux->moov_node = NULL;
4417 qtdemux->got_moov = TRUE;
4423 GstBuffer *ftyp = NULL;
4425 /* extract major brand; might come in handy for ISO vs QT issues */
4426 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4427 if (ret != GST_FLOW_OK)
4429 qtdemux->offset += length;
4430 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4431 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4432 gst_buffer_unmap (ftyp, &map);
4433 gst_buffer_unref (ftyp);
4438 GstBuffer *uuid = NULL;
4440 /* uuid are extension atoms */
4441 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4442 if (ret != GST_FLOW_OK)
4444 qtdemux->offset += length;
4445 gst_buffer_map (uuid, &map, GST_MAP_READ);
4446 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4447 gst_buffer_unmap (uuid, &map);
4448 gst_buffer_unref (uuid);
4453 GstBuffer *sidx = NULL;
4454 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4455 if (ret != GST_FLOW_OK)
4457 qtdemux->offset += length;
4458 gst_buffer_map (sidx, &map, GST_MAP_READ);
4459 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4460 gst_buffer_unmap (sidx, &map);
4461 gst_buffer_unref (sidx);
4466 GstBuffer *unknown = NULL;
4468 GST_LOG_OBJECT (qtdemux,
4469 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4470 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4472 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4473 if (ret != GST_FLOW_OK)
4475 gst_buffer_map (unknown, &map, GST_MAP_READ);
4476 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4477 gst_buffer_unmap (unknown, &map);
4478 gst_buffer_unref (unknown);
4479 qtdemux->offset += length;
4485 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4486 /* digested all data, show what we have */
4487 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
4488 if (spherical_metadata)
4489 _send_message_to_bus (qtdemux, spherical_metadata);
4490 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
4491 qtdemux_prepare_streams (qtdemux);
4492 ret = qtdemux_expose_streams (qtdemux);
4494 qtdemux->state = QTDEMUX_STATE_MOVIE;
4495 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4502 /* Seeks to the previous keyframe of the indexed stream and
4503 * aligns other streams with respect to the keyframe timestamp
4504 * of indexed stream. Only called in case of Reverse Playback
4506 static GstFlowReturn
4507 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4510 guint32 seg_idx = 0, k_index = 0;
4511 guint32 ref_seg_idx, ref_k_index;
4512 GstClockTime k_pos = 0, last_stop = 0;
4513 QtDemuxSegment *seg = NULL;
4514 QtDemuxStream *ref_str = NULL;
4515 guint64 seg_media_start_mov; /* segment media start time in mov format */
4518 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4519 * and finally align all the other streams on that timestamp with their
4520 * respective keyframes */
4521 for (n = 0; n < qtdemux->n_streams; n++) {
4522 QtDemuxStream *str = qtdemux->streams[n];
4524 /* No candidate yet, take the first stream */
4530 /* So that stream has a segment, we prefer video streams */
4531 if (str->subtype == FOURCC_vide) {
4537 if (G_UNLIKELY (!ref_str)) {
4538 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4542 if (G_UNLIKELY (!ref_str->from_sample)) {
4543 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4547 /* So that stream has been playing from from_sample to to_sample. We will
4548 * get the timestamp of the previous sample and search for a keyframe before
4549 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4550 if (ref_str->subtype == FOURCC_vide) {
4551 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4552 ref_str->from_sample - 1);
4554 if (ref_str->from_sample >= 10)
4555 k_index = ref_str->from_sample - 10;
4561 ref_str->samples[k_index].timestamp +
4562 ref_str->samples[k_index].pts_offset;
4564 /* get current segment for that stream */
4565 seg = &ref_str->segments[ref_str->segment_index];
4566 /* Use segment start in original timescale for comparisons */
4567 seg_media_start_mov = seg->trak_media_start;
4569 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4570 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT "\n",
4571 k_index, target_ts, seg_media_start_mov,
4572 GST_TIME_ARGS (seg->media_start));
4574 /* Crawl back through segments to find the one containing this I frame */
4575 while (target_ts < seg_media_start_mov) {
4576 GST_DEBUG_OBJECT (qtdemux,
4577 "keyframe position (sample %u) is out of segment %u " " target %"
4578 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4579 ref_str->segment_index, target_ts, seg_media_start_mov);
4581 if (G_UNLIKELY (!ref_str->segment_index)) {
4582 /* Reached first segment, let's consider it's EOS */
4585 ref_str->segment_index--;
4586 seg = &ref_str->segments[ref_str->segment_index];
4587 /* Use segment start in original timescale for comparisons */
4588 seg_media_start_mov = seg->trak_media_start;
4590 /* Calculate time position of the keyframe and where we should stop */
4592 QTSTREAMTIME_TO_GSTTIME (ref_str,
4593 target_ts - seg->trak_media_start) + seg->time;
4595 QTSTREAMTIME_TO_GSTTIME (ref_str,
4596 ref_str->samples[ref_str->from_sample].timestamp -
4597 seg->trak_media_start) + seg->time;
4599 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4600 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4601 k_index, GST_TIME_ARGS (k_pos));
4603 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4604 qtdemux->segment.position = last_stop;
4605 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4606 GST_TIME_ARGS (last_stop));
4608 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4609 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4613 ref_seg_idx = ref_str->segment_index;
4614 ref_k_index = k_index;
4616 /* Align them all on this */
4617 for (n = 0; n < qtdemux->n_streams; n++) {
4619 GstClockTime seg_time = 0;
4620 QtDemuxStream *str = qtdemux->streams[n];
4622 /* aligning reference stream again might lead to backing up to yet another
4623 * keyframe (due to timestamp rounding issues),
4624 * potentially putting more load on downstream; so let's try to avoid */
4625 if (str == ref_str) {
4626 seg_idx = ref_seg_idx;
4627 seg = &str->segments[seg_idx];
4628 k_index = ref_k_index;
4629 GST_DEBUG_OBJECT (qtdemux, "reference stream %d segment %d, "
4630 "sample at index %d", n, ref_str->segment_index, k_index);
4632 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4633 GST_DEBUG_OBJECT (qtdemux,
4634 "stream %d align segment %d for keyframe pos %" GST_TIME_FORMAT, n,
4635 seg_idx, GST_TIME_ARGS (k_pos));
4637 /* get segment and time in the segment */
4638 seg = &str->segments[seg_idx];
4639 seg_time = k_pos - seg->time;
4641 /* get the media time in the segment.
4642 * No adjustment for empty "filler" segments */
4643 if (seg->media_start != GST_CLOCK_TIME_NONE)
4644 seg_time += seg->media_start;
4646 /* get the index of the sample with media time */
4647 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4648 GST_DEBUG_OBJECT (qtdemux,
4649 "stream %d sample for %" GST_TIME_FORMAT " at %u", n,
4650 GST_TIME_ARGS (seg_time), index);
4652 /* find previous keyframe */
4653 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index);
4656 /* Remember until where we want to go */
4657 str->to_sample = str->from_sample - 1;
4658 /* Define our time position */
4660 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4661 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4662 if (seg->media_start != GST_CLOCK_TIME_NONE)
4663 str->time_position -= seg->media_start;
4665 /* Now seek back in time */
4666 gst_qtdemux_move_stream (qtdemux, str, k_index);
4667 GST_DEBUG_OBJECT (qtdemux, "stream %d keyframe at %u, time position %"
4668 GST_TIME_FORMAT " playing from sample %u to %u", n, k_index,
4669 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4675 return GST_FLOW_EOS;
4678 /* activate the given segment number @seg_idx of @stream at time @offset.
4679 * @offset is an absolute global position over all the segments.
4681 * This will push out a NEWSEGMENT event with the right values and
4682 * position the stream index to the first decodable sample before
4688 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4689 guint32 seg_idx, GstClockTime offset)
4692 QtDemuxSegment *segment;
4693 guint32 index, kf_index;
4694 GstClockTime seg_time;
4695 GstClockTime start, stop, time;
4698 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
4699 seg_idx, GST_TIME_ARGS (offset));
4701 /* update the current segment */
4702 stream->segment_index = seg_idx;
4704 /* get the segment */
4705 segment = &stream->segments[seg_idx];
4707 if (G_UNLIKELY (offset < segment->time)) {
4708 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
4709 GST_TIME_ARGS (segment->time));
4713 /* segment lies beyond total indicated duration */
4714 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
4715 segment->time > qtdemux->segment.duration)) {
4716 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
4717 " < segment->time %" GST_TIME_FORMAT,
4718 GST_TIME_ARGS (qtdemux->segment.duration),
4719 GST_TIME_ARGS (segment->time));
4723 /* get time in this segment */
4724 seg_time = offset - segment->time;
4726 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4727 GST_TIME_ARGS (seg_time));
4729 if (G_UNLIKELY (seg_time > segment->duration)) {
4730 GST_LOG_OBJECT (stream->pad,
4731 "seg_time > segment->duration %" GST_TIME_FORMAT,
4732 GST_TIME_ARGS (segment->duration));
4733 seg_time = segment->duration;
4736 /* qtdemux->segment.stop is in outside-time-realm, whereas
4737 * segment->media_stop is in track-time-realm.
4739 * In order to compare the two, we need to bring segment.stop
4740 * into the track-time-realm */
4742 stop = qtdemux->segment.stop;
4743 if (stop == GST_CLOCK_TIME_NONE)
4744 stop = qtdemux->segment.duration;
4745 if (stop == GST_CLOCK_TIME_NONE)
4746 stop = segment->media_stop;
4749 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4751 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4752 start = segment->time + seg_time;
4754 stop = start - seg_time + segment->duration;
4755 } else if (qtdemux->segment.rate >= 0) {
4756 start = MIN (segment->media_start + seg_time, stop);
4759 if (segment->media_start >= qtdemux->segment.start) {
4760 time = segment->time;
4762 time = segment->time + (qtdemux->segment.start - segment->media_start);
4765 start = MAX (segment->media_start, qtdemux->segment.start);
4766 stop = MIN (segment->media_start + seg_time, stop);
4769 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
4770 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
4771 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
4773 /* combine global rate with that of the segment */
4774 rate = segment->rate * qtdemux->segment.rate;
4776 /* Copy flags from main segment */
4777 stream->segment.flags = qtdemux->segment.flags;
4779 /* update the segment values used for clipping */
4780 stream->segment.offset = qtdemux->segment.offset;
4781 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
4782 stream->segment.applied_rate = qtdemux->segment.applied_rate;
4783 stream->segment.rate = rate;
4784 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
4785 stream->cslg_shift);
4786 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
4787 stream->cslg_shift);
4788 stream->segment.time = time;
4789 stream->segment.position = stream->segment.start;
4791 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
4794 /* now prepare and send the segment */
4796 event = gst_event_new_segment (&stream->segment);
4797 if (stream->segment_seqnum) {
4798 gst_event_set_seqnum (event, stream->segment_seqnum);
4800 gst_pad_push_event (stream->pad, event);
4801 /* assume we can send more data now */
4802 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
4803 /* clear to send tags on this pad now */
4804 gst_qtdemux_push_tags (qtdemux, stream);
4807 /* in the fragmented case, we pick a fragment that starts before our
4808 * desired position and rely on downstream to wait for a keyframe
4809 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
4810 * tfra entries tells us which trun/sample the key unit is in, but we don't
4811 * make use of this additional information at the moment) */
4812 if (qtdemux->fragmented) {
4813 stream->to_sample = G_MAXUINT32;
4817 /* and move to the keyframe before the indicated media time of the
4819 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
4820 if (qtdemux->segment.rate >= 0) {
4821 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
4822 stream->to_sample = G_MAXUINT32;
4823 GST_DEBUG_OBJECT (stream->pad,
4824 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4825 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
4826 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4828 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
4829 stream->to_sample = index;
4830 GST_DEBUG_OBJECT (stream->pad,
4831 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4832 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
4833 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4836 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
4837 "this is an empty segment");
4841 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
4842 * encountered an error and printed a message so we return appropriately */
4846 /* we're at the right spot */
4847 if (index == stream->sample_index) {
4848 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
4852 /* find keyframe of the target index */
4853 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
4856 /* indent does stupid stuff with stream->samples[].timestamp */
4858 /* if we move forwards, we don't have to go back to the previous
4859 * keyframe since we already sent that. We can also just jump to
4860 * the keyframe right before the target index if there is one. */
4861 if (index > stream->sample_index) {
4862 /* moving forwards check if we move past a keyframe */
4863 if (kf_index > stream->sample_index) {
4864 GST_DEBUG_OBJECT (stream->pad,
4865 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
4866 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
4867 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
4868 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
4870 GST_DEBUG_OBJECT (stream->pad,
4871 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
4872 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
4873 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
4876 GST_DEBUG_OBJECT (stream->pad,
4877 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
4878 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
4879 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
4880 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
4888 /* prepare to get the current sample of @stream, getting essential values.
4890 * This function will also prepare and send the segment when needed.
4892 * Return FALSE if the stream is EOS.
4897 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
4898 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
4899 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
4900 gboolean * keyframe)
4902 QtDemuxSample *sample;
4903 GstClockTime time_position;
4906 g_return_val_if_fail (stream != NULL, FALSE);
4908 time_position = stream->time_position;
4909 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
4912 seg_idx = stream->segment_index;
4913 if (G_UNLIKELY (seg_idx == -1)) {
4914 /* find segment corresponding to time_position if we are looking
4916 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
4919 /* different segment, activate it, sample_index will be set. */
4920 if (G_UNLIKELY (stream->segment_index != seg_idx))
4921 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
4923 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream->
4925 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
4927 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
4928 " prepare empty sample");
4931 *pts = *dts = time_position;
4932 *duration = seg->duration - (time_position - seg->time);
4939 if (stream->sample_index == -1)
4940 stream->sample_index = 0;
4942 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
4943 stream->sample_index, stream->n_samples);
4945 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
4946 if (!qtdemux->fragmented)
4949 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
4953 GST_OBJECT_LOCK (qtdemux);
4954 flow = qtdemux_add_fragmented_samples (qtdemux);
4955 GST_OBJECT_UNLOCK (qtdemux);
4957 if (flow != GST_FLOW_OK)
4960 while (stream->sample_index >= stream->n_samples);
4963 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
4964 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
4965 stream->sample_index);
4969 /* now get the info for the sample we're at */
4970 sample = &stream->samples[stream->sample_index];
4972 *dts = QTSAMPLE_DTS (stream, sample);
4973 *pts = QTSAMPLE_PTS (stream, sample);
4974 *offset = sample->offset;
4975 *size = sample->size;
4976 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
4977 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
4984 stream->time_position = GST_CLOCK_TIME_NONE;
4989 /* move to the next sample in @stream.
4991 * Moves to the next segment when needed.
4994 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
4996 QtDemuxSample *sample;
4997 QtDemuxSegment *segment;
4999 /* get current segment */
5000 segment = &stream->segments[stream->segment_index];
5002 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5003 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5007 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5008 /* Mark the stream as EOS */
5009 GST_DEBUG_OBJECT (qtdemux,
5010 "reached max allowed sample %u, mark EOS", stream->to_sample);
5011 stream->time_position = GST_CLOCK_TIME_NONE;
5015 /* move to next sample */
5016 stream->sample_index++;
5017 stream->offset_in_sample = 0;
5019 /* reached the last sample, we need the next segment */
5020 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5023 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5024 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5025 stream->sample_index);
5029 /* get next sample */
5030 sample = &stream->samples[stream->sample_index];
5032 /* see if we are past the segment */
5033 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
5036 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
5037 /* inside the segment, update time_position, looks very familiar to
5038 * GStreamer segments, doesn't it? */
5039 stream->time_position =
5040 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
5042 /* not yet in segment, time does not yet increment. This means
5043 * that we are still prerolling keyframes to the decoder so it can
5044 * decode the first sample of the segment. */
5045 stream->time_position = segment->time;
5049 /* move to the next segment */
5052 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5054 if (stream->segment_index == stream->n_segments - 1) {
5055 /* are we at the end of the last segment, we're EOS */
5056 stream->time_position = GST_CLOCK_TIME_NONE;
5058 /* else we're only at the end of the current segment */
5059 stream->time_position = segment->stop_time;
5061 /* make sure we select a new segment */
5063 /* accumulate previous segments */
5064 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5065 stream->accumulated_base +=
5066 (stream->segment.stop -
5067 stream->segment.start) / ABS (stream->segment.rate);
5069 stream->segment_index = -1;
5074 gst_qtdemux_sync_streams (GstQTDemux * demux)
5078 if (demux->n_streams <= 1)
5081 for (i = 0; i < demux->n_streams; i++) {
5082 QtDemuxStream *stream;
5083 GstClockTime end_time;
5085 stream = demux->streams[i];
5090 /* TODO advance time on subtitle streams here, if any some day */
5092 /* some clips/trailers may have unbalanced streams at the end,
5093 * so send EOS on shorter stream to prevent stalling others */
5095 /* do not mess with EOS if SEGMENT seeking */
5096 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5099 if (demux->pullbased) {
5100 /* loop mode is sample time based */
5101 if (!STREAM_IS_EOS (stream))
5104 /* push mode is byte position based */
5105 if (stream->n_samples &&
5106 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5110 if (stream->sent_eos)
5113 /* only act if some gap */
5114 end_time = stream->segments[stream->n_segments - 1].stop_time;
5115 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5116 ", stream end: %" GST_TIME_FORMAT,
5117 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5118 if (GST_CLOCK_TIME_IS_VALID (end_time)
5119 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5120 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5121 GST_PAD_NAME (stream->pad));
5122 stream->sent_eos = TRUE;
5123 gst_pad_push_event (stream->pad, gst_event_new_eos ());
5128 /* EOS and NOT_LINKED need to be combined. This means that we return:
5130 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5131 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5133 static GstFlowReturn
5134 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5137 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5140 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5143 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5145 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5149 /* the input buffer metadata must be writable. Returns NULL when the buffer is
5150 * completely clipped
5152 * Should be used only with raw buffers */
5154 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5157 guint64 start, stop, cstart, cstop, diff;
5158 GstClockTime pts, duration;
5160 gint num_rate, denom_rate;
5165 osize = size = gst_buffer_get_size (buf);
5168 /* depending on the type, setup the clip parameters */
5169 if (stream->subtype == FOURCC_soun) {
5170 frame_size = stream->bytes_per_frame;
5171 num_rate = GST_SECOND;
5172 denom_rate = (gint) stream->rate;
5174 } else if (stream->subtype == FOURCC_vide) {
5176 num_rate = stream->fps_n;
5177 denom_rate = stream->fps_d;
5182 if (frame_size <= 0)
5183 goto bad_frame_size;
5185 /* we can only clip if we have a valid pts */
5186 pts = GST_BUFFER_PTS (buf);
5187 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5190 duration = GST_BUFFER_DURATION (buf);
5192 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5194 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5198 stop = start + duration;
5200 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5201 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5204 /* see if some clipping happened */
5205 diff = cstart - start;
5211 /* bring clipped time to samples and to bytes */
5212 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5215 GST_DEBUG_OBJECT (qtdemux,
5216 "clipping start to %" GST_TIME_FORMAT " %"
5217 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5223 diff = stop - cstop;
5228 /* bring clipped time to samples and then to bytes */
5229 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5231 GST_DEBUG_OBJECT (qtdemux,
5232 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5233 " bytes", GST_TIME_ARGS (cstop), diff);
5238 if (offset != 0 || size != osize)
5239 gst_buffer_resize (buf, offset, size);
5241 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5242 GST_BUFFER_PTS (buf) = pts;
5243 GST_BUFFER_DURATION (buf) = duration;
5247 /* dropped buffer */
5250 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5255 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5260 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5265 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5266 gst_buffer_unref (buf);
5271 /* the input buffer metadata must be writable,
5272 * but time/duration etc not yet set and need not be preserved */
5274 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5281 /* not many cases for now */
5282 if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
5283 /* send a one time dvd clut event */
5284 if (stream->pending_event && stream->pad)
5285 gst_pad_push_event (stream->pad, stream->pending_event);
5286 stream->pending_event = NULL;
5289 if (G_UNLIKELY (stream->subtype != FOURCC_text
5290 && stream->subtype != FOURCC_sbtl &&
5291 stream->subtype != FOURCC_subp)) {
5295 gst_buffer_map (buf, &map, GST_MAP_READ);
5297 /* empty buffer is sent to terminate previous subtitle */
5298 if (map.size <= 2) {
5299 gst_buffer_unmap (buf, &map);
5300 gst_buffer_unref (buf);
5303 if (stream->subtype == FOURCC_subp) {
5304 /* That's all the processing needed for subpictures */
5305 gst_buffer_unmap (buf, &map);
5309 nsize = GST_READ_UINT16_BE (map.data);
5310 nsize = MIN (nsize, map.size - 2);
5312 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5315 /* takes care of UTF-8 validation or UTF-16 recognition,
5316 * no other encoding expected */
5317 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5318 gst_buffer_unmap (buf, &map);
5320 gst_buffer_unref (buf);
5321 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5323 /* this should not really happen unless the subtitle is corrupted */
5324 gst_buffer_unref (buf);
5328 /* FIXME ? convert optional subsequent style info to markup */
5333 /* Sets a buffer's attributes properly and pushes it downstream.
5334 * Also checks for additional actions and custom processing that may
5335 * need to be done first.
5337 static GstFlowReturn
5338 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
5339 QtDemuxStream * stream, GstBuffer * buf,
5340 GstClockTime dts, GstClockTime pts, GstClockTime duration,
5341 gboolean keyframe, GstClockTime position, guint64 byte_position)
5343 GstFlowReturn ret = GST_FLOW_OK;
5345 /* offset the timestamps according to the edit list */
5347 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
5351 gst_buffer_map (buf, &map, GST_MAP_READ);
5352 url = g_strndup ((gchar *) map.data, map.size);
5353 gst_buffer_unmap (buf, &map);
5354 if (url != NULL && strlen (url) != 0) {
5355 /* we have RTSP redirect now */
5356 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5357 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
5358 gst_structure_new ("redirect",
5359 "new-location", G_TYPE_STRING, url, NULL)));
5360 qtdemux->posted_redirect = TRUE;
5362 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
5368 /* position reporting */
5369 if (qtdemux->segment.rate >= 0) {
5370 qtdemux->segment.position = position;
5371 gst_qtdemux_sync_streams (qtdemux);
5374 if (G_UNLIKELY (!stream->pad)) {
5375 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
5376 gst_buffer_unref (buf);
5380 /* send out pending buffers */
5381 while (stream->buffers) {
5382 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
5384 if (G_UNLIKELY (stream->discont)) {
5385 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5386 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
5387 stream->discont = FALSE;
5389 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5392 gst_pad_push (stream->pad, buffer);
5394 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
5397 /* we're going to modify the metadata */
5398 buf = gst_buffer_make_writable (buf);
5400 if (G_UNLIKELY (stream->need_process))
5401 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
5407 GST_BUFFER_DTS (buf) = dts;
5408 GST_BUFFER_PTS (buf) = pts;
5409 GST_BUFFER_DURATION (buf) = duration;
5410 GST_BUFFER_OFFSET (buf) = -1;
5411 GST_BUFFER_OFFSET_END (buf) = -1;
5413 if (G_UNLIKELY (stream->rgb8_palette))
5414 gst_buffer_append_memory (buf, gst_memory_ref (stream->rgb8_palette));
5416 if (G_UNLIKELY (stream->padding)) {
5417 gst_buffer_resize (buf, stream->padding, -1);
5420 if (G_UNLIKELY (qtdemux->element_index)) {
5421 GstClockTime stream_time;
5424 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
5426 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
5427 GST_LOG_OBJECT (qtdemux,
5428 "adding association %" GST_TIME_FORMAT "-> %"
5429 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
5430 gst_index_add_association (qtdemux->element_index,
5432 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
5433 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
5434 GST_FORMAT_BYTES, byte_position, NULL);
5439 if (stream->need_clip)
5440 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5442 if (G_UNLIKELY (buf == NULL))
5445 if (G_UNLIKELY (stream->discont)) {
5446 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5447 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5448 stream->discont = FALSE;
5450 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5454 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5455 stream->on_keyframe = FALSE;
5457 stream->on_keyframe = TRUE;
5461 GST_LOG_OBJECT (qtdemux,
5462 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
5463 ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
5464 GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
5465 GST_PAD_NAME (stream->pad));
5467 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
5468 GstStructure *crypto_info;
5469 QtDemuxCencSampleSetInfo *info =
5470 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5474 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5475 gst_pad_push_event (stream->pad, event);
5478 index = stream->sample_index - (stream->n_samples - info->crypto_info->len);
5479 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5480 /* steal structure from array */
5481 crypto_info = g_ptr_array_index (info->crypto_info, index);
5482 g_ptr_array_index (info->crypto_info, index) = NULL;
5483 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u]", index);
5484 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5485 GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
5489 ret = gst_pad_push (stream->pad, buf);
5491 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
5492 /* mark position in stream, we'll need this to know when to send GAP event */
5493 stream->segment.position = pts + duration;
5500 static const QtDemuxRandomAccessEntry *
5501 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5502 GstClockTime pos, gboolean after)
5504 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
5505 guint n_entries = stream->n_ra_entries;
5508 /* we assume the table is sorted */
5509 for (i = 0; i < n_entries; ++i) {
5510 if (entries[i].ts > pos)
5514 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
5515 * probably okay to assume that the index lists the very first fragment */
5522 return &entries[i - 1];
5526 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
5528 const QtDemuxRandomAccessEntry *best_entry = NULL;
5531 GST_OBJECT_LOCK (qtdemux);
5533 g_assert (qtdemux->n_streams > 0);
5535 for (i = 0; i < qtdemux->n_streams; i++) {
5536 const QtDemuxRandomAccessEntry *entry;
5537 QtDemuxStream *stream;
5538 gboolean is_audio_or_video;
5540 stream = qtdemux->streams[i];
5542 g_free (stream->samples);
5543 stream->samples = NULL;
5544 stream->n_samples = 0;
5545 stream->stbl_index = -1; /* no samples have yet been parsed */
5546 stream->sample_index = -1;
5548 if (stream->ra_entries == NULL)
5551 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
5552 is_audio_or_video = TRUE;
5554 is_audio_or_video = FALSE;
5557 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
5558 stream->time_position, !is_audio_or_video);
5560 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
5561 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
5563 stream->pending_seek = entry;
5565 /* decide position to jump to just based on audio/video tracks, not subs */
5566 if (!is_audio_or_video)
5569 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
5573 if (best_entry == NULL) {
5574 GST_OBJECT_UNLOCK (qtdemux);
5578 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
5579 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
5580 GST_TIME_ARGS (qtdemux->streams[0]->time_position),
5581 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
5583 qtdemux->moof_offset = best_entry->moof_offset;
5585 qtdemux_add_fragmented_samples (qtdemux);
5587 GST_OBJECT_UNLOCK (qtdemux);
5591 static GstFlowReturn
5592 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
5594 GstFlowReturn ret = GST_FLOW_OK;
5595 GstBuffer *buf = NULL;
5596 QtDemuxStream *stream;
5597 GstClockTime min_time;
5599 GstClockTime dts = GST_CLOCK_TIME_NONE;
5600 GstClockTime pts = GST_CLOCK_TIME_NONE;
5601 GstClockTime duration = 0;
5602 gboolean keyframe = FALSE;
5603 guint sample_size = 0;
5609 gst_qtdemux_push_pending_newsegment (qtdemux);
5611 if (qtdemux->fragmented_seek_pending) {
5612 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
5613 gst_qtdemux_do_fragmented_seek (qtdemux);
5614 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
5615 qtdemux->fragmented_seek_pending = FALSE;
5618 /* Figure out the next stream sample to output, min_time is expressed in
5619 * global time and runs over the edit list segments. */
5620 min_time = G_MAXUINT64;
5622 for (i = 0; i < qtdemux->n_streams; i++) {
5623 GstClockTime position;
5625 stream = qtdemux->streams[i];
5626 position = stream->time_position;
5628 /* position of -1 is EOS */
5629 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
5630 min_time = position;
5635 if (G_UNLIKELY (index == -1)) {
5636 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
5640 /* check for segment end */
5641 if (G_UNLIKELY (qtdemux->segment.stop != -1
5642 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
5643 || (qtdemux->segment.rate < 0
5644 && qtdemux->segment.start > min_time))
5645 && qtdemux->streams[index]->on_keyframe)) {
5646 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
5647 qtdemux->streams[index]->time_position = GST_CLOCK_TIME_NONE;
5651 /* gap events for subtitle streams */
5652 for (i = 0; i < qtdemux->n_streams; i++) {
5653 stream = qtdemux->streams[i];
5654 if (stream->pad && (stream->subtype == FOURCC_subp
5655 || stream->subtype == FOURCC_text
5656 || stream->subtype == FOURCC_sbtl)) {
5657 /* send one second gap events until the stream catches up */
5658 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
5659 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
5660 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
5661 stream->segment.position + GST_SECOND < min_time) {
5663 gst_event_new_gap (stream->segment.position, GST_SECOND);
5664 gst_pad_push_event (stream->pad, gap);
5665 stream->segment.position += GST_SECOND;
5670 stream = qtdemux->streams[index];
5671 if (stream->new_caps) {
5672 gst_qtdemux_configure_stream (qtdemux, stream);
5673 qtdemux_do_allocation (qtdemux, stream);
5676 /* fetch info for the current sample of this stream */
5677 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
5678 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
5681 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
5682 if (G_UNLIKELY (qtdemux->
5683 segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
5684 if (stream->subtype == FOURCC_vide && !keyframe) {
5685 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on stream %d", index);
5690 GST_DEBUG_OBJECT (qtdemux,
5691 "pushing from stream %d, empty %d offset %" G_GUINT64_FORMAT
5692 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
5693 ", duration %" GST_TIME_FORMAT, index, empty, offset, sample_size,
5694 GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
5696 if (G_UNLIKELY (empty)) {
5697 /* empty segment, push a gap and move to the next one */
5698 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
5699 stream->segment.position = pts + duration;
5703 /* hmm, empty sample, skip and move to next sample */
5704 if (G_UNLIKELY (sample_size <= 0))
5707 /* last pushed sample was out of boundary, goto next sample */
5708 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
5711 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
5714 GST_DEBUG_OBJECT (qtdemux,
5715 "size %d larger than stream max_buffer_size %d, trimming",
5716 sample_size, stream->max_buffer_size);
5718 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
5721 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
5724 if (stream->use_allocator) {
5725 /* if we have a per-stream allocator, use it */
5726 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
5729 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
5731 if (G_UNLIKELY (ret != GST_FLOW_OK))
5734 if (size != sample_size) {
5735 pts += gst_util_uint64_scale_int (GST_SECOND,
5736 stream->offset_in_sample / stream->bytes_per_frame, stream->timescale);
5737 dts += gst_util_uint64_scale_int (GST_SECOND,
5738 stream->offset_in_sample / stream->bytes_per_frame, stream->timescale);
5739 duration = gst_util_uint64_scale_int (GST_SECOND,
5740 size / stream->bytes_per_frame, stream->timescale);
5743 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
5744 dts, pts, duration, keyframe, min_time, offset);
5746 if (size != sample_size) {
5747 QtDemuxSample *sample = &stream->samples[stream->sample_index];
5748 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
5750 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
5751 sample->timestamp + stream->offset_in_sample / stream->bytes_per_frame);
5752 if (time_position >= segment->media_start) {
5753 /* inside the segment, update time_position, looks very familiar to
5754 * GStreamer segments, doesn't it? */
5755 stream->time_position = (time_position - segment->media_start) +
5758 /* not yet in segment, time does not yet increment. This means
5759 * that we are still prerolling keyframes to the decoder so it can
5760 * decode the first sample of the segment. */
5761 stream->time_position = segment->time;
5766 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
5767 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
5768 * we have no more data for the pad to push */
5769 if (ret == GST_FLOW_EOS)
5772 stream->offset_in_sample += size;
5773 if (stream->offset_in_sample >= sample_size) {
5774 gst_qtdemux_advance_sample (qtdemux, stream);
5779 gst_qtdemux_advance_sample (qtdemux, stream);
5787 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
5793 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
5794 /* EOS will be raised if all are EOS */
5801 gst_qtdemux_loop (GstPad * pad)
5803 GstQTDemux *qtdemux;
5807 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
5809 cur_offset = qtdemux->offset;
5810 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
5811 cur_offset, qtdemux->state);
5813 switch (qtdemux->state) {
5814 case QTDEMUX_STATE_INITIAL:
5815 case QTDEMUX_STATE_HEADER:
5816 ret = gst_qtdemux_loop_state_header (qtdemux);
5818 case QTDEMUX_STATE_MOVIE:
5819 ret = gst_qtdemux_loop_state_movie (qtdemux);
5820 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
5821 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
5829 /* if something went wrong, pause */
5830 if (ret != GST_FLOW_OK)
5834 gst_object_unref (qtdemux);
5840 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
5841 (NULL), ("streaming stopped, invalid state"));
5842 gst_pad_pause_task (pad);
5843 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5848 const gchar *reason = gst_flow_get_name (ret);
5850 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
5852 gst_pad_pause_task (pad);
5854 /* fatal errors need special actions */
5856 if (ret == GST_FLOW_EOS) {
5857 if (qtdemux->n_streams == 0) {
5858 /* we have no streams, post an error */
5859 gst_qtdemux_post_no_playable_stream_error (qtdemux);
5861 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
5864 if ((stop = qtdemux->segment.stop) == -1)
5865 stop = qtdemux->segment.duration;
5867 if (qtdemux->segment.rate >= 0) {
5868 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
5869 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5870 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
5871 GST_FORMAT_TIME, stop));
5872 gst_qtdemux_push_event (qtdemux,
5873 gst_event_new_segment_done (GST_FORMAT_TIME, stop));
5875 /* For Reverse Playback */
5876 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
5877 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5878 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
5879 GST_FORMAT_TIME, qtdemux->segment.start));
5880 gst_qtdemux_push_event (qtdemux,
5881 gst_event_new_segment_done (GST_FORMAT_TIME,
5882 qtdemux->segment.start));
5885 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
5886 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5888 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
5889 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
5890 (NULL), ("streaming stopped, reason %s", reason));
5891 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5900 * Returns if there are samples to be played.
5903 has_next_entry (GstQTDemux * demux)
5905 QtDemuxStream *stream;
5908 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
5910 for (i = 0; i < demux->n_streams; i++) {
5911 stream = demux->streams[i];
5913 if (stream->sample_index == -1) {
5914 stream->sample_index = 0;
5915 stream->offset_in_sample = 0;
5918 if (stream->sample_index >= stream->n_samples) {
5919 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
5922 GST_DEBUG_OBJECT (demux, "Found a sample");
5926 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
5933 * Returns the size of the first entry at the current offset.
5934 * If -1, there are none (which means EOS or empty file).
5937 next_entry_size (GstQTDemux * demux)
5939 QtDemuxStream *stream;
5942 guint64 smalloffs = (guint64) - 1;
5943 QtDemuxSample *sample;
5945 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
5948 for (i = 0; i < demux->n_streams; i++) {
5949 stream = demux->streams[i];
5951 if (stream->sample_index == -1) {
5952 stream->sample_index = 0;
5953 stream->offset_in_sample = 0;
5956 if (stream->sample_index >= stream->n_samples) {
5957 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
5961 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
5962 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
5963 stream->sample_index);
5967 sample = &stream->samples[stream->sample_index];
5969 GST_LOG_OBJECT (demux,
5970 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
5971 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
5972 sample->offset, sample->size);
5974 if (((smalloffs == -1)
5975 || (sample->offset < smalloffs)) && (sample->size)) {
5977 smalloffs = sample->offset;
5981 GST_LOG_OBJECT (demux,
5982 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
5983 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
5988 stream = demux->streams[smallidx];
5989 sample = &stream->samples[stream->sample_index];
5991 if (sample->offset >= demux->offset) {
5992 demux->todrop = sample->offset - demux->offset;
5993 return sample->size + demux->todrop;
5996 GST_DEBUG_OBJECT (demux,
5997 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6002 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6004 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6006 gst_element_post_message (GST_ELEMENT_CAST (demux),
6007 gst_message_new_element (GST_OBJECT_CAST (demux),
6008 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6012 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6017 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6020 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6021 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6022 GST_SEEK_TYPE_NONE, -1);
6024 res = gst_pad_push_event (demux->sinkpad, event);
6029 /* check for seekable upstream, above and beyond a mere query */
6031 gst_qtdemux_check_seekability (GstQTDemux * demux)
6034 gboolean seekable = FALSE;
6035 gint64 start = -1, stop = -1;
6037 if (demux->upstream_size)
6040 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6041 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6042 GST_DEBUG_OBJECT (demux, "seeking query failed");
6046 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
6048 /* try harder to query upstream size if we didn't get it the first time */
6049 if (seekable && stop == -1) {
6050 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
6051 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
6054 /* if upstream doesn't know the size, it's likely that it's not seekable in
6055 * practice even if it technically may be seekable */
6056 if (seekable && (start != 0 || stop <= start)) {
6057 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
6062 gst_query_unref (query);
6064 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
6065 G_GUINT64_FORMAT ")", seekable, start, stop);
6066 demux->upstream_seekable = seekable;
6067 demux->upstream_size = seekable ? stop : -1;
6071 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
6073 g_return_if_fail (bytes <= demux->todrop);
6075 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
6076 gst_adapter_flush (demux->adapter, bytes);
6077 demux->neededbytes -= bytes;
6078 demux->offset += bytes;
6079 demux->todrop -= bytes;
6082 static GstFlowReturn
6083 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
6087 demux = GST_QTDEMUX (parent);
6089 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
6092 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
6094 for (i = 0; i < demux->n_streams; i++) {
6095 demux->streams[i]->discont = TRUE;
6098 /* Reverse fragmented playback, need to flush all we have before
6099 * consuming a new fragment.
6100 * The samples array have the timestamps calculated by accumulating the
6101 * durations but this won't work for reverse playback of fragments as
6102 * the timestamps of a subsequent fragment should be smaller than the
6103 * previously received one. */
6104 if (demux->fragmented && demux->segment.rate < 0) {
6105 gst_qtdemux_process_adapter (demux, TRUE);
6106 for (i = 0; i < demux->n_streams; i++)
6107 gst_qtdemux_stream_flush_samples_data (demux, demux->streams[i]);
6111 gst_adapter_push (demux->adapter, inbuf);
6113 GST_DEBUG_OBJECT (demux,
6114 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
6115 demux->neededbytes, gst_adapter_available (demux->adapter));
6117 return gst_qtdemux_process_adapter (demux, FALSE);
6120 static GstFlowReturn
6121 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
6123 GstFlowReturn ret = GST_FLOW_OK;
6125 /* we never really mean to buffer that much */
6126 if (demux->neededbytes == -1) {
6130 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
6131 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
6133 GST_DEBUG_OBJECT (demux,
6134 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
6135 demux->state, demux->neededbytes, demux->offset);
6137 switch (demux->state) {
6138 case QTDEMUX_STATE_INITIAL:{
6143 gst_qtdemux_check_seekability (demux);
6145 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6147 /* get fourcc/length, set neededbytes */
6148 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
6150 gst_adapter_unmap (demux->adapter);
6152 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
6153 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
6155 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6156 (_("This file is invalid and cannot be played.")),
6157 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
6158 GST_FOURCC_ARGS (fourcc)));
6159 ret = GST_FLOW_ERROR;
6162 if (fourcc == FOURCC_mdat) {
6163 gint next_entry = next_entry_size (demux);
6164 if (demux->n_streams > 0 && (next_entry != -1 || !demux->fragmented)) {
6165 /* we have the headers, start playback */
6166 demux->state = QTDEMUX_STATE_MOVIE;
6167 demux->neededbytes = next_entry;
6168 demux->mdatleft = size;
6170 /* no headers yet, try to get them */
6173 guint64 old, target;
6176 old = demux->offset;
6177 target = old + size;
6179 /* try to jump over the atom with a seek */
6180 /* only bother if it seems worth doing so,
6181 * and avoids possible upstream/server problems */
6182 if (demux->upstream_seekable &&
6183 demux->upstream_size > 4 * (1 << 20)) {
6184 res = qtdemux_seek_offset (demux, target);
6186 GST_DEBUG_OBJECT (demux, "skipping seek");
6191 GST_DEBUG_OBJECT (demux, "seek success");
6192 /* remember the offset fo the first mdat so we can seek back to it
6193 * after we have the headers */
6194 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
6195 demux->first_mdat = old;
6196 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
6199 /* seek worked, continue reading */
6200 demux->offset = target;
6201 demux->neededbytes = 16;
6202 demux->state = QTDEMUX_STATE_INITIAL;
6204 /* seek failed, need to buffer */
6205 demux->offset = old;
6206 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
6207 /* there may be multiple mdat (or alike) buffers */
6209 if (demux->mdatbuffer)
6210 bs = gst_buffer_get_size (demux->mdatbuffer);
6213 if (size + bs > 10 * (1 << 20))
6215 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
6216 demux->neededbytes = size;
6217 if (!demux->mdatbuffer)
6218 demux->mdatoffset = demux->offset;
6221 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
6222 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6223 (_("This file is invalid and cannot be played.")),
6224 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
6225 GST_FOURCC_ARGS (fourcc), size));
6226 ret = GST_FLOW_ERROR;
6229 /* this means we already started buffering and still no moov header,
6230 * let's continue buffering everything till we get moov */
6231 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
6232 || fourcc == FOURCC_moof))
6234 demux->neededbytes = size;
6235 demux->state = QTDEMUX_STATE_HEADER;
6239 case QTDEMUX_STATE_HEADER:{
6243 GST_DEBUG_OBJECT (demux, "In header");
6245 data = gst_adapter_map (demux->adapter, demux->neededbytes);
6247 /* parse the header */
6248 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
6250 if (fourcc == FOURCC_moov) {
6251 /* in usual fragmented setup we could try to scan for more
6252 * and end up at the the moov (after mdat) again */
6253 if (demux->got_moov && demux->n_streams > 0 &&
6255 || demux->last_moov_offset == demux->offset)) {
6256 GST_DEBUG_OBJECT (demux,
6257 "Skipping moov atom as we have (this) one already");
6259 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
6261 if (demux->got_moov && demux->fragmented) {
6262 GST_DEBUG_OBJECT (demux,
6263 "Got a second moov, clean up data from old one");
6264 if (demux->moov_node)
6265 g_node_destroy (demux->moov_node);
6266 demux->moov_node = NULL;
6267 demux->moov_node_compressed = NULL;
6269 /* prepare newsegment to send when streaming actually starts */
6270 if (!demux->pending_newsegment)
6271 demux->pending_newsegment =
6272 gst_event_new_segment (&demux->segment);
6275 demux->last_moov_offset = demux->offset;
6277 qtdemux_parse_moov (demux, data, demux->neededbytes);
6278 qtdemux_node_dump (demux, demux->moov_node);
6279 qtdemux_parse_tree (demux);
6280 qtdemux_prepare_streams (demux);
6281 if (!demux->got_moov)
6282 qtdemux_expose_streams (demux);
6286 for (n = 0; n < demux->n_streams; n++) {
6287 QtDemuxStream *stream = demux->streams[n];
6289 gst_qtdemux_configure_stream (demux, stream);
6293 demux->got_moov = TRUE;
6295 g_node_destroy (demux->moov_node);
6296 demux->moov_node = NULL;
6297 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
6299 } else if (fourcc == FOURCC_moof) {
6300 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
6302 GstClockTime prev_pts;
6303 guint64 prev_offset;
6305 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
6308 * The timestamp of the moof buffer is relevant as some scenarios
6309 * won't have the initial timestamp in the atoms. Whenever a new
6310 * buffer has started, we get that buffer's PTS and use it as a base
6311 * timestamp for the trun entries.
6313 * To keep track of the current buffer timestamp and starting point
6314 * we use gst_adapter_prev_pts that gives us the PTS and the distance
6315 * from the beggining of the buffer, with the distance and demux->offset
6316 * we know if it is still the same buffer or not.
6318 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
6319 prev_offset = demux->offset - dist;
6320 if (demux->fragment_start_offset == -1
6321 || prev_offset > demux->fragment_start_offset) {
6322 demux->fragment_start_offset = prev_offset;
6323 demux->fragment_start = prev_pts;
6324 GST_DEBUG_OBJECT (demux,
6325 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
6326 GST_TIME_FORMAT, demux->fragment_start_offset,
6327 GST_TIME_ARGS (demux->fragment_start));
6330 demux->moof_offset = demux->offset;
6331 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
6332 demux->offset, NULL)) {
6333 gst_adapter_unmap (demux->adapter);
6334 ret = GST_FLOW_ERROR;
6337 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
6338 if (demux->mss_mode && !demux->exposed) {
6339 if (!demux->pending_newsegment) {
6341 gst_segment_init (&segment, GST_FORMAT_TIME);
6342 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
6343 demux->pending_newsegment = gst_event_new_segment (&segment);
6345 qtdemux_expose_streams (demux);
6348 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
6350 } else if (fourcc == FOURCC_ftyp) {
6351 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
6352 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
6353 } else if (fourcc == FOURCC_uuid) {
6354 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
6355 qtdemux_parse_uuid (demux, data, demux->neededbytes);
6356 } else if (fourcc == FOURCC_sidx) {
6357 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
6358 qtdemux_parse_sidx (demux, data, demux->neededbytes);
6360 GST_WARNING_OBJECT (demux,
6361 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
6362 GST_FOURCC_ARGS (fourcc));
6363 /* Let's jump that one and go back to initial state */
6365 gst_adapter_unmap (demux->adapter);
6368 if (demux->mdatbuffer && demux->n_streams) {
6369 gsize remaining_data_size = 0;
6371 /* the mdat was before the header */
6372 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
6373 demux->n_streams, demux->mdatbuffer);
6374 /* restore our adapter/offset view of things with upstream;
6375 * put preceding buffered data ahead of current moov data.
6376 * This should also handle evil mdat, moov, mdat cases and alike */
6377 gst_adapter_flush (demux->adapter, demux->neededbytes);
6379 /* Store any remaining data after the mdat for later usage */
6380 remaining_data_size = gst_adapter_available (demux->adapter);
6381 if (remaining_data_size > 0) {
6382 g_assert (demux->restoredata_buffer == NULL);
6383 demux->restoredata_buffer =
6384 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
6385 demux->restoredata_offset = demux->offset + demux->neededbytes;
6386 GST_DEBUG_OBJECT (demux,
6387 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
6388 G_GUINT64_FORMAT, remaining_data_size,
6389 demux->restoredata_offset);
6392 gst_adapter_push (demux->adapter, demux->mdatbuffer);
6393 demux->mdatbuffer = NULL;
6394 demux->offset = demux->mdatoffset;
6395 demux->neededbytes = next_entry_size (demux);
6396 demux->state = QTDEMUX_STATE_MOVIE;
6397 demux->mdatleft = gst_adapter_available (demux->adapter);
6399 GST_DEBUG_OBJECT (demux, "Carrying on normally");
6400 gst_adapter_flush (demux->adapter, demux->neededbytes);
6402 /* only go back to the mdat if there are samples to play */
6403 if (demux->got_moov && demux->first_mdat != -1
6404 && has_next_entry (demux)) {
6407 /* we need to seek back */
6408 res = qtdemux_seek_offset (demux, demux->first_mdat);
6410 demux->offset = demux->first_mdat;
6412 GST_DEBUG_OBJECT (demux, "Seek back failed");
6415 demux->offset += demux->neededbytes;
6417 demux->neededbytes = 16;
6418 demux->state = QTDEMUX_STATE_INITIAL;
6423 case QTDEMUX_STATE_BUFFER_MDAT:{
6427 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
6429 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
6430 gst_buffer_extract (buf, 0, fourcc, 4);
6431 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
6432 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
6433 if (demux->mdatbuffer)
6434 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
6436 demux->mdatbuffer = buf;
6437 demux->offset += demux->neededbytes;
6438 demux->neededbytes = 16;
6439 demux->state = QTDEMUX_STATE_INITIAL;
6440 gst_qtdemux_post_progress (demux, 1, 1);
6444 case QTDEMUX_STATE_MOVIE:{
6445 QtDemuxStream *stream = NULL;
6446 QtDemuxSample *sample;
6448 GstClockTime dts, pts, duration;
6451 GST_DEBUG_OBJECT (demux,
6452 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
6454 if (demux->fragmented) {
6455 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
6457 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
6458 /* if needed data starts within this atom,
6459 * then it should not exceed this atom */
6460 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
6461 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6462 (_("This file is invalid and cannot be played.")),
6463 ("sample data crosses atom boundary"));
6464 ret = GST_FLOW_ERROR;
6467 demux->mdatleft -= demux->neededbytes;
6469 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
6470 /* so we are dropping more than left in this atom */
6471 gst_qtdemux_drop_data (demux, demux->mdatleft);
6472 demux->mdatleft = 0;
6474 /* need to resume atom parsing so we do not miss any other pieces */
6475 demux->state = QTDEMUX_STATE_INITIAL;
6476 demux->neededbytes = 16;
6478 /* check if there was any stored post mdat data from previous buffers */
6479 if (demux->restoredata_buffer) {
6480 g_assert (gst_adapter_available (demux->adapter) == 0);
6482 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
6483 demux->restoredata_buffer = NULL;
6484 demux->offset = demux->restoredata_offset;
6491 if (demux->todrop) {
6492 gst_qtdemux_drop_data (demux, demux->todrop);
6496 /* initial newsegment sent here after having added pads,
6497 * possible others in sink_event */
6498 if (G_UNLIKELY (demux->pending_newsegment)) {
6499 gst_qtdemux_push_pending_newsegment (demux);
6500 /* clear to send tags on all streams */
6501 for (i = 0; i < demux->n_streams; i++) {
6502 stream = demux->streams[i];
6503 gst_qtdemux_push_tags (demux, stream);
6504 if (stream->sparse) {
6505 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
6506 gst_pad_push_event (stream->pad,
6507 gst_event_new_gap (stream->segment.position,
6508 GST_CLOCK_TIME_NONE));
6513 /* Figure out which stream this packet belongs to */
6514 for (i = 0; i < demux->n_streams; i++) {
6515 stream = demux->streams[i];
6516 if (stream->sample_index >= stream->n_samples)
6518 GST_LOG_OBJECT (demux,
6519 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
6520 " / size:%d)", i, stream->sample_index,
6521 stream->samples[stream->sample_index].offset,
6522 stream->samples[stream->sample_index].size);
6524 if (stream->samples[stream->sample_index].offset == demux->offset)
6528 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
6529 goto unknown_stream;
6531 if (stream->new_caps) {
6532 gst_qtdemux_configure_stream (demux, stream);
6535 /* Put data in a buffer, set timestamps, caps, ... */
6536 sample = &stream->samples[stream->sample_index];
6538 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
6539 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
6540 GST_FOURCC_ARGS (stream->fourcc));
6542 dts = QTSAMPLE_DTS (stream, sample);
6543 pts = QTSAMPLE_PTS (stream, sample);
6544 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
6545 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
6547 /* check for segment end */
6548 if (G_UNLIKELY (demux->segment.stop != -1
6549 && demux->segment.stop <= pts && stream->on_keyframe)) {
6550 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
6551 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
6553 /* skip this data, stream is EOS */
6554 gst_adapter_flush (demux->adapter, demux->neededbytes);
6556 /* check if all streams are eos */
6558 for (i = 0; i < demux->n_streams; i++) {
6559 if (!STREAM_IS_EOS (demux->streams[i])) {
6565 if (ret == GST_FLOW_EOS) {
6566 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
6573 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
6575 /* FIXME: should either be an assert or a plain check */
6576 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
6578 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
6579 dts, pts, duration, keyframe, dts, demux->offset);
6583 ret = gst_qtdemux_combine_flows (demux, stream, ret);
6585 /* skip this data, stream is EOS */
6586 gst_adapter_flush (demux->adapter, demux->neededbytes);
6589 stream->sample_index++;
6590 stream->offset_in_sample = 0;
6592 /* update current offset and figure out size of next buffer */
6593 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
6594 demux->offset, demux->neededbytes);
6595 demux->offset += demux->neededbytes;
6596 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
6599 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
6600 if (demux->fragmented) {
6601 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
6602 /* there may be more to follow, only finish this atom */
6603 demux->todrop = demux->mdatleft;
6604 demux->neededbytes = demux->todrop;
6616 /* when buffering movie data, at least show user something is happening */
6617 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
6618 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
6619 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
6620 demux->neededbytes);
6629 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
6630 ret = GST_FLOW_ERROR;
6635 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
6641 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
6642 (NULL), ("qtdemuxer invalid state %d", demux->state));
6643 ret = GST_FLOW_ERROR;
6648 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
6649 (NULL), ("no 'moov' atom within the first 10 MB"));
6650 ret = GST_FLOW_ERROR;
6656 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
6661 query = gst_query_new_scheduling ();
6663 if (!gst_pad_peer_query (sinkpad, query)) {
6664 gst_query_unref (query);
6668 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
6669 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
6670 gst_query_unref (query);
6675 GST_DEBUG_OBJECT (sinkpad, "activating pull");
6676 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
6680 GST_DEBUG_OBJECT (sinkpad, "activating push");
6681 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
6686 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
6687 GstPadMode mode, gboolean active)
6690 GstQTDemux *demux = GST_QTDEMUX (parent);
6693 case GST_PAD_MODE_PUSH:
6694 demux->pullbased = FALSE;
6697 case GST_PAD_MODE_PULL:
6699 demux->pullbased = TRUE;
6700 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
6703 res = gst_pad_stop_task (sinkpad);
6715 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size)
6717 return g_malloc (items * size);
6721 qtdemux_zfree (void *opaque, void *addr)
6727 qtdemux_inflate (void *z_buffer, guint z_length, guint length)
6733 z = g_new0 (z_stream, 1);
6734 z->zalloc = qtdemux_zalloc;
6735 z->zfree = qtdemux_zfree;
6738 z->next_in = z_buffer;
6739 z->avail_in = z_length;
6741 buffer = (guint8 *) g_malloc (length);
6742 ret = inflateInit (z);
6743 while (z->avail_in > 0) {
6744 if (z->avail_out == 0) {
6746 buffer = (guint8 *) g_realloc (buffer, length);
6747 z->next_out = buffer + z->total_out;
6748 z->avail_out = 1024;
6750 ret = inflate (z, Z_SYNC_FLUSH);
6754 if (ret != Z_STREAM_END) {
6755 g_warning ("inflate() returned %d", ret);
6761 #endif /* HAVE_ZLIB */
6764 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
6768 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
6770 /* counts as header data */
6771 qtdemux->header_size += length;
6773 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
6774 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
6776 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
6782 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
6783 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
6784 if (dcom == NULL || cmvd == NULL)
6785 goto invalid_compression;
6787 method = QT_FOURCC ((guint8 *) dcom->data + 8);
6790 case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{
6791 guint uncompressed_length;
6792 guint compressed_length;
6795 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
6796 compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12;
6797 GST_LOG ("length = %u", uncompressed_length);
6800 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
6801 compressed_length, uncompressed_length);
6803 qtdemux->moov_node_compressed = qtdemux->moov_node;
6804 qtdemux->moov_node = g_node_new (buf);
6806 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
6807 uncompressed_length);
6810 #endif /* HAVE_ZLIB */
6812 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
6813 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
6820 invalid_compression:
6822 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
6828 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
6831 while (G_UNLIKELY (buf < end)) {
6835 if (G_UNLIKELY (buf + 4 > end)) {
6836 GST_LOG_OBJECT (qtdemux, "buffer overrun");
6839 len = QT_UINT32 (buf);
6840 if (G_UNLIKELY (len == 0)) {
6841 GST_LOG_OBJECT (qtdemux, "empty container");
6844 if (G_UNLIKELY (len < 8)) {
6845 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
6848 if (G_UNLIKELY (len > (end - buf))) {
6849 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
6850 (gint) (end - buf));
6854 child = g_node_new ((guint8 *) buf);
6855 g_node_append (node, child);
6856 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
6857 qtdemux_parse_node (qtdemux, child, buf, len);
6865 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
6868 int len = QT_UINT32 (xdxt->data);
6869 guint8 *buf = xdxt->data;
6870 guint8 *end = buf + len;
6873 /* skip size and type */
6881 size = QT_UINT32 (buf);
6882 type = QT_FOURCC (buf + 4);
6884 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
6886 if (buf + size > end || size <= 0)
6892 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
6893 GST_FOURCC_ARGS (type));
6897 buffer = gst_buffer_new_and_alloc (size);
6898 gst_buffer_fill (buffer, 0, buf, size);
6899 stream->buffers = g_slist_append (stream->buffers, buffer);
6900 GST_LOG_OBJECT (qtdemux, "parsing theora header");
6903 buffer = gst_buffer_new_and_alloc (size);
6904 gst_buffer_fill (buffer, 0, buf, size);
6905 stream->buffers = g_slist_append (stream->buffers, buffer);
6906 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
6909 buffer = gst_buffer_new_and_alloc (size);
6910 gst_buffer_fill (buffer, 0, buf, size);
6911 stream->buffers = g_slist_append (stream->buffers, buffer);
6912 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
6915 GST_WARNING_OBJECT (qtdemux,
6916 "unknown theora cookie %" GST_FOURCC_FORMAT,
6917 GST_FOURCC_ARGS (type));
6926 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
6930 guint32 node_length = 0;
6931 const QtNodeType *type;
6934 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
6936 if (G_UNLIKELY (length < 8))
6937 goto not_enough_data;
6939 node_length = QT_UINT32 (buffer);
6940 fourcc = QT_FOURCC (buffer + 4);
6942 /* ignore empty nodes */
6943 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
6946 type = qtdemux_type_get (fourcc);
6948 end = buffer + length;
6950 GST_LOG_OBJECT (qtdemux,
6951 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
6952 GST_FOURCC_ARGS (fourcc), node_length, type->name);
6954 if (node_length > length)
6955 goto broken_atom_size;
6957 if (type->flags & QT_FLAG_CONTAINER) {
6958 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
6963 if (node_length < 20) {
6964 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
6967 GST_DEBUG_OBJECT (qtdemux,
6968 "parsing stsd (sample table, sample description) atom");
6969 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
6970 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
6980 /* also read alac (or whatever) in stead of mp4a in the following,
6981 * since a similar layout is used in other cases as well */
6982 if (fourcc == FOURCC_mp4a)
6987 /* There are two things we might encounter here: a true mp4a atom, and
6988 an mp4a entry in an stsd atom. The latter is what we're interested
6989 in, and it looks like an atom, but isn't really one. The true mp4a
6990 atom is short, so we detect it based on length here. */
6991 if (length < min_size) {
6992 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
6993 GST_FOURCC_ARGS (fourcc));
6997 /* 'version' here is the sound sample description version. Types 0 and
6998 1 are documented in the QTFF reference, but type 2 is not: it's
6999 described in Apple header files instead (struct SoundDescriptionV2
7001 version = QT_UINT16 (buffer + 16);
7003 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
7004 GST_FOURCC_ARGS (fourcc), version);
7006 /* parse any esds descriptors */
7018 GST_WARNING_OBJECT (qtdemux,
7019 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
7020 GST_FOURCC_ARGS (fourcc), version);
7025 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7042 /* codec_data is contained inside these atoms, which all have
7043 * the same format. */
7045 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
7046 GST_FOURCC_ARGS (fourcc));
7047 version = QT_UINT32 (buffer + 16);
7048 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
7049 if (1 || version == 0x00000000) {
7050 buf = buffer + 0x32;
7052 /* FIXME Quicktime uses PASCAL string while
7053 * the iso format uses C strings. Check the file
7054 * type before attempting to parse the string here. */
7055 tlen = QT_UINT8 (buf);
7056 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen);
7058 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf);
7059 /* the string has a reserved space of 32 bytes so skip
7060 * the remaining 31 */
7062 buf += 4; /* and 4 bytes reserved */
7064 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf);
7066 qtdemux_parse_container (qtdemux, node, buf, end);
7072 GST_MEMDUMP_OBJECT (qtdemux, "H264", buffer, end - buffer);
7073 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7078 GST_MEMDUMP_OBJECT (qtdemux, "avc1", buffer, end - buffer);
7079 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7084 GST_MEMDUMP_OBJECT (qtdemux, "avc3", buffer, end - buffer);
7085 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7090 GST_MEMDUMP_OBJECT (qtdemux, "H265", buffer, end - buffer);
7091 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7096 GST_MEMDUMP_OBJECT (qtdemux, "hvc1", buffer, end - buffer);
7097 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7102 GST_MEMDUMP_OBJECT (qtdemux, "hev1", buffer, end - buffer);
7103 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
7108 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
7113 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
7114 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
7119 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
7120 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
7121 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
7129 version = QT_UINT32 (buffer + 12);
7130 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
7137 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
7142 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
7147 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
7152 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
7157 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
7162 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
7165 #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
7168 qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
7171 #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
7173 if (!strcmp (type->name, "unknown"))
7174 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
7178 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
7179 GST_FOURCC_ARGS (fourcc));
7185 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7186 (_("This file is corrupt and cannot be played.")),
7187 ("Not enough data for an atom header, got only %u bytes", length));
7192 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7193 (_("This file is corrupt and cannot be played.")),
7194 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
7195 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
7202 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
7206 guint32 child_fourcc;
7208 for (child = g_node_first_child (node); child;
7209 child = g_node_next_sibling (child)) {
7210 buffer = (guint8 *) child->data;
7212 child_fourcc = QT_FOURCC (buffer + 4);
7214 if (G_UNLIKELY (child_fourcc == fourcc)) {
7222 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
7223 GstByteReader * parser)
7227 guint32 child_fourcc, child_len;
7229 for (child = g_node_first_child (node); child;
7230 child = g_node_next_sibling (child)) {
7231 buffer = (guint8 *) child->data;
7233 child_len = QT_UINT32 (buffer);
7234 child_fourcc = QT_FOURCC (buffer + 4);
7236 if (G_UNLIKELY (child_fourcc == fourcc)) {
7237 if (G_UNLIKELY (child_len < (4 + 4)))
7239 /* FIXME: must verify if atom length < parent atom length */
7240 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
7248 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
7249 GstByteReader * parser)
7253 guint32 child_fourcc, child_len;
7255 for (child = g_node_next_sibling (node); child;
7256 child = g_node_next_sibling (child)) {
7257 buffer = (guint8 *) child->data;
7259 child_fourcc = QT_FOURCC (buffer + 4);
7261 if (child_fourcc == fourcc) {
7263 child_len = QT_UINT32 (buffer);
7264 if (G_UNLIKELY (child_len < (4 + 4)))
7266 /* FIXME: must verify if atom length < parent atom length */
7267 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
7276 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
7278 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
7282 qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
7284 /* FIXME: This can only reliably work if demuxers have a
7285 * separate streaming thread per srcpad. This should be
7286 * done in a demuxer base class, which integrates parts
7289 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
7294 query = gst_query_new_allocation (stream->caps, FALSE);
7296 if (!gst_pad_peer_query (stream->pad, query)) {
7297 /* not a problem, just debug a little */
7298 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
7301 if (stream->allocator)
7302 gst_object_unref (stream->allocator);
7304 if (gst_query_get_n_allocation_params (query) > 0) {
7305 /* try the allocator */
7306 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
7308 stream->use_allocator = TRUE;
7310 stream->allocator = NULL;
7311 gst_allocation_params_init (&stream->params);
7312 stream->use_allocator = FALSE;
7314 gst_query_unref (query);
7319 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
7320 QtDemuxStream * stream)
7323 const gchar *selected_system;
7325 g_return_val_if_fail (qtdemux != NULL, FALSE);
7326 g_return_val_if_fail (stream != NULL, FALSE);
7327 g_return_val_if_fail (gst_caps_get_size (stream->caps) == 1, FALSE);
7329 if (stream->protection_scheme_type != FOURCC_cenc) {
7330 GST_ERROR_OBJECT (qtdemux, "unsupported protection scheme");
7333 if (qtdemux->protection_system_ids == NULL) {
7334 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
7335 "cenc protection system information has been found");
7338 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
7339 selected_system = gst_protection_select_system ((const gchar **)
7340 qtdemux->protection_system_ids->pdata);
7341 g_ptr_array_remove_index (qtdemux->protection_system_ids,
7342 qtdemux->protection_system_ids->len - 1);
7343 if (!selected_system) {
7344 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
7345 "suitable decryptor element has been found");
7349 s = gst_caps_get_structure (stream->caps, 0);
7350 gst_structure_set (s,
7351 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
7352 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
7354 gst_structure_set_name (s, "application/x-cenc");
7359 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
7361 if (stream->subtype == FOURCC_vide) {
7362 /* fps is calculated base on the duration of the average framerate since
7363 * qt does not have a fixed framerate. */
7364 if ((stream->n_samples == 1) && (stream->first_duration == 0)) {
7369 if (stream->duration == 0 || stream->n_samples < 2) {
7370 stream->fps_n = stream->timescale;
7373 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
7374 /* stream->duration is guint64, timescale, n_samples are guint32 */
7375 GstClockTime avg_duration =
7376 gst_util_uint64_scale_round (stream->duration -
7377 stream->first_duration, GST_SECOND,
7378 (guint64) (stream->timescale) * (stream->n_samples - 1));
7380 GST_LOG_OBJECT (qtdemux,
7381 "Calculating avg sample duration based on stream duration %"
7383 " minus first sample %u, leaving %d samples gives %"
7384 GST_TIME_FORMAT, stream->duration, stream->first_duration,
7385 stream->n_samples - 1, GST_TIME_ARGS (avg_duration));
7387 gst_video_guess_framerate (avg_duration, &stream->fps_n,
7390 GST_DEBUG_OBJECT (qtdemux,
7391 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
7392 stream->timescale, stream->fps_n, stream->fps_d);
7396 stream->caps = gst_caps_make_writable (stream->caps);
7398 gst_caps_set_simple (stream->caps,
7399 "width", G_TYPE_INT, stream->width,
7400 "height", G_TYPE_INT, stream->height,
7401 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
7403 /* calculate pixel-aspect-ratio using display width and height */
7404 GST_DEBUG_OBJECT (qtdemux,
7405 "video size %dx%d, target display size %dx%d", stream->width,
7406 stream->height, stream->display_width, stream->display_height);
7407 /* qt file might have pasp atom */
7408 if (stream->par_w > 0 && stream->par_h > 0) {
7409 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
7410 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
7411 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
7412 } else if (stream->display_width > 0 && stream->display_height > 0 &&
7413 stream->width > 0 && stream->height > 0) {
7416 /* calculate the pixel aspect ratio using the display and pixel w/h */
7417 n = stream->display_width * stream->height;
7418 d = stream->display_height * stream->width;
7421 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
7424 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
7425 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
7428 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
7429 guint par_w = 1, par_h = 1;
7431 if (stream->par_w > 0 && stream->par_h > 0) {
7432 par_w = stream->par_w;
7433 par_h = stream->par_h;
7436 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
7437 stream->width, stream->height, par_w, par_h)) {
7438 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
7441 gst_caps_set_simple (stream->caps,
7442 "multiview-mode", G_TYPE_STRING,
7443 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
7444 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
7445 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
7450 else if (stream->subtype == FOURCC_soun) {
7452 stream->caps = gst_caps_make_writable (stream->caps);
7453 if (stream->rate > 0)
7454 gst_caps_set_simple (stream->caps,
7455 "rate", G_TYPE_INT, (int) stream->rate, NULL);
7456 if (stream->n_channels > 0)
7457 gst_caps_set_simple (stream->caps,
7458 "channels", G_TYPE_INT, stream->n_channels, NULL);
7459 if (stream->n_channels > 2) {
7460 /* FIXME: Need to parse the 'chan' atom to get channel layouts
7461 * correctly; this is just the minimum we can do - assume
7462 * we don't actually have any channel positions. */
7463 gst_caps_set_simple (stream->caps,
7464 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
7470 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
7471 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
7472 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
7473 gst_pad_set_active (stream->pad, TRUE);
7475 gst_pad_use_fixed_caps (stream->pad);
7477 if (stream->protected) {
7478 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
7479 GST_ERROR_OBJECT (qtdemux,
7480 "Failed to configure protected stream caps.");
7485 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
7486 if (stream->new_stream) {
7489 GstStreamFlags stream_flags;
7492 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
7495 if (gst_event_parse_group_id (event, &qtdemux->group_id))
7496 qtdemux->have_group_id = TRUE;
7498 qtdemux->have_group_id = FALSE;
7499 gst_event_unref (event);
7500 } else if (!qtdemux->have_group_id) {
7501 qtdemux->have_group_id = TRUE;
7502 qtdemux->group_id = gst_util_group_id_next ();
7505 stream->new_stream = FALSE;
7507 gst_pad_create_stream_id_printf (stream->pad,
7508 GST_ELEMENT_CAST (qtdemux), "%03u", stream->track_id);
7509 event = gst_event_new_stream_start (stream_id);
7510 if (qtdemux->have_group_id)
7511 gst_event_set_group_id (event, qtdemux->group_id);
7512 stream_flags = GST_STREAM_FLAG_NONE;
7513 if (stream->disabled)
7514 stream_flags |= GST_STREAM_FLAG_UNSELECT;
7516 stream_flags |= GST_STREAM_FLAG_SPARSE;
7517 gst_event_set_stream_flags (event, stream_flags);
7518 gst_pad_push_event (stream->pad, event);
7521 gst_pad_set_caps (stream->pad, stream->caps);
7522 stream->new_caps = FALSE;
7528 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
7529 QtDemuxStream * stream, GstTagList * list)
7531 /* consistent default for push based mode */
7532 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
7534 if (stream->subtype == FOURCC_vide) {
7535 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
7538 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
7541 gst_qtdemux_configure_stream (qtdemux, stream);
7542 qtdemux->n_video_streams++;
7543 } else if (stream->subtype == FOURCC_soun) {
7544 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
7547 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
7549 gst_qtdemux_configure_stream (qtdemux, stream);
7550 qtdemux->n_audio_streams++;
7551 } else if (stream->subtype == FOURCC_strm) {
7552 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
7553 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
7554 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
7555 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
7558 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
7560 gst_qtdemux_configure_stream (qtdemux, stream);
7561 qtdemux->n_sub_streams++;
7562 } else if (stream->caps) {
7563 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
7566 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
7568 gst_qtdemux_configure_stream (qtdemux, stream);
7569 qtdemux->n_video_streams++;
7571 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
7578 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
7579 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
7580 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
7581 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
7583 if (stream->pending_tags)
7584 gst_tag_list_unref (stream->pending_tags);
7585 stream->pending_tags = list;
7587 /* global tags go on each pad anyway */
7588 stream->send_global_tags = TRUE;
7589 /* send upstream GST_EVENT_PROTECTION events that were received before
7590 this source pad was created */
7591 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
7592 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
7596 gst_tag_list_unref (list);
7600 /* find next atom with @fourcc starting at @offset */
7601 static GstFlowReturn
7602 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
7603 guint64 * length, guint32 fourcc)
7609 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
7610 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
7616 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
7617 if (G_UNLIKELY (ret != GST_FLOW_OK))
7619 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
7622 gst_buffer_unref (buf);
7625 gst_buffer_map (buf, &map, GST_MAP_READ);
7626 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
7627 gst_buffer_unmap (buf, &map);
7628 gst_buffer_unref (buf);
7630 if (G_UNLIKELY (*length == 0)) {
7631 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
7632 ret = GST_FLOW_ERROR;
7636 if (lfourcc == fourcc) {
7637 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
7641 GST_LOG_OBJECT (qtdemux,
7642 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
7643 GST_FOURCC_ARGS (fourcc), *offset);
7652 /* might simply have had last one */
7653 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
7658 /* should only do something in pull mode */
7659 /* call with OBJECT lock */
7660 static GstFlowReturn
7661 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
7663 guint64 length, offset;
7664 GstBuffer *buf = NULL;
7665 GstFlowReturn ret = GST_FLOW_OK;
7666 GstFlowReturn res = GST_FLOW_OK;
7669 offset = qtdemux->moof_offset;
7670 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
7673 GST_DEBUG_OBJECT (qtdemux, "no next moof");
7674 return GST_FLOW_EOS;
7677 /* best not do pull etc with lock held */
7678 GST_OBJECT_UNLOCK (qtdemux);
7680 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
7681 if (ret != GST_FLOW_OK)
7684 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
7685 if (G_UNLIKELY (ret != GST_FLOW_OK))
7687 gst_buffer_map (buf, &map, GST_MAP_READ);
7688 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
7689 gst_buffer_unmap (buf, &map);
7690 gst_buffer_unref (buf);
7695 gst_buffer_unmap (buf, &map);
7696 gst_buffer_unref (buf);
7700 /* look for next moof */
7701 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
7702 if (G_UNLIKELY (ret != GST_FLOW_OK))
7706 GST_OBJECT_LOCK (qtdemux);
7708 qtdemux->moof_offset = offset;
7714 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
7716 res = GST_FLOW_ERROR;
7721 /* maybe upstream temporarily flushing */
7722 if (ret != GST_FLOW_FLUSHING) {
7723 GST_DEBUG_OBJECT (qtdemux, "no next moof");
7726 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
7727 /* resume at current position next time */
7734 /* initialise bytereaders for stbl sub-atoms */
7736 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
7738 stream->stbl_index = -1; /* no samples have yet been parsed */
7739 stream->sample_index = -1;
7741 /* time-to-sample atom */
7742 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
7745 /* copy atom data into a new buffer for later use */
7746 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
7748 /* skip version + flags */
7749 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
7750 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
7752 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
7754 /* make sure there's enough data */
7755 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
7756 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
7757 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
7758 stream->n_sample_times);
7759 if (!stream->n_sample_times)
7763 /* sync sample atom */
7764 stream->stps_present = FALSE;
7765 if ((stream->stss_present =
7766 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
7767 &stream->stss) ? TRUE : FALSE) == TRUE) {
7768 /* copy atom data into a new buffer for later use */
7769 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
7771 /* skip version + flags */
7772 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
7773 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
7776 if (stream->n_sample_syncs) {
7777 /* make sure there's enough data */
7778 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
7782 /* partial sync sample atom */
7783 if ((stream->stps_present =
7784 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
7785 &stream->stps) ? TRUE : FALSE) == TRUE) {
7786 /* copy atom data into a new buffer for later use */
7787 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
7789 /* skip version + flags */
7790 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
7791 !gst_byte_reader_get_uint32_be (&stream->stps,
7792 &stream->n_sample_partial_syncs))
7795 /* if there are no entries, the stss table contains the real
7797 if (stream->n_sample_partial_syncs) {
7798 /* make sure there's enough data */
7799 if (!qt_atom_parser_has_chunks (&stream->stps,
7800 stream->n_sample_partial_syncs, 4))
7807 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
7810 /* copy atom data into a new buffer for later use */
7811 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
7813 /* skip version + flags */
7814 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
7815 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
7818 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
7821 if (!stream->n_samples)
7824 /* sample-to-chunk atom */
7825 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
7828 /* copy atom data into a new buffer for later use */
7829 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
7831 /* skip version + flags */
7832 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
7833 !gst_byte_reader_get_uint32_be (&stream->stsc,
7834 &stream->n_samples_per_chunk))
7837 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
7838 stream->n_samples_per_chunk);
7840 /* make sure there's enough data */
7841 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
7847 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
7848 stream->co_size = sizeof (guint32);
7849 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
7851 stream->co_size = sizeof (guint64);
7855 /* copy atom data into a new buffer for later use */
7856 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
7858 /* skip version + flags */
7859 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
7862 /* chunks_are_samples == TRUE means treat chunks as samples */
7863 stream->chunks_are_samples = stream->sample_size && !stream->sampled;
7864 if (stream->chunks_are_samples) {
7865 /* treat chunks as samples */
7866 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
7869 /* skip number of entries */
7870 if (!gst_byte_reader_skip (&stream->stco, 4))
7873 /* make sure there are enough data in the stsz atom */
7874 if (!stream->sample_size) {
7875 /* different sizes for each sample */
7876 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
7881 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
7882 stream->n_samples, (guint) sizeof (QtDemuxSample),
7883 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
7885 if (stream->n_samples >=
7886 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
7887 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
7888 "be larger than %uMB (broken file?)", stream->n_samples,
7889 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
7893 g_assert (stream->samples == NULL);
7894 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
7895 if (!stream->samples) {
7896 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
7901 /* composition time-to-sample */
7902 if ((stream->ctts_present =
7903 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
7904 &stream->ctts) ? TRUE : FALSE) == TRUE) {
7905 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
7907 /* copy atom data into a new buffer for later use */
7908 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
7910 /* skip version + flags */
7911 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
7912 || !gst_byte_reader_get_uint32_be (&stream->ctts,
7913 &stream->n_composition_times))
7916 /* make sure there's enough data */
7917 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
7921 /* This is optional, if missing we iterate the ctts */
7922 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
7923 if (!gst_byte_reader_skip (&cslg, 1 + 3)
7924 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
7925 g_free ((gpointer) cslg.data);
7929 gint32 cslg_least = 0;
7930 guint num_entries, pos;
7933 pos = gst_byte_reader_get_pos (&stream->ctts);
7934 num_entries = stream->n_composition_times;
7936 stream->cslg_shift = 0;
7938 for (i = 0; i < num_entries; i++) {
7941 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
7942 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
7944 if (offset < cslg_least)
7945 cslg_least = offset;
7949 stream->cslg_shift = ABS (cslg_least);
7951 stream->cslg_shift = 0;
7953 /* reset the reader so we can generate sample table */
7954 gst_byte_reader_set_pos (&stream->ctts, pos);
7957 /* Ensure the cslg_shift value is consistent so we can use it
7958 * unconditionnally to produce TS and Segment */
7959 stream->cslg_shift = 0;
7966 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7967 (_("This file is corrupt and cannot be played.")), (NULL));
7972 gst_qtdemux_stbl_free (stream);
7973 if (!qtdemux->fragmented) {
7974 /* not quite good */
7975 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
7978 /* may pick up samples elsewhere */
7984 /* collect samples from the next sample to be parsed up to sample @n for @stream
7985 * by reading the info from @stbl
7987 * This code can be executed from both the streaming thread and the seeking
7988 * thread so it takes the object lock to protect itself
7991 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
7994 QtDemuxSample *samples, *first, *cur, *last;
7995 guint32 n_samples_per_chunk;
7998 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
7999 GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc),
8000 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
8002 n_samples = stream->n_samples;
8005 goto out_of_samples;
8007 GST_OBJECT_LOCK (qtdemux);
8008 if (n <= stream->stbl_index)
8009 goto already_parsed;
8011 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
8013 if (!stream->stsz.data) {
8014 /* so we already parsed and passed all the moov samples;
8015 * onto fragmented ones */
8016 g_assert (qtdemux->fragmented);
8020 /* pointer to the sample table */
8021 samples = stream->samples;
8023 /* starts from -1, moves to the next sample index to parse */
8024 stream->stbl_index++;
8026 /* keep track of the first and last sample to fill */
8027 first = &samples[stream->stbl_index];
8030 if (!stream->chunks_are_samples) {
8031 /* set the sample sizes */
8032 if (stream->sample_size == 0) {
8033 /* different sizes for each sample */
8034 for (cur = first; cur <= last; cur++) {
8035 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
8036 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
8037 (guint) (cur - samples), cur->size);
8040 /* samples have the same size */
8041 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
8042 for (cur = first; cur <= last; cur++)
8043 cur->size = stream->sample_size;
8047 n_samples_per_chunk = stream->n_samples_per_chunk;
8050 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
8053 if (stream->stsc_chunk_index >= stream->last_chunk
8054 || stream->stsc_chunk_index < stream->first_chunk) {
8055 stream->first_chunk =
8056 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
8057 stream->samples_per_chunk =
8058 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
8059 gst_byte_reader_skip_unchecked (&stream->stsc, 4);
8061 /* chunk numbers are counted from 1 it seems */
8062 if (G_UNLIKELY (stream->first_chunk == 0))
8065 --stream->first_chunk;
8067 /* the last chunk of each entry is calculated by taking the first chunk
8068 * of the next entry; except if there is no next, where we fake it with
8070 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
8071 stream->last_chunk = G_MAXUINT32;
8073 stream->last_chunk =
8074 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
8075 if (G_UNLIKELY (stream->last_chunk == 0))
8078 --stream->last_chunk;
8081 GST_LOG_OBJECT (qtdemux,
8082 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i,
8083 stream->first_chunk, stream->last_chunk, stream->samples_per_chunk);
8085 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
8088 if (stream->last_chunk != G_MAXUINT32) {
8089 if (!qt_atom_parser_peek_sub (&stream->stco,
8090 stream->first_chunk * stream->co_size,
8091 (stream->last_chunk - stream->first_chunk) * stream->co_size,
8096 stream->co_chunk = stream->stco;
8097 if (!gst_byte_reader_skip (&stream->co_chunk,
8098 stream->first_chunk * stream->co_size))
8102 stream->stsc_chunk_index = stream->first_chunk;
8105 last_chunk = stream->last_chunk;
8107 if (stream->chunks_are_samples) {
8108 cur = &samples[stream->stsc_chunk_index];
8110 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
8113 stream->stsc_chunk_index = j;
8118 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
8121 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
8122 "%" G_GUINT64_FORMAT, j, cur->offset);
8124 if (stream->samples_per_frame * stream->bytes_per_frame) {
8126 (stream->samples_per_chunk * stream->n_channels) /
8127 stream->samples_per_frame * stream->bytes_per_frame;
8129 cur->size = stream->samples_per_chunk;
8132 GST_DEBUG_OBJECT (qtdemux,
8133 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
8134 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
8135 stream->stco_sample_index)), cur->size);
8137 cur->timestamp = stream->stco_sample_index;
8138 cur->duration = stream->samples_per_chunk;
8139 cur->keyframe = TRUE;
8142 stream->stco_sample_index += stream->samples_per_chunk;
8144 stream->stsc_chunk_index = j;
8146 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
8147 guint32 samples_per_chunk;
8148 guint64 chunk_offset;
8150 if (!stream->stsc_sample_index
8151 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
8152 &stream->chunk_offset))
8155 samples_per_chunk = stream->samples_per_chunk;
8156 chunk_offset = stream->chunk_offset;
8158 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
8159 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
8160 G_GUINT64_FORMAT " and size %d",
8161 (guint) (cur - samples), chunk_offset, cur->size);
8163 cur->offset = chunk_offset;
8164 chunk_offset += cur->size;
8167 if (G_UNLIKELY (cur > last)) {
8169 stream->stsc_sample_index = k + 1;
8170 stream->chunk_offset = chunk_offset;
8171 stream->stsc_chunk_index = j;
8175 stream->stsc_sample_index = 0;
8177 stream->stsc_chunk_index = j;
8179 stream->stsc_index++;
8182 if (stream->chunks_are_samples)
8186 guint32 n_sample_times;
8188 n_sample_times = stream->n_sample_times;
8191 for (i = stream->stts_index; i < n_sample_times; i++) {
8192 guint32 stts_samples;
8193 gint32 stts_duration;
8196 if (stream->stts_sample_index >= stream->stts_samples
8197 || !stream->stts_sample_index) {
8199 stream->stts_samples =
8200 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
8201 stream->stts_duration =
8202 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
8204 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
8205 i, stream->stts_samples, stream->stts_duration);
8207 stream->stts_sample_index = 0;
8210 stts_samples = stream->stts_samples;
8211 stts_duration = stream->stts_duration;
8212 stts_time = stream->stts_time;
8214 for (j = stream->stts_sample_index; j < stts_samples; j++) {
8215 GST_DEBUG_OBJECT (qtdemux,
8216 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
8217 (guint) (cur - samples), j,
8218 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
8220 cur->timestamp = stts_time;
8221 cur->duration = stts_duration;
8223 /* avoid 32-bit wrap-around,
8224 * but still mind possible 'negative' duration */
8225 stts_time += (gint64) stts_duration;
8228 if (G_UNLIKELY (cur > last)) {
8230 stream->stts_time = stts_time;
8231 stream->stts_sample_index = j + 1;
8235 stream->stts_sample_index = 0;
8236 stream->stts_time = stts_time;
8237 stream->stts_index++;
8239 /* fill up empty timestamps with the last timestamp, this can happen when
8240 * the last samples do not decode and so we don't have timestamps for them.
8241 * We however look at the last timestamp to estimate the track length so we
8242 * need something in here. */
8243 for (; cur < last; cur++) {
8244 GST_DEBUG_OBJECT (qtdemux,
8245 "fill sample %d: timestamp %" GST_TIME_FORMAT,
8246 (guint) (cur - samples),
8247 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
8248 cur->timestamp = stream->stts_time;
8254 /* sample sync, can be NULL */
8255 if (stream->stss_present == TRUE) {
8256 guint32 n_sample_syncs;
8258 n_sample_syncs = stream->n_sample_syncs;
8260 if (!n_sample_syncs) {
8261 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
8262 stream->all_keyframe = TRUE;
8264 for (i = stream->stss_index; i < n_sample_syncs; i++) {
8265 /* note that the first sample is index 1, not 0 */
8268 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
8270 if (G_LIKELY (index > 0 && index <= n_samples)) {
8272 samples[index].keyframe = TRUE;
8273 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
8274 /* and exit if we have enough samples */
8275 if (G_UNLIKELY (index >= n)) {
8282 stream->stss_index = i;
8285 /* stps marks partial sync frames like open GOP I-Frames */
8286 if (stream->stps_present == TRUE) {
8287 guint32 n_sample_partial_syncs;
8289 n_sample_partial_syncs = stream->n_sample_partial_syncs;
8291 /* if there are no entries, the stss table contains the real
8293 if (n_sample_partial_syncs) {
8294 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
8295 /* note that the first sample is index 1, not 0 */
8298 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
8300 if (G_LIKELY (index > 0 && index <= n_samples)) {
8302 samples[index].keyframe = TRUE;
8303 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
8304 /* and exit if we have enough samples */
8305 if (G_UNLIKELY (index >= n)) {
8312 stream->stps_index = i;
8316 /* no stss, all samples are keyframes */
8317 stream->all_keyframe = TRUE;
8318 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
8323 /* composition time to sample */
8324 if (stream->ctts_present == TRUE) {
8325 guint32 n_composition_times;
8327 gint32 ctts_soffset;
8329 /* Fill in the pts_offsets */
8331 n_composition_times = stream->n_composition_times;
8333 for (i = stream->ctts_index; i < n_composition_times; i++) {
8334 if (stream->ctts_sample_index >= stream->ctts_count
8335 || !stream->ctts_sample_index) {
8336 stream->ctts_count =
8337 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
8338 stream->ctts_soffset =
8339 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
8340 stream->ctts_sample_index = 0;
8343 ctts_count = stream->ctts_count;
8344 ctts_soffset = stream->ctts_soffset;
8346 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
8347 cur->pts_offset = ctts_soffset;
8350 if (G_UNLIKELY (cur > last)) {
8352 stream->ctts_sample_index = j + 1;
8356 stream->ctts_sample_index = 0;
8357 stream->ctts_index++;
8361 stream->stbl_index = n;
8362 /* if index has been completely parsed, free data that is no-longer needed */
8363 if (n + 1 == stream->n_samples) {
8364 gst_qtdemux_stbl_free (stream);
8365 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
8366 if (qtdemux->pullbased) {
8367 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
8368 while (n + 1 == stream->n_samples)
8369 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
8373 GST_OBJECT_UNLOCK (qtdemux);
8380 GST_LOG_OBJECT (qtdemux,
8381 "Tried to parse up to sample %u but this sample has already been parsed",
8383 /* if fragmented, there may be more */
8384 if (qtdemux->fragmented && n == stream->stbl_index)
8386 GST_OBJECT_UNLOCK (qtdemux);
8392 GST_LOG_OBJECT (qtdemux,
8393 "Tried to parse up to sample %u but there are only %u samples", n + 1,
8395 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8396 (_("This file is corrupt and cannot be played.")), (NULL));
8401 GST_OBJECT_UNLOCK (qtdemux);
8402 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8403 (_("This file is corrupt and cannot be played.")), (NULL));
8408 /* collect all segment info for @stream.
8411 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
8416 /* parse and prepare segment info from the edit list */
8417 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
8418 stream->n_segments = 0;
8419 stream->segments = NULL;
8420 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
8428 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
8429 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
8432 buffer = elst->data;
8434 n_segments = QT_UINT32 (buffer + 12);
8436 /* we might allocate a bit too much, at least allocate 1 segment */
8437 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
8439 /* segments always start from 0 */
8443 for (i = 0; i < n_segments; i++) {
8446 QtDemuxSegment *segment;
8448 #ifdef TIZEN_FEATURE_GST_UPSTREAM
8449 GstClockTime media_start = GST_CLOCK_TIME_NONE;
8452 media_time = QT_UINT32 (buffer + 20 + i * 12);
8453 duration = QT_UINT32 (buffer + 16 + i * 12);
8455 #ifdef TIZEN_FEATURE_GST_UPSTREAM
8456 if (media_time != G_MAXUINT32)
8457 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
8460 segment = &stream->segments[count++];
8462 /* time and duration expressed in global timescale */
8463 segment->time = stime;
8464 /* add non scaled values so we don't cause roundoff errors */
8465 #ifdef TIZEN_FEATURE_GST_UPSTREAM
8466 if (duration || media_start == GST_CLOCK_TIME_NONE) {
8468 stime = QTTIME_TO_GSTTIME (qtdemux, time);
8469 segment->duration = stime - segment->time;
8471 /* zero duration does not imply media_start == media_stop
8472 * but, only specify media_start.*/
8473 stime = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
8474 if (GST_CLOCK_TIME_IS_VALID (stime) && media_time != G_MAXUINT32
8475 && stime >= media_start) {
8476 segment->duration = stime - media_start;
8478 segment->duration = GST_CLOCK_TIME_NONE;
8483 stime = QTTIME_TO_GSTTIME (qtdemux, time);
8485 segment->stop_time = stime;
8487 #ifndef TIZEN_FEATURE_GST_UPSTREAM
8488 segment->duration = stime - segment->time;
8491 segment->trak_media_start = media_time;
8492 /* media_time expressed in stream timescale */
8493 if (media_time != G_MAXUINT32) {
8494 #ifdef TIZEN_FEATURE_GST_UPSTREAM
8495 segment->media_start = media_start;
8497 segment->media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
8499 segment->media_stop = segment->media_start + segment->duration;
8501 segment->media_start = GST_CLOCK_TIME_NONE;
8502 segment->media_stop = GST_CLOCK_TIME_NONE;
8504 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
8506 if (rate_int <= 1) {
8507 /* 0 is not allowed, some programs write 1 instead of the floating point
8509 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
8513 segment->rate = rate_int / 65536.0;
8516 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
8517 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
8518 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
8519 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
8520 i, GST_TIME_ARGS (segment->time),
8521 GST_TIME_ARGS (segment->duration),
8522 GST_TIME_ARGS (segment->media_start), media_time,
8523 GST_TIME_ARGS (segment->media_stop),
8524 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
8526 if (segment->stop_time > qtdemux->segment.stop) {
8527 GST_WARNING_OBJECT (qtdemux, "Segment %d "
8528 " extends to %" GST_TIME_FORMAT
8529 " past the end of the file duration %" GST_TIME_FORMAT
8530 " it will be truncated", i, GST_TIME_ARGS (segment->stop_time),
8531 GST_TIME_ARGS (qtdemux->segment.stop));
8532 qtdemux->segment.stop = segment->stop_time;
8535 GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
8536 stream->n_segments = count;
8540 /* push based does not handle segments, so act accordingly here,
8541 * and warn if applicable */
8542 if (!qtdemux->pullbased) {
8543 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
8544 /* remove and use default one below, we stream like it anyway */
8545 g_free (stream->segments);
8546 stream->segments = NULL;
8547 stream->n_segments = 0;
8550 /* no segments, create one to play the complete trak */
8551 if (stream->n_segments == 0) {
8552 GstClockTime stream_duration =
8553 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
8555 if (stream->segments == NULL)
8556 stream->segments = g_new (QtDemuxSegment, 1);
8558 /* represent unknown our way */
8559 if (stream_duration == 0)
8560 stream_duration = GST_CLOCK_TIME_NONE;
8562 stream->segments[0].time = 0;
8563 stream->segments[0].stop_time = stream_duration;
8564 stream->segments[0].duration = stream_duration;
8565 stream->segments[0].media_start = 0;
8566 stream->segments[0].media_stop = stream_duration;
8567 stream->segments[0].rate = 1.0;
8568 stream->segments[0].trak_media_start = 0;
8570 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
8571 GST_TIME_ARGS (stream_duration));
8572 stream->n_segments = 1;
8573 stream->dummy_segment = TRUE;
8575 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
8581 * Parses the stsd atom of a svq3 trak looking for
8582 * the SMI and gama atoms.
8585 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
8586 guint8 ** gamma, GstBuffer ** seqh)
8588 guint8 *_gamma = NULL;
8589 GstBuffer *_seqh = NULL;
8590 guint8 *stsd_data = stsd->data;
8591 guint32 length = QT_UINT32 (stsd_data);
8595 GST_WARNING_OBJECT (qtdemux, "stsd too short");
8601 version = QT_UINT16 (stsd_data);
8606 while (length > 8) {
8607 guint32 fourcc, size;
8609 size = QT_UINT32 (stsd_data);
8610 fourcc = QT_FOURCC (stsd_data + 4);
8611 data = stsd_data + 8;
8614 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
8615 "svq3 atom parsing");
8624 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
8625 " for gama atom, expected 12", size);
8630 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
8632 if (_seqh != NULL) {
8633 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
8634 " found, ignoring");
8636 seqh_size = QT_UINT32 (data + 4);
8637 if (seqh_size > 0) {
8638 _seqh = gst_buffer_new_and_alloc (seqh_size);
8639 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
8646 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
8647 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
8651 if (size <= length) {
8657 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
8660 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
8661 G_GUINT16_FORMAT, version);
8672 gst_buffer_unref (_seqh);
8677 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
8684 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
8685 * atom that might contain a 'data' atom with the rtsp uri.
8686 * This case was reported in bug #597497, some info about
8687 * the hndl atom can be found in TN1195
8689 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
8690 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
8693 guint32 dref_num_entries = 0;
8694 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
8695 gst_byte_reader_skip (&dref, 4) &&
8696 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
8699 /* search dref entries for hndl atom */
8700 for (i = 0; i < dref_num_entries; i++) {
8701 guint32 size = 0, type;
8702 guint8 string_len = 0;
8703 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
8704 qt_atom_parser_get_fourcc (&dref, &type)) {
8705 if (type == FOURCC_hndl) {
8706 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
8708 /* skip data reference handle bytes and the
8709 * following pascal string and some extra 4
8710 * bytes I have no idea what are */
8711 if (!gst_byte_reader_skip (&dref, 4) ||
8712 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
8713 !gst_byte_reader_skip (&dref, string_len + 4)) {
8714 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
8718 /* iterate over the atoms to find the data atom */
8719 while (gst_byte_reader_get_remaining (&dref) >= 8) {
8723 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
8724 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
8725 if (atom_type == FOURCC_data) {
8726 const guint8 *uri_aux = NULL;
8728 /* found the data atom that might contain the rtsp uri */
8729 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
8730 "hndl atom, interpreting it as an URI");
8731 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
8733 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
8734 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
8736 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
8737 "didn't contain a rtsp address");
8739 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
8744 /* skipping to the next entry */
8745 if (!gst_byte_reader_skip (&dref, atom_size - 8))
8748 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
8755 /* skip to the next entry */
8756 if (!gst_byte_reader_skip (&dref, size - 8))
8759 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
8762 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
8768 #define AMR_NB_ALL_MODES 0x81ff
8769 #define AMR_WB_ALL_MODES 0x83ff
8771 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
8773 /* The 'damr' atom is of the form:
8775 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
8776 * 32 b 8 b 16 b 8 b 8 b
8778 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
8779 * represents the highest mode used in the stream (and thus the maximum
8780 * bitrate), with a couple of special cases as seen below.
8783 /* Map of frame type ID -> bitrate */
8784 static const guint nb_bitrates[] = {
8785 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
8787 static const guint wb_bitrates[] = {
8788 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
8794 gst_buffer_map (buf, &map, GST_MAP_READ);
8796 if (map.size != 0x11) {
8797 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
8801 if (QT_FOURCC (map.data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
8802 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
8803 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
8807 mode_set = QT_UINT16 (map.data + 13);
8809 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
8810 max_mode = 7 + (wb ? 1 : 0);
8812 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
8813 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
8815 if (max_mode == -1) {
8816 GST_DEBUG ("No mode indication was found (mode set) = %x",
8821 gst_buffer_unmap (buf, &map);
8822 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
8825 gst_buffer_unmap (buf, &map);
8830 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
8831 GstByteReader * reader, guint32 * matrix, const gchar * atom)
8834 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
8840 if (gst_byte_reader_get_remaining (reader) < 36)
8843 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
8844 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
8845 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
8846 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
8847 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
8848 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
8849 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
8850 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
8851 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
8853 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
8854 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
8855 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
8857 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
8858 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
8860 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
8861 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
8868 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
8869 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
8876 * This macro will only compare value abdegh, it expects cfi to have already
8879 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
8880 (m)[3] == (d << 16) && (m)[4] == (e << 16))
8882 /* only handle the cases where the last column has standard values */
8883 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
8884 const gchar *rotation_tag = NULL;
8886 /* no rotation needed */
8887 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
8889 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
8890 rotation_tag = "rotate-90";
8891 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
8892 rotation_tag = "rotate-180";
8893 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
8894 rotation_tag = "rotate-270";
8896 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
8899 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
8901 if (rotation_tag != NULL) {
8902 if (*taglist == NULL)
8903 *taglist = gst_tag_list_new_empty ();
8904 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
8905 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
8908 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
8912 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
8913 * protected streams (sinf, frma, schm and schi); if the protection scheme is
8914 * Common Encryption (cenc), the function will also parse the tenc box (defined
8915 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
8916 * (typically an enc[v|a|t|s] sample entry); the function will set
8917 * @original_fmt to the fourcc of the original unencrypted stream format.
8918 * Returns TRUE if successful; FALSE otherwise. */
8920 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
8921 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
8928 g_return_val_if_fail (qtdemux != NULL, FALSE);
8929 g_return_val_if_fail (stream != NULL, FALSE);
8930 g_return_val_if_fail (container != NULL, FALSE);
8931 g_return_val_if_fail (original_fmt != NULL, FALSE);
8933 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
8934 if (G_UNLIKELY (!sinf)) {
8935 if (stream->protection_scheme_type == FOURCC_cenc) {
8936 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
8937 "mandatory for Common Encryption");
8943 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
8944 if (G_UNLIKELY (!frma)) {
8945 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
8949 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
8950 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
8951 GST_FOURCC_ARGS (*original_fmt));
8953 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
8955 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
8958 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
8959 stream->protection_scheme_version =
8960 QT_UINT32 ((const guint8 *) schm->data + 16);
8962 GST_DEBUG_OBJECT (qtdemux,
8963 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
8964 "protection_scheme_version: %#010x",
8965 GST_FOURCC_ARGS (stream->protection_scheme_type),
8966 stream->protection_scheme_version);
8968 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
8970 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
8973 if (stream->protection_scheme_type == FOURCC_cenc) {
8974 QtDemuxCencSampleSetInfo *info;
8976 const guint8 *tenc_data;
8977 guint32 isEncrypted;
8979 const guint8 *default_kid;
8982 if (G_UNLIKELY (!stream->protection_scheme_info))
8983 stream->protection_scheme_info =
8984 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
8986 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
8988 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
8990 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
8991 "which is mandatory for Common Encryption");
8994 tenc_data = (const guint8 *) tenc->data + 12;
8995 isEncrypted = QT_UINT24 (tenc_data);
8996 iv_size = QT_UINT8 (tenc_data + 3);
8997 default_kid = (tenc_data + 4);
8998 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
8999 gst_buffer_fill (kid_buf, 0, default_kid, 16);
9000 if (info->default_properties)
9001 gst_structure_free (info->default_properties);
9002 info->default_properties =
9003 gst_structure_new ("application/x-cenc",
9004 "iv_size", G_TYPE_UINT, iv_size,
9005 "encrypted", G_TYPE_BOOLEAN, (isEncrypted == 1),
9006 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
9007 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
9008 "is_encrypted=%u, iv_size=%u", isEncrypted, iv_size);
9009 gst_buffer_unref (kid_buf);
9015 * With each track we associate a new QtDemuxStream that contains all the info
9017 * traks that do not decode to something (like strm traks) will not have a pad.
9020 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
9039 QtDemuxStream *stream = NULL;
9040 gboolean new_stream = FALSE;
9041 gchar *codec = NULL;
9042 const guint8 *stsd_data;
9043 guint16 lang_code; /* quicktime lang code or packed iso code */
9045 guint32 tkhd_flags = 0;
9046 guint8 tkhd_version = 0;
9048 guint value_size, stsd_len, len;
9051 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
9053 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
9054 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
9055 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
9058 /* pick between 64 or 32 bits */
9059 value_size = tkhd_version == 1 ? 8 : 4;
9060 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
9061 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
9064 if (!qtdemux->got_moov) {
9065 if (qtdemux_find_stream (qtdemux, track_id))
9066 goto existing_stream;
9067 stream = _create_stream ();
9068 stream->track_id = track_id;
9071 stream = qtdemux_find_stream (qtdemux, track_id);
9073 GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
9077 /* flush samples data from this track from previous moov */
9078 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
9079 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
9082 if (stream->pending_tags == NULL)
9083 stream->pending_tags = gst_tag_list_new_empty ();
9085 if ((tkhd_flags & 1) == 0)
9086 stream->disabled = TRUE;
9088 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
9089 tkhd_version, tkhd_flags, stream->track_id);
9091 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
9094 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
9095 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
9096 if (qtdemux->major_brand != FOURCC_mjp2 ||
9097 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
9101 len = QT_UINT32 ((guint8 *) mdhd->data);
9102 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
9103 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
9104 if (version == 0x01000000) {
9107 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
9108 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
9109 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
9113 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
9114 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
9115 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
9118 if (lang_code < 0x400) {
9119 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
9120 } else if (lang_code == 0x7fff) {
9121 stream->lang_id[0] = 0; /* unspecified */
9123 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
9124 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
9125 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
9126 stream->lang_id[3] = 0;
9129 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
9131 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
9133 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
9134 lang_code, stream->lang_id);
9136 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
9139 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
9140 /* chapters track reference */
9141 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
9143 gsize length = GST_READ_UINT32_BE (chap->data);
9144 if (qtdemux->chapters_track_id)
9145 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
9148 qtdemux->chapters_track_id =
9149 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
9154 /* fragmented files may have bogus duration in moov */
9155 if (!qtdemux->fragmented &&
9156 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
9157 guint64 tdur1, tdur2;
9159 /* don't overflow */
9160 tdur1 = stream->timescale * (guint64) qtdemux->duration;
9161 tdur2 = qtdemux->timescale * (guint64) stream->duration;
9164 * some of those trailers, nowadays, have prologue images that are
9165 * themselves vide tracks as well. I haven't really found a way to
9166 * identify those yet, except for just looking at their duration. */
9167 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
9168 GST_WARNING_OBJECT (qtdemux,
9169 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
9170 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
9171 "found, assuming preview image or something; skipping track",
9172 stream->duration, stream->timescale, qtdemux->duration,
9173 qtdemux->timescale);
9175 gst_qtdemux_stream_free (qtdemux, stream);
9180 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
9183 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
9184 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
9186 len = QT_UINT32 ((guint8 *) hdlr->data);
9188 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
9189 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
9190 GST_FOURCC_ARGS (stream->subtype));
9192 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
9195 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
9198 /*parse svmi header if existing */
9199 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
9201 len = QT_UINT32 ((guint8 *) svmi->data);
9202 version = QT_UINT32 ((guint8 *) svmi->data + 8);
9204 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
9205 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
9206 guint8 frame_type, frame_layout;
9208 /* MPEG-A stereo video */
9209 if (qtdemux->major_brand == FOURCC_ss02)
9210 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
9212 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
9213 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
9214 switch (frame_type) {
9216 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
9219 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
9222 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
9225 /* mode 3 is primary/secondary view sequence, ie
9226 * left/right views in separate tracks. See section 7.2
9227 * of ISO/IEC 23000-11:2009 */
9228 GST_FIXME_OBJECT (qtdemux,
9229 "Implement stereo video in separate streams");
9232 if ((frame_layout & 0x1) == 0)
9233 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
9235 GST_LOG_OBJECT (qtdemux,
9236 "StereoVideo: composition type: %u, is_left_first: %u",
9237 frame_type, frame_layout);
9238 stream->multiview_mode = mode;
9239 stream->multiview_flags = flags;
9244 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
9246 stsd_data = (const guint8 *) stsd->data;
9248 /* stsd should at least have one entry */
9249 stsd_len = QT_UINT32 (stsd_data);
9250 if (stsd_len < 24) {
9251 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
9252 if (stream->subtype == FOURCC_vivo) {
9254 gst_qtdemux_stream_free (qtdemux, stream);
9261 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
9263 /* and that entry should fit within stsd */
9264 len = QT_UINT32 (stsd_data + 16);
9265 if (len > stsd_len + 16)
9268 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
9269 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
9270 GST_FOURCC_ARGS (stream->fourcc));
9271 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
9273 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
9274 goto error_encrypted;
9276 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
9277 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
9278 stream->protected = TRUE;
9279 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
9280 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
9283 if (stream->subtype == FOURCC_vide) {
9284 guint32 w = 0, h = 0;
9286 gint depth, palette_size, palette_count;
9288 guint32 *palette_data = NULL;
9290 stream->sampled = TRUE;
9292 /* version 1 uses some 64-bit ints */
9293 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
9296 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
9299 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
9300 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
9303 stream->display_width = w >> 16;
9304 stream->display_height = h >> 16;
9306 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
9307 &stream->pending_tags);
9313 stream->width = QT_UINT16 (stsd_data + offset + 32);
9314 stream->height = QT_UINT16 (stsd_data + offset + 34);
9315 stream->fps_n = 0; /* this is filled in later */
9316 stream->fps_d = 0; /* this is filled in later */
9317 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
9318 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);
9320 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
9321 stream->width, stream->height, stream->bits_per_sample,
9322 stream->color_table_id);
9324 depth = stream->bits_per_sample;
9326 /* more than 32 bits means grayscale */
9327 gray = (depth > 32);
9328 /* low 32 bits specify the depth */
9331 /* different number of palette entries is determined by depth. */
9333 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
9334 palette_count = (1 << depth);
9335 palette_size = palette_count * 4;
9337 if (stream->color_table_id) {
9338 switch (palette_count) {
9342 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
9345 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
9349 palette_data = g_memdup (ff_qt_grayscale_palette_16, palette_size);
9351 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
9355 palette_data = g_memdup (ff_qt_grayscale_palette_256, palette_size);
9357 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
9360 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
9361 (_("The video in this file might not play correctly.")),
9362 ("unsupported palette depth %d", depth));
9366 gint i, j, start, end;
9372 start = QT_UINT32 (stsd_data + offset + 86);
9373 palette_count = QT_UINT16 (stsd_data + offset + 90);
9374 end = QT_UINT16 (stsd_data + offset + 92);
9376 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
9377 start, end, palette_count);
9384 if (len < 94 + (end - start) * 8)
9387 /* palette is always the same size */
9388 palette_data = g_malloc0 (256 * 4);
9389 palette_size = 256 * 4;
9391 for (j = 0, i = start; i <= end; j++, i++) {
9394 a = QT_UINT16 (stsd_data + offset + 94 + (j * 8));
9395 r = QT_UINT16 (stsd_data + offset + 96 + (j * 8));
9396 g = QT_UINT16 (stsd_data + offset + 98 + (j * 8));
9397 b = QT_UINT16 (stsd_data + offset + 100 + (j * 8));
9399 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
9400 (g & 0xff00) | (b >> 8);
9405 gst_caps_unref (stream->caps);
9408 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
9409 if (G_UNLIKELY (!stream->caps)) {
9410 g_free (palette_data);
9411 goto unknown_stream;
9415 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9416 GST_TAG_VIDEO_CODEC, codec, NULL);
9425 if (stream->rgb8_palette)
9426 gst_memory_unref (stream->rgb8_palette);
9427 stream->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
9428 palette_data, palette_size, 0, palette_size, palette_data, g_free);
9430 s = gst_caps_get_structure (stream->caps, 0);
9432 /* non-raw video has a palette_data property. raw video has the palette as
9433 * an extra plane that we append to the output buffers before we push
9435 if (!gst_structure_has_name (s, "video/x-raw")) {
9438 palette = gst_buffer_new ();
9439 gst_buffer_append_memory (palette, stream->rgb8_palette);
9440 stream->rgb8_palette = NULL;
9442 gst_caps_set_simple (stream->caps, "palette_data",
9443 GST_TYPE_BUFFER, palette, NULL);
9444 gst_buffer_unref (palette);
9446 } else if (palette_count != 0) {
9447 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
9448 (NULL), ("Unsupported palette depth %d", depth));
9451 GST_LOG_OBJECT (qtdemux, "frame count: %u",
9452 QT_UINT16 (stsd_data + offset + 48));
9456 /* pick 'the' stsd child */
9457 if (!stream->protected)
9458 mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc);
9460 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_encv);
9463 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
9464 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
9468 const guint8 *pasp_data = (const guint8 *) pasp->data;
9470 stream->par_w = QT_UINT32 (pasp_data + 8);
9471 stream->par_h = QT_UINT32 (pasp_data + 12);
9478 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
9485 gint len = QT_UINT32 (stsd_data) - 0x66;
9486 const guint8 *avc_data = stsd_data + 0x66;
9489 while (len >= 0x8) {
9492 if (QT_UINT32 (avc_data) <= len)
9493 size = QT_UINT32 (avc_data) - 0x8;
9498 /* No real data, so break out */
9501 switch (QT_FOURCC (avc_data + 0x4)) {
9504 /* parse, if found */
9507 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
9509 /* First 4 bytes are the length of the atom, the next 4 bytes
9510 * are the fourcc, the next 1 byte is the version, and the
9511 * subsequent bytes are profile_tier_level structure like data. */
9512 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
9513 avc_data + 8 + 1, size - 1);
9514 buf = gst_buffer_new_and_alloc (size);
9515 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
9516 gst_caps_set_simple (stream->caps,
9517 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9518 gst_buffer_unref (buf);
9526 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
9528 /* First 4 bytes are the length of the atom, the next 4 bytes
9529 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
9530 * next 1 byte is the version, and the
9531 * subsequent bytes are sequence parameter set like data. */
9533 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
9535 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
9536 avc_data + 8 + 40 + 1, size - 1);
9538 buf = gst_buffer_new_and_alloc (size);
9539 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
9540 gst_caps_set_simple (stream->caps,
9541 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9542 gst_buffer_unref (buf);
9548 guint avg_bitrate, max_bitrate;
9550 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
9554 max_bitrate = QT_UINT32 (avc_data + 0xc);
9555 avg_bitrate = QT_UINT32 (avc_data + 0x10);
9557 if (!max_bitrate && !avg_bitrate)
9560 /* Some muxers seem to swap the average and maximum bitrates
9561 * (I'm looking at you, YouTube), so we swap for sanity. */
9562 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
9563 guint temp = avg_bitrate;
9565 avg_bitrate = max_bitrate;
9569 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
9570 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9571 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
9573 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
9574 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9575 GST_TAG_BITRATE, avg_bitrate, NULL);
9586 avc_data += size + 8;
9595 gint len = QT_UINT32 (stsd_data) - 0x66;
9596 const guint8 *hevc_data = stsd_data + 0x66;
9599 while (len >= 0x8) {
9602 if (QT_UINT32 (hevc_data) <= len)
9603 size = QT_UINT32 (hevc_data) - 0x8;
9608 /* No real data, so break out */
9611 switch (QT_FOURCC (hevc_data + 0x4)) {
9614 /* parse, if found */
9617 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
9619 /* First 4 bytes are the length of the atom, the next 4 bytes
9620 * are the fourcc, the next 1 byte is the version, and the
9621 * subsequent bytes are sequence parameter set like data. */
9622 gst_codec_utils_h265_caps_set_level_tier_and_profile
9623 (stream->caps, hevc_data + 8 + 1, size - 1);
9625 buf = gst_buffer_new_and_alloc (size);
9626 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
9627 gst_caps_set_simple (stream->caps,
9628 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9629 gst_buffer_unref (buf);
9636 hevc_data += size + 8;
9647 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
9648 GST_FOURCC_ARGS (fourcc));
9650 /* codec data might be in glbl extension atom */
9652 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
9658 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
9660 len = QT_UINT32 (data);
9663 buf = gst_buffer_new_and_alloc (len);
9664 gst_buffer_fill (buf, 0, data + 8, len);
9665 gst_caps_set_simple (stream->caps,
9666 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9667 gst_buffer_unref (buf);
9674 /* see annex I of the jpeg2000 spec */
9675 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
9677 const gchar *colorspace = NULL;
9679 guint32 ncomp_map = 0;
9680 gint32 *comp_map = NULL;
9681 guint32 nchan_def = 0;
9682 gint32 *chan_def = NULL;
9684 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
9685 /* some required atoms */
9686 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
9689 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
9693 /* number of components; redundant with info in codestream, but useful
9695 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
9696 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
9698 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
9700 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
9703 GST_DEBUG_OBJECT (qtdemux, "found colr");
9704 /* extract colour space info */
9705 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
9706 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
9708 colorspace = "sRGB";
9711 colorspace = "GRAY";
9714 colorspace = "sYUV";
9722 /* colr is required, and only values 16, 17, and 18 are specified,
9723 so error if we have no colorspace */
9726 /* extract component mapping */
9727 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
9729 guint32 cmap_len = 0;
9731 cmap_len = QT_UINT32 (cmap->data);
9732 if (cmap_len >= 8) {
9733 /* normal box, subtract off header */
9735 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
9736 if (cmap_len % 4 == 0) {
9737 ncomp_map = (cmap_len / 4);
9738 comp_map = g_new0 (gint32, ncomp_map);
9739 for (i = 0; i < ncomp_map; i++) {
9742 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
9743 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
9744 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
9745 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
9750 /* extract channel definitions */
9751 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
9753 guint32 cdef_len = 0;
9755 cdef_len = QT_UINT32 (cdef->data);
9756 if (cdef_len >= 10) {
9757 /* normal box, subtract off header and len */
9759 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
9760 if (cdef_len % 6 == 0) {
9761 nchan_def = (cdef_len / 6);
9762 chan_def = g_new0 (gint32, nchan_def);
9763 for (i = 0; i < nchan_def; i++)
9765 for (i = 0; i < nchan_def; i++) {
9766 guint16 cn, typ, asoc;
9767 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
9768 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
9769 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
9770 if (cn < nchan_def) {
9773 chan_def[cn] = asoc;
9776 chan_def[cn] = 0; /* alpha */
9779 chan_def[cn] = -typ;
9787 gst_caps_set_simple (stream->caps,
9788 "num-components", G_TYPE_INT, ncomp, NULL);
9789 gst_caps_set_simple (stream->caps,
9790 "colorspace", G_TYPE_STRING, colorspace, NULL);
9793 GValue arr = { 0, };
9794 GValue elt = { 0, };
9796 g_value_init (&arr, GST_TYPE_ARRAY);
9797 g_value_init (&elt, G_TYPE_INT);
9798 for (i = 0; i < ncomp_map; i++) {
9799 g_value_set_int (&elt, comp_map[i]);
9800 gst_value_array_append_value (&arr, &elt);
9802 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
9803 "component-map", &arr);
9804 g_value_unset (&elt);
9805 g_value_unset (&arr);
9810 GValue arr = { 0, };
9811 GValue elt = { 0, };
9813 g_value_init (&arr, GST_TYPE_ARRAY);
9814 g_value_init (&elt, G_TYPE_INT);
9815 for (i = 0; i < nchan_def; i++) {
9816 g_value_set_int (&elt, chan_def[i]);
9817 gst_value_array_append_value (&arr, &elt);
9819 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
9820 "channel-definitions", &arr);
9821 g_value_unset (&elt);
9822 g_value_unset (&arr);
9826 /* some optional atoms */
9827 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
9828 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
9830 /* indicate possible fields in caps */
9832 data = (guint8 *) field->data + 8;
9834 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
9835 (gint) * data, NULL);
9837 /* add codec_data if provided */
9842 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
9843 data = prefix->data;
9844 len = QT_UINT32 (data);
9847 buf = gst_buffer_new_and_alloc (len);
9848 gst_buffer_fill (buf, 0, data + 8, len);
9849 gst_caps_set_simple (stream->caps,
9850 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9851 gst_buffer_unref (buf);
9860 GstBuffer *seqh = NULL;
9861 guint8 *gamma_data = NULL;
9862 gint len = QT_UINT32 (stsd_data);
9864 qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
9866 gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
9867 QT_FP32 (gamma_data), NULL);
9870 /* sorry for the bad name, but we don't know what this is, other
9871 * than its own fourcc */
9872 gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
9876 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
9877 buf = gst_buffer_new_and_alloc (len);
9878 gst_buffer_fill (buf, 0, stsd_data, len);
9879 gst_caps_set_simple (stream->caps,
9880 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9881 gst_buffer_unref (buf);
9887 gst_caps_set_simple (stream->caps,
9888 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
9895 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
9896 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
9900 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
9904 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
9905 /* collect the headers and store them in a stream list so that we can
9906 * send them out first */
9907 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
9917 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
9918 ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
9921 ovc1_data = ovc1->data;
9922 ovc1_len = QT_UINT32 (ovc1_data);
9923 if (ovc1_len <= 198) {
9924 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
9927 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
9928 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
9929 gst_caps_set_simple (stream->caps,
9930 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9931 gst_buffer_unref (buf);
9934 case GST_MAKE_FOURCC ('v', 'c', '-', '1'):
9936 gint len = QT_UINT32 (stsd_data) - 0x66;
9937 const guint8 *vc1_data = stsd_data + 0x66;
9943 if (QT_UINT32 (vc1_data) <= len)
9944 size = QT_UINT32 (vc1_data) - 8;
9949 /* No real data, so break out */
9952 switch (QT_FOURCC (vc1_data + 0x4)) {
9953 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
9957 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
9958 buf = gst_buffer_new_and_alloc (size);
9959 gst_buffer_fill (buf, 0, vc1_data + 8, size);
9960 gst_caps_set_simple (stream->caps,
9961 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9962 gst_buffer_unref (buf);
9969 vc1_data += size + 8;
9978 GST_INFO_OBJECT (qtdemux,
9979 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
9980 GST_FOURCC_ARGS (fourcc), stream->caps);
9982 } else if (stream->subtype == FOURCC_soun) {
9983 int version, samplesize;
9984 guint16 compression_id;
9985 gboolean amrwb = FALSE;
9988 /* sample description entry (16) + sound sample description v0 (20) */
9992 version = QT_UINT32 (stsd_data + offset);
9993 stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
9994 samplesize = QT_UINT16 (stsd_data + offset + 10);
9995 compression_id = QT_UINT16 (stsd_data + offset + 12);
9996 stream->rate = QT_FP32 (stsd_data + offset + 16);
9998 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
9999 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
10000 QT_UINT32 (stsd_data + offset + 4));
10001 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
10002 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
10003 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
10004 GST_LOG_OBJECT (qtdemux, "packet size: %d",
10005 QT_UINT16 (stsd_data + offset + 14));
10006 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
10008 if (compression_id == 0xfffe)
10009 stream->sampled = TRUE;
10011 /* first assume uncompressed audio */
10012 stream->bytes_per_sample = samplesize / 8;
10013 stream->samples_per_frame = stream->n_channels;
10014 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
10015 stream->samples_per_packet = stream->samples_per_frame;
10016 stream->bytes_per_packet = stream->bytes_per_sample;
10020 /* Yes, these have to be hard-coded */
10023 stream->samples_per_packet = 6;
10024 stream->bytes_per_packet = 1;
10025 stream->bytes_per_frame = 1 * stream->n_channels;
10026 stream->bytes_per_sample = 1;
10027 stream->samples_per_frame = 6 * stream->n_channels;
10032 stream->samples_per_packet = 3;
10033 stream->bytes_per_packet = 1;
10034 stream->bytes_per_frame = 1 * stream->n_channels;
10035 stream->bytes_per_sample = 1;
10036 stream->samples_per_frame = 3 * stream->n_channels;
10041 stream->samples_per_packet = 64;
10042 stream->bytes_per_packet = 34;
10043 stream->bytes_per_frame = 34 * stream->n_channels;
10044 stream->bytes_per_sample = 2;
10045 stream->samples_per_frame = 64 * stream->n_channels;
10051 stream->samples_per_packet = 1;
10052 stream->bytes_per_packet = 1;
10053 stream->bytes_per_frame = 1 * stream->n_channels;
10054 stream->bytes_per_sample = 1;
10055 stream->samples_per_frame = 1 * stream->n_channels;
10060 stream->samples_per_packet = 160;
10061 stream->bytes_per_packet = 33;
10062 stream->bytes_per_frame = 33 * stream->n_channels;
10063 stream->bytes_per_sample = 2;
10064 stream->samples_per_frame = 160 * stream->n_channels;
10071 if (version == 0x00010000) {
10072 /* sample description entry (16) + sound sample description v1 (20+16) */
10083 /* only parse extra decoding config for non-pcm audio */
10084 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
10085 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
10086 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
10087 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);
10089 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
10090 stream->samples_per_packet);
10091 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
10092 stream->bytes_per_packet);
10093 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
10094 stream->bytes_per_frame);
10095 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
10096 stream->bytes_per_sample);
10098 if (!stream->sampled && stream->bytes_per_packet) {
10099 stream->samples_per_frame = (stream->bytes_per_frame /
10100 stream->bytes_per_packet) * stream->samples_per_packet;
10101 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
10102 stream->samples_per_frame);
10107 } else if (version == 0x00020000) {
10114 /* sample description entry (16) + sound sample description v2 (56) */
10118 qtfp.val = QT_UINT64 (stsd_data + offset + 4);
10119 stream->rate = qtfp.fp;
10120 stream->n_channels = QT_UINT32 (stsd_data + offset + 12);
10122 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
10123 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
10124 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
10125 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
10126 QT_UINT32 (stsd_data + offset + 20));
10127 GST_LOG_OBJECT (qtdemux, "format flags: %X",
10128 QT_UINT32 (stsd_data + offset + 24));
10129 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
10130 QT_UINT32 (stsd_data + offset + 28));
10131 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
10132 QT_UINT32 (stsd_data + offset + 32));
10133 } else if (version != 0x00000) {
10134 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x", version);
10138 gst_caps_unref (stream->caps);
10140 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc,
10141 stsd_data + 32, len - 16, &codec);
10149 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
10151 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
10153 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
10155 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
10158 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
10159 gst_caps_set_simple (stream->caps,
10160 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE", NULL);
10167 const guint8 *owma_data;
10168 const gchar *codec_name = NULL;
10172 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
10173 /* FIXME this should also be gst_riff_strf_auds,
10174 * but the latter one is actually missing bits-per-sample :( */
10179 gint32 nSamplesPerSec;
10180 gint32 nAvgBytesPerSec;
10181 gint16 nBlockAlign;
10182 gint16 wBitsPerSample;
10185 WAVEFORMATEX *wfex;
10187 GST_DEBUG_OBJECT (qtdemux, "parse owma");
10188 owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
10191 owma_data = owma->data;
10192 owma_len = QT_UINT32 (owma_data);
10193 if (owma_len <= 54) {
10194 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
10197 wfex = (WAVEFORMATEX *) (owma_data + 36);
10198 buf = gst_buffer_new_and_alloc (owma_len - 54);
10199 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
10200 if (wfex->wFormatTag == 0x0161) {
10201 codec_name = "Windows Media Audio";
10203 } else if (wfex->wFormatTag == 0x0162) {
10204 codec_name = "Windows Media Audio 9 Pro";
10206 } else if (wfex->wFormatTag == 0x0163) {
10207 codec_name = "Windows Media Audio 9 Lossless";
10208 /* is that correct? gstffmpegcodecmap.c is missing it, but
10209 * fluendo codec seems to support it */
10213 gst_caps_set_simple (stream->caps,
10214 "codec_data", GST_TYPE_BUFFER, buf,
10215 "wmaversion", G_TYPE_INT, version,
10216 "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
10217 "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
10218 "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
10219 "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
10221 gst_buffer_unref (buf);
10225 codec = g_strdup (codec_name);
10229 case GST_MAKE_FOURCC ('w', 'm', 'a', ' '):
10231 gint len = QT_UINT32 (stsd_data) - offset;
10232 const guint8 *wfex_data = stsd_data + offset;
10233 const gchar *codec_name = NULL;
10235 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
10236 /* FIXME this should also be gst_riff_strf_auds,
10237 * but the latter one is actually missing bits-per-sample :( */
10242 gint32 nSamplesPerSec;
10243 gint32 nAvgBytesPerSec;
10244 gint16 nBlockAlign;
10245 gint16 wBitsPerSample;
10250 /* FIXME: unify with similar wavformatex parsing code above */
10251 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
10257 if (QT_UINT32 (wfex_data) <= len)
10258 size = QT_UINT32 (wfex_data) - 8;
10263 /* No real data, so break out */
10266 switch (QT_FOURCC (wfex_data + 4)) {
10267 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
10269 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
10274 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
10275 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
10276 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
10277 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
10278 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
10279 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
10280 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
10282 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
10283 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
10284 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
10285 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
10286 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
10287 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
10289 if (wfex.wFormatTag == 0x0161) {
10290 codec_name = "Windows Media Audio";
10292 } else if (wfex.wFormatTag == 0x0162) {
10293 codec_name = "Windows Media Audio 9 Pro";
10295 } else if (wfex.wFormatTag == 0x0163) {
10296 codec_name = "Windows Media Audio 9 Lossless";
10297 /* is that correct? gstffmpegcodecmap.c is missing it, but
10298 * fluendo codec seems to support it */
10302 gst_caps_set_simple (stream->caps,
10303 "wmaversion", G_TYPE_INT, version,
10304 "block_align", G_TYPE_INT, wfex.nBlockAlign,
10305 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
10306 "width", G_TYPE_INT, wfex.wBitsPerSample,
10307 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
10309 if (size > wfex.cbSize) {
10312 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
10313 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
10314 size - wfex.cbSize);
10315 gst_caps_set_simple (stream->caps,
10316 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10317 gst_buffer_unref (buf);
10319 GST_WARNING_OBJECT (qtdemux, "no codec data");
10324 codec = g_strdup (codec_name);
10332 wfex_data += size + 8;
10344 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10345 GST_TAG_AUDIO_CODEC, codec, NULL);
10349 /* some bitrate info may have ended up in caps */
10350 s = gst_caps_get_structure (stream->caps, 0);
10351 gst_structure_get_int (s, "bitrate", &bitrate);
10353 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10354 GST_TAG_BITRATE, bitrate, NULL);
10357 if (stream->protected && fourcc == FOURCC_mp4a)
10358 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_enca);
10360 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
10365 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
10367 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
10369 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
10373 /* If the fourcc's bottom 16 bits gives 'sm', then the top
10374 16 bits is a byte-swapped wave-style codec identifier,
10375 and we can find a WAVE header internally to a 'wave' atom here.
10376 This can more clearly be thought of as 'ms' as the top 16 bits, and a
10377 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
10380 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
10381 if (len < offset + 20) {
10382 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
10384 guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
10385 const guint8 *data = stsd_data + offset + 16;
10387 GNode *waveheadernode;
10389 wavenode = g_node_new ((guint8 *) data);
10390 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
10391 const guint8 *waveheader;
10394 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
10395 if (waveheadernode) {
10396 waveheader = (const guint8 *) waveheadernode->data;
10397 headerlen = QT_UINT32 (waveheader);
10399 if (headerlen > 8) {
10400 gst_riff_strf_auds *header = NULL;
10401 GstBuffer *headerbuf;
10407 headerbuf = gst_buffer_new_and_alloc (headerlen);
10408 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
10410 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
10411 headerbuf, &header, &extra)) {
10412 gst_caps_unref (stream->caps);
10413 /* FIXME: Need to do something with the channel reorder map */
10414 stream->caps = gst_riff_create_audio_caps (header->format, NULL,
10415 header, extra, NULL, NULL, NULL);
10418 gst_buffer_unref (extra);
10423 GST_DEBUG ("Didn't find waveheadernode for this codec");
10425 g_node_destroy (wavenode);
10428 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
10432 /* FIXME: what is in the chunk? */
10435 gint len = QT_UINT32 (stsd_data);
10437 /* seems to be always = 116 = 0x74 */
10443 gint len = QT_UINT32 (stsd_data);
10446 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
10448 gst_buffer_fill (buf, 0, stsd_data + 0x4C, len - 0x4C);
10449 gst_caps_set_simple (stream->caps,
10450 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10451 gst_buffer_unref (buf);
10453 gst_caps_set_simple (stream->caps,
10454 "samplesize", G_TYPE_INT, samplesize, NULL);
10459 GNode *alac, *wave = NULL;
10461 /* apparently, m4a has this atom appended directly in the stsd entry,
10462 * while mov has it in a wave atom */
10463 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
10465 /* alac now refers to stsd entry atom */
10466 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
10468 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
10470 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
10473 const guint8 *alac_data = alac->data;
10474 gint len = QT_UINT32 (alac->data);
10478 GST_DEBUG_OBJECT (qtdemux,
10479 "discarding alac atom with unexpected len %d", len);
10481 /* codec-data contains alac atom size and prefix,
10482 * ffmpeg likes it that way, not quite gst-ish though ...*/
10483 buf = gst_buffer_new_and_alloc (len);
10484 gst_buffer_fill (buf, 0, alac->data, len);
10485 gst_caps_set_simple (stream->caps,
10486 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10487 gst_buffer_unref (buf);
10489 stream->bytes_per_frame = QT_UINT32 (alac_data + 12);
10490 stream->n_channels = QT_UINT8 (alac_data + 21);
10491 stream->rate = QT_UINT32 (alac_data + 32);
10494 gst_caps_set_simple (stream->caps,
10495 "samplesize", G_TYPE_INT, samplesize, NULL);
10503 gint len = QT_UINT32 (stsd_data);
10506 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
10509 gst_buffer_fill (buf, 0, stsd_data + 0x34, len - 0x34);
10511 /* If we have enough data, let's try to get the 'damr' atom. See
10512 * the 3GPP container spec (26.244) for more details. */
10513 if ((len - 0x34) > 8 &&
10514 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
10515 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10516 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
10519 gst_caps_set_simple (stream->caps,
10520 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10521 gst_buffer_unref (buf);
10527 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
10528 gint len = QT_UINT32 (stsd_data);
10531 guint16 sound_version = QT_UINT16 (stsd_data + 32);
10533 if (sound_version == 1) {
10534 guint16 channels = QT_UINT16 (stsd_data + 40);
10535 guint32 time_scale = QT_UINT32 (stsd_data + 46);
10536 guint8 codec_data[2];
10538 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
10540 gint sample_rate_index =
10541 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
10543 /* build AAC codec data */
10544 codec_data[0] = profile << 3;
10545 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
10546 codec_data[1] = (sample_rate_index & 0x01) << 7;
10547 codec_data[1] |= (channels & 0xF) << 3;
10549 buf = gst_buffer_new_and_alloc (2);
10550 gst_buffer_fill (buf, 0, codec_data, 2);
10551 gst_caps_set_simple (stream->caps,
10552 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10553 gst_buffer_unref (buf);
10559 GST_INFO_OBJECT (qtdemux,
10560 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
10564 GST_INFO_OBJECT (qtdemux,
10565 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
10566 GST_FOURCC_ARGS (fourcc), stream->caps);
10568 } else if (stream->subtype == FOURCC_strm) {
10569 if (fourcc == FOURCC_rtsp) {
10570 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
10572 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
10573 GST_FOURCC_ARGS (fourcc));
10574 goto unknown_stream;
10576 stream->sampled = TRUE;
10577 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
10578 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
10580 stream->sampled = TRUE;
10581 stream->sparse = TRUE;
10584 qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
10586 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10587 GST_TAG_SUBTITLE_CODEC, codec, NULL);
10592 /* hunt for sort-of codec data */
10596 GNode *mp4s = NULL;
10597 GNode *esds = NULL;
10599 /* look for palette in a stsd->mp4s->esds sub-atom */
10600 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
10602 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
10603 if (esds == NULL) {
10605 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
10609 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
10613 GST_INFO_OBJECT (qtdemux,
10614 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
10617 GST_INFO_OBJECT (qtdemux,
10618 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
10619 GST_FOURCC_ARGS (fourcc), stream->caps);
10621 /* everything in 1 sample */
10622 stream->sampled = TRUE;
10625 qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec);
10627 if (stream->caps == NULL)
10628 goto unknown_stream;
10631 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10632 GST_TAG_SUBTITLE_CODEC, codec, NULL);
10638 /* promote to sampled format */
10639 if (stream->fourcc == FOURCC_samr) {
10640 /* force mono 8000 Hz for AMR */
10641 stream->sampled = TRUE;
10642 stream->n_channels = 1;
10643 stream->rate = 8000;
10644 } else if (stream->fourcc == FOURCC_sawb) {
10645 /* force mono 16000 Hz for AMR-WB */
10646 stream->sampled = TRUE;
10647 stream->n_channels = 1;
10648 stream->rate = 16000;
10649 } else if (stream->fourcc == FOURCC_mp4a) {
10650 stream->sampled = TRUE;
10653 /* collect sample information */
10654 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
10655 goto samples_failed;
10657 if (qtdemux->fragmented) {
10661 /* need all moov samples as basis; probably not many if any at all */
10662 /* prevent moof parsing taking of at this time */
10663 offset = qtdemux->moof_offset;
10664 qtdemux->moof_offset = 0;
10665 if (stream->n_samples &&
10666 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
10667 qtdemux->moof_offset = offset;
10668 goto samples_failed;
10670 qtdemux->moof_offset = 0;
10671 /* movie duration more reliable in this case (e.g. mehd) */
10672 if (qtdemux->segment.duration &&
10673 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
10675 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
10676 /* need defaults for fragments */
10677 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
10680 /* configure segments */
10681 if (!qtdemux_parse_segments (qtdemux, stream, trak))
10682 goto segments_failed;
10684 /* add some language tag, if useful */
10685 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
10686 strcmp (stream->lang_id, "und")) {
10687 const gchar *lang_code;
10689 /* convert ISO 639-2 code to ISO 639-1 */
10690 lang_code = gst_tag_get_language_code (stream->lang_id);
10691 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10692 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
10695 /* Check for UDTA tags */
10696 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
10697 qtdemux_parse_udta (qtdemux, stream->pending_tags, udta);
10700 /* now we are ready to add the stream */
10701 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
10702 goto too_many_streams;
10704 if (!qtdemux->got_moov) {
10705 qtdemux->streams[qtdemux->n_streams] = stream;
10706 qtdemux->n_streams++;
10707 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
10715 GST_INFO_OBJECT (qtdemux, "skip disabled track");
10717 gst_qtdemux_stream_free (qtdemux, stream);
10722 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10723 (_("This file is corrupt and cannot be played.")), (NULL));
10725 gst_qtdemux_stream_free (qtdemux, stream);
10730 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
10732 gst_qtdemux_stream_free (qtdemux, stream);
10738 /* we posted an error already */
10739 /* free stbl sub-atoms */
10740 gst_qtdemux_stbl_free (stream);
10742 gst_qtdemux_stream_free (qtdemux, stream);
10747 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
10750 gst_qtdemux_stream_free (qtdemux, stream);
10755 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
10756 GST_FOURCC_ARGS (stream->subtype));
10758 gst_qtdemux_stream_free (qtdemux, stream);
10763 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10764 (_("This file contains too many streams. Only playing first %d"),
10765 GST_QTDEMUX_MAX_STREAMS), (NULL));
10770 /* If we can estimate the overall bitrate, and don't have information about the
10771 * stream bitrate for exactly one stream, this guesses the stream bitrate as
10772 * the overall bitrate minus the sum of the bitrates of all other streams. This
10773 * should be useful for the common case where we have one audio and one video
10774 * stream and can estimate the bitrate of one, but not the other. */
10776 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
10778 QtDemuxStream *stream = NULL;
10779 gint64 size, sys_bitrate, sum_bitrate = 0;
10780 GstClockTime duration;
10784 if (qtdemux->fragmented)
10787 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
10789 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
10791 GST_DEBUG_OBJECT (qtdemux,
10792 "Size in bytes of the stream not known - bailing");
10796 /* Subtract the header size */
10797 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
10798 size, qtdemux->header_size);
10800 if (size < qtdemux->header_size)
10803 size = size - qtdemux->header_size;
10805 if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
10806 duration == GST_CLOCK_TIME_NONE) {
10807 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
10811 for (i = 0; i < qtdemux->n_streams; i++) {
10812 switch (qtdemux->streams[i]->subtype) {
10815 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
10816 qtdemux->streams[i]->caps);
10817 /* retrieve bitrate, prefer avg then max */
10819 if (qtdemux->streams[i]->pending_tags) {
10820 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10821 GST_TAG_MAXIMUM_BITRATE, &bitrate);
10822 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
10823 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10824 GST_TAG_NOMINAL_BITRATE, &bitrate);
10825 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
10826 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10827 GST_TAG_BITRATE, &bitrate);
10828 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
10831 sum_bitrate += bitrate;
10834 GST_DEBUG_OBJECT (qtdemux,
10835 ">1 stream with unknown bitrate - bailing");
10838 stream = qtdemux->streams[i];
10842 /* For other subtypes, we assume no significant impact on bitrate */
10848 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
10852 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
10854 if (sys_bitrate < sum_bitrate) {
10855 /* This can happen, since sum_bitrate might be derived from maximum
10856 * bitrates and not average bitrates */
10857 GST_DEBUG_OBJECT (qtdemux,
10858 "System bitrate less than sum bitrate - bailing");
10862 bitrate = sys_bitrate - sum_bitrate;
10863 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
10864 ", Stream bitrate = %u", sys_bitrate, bitrate);
10866 if (!stream->pending_tags)
10867 stream->pending_tags = gst_tag_list_new_empty ();
10869 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10870 GST_TAG_BITRATE, bitrate, NULL);
10873 static GstFlowReturn
10874 qtdemux_prepare_streams (GstQTDemux * qtdemux)
10877 GstFlowReturn ret = GST_FLOW_OK;
10879 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
10881 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
10882 QtDemuxStream *stream = qtdemux->streams[i];
10883 guint32 sample_num = 0;
10885 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
10886 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
10888 if (qtdemux->fragmented) {
10889 /* need all moov samples first */
10890 GST_OBJECT_LOCK (qtdemux);
10891 while (stream->n_samples == 0)
10892 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
10894 GST_OBJECT_UNLOCK (qtdemux);
10896 /* discard any stray moof */
10897 qtdemux->moof_offset = 0;
10900 /* prepare braking */
10901 if (ret != GST_FLOW_ERROR)
10904 /* in pull mode, we should have parsed some sample info by now;
10905 * and quite some code will not handle no samples.
10906 * in push mode, we'll just have to deal with it */
10907 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
10908 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
10909 gst_qtdemux_remove_stream (qtdemux, i);
10914 /* parse the initial sample for use in setting the frame rate cap */
10915 while (sample_num == 0 && sample_num < stream->n_samples) {
10916 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
10920 if (stream->n_samples > 0 && stream->stbl_index >= 0) {
10921 stream->first_duration = stream->samples[0].duration;
10922 GST_LOG_OBJECT (qtdemux, "stream %d first duration %u",
10923 stream->track_id, stream->first_duration);
10930 static GstFlowReturn
10931 qtdemux_expose_streams (GstQTDemux * qtdemux)
10934 GstFlowReturn ret = GST_FLOW_OK;
10935 GSList *oldpads = NULL;
10938 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
10940 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
10941 QtDemuxStream *stream = qtdemux->streams[i];
10942 GstPad *oldpad = stream->pad;
10945 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
10946 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
10948 if ((stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) &&
10949 stream->track_id == qtdemux->chapters_track_id) {
10950 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
10951 so that it doesn't look like a subtitle track */
10952 gst_qtdemux_remove_stream (qtdemux, i);
10957 /* now we have all info and can expose */
10958 list = stream->pending_tags;
10959 stream->pending_tags = NULL;
10961 oldpads = g_slist_prepend (oldpads, oldpad);
10962 gst_qtdemux_add_stream (qtdemux, stream, list);
10965 gst_qtdemux_guess_bitrate (qtdemux);
10967 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
10969 for (iter = oldpads; iter; iter = g_slist_next (iter)) {
10970 GstPad *oldpad = iter->data;
10972 gst_pad_push_event (oldpad, gst_event_new_eos ());
10973 gst_pad_set_active (oldpad, FALSE);
10974 gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad);
10975 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, oldpad);
10976 gst_object_unref (oldpad);
10979 /* check if we should post a redirect in case there is a single trak
10980 * and it is a redirecting trak */
10981 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
10984 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
10985 "an external content");
10986 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
10987 gst_structure_new ("redirect",
10988 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
10990 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
10991 qtdemux->posted_redirect = TRUE;
10994 for (i = 0; i < qtdemux->n_streams; i++) {
10995 QtDemuxStream *stream = qtdemux->streams[i];
10997 qtdemux_do_allocation (qtdemux, stream);
11000 qtdemux->exposed = TRUE;
11004 /* check if major or compatible brand is 3GP */
11005 static inline gboolean
11006 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
11009 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
11010 GST_MAKE_FOURCC ('3', 'g', 0, 0));
11011 } else if (qtdemux->comp_brands != NULL) {
11015 gboolean res = FALSE;
11017 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
11020 while (size >= 4) {
11021 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
11022 GST_MAKE_FOURCC ('3', 'g', 0, 0));
11026 gst_buffer_unmap (qtdemux->comp_brands, &map);
11033 /* check if tag is a spec'ed 3GP tag keyword storing a string */
11034 static inline gboolean
11035 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
11037 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
11038 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
11039 || fourcc == FOURCC_albm;
11043 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
11044 const char *tag, const char *dummy, GNode * node)
11046 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
11050 gdouble longitude, latitude, altitude;
11053 len = QT_UINT32 (node->data);
11060 /* TODO: language code skipped */
11062 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
11065 /* do not alarm in trivial case, but bail out otherwise */
11066 if (*(data + offset) != 0) {
11067 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
11071 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
11072 GST_TAG_GEO_LOCATION_NAME, name, NULL);
11073 offset += strlen (name);
11077 if (len < offset + 2 + 4 + 4 + 4)
11080 /* +1 +1 = skip null-terminator and location role byte */
11082 /* table in spec says unsigned, semantics say negative has meaning ... */
11083 longitude = QT_SFP32 (data + offset);
11086 latitude = QT_SFP32 (data + offset);
11089 altitude = QT_SFP32 (data + offset);
11091 /* one invalid means all are invalid */
11092 if (longitude >= -180.0 && longitude <= 180.0 &&
11093 latitude >= -90.0 && latitude <= 90.0) {
11094 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
11095 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
11096 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
11097 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
11100 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
11107 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
11114 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
11115 const char *tag, const char *dummy, GNode * node)
11121 len = QT_UINT32 (node->data);
11125 y = QT_UINT16 ((guint8 *) node->data + 12);
11127 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
11130 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
11132 date = g_date_new_dmy (1, 1, y);
11133 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
11134 g_date_free (date);
11138 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
11139 const char *tag, const char *dummy, GNode * node)
11142 char *tag_str = NULL;
11147 len = QT_UINT32 (node->data);
11152 entity = (guint8 *) node->data + offset;
11153 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
11154 GST_DEBUG_OBJECT (qtdemux,
11155 "classification info: %c%c%c%c invalid classification entity",
11156 entity[0], entity[1], entity[2], entity[3]);
11161 table = QT_UINT16 ((guint8 *) node->data + offset);
11163 /* Language code skipped */
11167 /* Tag format: "XXXX://Y[YYYY]/classification info string"
11168 * XXXX: classification entity, fixed length 4 chars.
11169 * Y[YYYY]: classification table, max 5 chars.
11171 tag_str = g_strdup_printf ("----://%u/%s",
11172 table, (char *) node->data + offset);
11174 /* memcpy To be sure we're preserving byte order */
11175 memcpy (tag_str, entity, 4);
11176 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
11178 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
11187 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
11193 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
11194 const char *tag, const char *dummy, GNode * node)
11196 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
11202 gboolean ret = TRUE;
11203 const gchar *charset = NULL;
11205 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11207 len = QT_UINT32 (data->data);
11208 type = QT_UINT32 ((guint8 *) data->data + 8);
11209 if (type == 0x00000001 && len > 16) {
11210 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
11213 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
11214 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
11217 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
11221 len = QT_UINT32 (node->data);
11222 type = QT_UINT32 ((guint8 *) node->data + 4);
11223 if ((type >> 24) == 0xa9) {
11227 /* Type starts with the (C) symbol, so the next data is a list
11228 * of (string size(16), language code(16), string) */
11230 str_len = QT_UINT16 ((guint8 *) node->data + 8);
11231 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
11233 /* the string + fourcc + size + 2 16bit fields,
11234 * means that there are more tags in this atom */
11235 if (len > str_len + 8 + 4) {
11236 /* TODO how to represent the same tag in different languages? */
11237 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
11238 "text alternatives, reading only first one");
11242 len = str_len + 8 + 4; /* remove trailing strings that we don't use */
11243 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
11245 if (lang_code < 0x800) { /* MAC encoded string */
11248 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
11249 QT_FOURCC ((guint8 *) node->data + 4))) {
11250 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
11252 /* we go for 3GP style encoding if major brands claims so,
11253 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
11254 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
11255 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
11256 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
11258 /* 16-bit Language code is ignored here as well */
11259 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
11266 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
11267 ret = FALSE; /* may have to fallback */
11270 GError *err = NULL;
11272 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
11273 charset, NULL, NULL, &err);
11275 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
11276 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
11278 g_error_free (err);
11281 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
11282 len - offset, env_vars);
11285 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
11286 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
11290 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
11297 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
11298 const char *tag, const char *dummy, GNode * node)
11300 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
11304 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
11305 const char *tag, const char *dummy, GNode * node)
11307 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
11309 char *s, *t, *k = NULL;
11314 /* first try normal string tag if major brand not 3GP */
11315 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
11316 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
11317 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
11318 * let's try it 3gpp way after minor safety check */
11320 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
11326 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
11330 len = QT_UINT32 (data);
11334 count = QT_UINT8 (data + 14);
11336 for (; count; count--) {
11339 if (offset + 1 > len)
11341 slen = QT_UINT8 (data + offset);
11343 if (offset + slen > len)
11345 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
11348 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
11350 t = g_strjoin (",", k, s, NULL);
11358 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
11365 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
11366 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
11375 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
11381 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
11382 const char *tag1, const char *tag2, GNode * node)
11389 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11391 len = QT_UINT32 (data->data);
11392 type = QT_UINT32 ((guint8 *) data->data + 8);
11393 if (type == 0x00000000 && len >= 22) {
11394 n1 = QT_UINT16 ((guint8 *) data->data + 18);
11395 n2 = QT_UINT16 ((guint8 *) data->data + 20);
11397 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
11398 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
11401 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
11402 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
11409 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
11410 const char *tag1, const char *dummy, GNode * node)
11417 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11419 len = QT_UINT32 (data->data);
11420 type = QT_UINT32 ((guint8 *) data->data + 8);
11421 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
11422 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
11423 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
11424 n1 = QT_UINT16 ((guint8 *) data->data + 16);
11426 /* do not add bpm=0 */
11427 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
11428 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
11436 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
11437 const char *tag1, const char *dummy, GNode * node)
11444 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11446 len = QT_UINT32 (data->data);
11447 type = QT_UINT32 ((guint8 *) data->data + 8);
11448 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
11449 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
11450 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
11451 num = QT_UINT32 ((guint8 *) data->data + 16);
11453 /* do not add num=0 */
11454 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
11455 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
11462 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
11463 const char *tag1, const char *dummy, GNode * node)
11470 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11472 len = QT_UINT32 (data->data);
11473 type = QT_UINT32 ((guint8 *) data->data + 8);
11474 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
11475 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
11477 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
11478 len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
11479 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
11480 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
11481 gst_sample_unref (sample);
11488 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
11489 const char *tag, const char *dummy, GNode * node)
11496 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11498 len = QT_UINT32 (data->data);
11499 type = QT_UINT32 ((guint8 *) data->data + 8);
11500 if (type == 0x00000001 && len > 16) {
11501 guint y, m = 1, d = 1;
11504 s = g_strndup ((char *) data->data + 16, len - 16);
11505 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
11506 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
11507 if (ret >= 1 && y > 1500 && y < 3000) {
11510 date = g_date_new_dmy (d, m, y);
11511 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
11512 g_date_free (date);
11514 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
11522 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
11523 const char *tag, const char *dummy, GNode * node)
11527 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11529 /* re-route to normal string tag if major brand says so
11530 * or no data atom and compatible brand suggests so */
11531 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
11532 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
11533 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
11538 guint len, type, n;
11540 len = QT_UINT32 (data->data);
11541 type = QT_UINT32 ((guint8 *) data->data + 8);
11542 if (type == 0x00000000 && len >= 18) {
11543 n = QT_UINT16 ((guint8 *) data->data + 16);
11545 const gchar *genre;
11547 genre = gst_tag_id3_genre_get (n - 1);
11548 if (genre != NULL) {
11549 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
11550 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
11558 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
11559 const gchar * tag, guint8 * data, guint32 datasize)
11564 /* make a copy to have \0 at the end */
11565 datacopy = g_strndup ((gchar *) data, datasize);
11567 /* convert the str to double */
11568 if (sscanf (datacopy, "%lf", &value) == 1) {
11569 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
11570 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
11572 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
11580 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
11581 const char *tag, const char *tag_bis, GNode * node)
11590 const gchar *meanstr;
11591 const gchar *namestr;
11593 /* checking the whole ---- atom size for consistency */
11594 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
11595 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
11599 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
11601 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
11605 meansize = QT_UINT32 (mean->data);
11606 if (meansize <= 12) {
11607 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
11610 meanstr = ((gchar *) mean->data) + 12;
11613 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
11615 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
11619 namesize = QT_UINT32 (name->data);
11620 if (namesize <= 12) {
11621 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
11624 namestr = ((gchar *) name->data) + 12;
11632 * uint24 - data type
11636 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11638 GST_WARNING_OBJECT (demux, "No data atom in this tag");
11641 datasize = QT_UINT32 (data->data);
11642 if (datasize <= 16) {
11643 GST_WARNING_OBJECT (demux, "Data atom too small");
11646 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
11648 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
11649 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
11650 static const struct
11652 const gchar name[28];
11653 const gchar tag[28];
11656 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
11657 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
11658 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
11659 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
11660 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
11661 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
11662 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
11663 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
11667 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
11668 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
11669 switch (gst_tag_get_type (tags[i].tag)) {
11670 case G_TYPE_DOUBLE:
11671 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
11672 ((guint8 *) data->data) + 16, datasize - 16);
11674 case G_TYPE_STRING:
11675 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
11684 if (i == G_N_ELEMENTS (tags))
11694 #ifndef GST_DISABLE_GST_DEBUG
11696 gchar *namestr_dbg;
11697 gchar *meanstr_dbg;
11699 meanstr_dbg = g_strndup (meanstr, meansize);
11700 namestr_dbg = g_strndup (namestr, namesize);
11702 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
11703 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
11705 g_free (namestr_dbg);
11706 g_free (meanstr_dbg);
11713 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
11714 const char *tag_bis, GNode * node)
11719 GstTagList *id32_taglist = NULL;
11721 GST_LOG_OBJECT (demux, "parsing ID32");
11724 len = GST_READ_UINT32_BE (data);
11726 /* need at least full box and language tag */
11730 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
11731 gst_buffer_fill (buf, 0, data + 14, len - 14);
11733 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
11734 if (id32_taglist) {
11735 GST_LOG_OBJECT (demux, "parsing ok");
11736 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
11737 gst_tag_list_unref (id32_taglist);
11739 GST_LOG_OBJECT (demux, "parsing failed");
11742 gst_buffer_unref (buf);
11745 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
11746 const char *tag, const char *tag_bis, GNode * node);
11749 FOURCC_pcst -> if media is a podcast -> bool
11750 FOURCC_cpil -> if media is part of a compilation -> bool
11751 FOURCC_pgap -> if media is part of a gapless context -> bool
11752 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
11755 static const struct
11758 const gchar *gst_tag;
11759 const gchar *gst_tag_bis;
11760 const GstQTDemuxAddTagFunc func;
11763 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
11764 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
11765 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
11766 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
11767 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
11768 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
11769 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
11770 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
11771 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
11772 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
11773 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
11774 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
11775 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
11776 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11777 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11778 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11779 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
11780 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
11781 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
11782 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
11783 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
11784 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
11785 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
11786 qtdemux_tag_add_num}, {
11787 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
11788 qtdemux_tag_add_num}, {
11789 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
11790 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
11791 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
11792 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
11793 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
11794 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
11795 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
11796 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
11797 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
11798 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
11799 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
11800 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
11801 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
11802 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
11803 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
11804 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
11805 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
11806 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
11807 qtdemux_tag_add_classification}, {
11808 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
11809 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
11810 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
11812 /* This is a special case, some tags are stored in this
11813 * 'reverse dns naming', according to:
11814 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
11817 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
11818 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
11819 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
11822 struct _GstQtDemuxTagList
11825 GstTagList *taglist;
11827 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
11830 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
11836 const gchar *style;
11841 GstQTDemux *demux = qtdemuxtaglist->demux;
11842 GstTagList *taglist = qtdemuxtaglist->taglist;
11845 len = QT_UINT32 (data);
11846 buf = gst_buffer_new_and_alloc (len);
11847 gst_buffer_fill (buf, 0, data, len);
11849 /* heuristic to determine style of tag */
11850 if (QT_FOURCC (data + 4) == FOURCC_____ ||
11851 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
11853 else if (demux->major_brand == FOURCC_qt__)
11854 style = "quicktime";
11855 /* fall back to assuming iso/3gp tag style */
11859 /* santize the name for the caps. */
11860 for (i = 0; i < 4; i++) {
11861 guint8 d = data[4 + i];
11862 if (g_ascii_isalnum (d))
11863 ndata[i] = g_ascii_tolower (d);
11868 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
11869 ndata[0], ndata[1], ndata[2], ndata[3]);
11870 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
11872 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
11873 sample = gst_sample_new (buf, NULL, NULL, s);
11874 gst_buffer_unref (buf);
11875 g_free (media_type);
11877 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
11880 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
11881 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
11883 gst_sample_unref (sample);
11887 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
11894 GstQtDemuxTagList demuxtaglist;
11896 demuxtaglist.demux = qtdemux;
11897 demuxtaglist.taglist = taglist;
11899 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
11900 if (meta != NULL) {
11901 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
11902 if (ilst == NULL) {
11903 GST_LOG_OBJECT (qtdemux, "no ilst");
11908 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
11912 while (i < G_N_ELEMENTS (add_funcs)) {
11913 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
11917 len = QT_UINT32 (node->data);
11919 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
11920 GST_FOURCC_ARGS (add_funcs[i].fourcc));
11922 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
11923 add_funcs[i].gst_tag_bis, node);
11925 g_node_destroy (node);
11931 /* parsed nodes have been removed, pass along remainder as blob */
11932 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
11933 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
11935 /* parse up XMP_ node if existing */
11936 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
11937 if (xmp_ != NULL) {
11939 GstTagList *xmptaglist;
11941 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
11942 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
11943 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
11944 gst_buffer_unref (buf);
11946 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
11948 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
11954 GstStructure *structure; /* helper for sort function */
11956 guint min_req_bitrate;
11957 guint min_req_qt_version;
11961 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
11963 GstQtReference *ref_a = (GstQtReference *) a;
11964 GstQtReference *ref_b = (GstQtReference *) b;
11966 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
11967 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
11969 /* known bitrates go before unknown; higher bitrates go first */
11970 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
11973 /* sort the redirects and post a message for the application.
11976 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
11978 GstQtReference *best;
11981 GValue list_val = { 0, };
11984 g_assert (references != NULL);
11986 references = g_list_sort (references, qtdemux_redirects_sort_func);
11988 best = (GstQtReference *) references->data;
11990 g_value_init (&list_val, GST_TYPE_LIST);
11992 for (l = references; l != NULL; l = l->next) {
11993 GstQtReference *ref = (GstQtReference *) l->data;
11994 GValue struct_val = { 0, };
11996 ref->structure = gst_structure_new ("redirect",
11997 "new-location", G_TYPE_STRING, ref->location, NULL);
11999 if (ref->min_req_bitrate > 0) {
12000 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
12001 ref->min_req_bitrate, NULL);
12004 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
12005 g_value_set_boxed (&struct_val, ref->structure);
12006 gst_value_list_append_value (&list_val, &struct_val);
12007 g_value_unset (&struct_val);
12008 /* don't free anything here yet, since we need best->structure below */
12011 g_assert (best != NULL);
12012 s = gst_structure_copy (best->structure);
12014 if (g_list_length (references) > 1) {
12015 gst_structure_set_value (s, "locations", &list_val);
12018 g_value_unset (&list_val);
12020 for (l = references; l != NULL; l = l->next) {
12021 GstQtReference *ref = (GstQtReference *) l->data;
12023 gst_structure_free (ref->structure);
12024 g_free (ref->location);
12027 g_list_free (references);
12029 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
12030 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
12031 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
12032 qtdemux->posted_redirect = TRUE;
12035 /* look for redirect nodes, collect all redirect information and
12039 qtdemux_parse_redirects (GstQTDemux * qtdemux)
12041 GNode *rmra, *rmda, *rdrf;
12043 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
12045 GList *redirects = NULL;
12047 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
12049 GstQtReference ref = { NULL, NULL, 0, 0 };
12050 GNode *rmdr, *rmvc;
12052 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
12053 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
12054 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
12055 ref.min_req_bitrate);
12058 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
12059 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
12060 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
12062 #ifndef GST_DISABLE_GST_DEBUG
12063 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
12065 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
12067 GST_LOG_OBJECT (qtdemux,
12068 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
12069 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
12070 bitmask, check_type);
12071 if (package == FOURCC_qtim && check_type == 0) {
12072 ref.min_req_qt_version = version;
12076 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
12082 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
12083 if (ref_len > 20) {
12084 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
12085 ref_data = (guint8 *) rdrf->data + 20;
12086 if (ref_type == FOURCC_alis) {
12087 guint record_len, record_version, fn_len;
12089 if (ref_len > 70) {
12090 /* MacOSX alias record, google for alias-layout.txt */
12091 record_len = QT_UINT16 (ref_data + 4);
12092 record_version = QT_UINT16 (ref_data + 4 + 2);
12093 fn_len = QT_UINT8 (ref_data + 50);
12094 if (record_len > 50 && record_version == 2 && fn_len > 0) {
12095 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
12098 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
12101 } else if (ref_type == FOURCC_url_) {
12102 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
12104 GST_DEBUG_OBJECT (qtdemux,
12105 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
12106 GST_FOURCC_ARGS (ref_type));
12108 if (ref.location != NULL) {
12109 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
12111 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
12113 GST_WARNING_OBJECT (qtdemux,
12114 "Failed to extract redirect location from rdrf atom");
12117 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
12121 /* look for others */
12122 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
12125 if (redirects != NULL) {
12126 qtdemux_process_redirects (qtdemux, redirects);
12132 static GstTagList *
12133 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
12137 if (tags == NULL) {
12138 tags = gst_tag_list_new_empty ();
12139 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
12142 if (qtdemux->major_brand == FOURCC_mjp2)
12143 fmt = "Motion JPEG 2000";
12144 else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0))
12146 else if (qtdemux->major_brand == FOURCC_qt__)
12148 else if (qtdemux->fragmented)
12151 fmt = "ISO MP4/M4A";
12153 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
12154 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
12156 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
12162 /* we have read th complete moov node now.
12163 * This function parses all of the relevant info, creates the traks and
12164 * prepares all data structures for playback
12167 qtdemux_parse_tree (GstQTDemux * qtdemux)
12173 GstClockTime duration;
12175 guint64 creation_time;
12176 GstDateTime *datetime = NULL;
12179 /* make sure we have a usable taglist */
12180 if (!qtdemux->tag_list) {
12181 qtdemux->tag_list = gst_tag_list_new_empty ();
12182 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
12184 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
12187 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
12188 if (mvhd == NULL) {
12189 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
12190 return qtdemux_parse_redirects (qtdemux);
12193 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
12194 if (version == 1) {
12195 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
12196 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
12197 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
12198 } else if (version == 0) {
12199 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
12200 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
12201 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
12203 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
12207 /* Moving qt creation time (secs since 1904) to unix time */
12208 if (creation_time != 0) {
12209 /* Try to use epoch first as it should be faster and more commonly found */
12210 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
12213 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
12214 /* some data cleansing sanity */
12215 g_get_current_time (&now);
12216 if (now.tv_sec + 24 * 3600 < creation_time) {
12217 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
12219 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
12222 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
12223 GDateTime *dt, *dt_local;
12225 dt = g_date_time_add_seconds (base_dt, creation_time);
12226 dt_local = g_date_time_to_local (dt);
12227 datetime = gst_date_time_new_from_g_date_time (dt_local);
12229 g_date_time_unref (base_dt);
12230 g_date_time_unref (dt);
12234 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
12235 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
12237 gst_date_time_unref (datetime);
12240 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
12241 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
12243 /* check for fragmented file and get some (default) data */
12244 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
12247 GstByteReader mehd_data;
12249 /* let track parsing or anyone know weird stuff might happen ... */
12250 qtdemux->fragmented = TRUE;
12252 /* compensate for total duration */
12253 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
12255 qtdemux_parse_mehd (qtdemux, &mehd_data);
12258 /* set duration in the segment info */
12259 gst_qtdemux_get_duration (qtdemux, &duration);
12261 qtdemux->segment.duration = duration;
12262 /* also do not exceed duration; stop is set that way post seek anyway,
12263 * and segment activation falls back to duration,
12264 * whereas loop only checks stop, so let's align this here as well */
12265 qtdemux->segment.stop = duration;
12268 /* parse all traks */
12269 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
12271 qtdemux_parse_trak (qtdemux, trak);
12272 /* iterate all siblings */
12273 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
12276 if (!qtdemux->tag_list) {
12277 GST_DEBUG_OBJECT (qtdemux, "new tag list");
12278 qtdemux->tag_list = gst_tag_list_new_empty ();
12279 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
12281 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
12285 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
12287 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
12289 GST_LOG_OBJECT (qtdemux, "No udta node found.");
12292 /* maybe also some tags in meta box */
12293 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
12295 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
12296 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
12298 GST_LOG_OBJECT (qtdemux, "No meta node found.");
12301 /* parse any protection system info */
12302 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
12304 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
12305 qtdemux_parse_pssh (qtdemux, pssh);
12306 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
12309 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
12314 /* taken from ffmpeg */
12316 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
12328 len = (len << 7) | (c & 0x7f);
12336 /* this can change the codec originally present in @list */
12338 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
12339 GNode * esds, GstTagList * list)
12341 int len = QT_UINT32 (esds->data);
12342 guint8 *ptr = esds->data;
12343 guint8 *end = ptr + len;
12345 guint8 *data_ptr = NULL;
12347 guint8 object_type_id = 0;
12348 const char *codec_name = NULL;
12349 GstCaps *caps = NULL;
12351 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
12353 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
12355 while (ptr + 1 < end) {
12356 tag = QT_UINT8 (ptr);
12357 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
12359 len = read_descr_size (ptr, end, &ptr);
12360 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
12362 /* Check the stated amount of data is available for reading */
12363 if (len < 0 || ptr + len > end)
12367 case ES_DESCRIPTOR_TAG:
12368 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
12369 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
12372 case DECODER_CONFIG_DESC_TAG:{
12373 guint max_bitrate, avg_bitrate;
12375 object_type_id = QT_UINT8 (ptr);
12376 max_bitrate = QT_UINT32 (ptr + 5);
12377 avg_bitrate = QT_UINT32 (ptr + 9);
12378 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
12379 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
12380 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
12381 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
12382 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
12383 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12384 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
12385 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
12387 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12388 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
12389 avg_bitrate, NULL);
12394 case DECODER_SPECIFIC_INFO_TAG:
12395 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
12396 if (object_type_id == 0xe0 && len == 0x40) {
12402 GST_DEBUG_OBJECT (qtdemux,
12403 "Have VOBSUB palette. Creating palette event");
12404 /* move to decConfigDescr data and read palette */
12406 for (i = 0; i < 16; i++) {
12407 clut[i] = QT_UINT32 (data);
12411 s = gst_structure_new ("application/x-gst-dvd", "event",
12412 G_TYPE_STRING, "dvd-spu-clut-change",
12413 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
12414 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
12415 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
12416 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
12417 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
12418 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
12419 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
12420 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
12423 /* store event and trigger custom processing */
12424 stream->pending_event =
12425 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
12427 /* Generic codec_data handler puts it on the caps */
12434 case SL_CONFIG_DESC_TAG:
12435 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
12439 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
12441 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
12447 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
12448 * in use, and should also be used to override some other parameters for some
12450 switch (object_type_id) {
12451 case 0x20: /* MPEG-4 */
12452 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
12453 * profile_and_level_indication */
12454 if (data_ptr != NULL && data_len >= 5 &&
12455 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
12456 gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
12457 data_ptr + 4, data_len - 4);
12459 break; /* Nothing special needed here */
12460 case 0x21: /* H.264 */
12461 codec_name = "H.264 / AVC";
12462 caps = gst_caps_new_simple ("video/x-h264",
12463 "stream-format", G_TYPE_STRING, "avc",
12464 "alignment", G_TYPE_STRING, "au", NULL);
12466 case 0x40: /* AAC (any) */
12467 case 0x66: /* AAC Main */
12468 case 0x67: /* AAC LC */
12469 case 0x68: /* AAC SSR */
12470 /* Override channels and rate based on the codec_data, as it's often
12472 /* Only do so for basic setup without HE-AAC extension */
12473 if (data_ptr && data_len == 2) {
12474 guint channels, rateindex, rate;
12476 /* FIXME: add gst_codec_utils_aac_get_{channels|sample_rate}()? */
12477 channels = (data_ptr[1] & 0x7f) >> 3;
12478 if (channels > 0 && channels < 7) {
12479 stream->n_channels = channels;
12480 } else if (channels == 7) {
12481 stream->n_channels = 8;
12484 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7);
12485 rate = gst_codec_utils_aac_get_sample_rate_from_index (rateindex);
12487 stream->rate = rate;
12490 /* Set level and profile if possible */
12491 if (data_ptr != NULL && data_len >= 2) {
12492 gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
12493 data_ptr, data_len);
12496 case 0x60: /* MPEG-2, various profiles */
12502 codec_name = "MPEG-2 video";
12503 caps = gst_caps_new_simple ("video/mpeg",
12504 "mpegversion", G_TYPE_INT, 2,
12505 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12507 case 0x69: /* MPEG-2 BC audio */
12508 case 0x6B: /* MPEG-1 audio */
12509 caps = gst_caps_new_simple ("audio/mpeg",
12510 "mpegversion", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
12511 codec_name = "MPEG-1 audio";
12513 case 0x6A: /* MPEG-1 */
12514 codec_name = "MPEG-1 video";
12515 caps = gst_caps_new_simple ("video/mpeg",
12516 "mpegversion", G_TYPE_INT, 1,
12517 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12519 case 0x6C: /* MJPEG */
12521 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12523 codec_name = "Motion-JPEG";
12525 case 0x6D: /* PNG */
12527 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
12529 codec_name = "PNG still images";
12531 case 0x6E: /* JPEG2000 */
12532 codec_name = "JPEG-2000";
12533 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
12535 case 0xA4: /* Dirac */
12536 codec_name = "Dirac";
12537 caps = gst_caps_new_empty_simple ("video/x-dirac");
12539 case 0xA5: /* AC3 */
12540 codec_name = "AC-3 audio";
12541 caps = gst_caps_new_simple ("audio/x-ac3",
12542 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12544 case 0xA9: /* AC3 */
12545 codec_name = "DTS audio";
12546 caps = gst_caps_new_simple ("audio/x-dts",
12547 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12549 case 0xE1: /* QCELP */
12550 /* QCELP, the codec_data is a riff tag (little endian) with
12551 * 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). */
12552 caps = gst_caps_new_empty_simple ("audio/qcelp");
12553 codec_name = "QCELP";
12559 /* If we have a replacement caps, then change our caps for this stream */
12561 gst_caps_unref (stream->caps);
12562 stream->caps = caps;
12565 if (codec_name && list)
12566 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
12567 GST_TAG_AUDIO_CODEC, codec_name, NULL);
12569 /* Add the codec_data attribute to caps, if we have it */
12573 buffer = gst_buffer_new_and_alloc (data_len);
12574 gst_buffer_fill (buffer, 0, data_ptr, data_len);
12576 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
12577 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
12579 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
12581 gst_buffer_unref (buffer);
12586 #define _codec(name) \
12588 if (codec_name) { \
12589 *codec_name = g_strdup (name); \
12594 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
12595 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
12597 GstCaps *caps = NULL;
12598 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
12601 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
12602 _codec ("PNG still images");
12603 caps = gst_caps_new_empty_simple ("image/png");
12605 case GST_MAKE_FOURCC ('j', 'p', 'e', 'g'):
12606 _codec ("JPEG still images");
12608 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12611 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
12612 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
12613 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
12614 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
12615 _codec ("Motion-JPEG");
12617 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12620 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
12621 _codec ("Motion-JPEG format B");
12622 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
12624 case GST_MAKE_FOURCC ('m', 'j', 'p', '2'):
12625 _codec ("JPEG-2000");
12626 /* override to what it should be according to spec, avoid palette_data */
12627 stream->bits_per_sample = 24;
12628 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
12630 case GST_MAKE_FOURCC ('S', 'V', 'Q', '3'):
12631 _codec ("Sorensen video v.3");
12632 caps = gst_caps_new_simple ("video/x-svq",
12633 "svqversion", G_TYPE_INT, 3, NULL);
12635 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
12636 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
12637 _codec ("Sorensen video v.1");
12638 caps = gst_caps_new_simple ("video/x-svq",
12639 "svqversion", G_TYPE_INT, 1, NULL);
12641 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
12642 caps = gst_caps_new_empty_simple ("video/x-raw");
12643 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
12644 _codec ("Windows Raw RGB");
12646 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
12650 bps = QT_UINT16 (stsd_data + 98);
12653 format = GST_VIDEO_FORMAT_RGB15;
12656 format = GST_VIDEO_FORMAT_RGB16;
12659 format = GST_VIDEO_FORMAT_RGB;
12662 format = GST_VIDEO_FORMAT_ARGB;
12670 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
12671 format = GST_VIDEO_FORMAT_I420;
12673 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
12674 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
12675 format = GST_VIDEO_FORMAT_I420;
12677 case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'):
12678 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
12679 format = GST_VIDEO_FORMAT_UYVY;
12681 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
12682 format = GST_VIDEO_FORMAT_v308;
12684 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
12685 format = GST_VIDEO_FORMAT_v216;
12687 case GST_MAKE_FOURCC ('v', '2', '1', '0'):
12688 format = GST_VIDEO_FORMAT_v210;
12690 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
12691 format = GST_VIDEO_FORMAT_r210;
12693 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
12694 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
12695 format = GST_VIDEO_FORMAT_v410;
12698 /* Packed YUV 4:4:4:4 8 bit in 32 bits
12699 * but different order than AYUV
12700 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
12701 format = GST_VIDEO_FORMAT_v408;
12704 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
12705 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
12706 _codec ("MPEG-1 video");
12707 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
12708 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12710 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
12711 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
12712 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
12713 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
12714 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
12715 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
12716 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
12717 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
12718 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
12719 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
12720 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
12721 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
12722 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
12723 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
12724 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
12725 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
12726 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
12727 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
12728 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
12729 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
12730 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
12731 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
12732 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
12733 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
12734 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
12735 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
12736 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
12737 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
12738 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
12739 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
12740 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
12741 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
12742 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
12743 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
12744 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
12745 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
12746 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
12747 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
12748 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
12749 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
12750 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
12751 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
12752 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
12753 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
12754 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
12755 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
12756 _codec ("MPEG-2 video");
12757 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
12758 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12760 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
12761 _codec ("GIF still images");
12762 caps = gst_caps_new_empty_simple ("image/gif");
12764 case GST_MAKE_FOURCC ('h', '2', '6', '3'):
12765 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
12766 case GST_MAKE_FOURCC ('s', '2', '6', '3'):
12767 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
12769 /* ffmpeg uses the height/width props, don't know why */
12770 caps = gst_caps_new_simple ("video/x-h263",
12771 "variant", G_TYPE_STRING, "itu", NULL);
12773 case GST_MAKE_FOURCC ('m', 'p', '4', 'v'):
12774 case GST_MAKE_FOURCC ('M', 'P', '4', 'V'):
12775 _codec ("MPEG-4 video");
12776 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
12777 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12779 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
12780 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
12781 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
12782 caps = gst_caps_new_simple ("video/x-msmpeg",
12783 "msmpegversion", G_TYPE_INT, 43, NULL);
12785 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
12787 caps = gst_caps_new_simple ("video/x-divx",
12788 "divxversion", G_TYPE_INT, 3, NULL);
12790 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
12791 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
12793 caps = gst_caps_new_simple ("video/x-divx",
12794 "divxversion", G_TYPE_INT, 4, NULL);
12796 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
12798 caps = gst_caps_new_simple ("video/x-divx",
12799 "divxversion", G_TYPE_INT, 5, NULL);
12802 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
12803 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
12804 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
12805 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
12806 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
12807 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
12808 caps = gst_caps_new_simple ("video/mpeg",
12809 "mpegversion", G_TYPE_INT, 4, NULL);
12811 *codec_name = g_strdup ("MPEG-4");
12814 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
12815 _codec ("Cinepak");
12816 caps = gst_caps_new_empty_simple ("video/x-cinepak");
12818 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
12819 _codec ("Apple QuickDraw");
12820 caps = gst_caps_new_empty_simple ("video/x-qdrw");
12822 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
12823 _codec ("Apple video");
12824 caps = gst_caps_new_empty_simple ("video/x-apple-video");
12826 case GST_MAKE_FOURCC ('H', '2', '6', '4'):
12827 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
12828 _codec ("H.264 / AVC");
12829 caps = gst_caps_new_simple ("video/x-h264",
12830 "stream-format", G_TYPE_STRING, "avc",
12831 "alignment", G_TYPE_STRING, "au", NULL);
12833 case GST_MAKE_FOURCC ('a', 'v', 'c', '3'):
12834 _codec ("H.264 / AVC");
12835 caps = gst_caps_new_simple ("video/x-h264",
12836 "stream-format", G_TYPE_STRING, "avc3",
12837 "alignment", G_TYPE_STRING, "au", NULL);
12839 case GST_MAKE_FOURCC ('H', '2', '6', '5'):
12840 case GST_MAKE_FOURCC ('h', 'v', 'c', '1'):
12841 _codec ("H.265 / HEVC");
12842 caps = gst_caps_new_simple ("video/x-h265",
12843 "stream-format", G_TYPE_STRING, "hvc1",
12844 "alignment", G_TYPE_STRING, "au", NULL);
12846 case GST_MAKE_FOURCC ('h', 'e', 'v', '1'):
12847 _codec ("H.265 / HEVC");
12848 caps = gst_caps_new_simple ("video/x-h265",
12849 "stream-format", G_TYPE_STRING, "hev1",
12850 "alignment", G_TYPE_STRING, "au", NULL);
12852 case GST_MAKE_FOURCC ('r', 'l', 'e', ' '):
12853 _codec ("Run-length encoding");
12854 caps = gst_caps_new_simple ("video/x-rle",
12855 "layout", G_TYPE_STRING, "quicktime", NULL);
12857 case GST_MAKE_FOURCC ('W', 'R', 'L', 'E'):
12858 _codec ("Run-length encoding");
12859 caps = gst_caps_new_simple ("video/x-rle",
12860 "layout", G_TYPE_STRING, "microsoft", NULL);
12862 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
12863 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
12864 _codec ("Indeo Video 3");
12865 caps = gst_caps_new_simple ("video/x-indeo",
12866 "indeoversion", G_TYPE_INT, 3, NULL);
12868 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
12869 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
12870 _codec ("Intel Video 4");
12871 caps = gst_caps_new_simple ("video/x-indeo",
12872 "indeoversion", G_TYPE_INT, 4, NULL);
12874 case GST_MAKE_FOURCC ('d', 'v', 'c', 'p'):
12875 case GST_MAKE_FOURCC ('d', 'v', 'c', ' '):
12876 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
12877 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
12878 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
12879 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
12880 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
12881 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
12882 _codec ("DV Video");
12883 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
12884 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12886 case GST_MAKE_FOURCC ('d', 'v', '5', 'n'): /* DVCPRO50 NTSC */
12887 case GST_MAKE_FOURCC ('d', 'v', '5', 'p'): /* DVCPRO50 PAL */
12888 _codec ("DVCPro50 Video");
12889 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
12890 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12892 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
12893 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
12894 _codec ("DVCProHD Video");
12895 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
12896 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12898 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
12899 _codec ("Apple Graphics (SMC)");
12900 caps = gst_caps_new_empty_simple ("video/x-smc");
12902 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
12904 caps = gst_caps_new_empty_simple ("video/x-vp3");
12906 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
12907 _codec ("VP6 Flash");
12908 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
12910 case GST_MAKE_FOURCC ('X', 'i', 'T', 'h'):
12912 caps = gst_caps_new_empty_simple ("video/x-theora");
12913 /* theora uses one byte of padding in the data stream because it does not
12914 * allow 0 sized packets while theora does */
12915 stream->padding = 1;
12917 case GST_MAKE_FOURCC ('d', 'r', 'a', 'c'):
12919 caps = gst_caps_new_empty_simple ("video/x-dirac");
12921 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
12922 _codec ("TIFF still images");
12923 caps = gst_caps_new_empty_simple ("image/tiff");
12925 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
12926 _codec ("Apple Intermediate Codec");
12927 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
12929 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
12930 _codec ("AVID DNxHD");
12931 caps = gst_caps_from_string ("video/x-dnxhd");
12933 case GST_MAKE_FOURCC ('V', 'P', '8', '0'):
12934 _codec ("On2 VP8");
12935 caps = gst_caps_from_string ("video/x-vp8");
12937 case GST_MAKE_FOURCC ('a', 'p', 'c', 's'):
12938 _codec ("Apple ProRes LT");
12940 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
12943 case GST_MAKE_FOURCC ('a', 'p', 'c', 'h'):
12944 _codec ("Apple ProRes HQ");
12946 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
12949 case GST_MAKE_FOURCC ('a', 'p', 'c', 'n'):
12950 _codec ("Apple ProRes");
12952 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12955 case GST_MAKE_FOURCC ('a', 'p', 'c', 'o'):
12956 _codec ("Apple ProRes Proxy");
12958 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12961 case GST_MAKE_FOURCC ('a', 'p', '4', 'h'):
12962 _codec ("Apple ProRes 4444");
12964 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12967 case GST_MAKE_FOURCC ('v', 'c', '-', '1'):
12970 caps = gst_caps_new_simple ("video/x-wmv",
12971 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
12973 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
12976 char *s, fourstr[5];
12978 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12979 s = g_strdup_printf ("video/x-gst-fourcc-%s", g_strstrip (fourstr));
12980 caps = gst_caps_new_empty_simple (s);
12986 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
12989 gst_video_info_init (&info);
12990 gst_video_info_set_format (&info, format, stream->width, stream->height);
12992 caps = gst_video_info_to_caps (&info);
12993 *codec_name = gst_pb_utils_get_codec_description (caps);
12995 /* enable clipping for raw video streams */
12996 stream->need_clip = TRUE;
13003 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
13004 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
13007 const GstStructure *s;
13010 GstAudioFormat format = 0;
13013 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
13015 depth = stream->bytes_per_packet * 8;
13018 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
13020 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
13021 /* 8-bit audio is unsigned */
13023 format = GST_AUDIO_FORMAT_U8;
13024 /* otherwise it's signed and big-endian just like 'twos' */
13025 case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
13026 endian = G_BIG_ENDIAN;
13028 case GST_MAKE_FOURCC ('s', 'o', 'w', 't'):
13033 endian = G_LITTLE_ENDIAN;
13036 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
13038 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
13042 caps = gst_caps_new_simple ("audio/x-raw",
13043 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
13044 "layout", G_TYPE_STRING, "interleaved", NULL);
13047 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
13048 _codec ("Raw 64-bit floating-point audio");
13049 caps = gst_caps_new_simple ("audio/x-raw",
13050 "format", G_TYPE_STRING, "F64BE",
13051 "layout", G_TYPE_STRING, "interleaved", NULL);
13053 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
13054 _codec ("Raw 32-bit floating-point audio");
13055 caps = gst_caps_new_simple ("audio/x-raw",
13056 "format", G_TYPE_STRING, "F32BE",
13057 "layout", G_TYPE_STRING, "interleaved", NULL);
13060 _codec ("Raw 24-bit PCM audio");
13061 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
13063 caps = gst_caps_new_simple ("audio/x-raw",
13064 "format", G_TYPE_STRING, "S24BE",
13065 "layout", G_TYPE_STRING, "interleaved", NULL);
13067 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
13068 _codec ("Raw 32-bit PCM audio");
13069 caps = gst_caps_new_simple ("audio/x-raw",
13070 "format", G_TYPE_STRING, "S32BE",
13071 "layout", G_TYPE_STRING, "interleaved", NULL);
13073 case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
13074 _codec ("Mu-law audio");
13075 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
13077 case GST_MAKE_FOURCC ('a', 'l', 'a', 'w'):
13078 _codec ("A-law audio");
13079 caps = gst_caps_new_empty_simple ("audio/x-alaw");
13083 _codec ("Microsoft ADPCM");
13084 /* Microsoft ADPCM-ACM code 2 */
13085 caps = gst_caps_new_simple ("audio/x-adpcm",
13086 "layout", G_TYPE_STRING, "microsoft", NULL);
13090 _codec ("DVI/IMA ADPCM");
13091 caps = gst_caps_new_simple ("audio/x-adpcm",
13092 "layout", G_TYPE_STRING, "dvi", NULL);
13096 _codec ("DVI/Intel IMA ADPCM");
13097 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
13098 caps = gst_caps_new_simple ("audio/x-adpcm",
13099 "layout", G_TYPE_STRING, "quicktime", NULL);
13103 /* MPEG layer 3, CBR only (pre QT4.1) */
13104 case GST_MAKE_FOURCC ('.', 'm', 'p', '3'):
13105 _codec ("MPEG-1 layer 3");
13106 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
13107 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
13108 "mpegversion", G_TYPE_INT, 1, NULL);
13111 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
13112 _codec ("EAC-3 audio");
13113 caps = gst_caps_new_simple ("audio/x-eac3",
13114 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13115 stream->sampled = TRUE;
13117 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
13118 case GST_MAKE_FOURCC ('a', 'c', '-', '3'):
13119 _codec ("AC-3 audio");
13120 caps = gst_caps_new_simple ("audio/x-ac3",
13121 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13122 stream->sampled = TRUE;
13124 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
13125 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
13126 _codec ("DTS audio");
13127 caps = gst_caps_new_simple ("audio/x-dts",
13128 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13129 stream->sampled = TRUE;
13131 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
13132 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
13133 _codec ("DTS-HD audio");
13134 caps = gst_caps_new_simple ("audio/x-dts",
13135 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
13136 stream->sampled = TRUE;
13138 case GST_MAKE_FOURCC ('M', 'A', 'C', '3'):
13140 caps = gst_caps_new_simple ("audio/x-mace",
13141 "maceversion", G_TYPE_INT, 3, NULL);
13143 case GST_MAKE_FOURCC ('M', 'A', 'C', '6'):
13145 caps = gst_caps_new_simple ("audio/x-mace",
13146 "maceversion", G_TYPE_INT, 6, NULL);
13148 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
13150 caps = gst_caps_new_empty_simple ("application/ogg");
13152 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
13153 _codec ("DV audio");
13154 caps = gst_caps_new_empty_simple ("audio/x-dv");
13156 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
13157 _codec ("MPEG-4 AAC audio");
13158 caps = gst_caps_new_simple ("audio/mpeg",
13159 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
13160 "stream-format", G_TYPE_STRING, "raw", NULL);
13162 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
13163 _codec ("QDesign Music");
13164 caps = gst_caps_new_empty_simple ("audio/x-qdm");
13166 case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'):
13167 _codec ("QDesign Music v.2");
13168 /* FIXME: QDesign music version 2 (no constant) */
13169 if (FALSE && data) {
13170 caps = gst_caps_new_simple ("audio/x-qdm2",
13171 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
13172 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
13173 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
13175 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
13178 case GST_MAKE_FOURCC ('a', 'g', 's', 'm'):
13179 _codec ("GSM audio");
13180 caps = gst_caps_new_empty_simple ("audio/x-gsm");
13182 case GST_MAKE_FOURCC ('s', 'a', 'm', 'r'):
13183 _codec ("AMR audio");
13184 caps = gst_caps_new_empty_simple ("audio/AMR");
13186 case GST_MAKE_FOURCC ('s', 'a', 'w', 'b'):
13187 _codec ("AMR-WB audio");
13188 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
13190 case GST_MAKE_FOURCC ('i', 'm', 'a', '4'):
13191 _codec ("Quicktime IMA ADPCM");
13192 caps = gst_caps_new_simple ("audio/x-adpcm",
13193 "layout", G_TYPE_STRING, "quicktime", NULL);
13195 case GST_MAKE_FOURCC ('a', 'l', 'a', 'c'):
13196 _codec ("Apple lossless audio");
13197 caps = gst_caps_new_empty_simple ("audio/x-alac");
13199 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
13200 _codec ("QualComm PureVoice");
13201 caps = gst_caps_from_string ("audio/qcelp");
13203 case GST_MAKE_FOURCC ('w', 'm', 'a', ' '):
13206 caps = gst_caps_new_empty_simple ("audio/x-wma");
13208 case GST_MAKE_FOURCC ('l', 'p', 'c', 'm'):
13213 GstAudioFormat format;
13216 FLAG_IS_FLOAT = 0x1,
13217 FLAG_IS_BIG_ENDIAN = 0x2,
13218 FLAG_IS_SIGNED = 0x4,
13219 FLAG_IS_PACKED = 0x8,
13220 FLAG_IS_ALIGNED_HIGH = 0x10,
13221 FLAG_IS_NON_INTERLEAVED = 0x20
13223 _codec ("Raw LPCM audio");
13225 if (data && len >= 56) {
13226 depth = QT_UINT32 (data + 40);
13227 flags = QT_UINT32 (data + 44);
13228 width = QT_UINT32 (data + 48) * 8 / stream->n_channels;
13230 if ((flags & FLAG_IS_FLOAT) == 0) {
13235 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
13236 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
13237 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
13238 caps = gst_caps_new_simple ("audio/x-raw",
13239 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
13240 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
13241 "non-interleaved" : "interleaved", NULL);
13246 if (flags & FLAG_IS_BIG_ENDIAN)
13247 format = GST_AUDIO_FORMAT_F64BE;
13249 format = GST_AUDIO_FORMAT_F64LE;
13251 if (flags & FLAG_IS_BIG_ENDIAN)
13252 format = GST_AUDIO_FORMAT_F32BE;
13254 format = GST_AUDIO_FORMAT_F32LE;
13256 caps = gst_caps_new_simple ("audio/x-raw",
13257 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
13258 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
13259 "non-interleaved" : "interleaved", NULL);
13263 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
13267 char *s, fourstr[5];
13269 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13270 s = g_strdup_printf ("audio/x-gst-fourcc-%s", g_strstrip (fourstr));
13271 caps = gst_caps_new_empty_simple (s);
13278 GstCaps *templ_caps =
13279 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
13280 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
13281 gst_caps_unref (caps);
13282 gst_caps_unref (templ_caps);
13283 caps = intersection;
13286 /* enable clipping for raw audio streams */
13287 s = gst_caps_get_structure (caps, 0);
13288 name = gst_structure_get_name (s);
13289 if (g_str_has_prefix (name, "audio/x-raw")) {
13290 stream->need_clip = TRUE;
13291 stream->max_buffer_size = 4096 * stream->bytes_per_frame;
13292 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
13298 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
13299 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
13303 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
13306 case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
13307 _codec ("DVD subtitle");
13308 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
13309 stream->need_process = TRUE;
13311 case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
13312 _codec ("Quicktime timed text");
13314 case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
13315 _codec ("3GPP timed text");
13317 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
13319 /* actual text piece needs to be extracted */
13320 stream->need_process = TRUE;
13324 char *s, fourstr[5];
13326 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13327 s = g_strdup_printf ("text/x-gst-fourcc-%s", g_strstrip (fourstr));
13328 caps = gst_caps_new_empty_simple (s);
13337 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
13338 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
13343 case GST_MAKE_FOURCC ('m', '1', 'v', ' '):
13344 _codec ("MPEG 1 video");
13345 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
13346 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13356 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
13357 const gchar * system_id)
13361 if (!qtdemux->protection_system_ids)
13362 qtdemux->protection_system_ids =
13363 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
13364 /* Check whether we already have an entry for this system ID. */
13365 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
13366 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
13367 if (g_ascii_strcasecmp (system_id, id) == 0) {
13371 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
13372 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,