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 /* Macros for converting to/from timescale */
124 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
125 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
127 #define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
128 #define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
130 /* timestamp is the DTS */
131 #define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
132 /* timestamp + offset is the PTS */
133 #define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
134 /* timestamp + duration - dts is the duration */
135 #define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
137 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
140 * Quicktime has tracks and segments. A track is a continuous piece of
141 * multimedia content. The track is not always played from start to finish but
142 * instead, pieces of the track are 'cut out' and played in sequence. This is
143 * what the segments do.
145 * Inside the track we have keyframes (K) and delta frames. The track has its
146 * own timing, which starts from 0 and extends to end. The position in the track
147 * is called the media_time.
149 * The segments now describe the pieces that should be played from this track
150 * and are basically tuples of media_time/duration/rate entries. We can have
151 * multiple segments and they are all played after one another. An example:
153 * segment 1: media_time: 1 second, duration: 1 second, rate 1
154 * segment 2: media_time: 3 second, duration: 2 second, rate 2
156 * To correctly play back this track, one must play: 1 second of media starting
157 * from media_time 1 followed by 2 seconds of media starting from media_time 3
160 * Each of the segments will be played at a specific time, the first segment at
161 * time 0, the second one after the duration of the first one, etc.. Note that
162 * the time in resulting playback is not identical to the media_time of the
165 * Visually, assuming the track has 4 second of media_time:
168 * .-----------------------------------------------------------.
169 * track: | K.....K.........K........K.......K.......K...........K... |
170 * '-----------------------------------------------------------'
172 * .------------^ ^ .----------^ ^
173 * / .-------------' / .------------------'
175 * .--------------. .--------------.
176 * | segment 1 | | segment 2 |
177 * '--------------' '--------------'
179 * The challenge here is to cut out the right pieces of the track for each of
180 * the playback segments. This fortunately can easily be done with the SEGMENT
181 * events of GStreamer.
183 * For playback of segment 1, we need to provide the decoder with the keyframe
184 * (a), in the above figure, but we must instruct it only to output the decoded
185 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
186 * position set to the time of the segment: 0.
188 * We then proceed to push data from keyframe (a) to frame (b). The decoder
189 * decodes but clips all before media_time 1.
191 * After finishing a segment, we push out a new SEGMENT event with the clipping
192 * boundaries of the new data.
194 * This is a good usecase for the GStreamer accumulated SEGMENT events.
197 struct _QtDemuxSegment
199 /* global time and duration, all gst time */
201 GstClockTime stop_time;
202 GstClockTime duration;
203 /* media time of trak, all gst time */
204 GstClockTime media_start;
205 GstClockTime media_stop;
207 /* Media start time in trak timescale units */
208 guint32 trak_media_start;
211 #define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
213 /* Used with fragmented MP4 files (mfra atom) */
218 } QtDemuxRandomAccessEntry;
220 struct _QtDemuxStream
231 gboolean new_stream; /* signals that a stream_start is required */
232 gboolean on_keyframe; /* if this stream last pushed buffer was a
233 * keyframe. This is important to identify
234 * where to stop pushing buffers after a
235 * segment stop time */
237 /* if the stream has a redirect URI in its headers, we store it here */
244 guint64 duration; /* in timescale */
248 gchar lang_id[4]; /* ISO 639-2T language code */
252 QtDemuxSample *samples;
253 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
254 guint32 first_duration; /* duration in timescale of first sample, used for figuring out
255 the framerate, in timescale units */
256 guint32 n_samples_moof; /* sample count in a moof */
257 guint64 duration_moof; /* duration in timescale of a moof, used for figure out
258 * the framerate of fragmented format stream */
259 guint32 offset_in_sample;
260 guint32 max_buffer_size;
262 /* if we use chunks or samples */
274 /* Numerator/denominator framerate */
277 guint16 bits_per_sample;
278 guint16 color_table_id;
279 GstMemory *rgb8_palette;
284 guint samples_per_packet;
285 guint samples_per_frame;
286 guint bytes_per_packet;
287 guint bytes_per_sample;
288 guint bytes_per_frame;
292 gboolean use_allocator;
293 GstAllocator *allocator;
294 GstAllocationParams params;
296 /* when a discontinuity is pending */
299 /* list of buffers to push first */
302 /* if we need to clip this buffer. This is only needed for uncompressed
306 /* buffer needs some custom processing, e.g. subtitles */
307 gboolean need_process;
309 /* current position */
310 guint32 segment_index;
311 guint32 sample_index;
312 GstClockTime time_position; /* in gst time */
313 guint64 accumulated_base;
315 /* the Gst segment we are processing out, used for clipping */
317 guint32 segment_seqnum; /* segment event seqnum obtained from seek */
319 /* quicktime segments */
321 QtDemuxSegment *segments;
322 gboolean dummy_segment;
327 GstTagList *pending_tags;
328 gboolean send_global_tags;
330 GstEvent *pending_event;
340 gboolean chunks_are_samples; /* TRUE means treat chunks as samples */
344 GstByteReader co_chunk;
346 guint32 current_chunk;
348 guint32 samples_per_chunk;
349 guint32 stco_sample_index;
351 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
354 guint32 n_samples_per_chunk;
355 guint32 stsc_chunk_index;
356 guint32 stsc_sample_index;
357 guint64 chunk_offset;
360 guint32 stts_samples;
361 guint32 n_sample_times;
362 guint32 stts_sample_index;
364 guint32 stts_duration;
366 gboolean stss_present;
367 guint32 n_sample_syncs;
370 gboolean stps_present;
371 guint32 n_sample_partial_syncs;
373 QtDemuxRandomAccessEntry *ra_entries;
376 const QtDemuxRandomAccessEntry *pending_seek;
379 gboolean ctts_present;
380 guint32 n_composition_times;
382 guint32 ctts_sample_index;
390 gboolean parsed_trex;
391 guint32 def_sample_duration;
392 guint32 def_sample_size;
393 guint32 def_sample_flags;
397 /* stereoscopic video streams */
398 GstVideoMultiviewMode multiview_mode;
399 GstVideoMultiviewFlags multiview_flags;
401 /* protected streams */
403 guint32 protection_scheme_type;
404 guint32 protection_scheme_version;
405 gpointer protection_scheme_info; /* specific to the protection scheme */
406 GQueue protection_scheme_event_queue;
409 /* Contains properties and cryptographic info for a set of samples from a
410 * track protected using Common Encryption (cenc) */
411 struct _QtDemuxCencSampleSetInfo
413 GstStructure *default_properties;
415 /* @crypto_info holds one GstStructure per sample */
416 GPtrArray *crypto_info;
421 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
422 QTDEMUX_STATE_HEADER, /* Parsing the header */
423 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
424 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
427 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
428 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
429 guint32 fourcc, GstByteReader * parser);
430 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
431 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
432 guint32 fourcc, GstByteReader * parser);
434 static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
436 static GstStaticPadTemplate gst_qtdemux_sink_template =
437 GST_STATIC_PAD_TEMPLATE ("sink",
440 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
444 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
445 GST_STATIC_PAD_TEMPLATE ("video_%u",
448 GST_STATIC_CAPS_ANY);
450 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
451 GST_STATIC_PAD_TEMPLATE ("audio_%u",
454 GST_STATIC_CAPS_ANY);
456 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
457 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
460 GST_STATIC_CAPS_ANY);
462 #define gst_qtdemux_parent_class parent_class
463 G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
465 static void gst_qtdemux_dispose (GObject * object);
468 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
469 GstClockTime media_time);
471 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
472 QtDemuxStream * str, gint64 media_offset);
475 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
476 static GstIndex *gst_qtdemux_get_index (GstElement * element);
478 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
479 GstStateChange transition);
480 static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
481 static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
482 GstObject * parent, GstPadMode mode, gboolean active);
484 static void gst_qtdemux_loop (GstPad * pad);
485 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
487 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
489 static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
490 static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
491 QtDemuxStream * stream);
492 static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
495 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
496 const guint8 * buffer, guint length);
497 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
498 const guint8 * buffer, guint length);
499 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
500 static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
503 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
504 QtDemuxStream * stream, GNode * esds, GstTagList * list);
505 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
506 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
507 gchar ** codec_name);
508 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
509 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
510 gchar ** codec_name);
511 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
512 QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
513 gchar ** codec_name);
514 static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
515 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
516 gchar ** codec_name);
518 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
519 QtDemuxStream * stream, guint32 n);
520 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
521 static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
522 QtDemuxStream * stream);
523 static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux,
524 QtDemuxStream * stream);
525 static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index);
526 static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
527 static void qtdemux_do_allocation (GstQTDemux * qtdemux,
528 QtDemuxStream * stream);
530 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
531 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
533 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
537 gst_qtdemux_class_init (GstQTDemuxClass * klass)
539 GObjectClass *gobject_class;
540 GstElementClass *gstelement_class;
542 gobject_class = (GObjectClass *) klass;
543 gstelement_class = (GstElementClass *) klass;
545 parent_class = g_type_class_peek_parent (klass);
547 gobject_class->dispose = gst_qtdemux_dispose;
549 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
551 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
552 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
555 gst_tag_register_musicbrainz_tags ();
557 gst_element_class_add_pad_template (gstelement_class,
558 gst_static_pad_template_get (&gst_qtdemux_sink_template));
559 gst_element_class_add_pad_template (gstelement_class,
560 gst_static_pad_template_get (&gst_qtdemux_videosrc_template));
561 gst_element_class_add_pad_template (gstelement_class,
562 gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
563 gst_element_class_add_pad_template (gstelement_class,
564 gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
565 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
567 "Demultiplex a QuickTime file into audio and video streams",
568 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
570 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
575 gst_qtdemux_init (GstQTDemux * qtdemux)
578 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
579 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
580 gst_pad_set_activatemode_function (qtdemux->sinkpad,
581 qtdemux_sink_activate_mode);
582 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
583 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
584 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
586 qtdemux->state = QTDEMUX_STATE_INITIAL;
587 qtdemux->pullbased = FALSE;
588 qtdemux->posted_redirect = FALSE;
589 qtdemux->pending_configure = FALSE;
590 qtdemux->neededbytes = 16;
592 qtdemux->adapter = gst_adapter_new ();
594 qtdemux->first_mdat = -1;
595 qtdemux->got_moov = FALSE;
596 qtdemux->mdatoffset = -1;
597 qtdemux->mdatbuffer = NULL;
598 qtdemux->restoredata_buffer = NULL;
599 qtdemux->restoredata_offset = -1;
600 qtdemux->fragment_start = -1;
601 qtdemux->fragment_start_offset = -1;
602 qtdemux->media_caps = NULL;
603 qtdemux->exposed = FALSE;
604 qtdemux->mss_mode = FALSE;
605 qtdemux->pending_newsegment = NULL;
606 qtdemux->upstream_format_is_time = FALSE;
607 qtdemux->have_group_id = FALSE;
608 qtdemux->group_id = G_MAXUINT;
609 qtdemux->cenc_aux_info_offset = 0;
610 qtdemux->cenc_aux_info_sizes = NULL;
611 qtdemux->cenc_aux_sample_count = 0;
612 qtdemux->protection_system_ids = NULL;
613 g_queue_init (&qtdemux->protection_event_queue);
614 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
615 qtdemux->flowcombiner = gst_flow_combiner_new ();
617 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
621 gst_qtdemux_dispose (GObject * object)
623 GstQTDemux *qtdemux = GST_QTDEMUX (object);
625 if (qtdemux->adapter) {
626 g_object_unref (G_OBJECT (qtdemux->adapter));
627 qtdemux->adapter = NULL;
629 gst_flow_combiner_free (qtdemux->flowcombiner);
630 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
632 g_queue_clear (&qtdemux->protection_event_queue);
634 g_free (qtdemux->cenc_aux_info_sizes);
635 qtdemux->cenc_aux_info_sizes = NULL;
637 G_OBJECT_CLASS (parent_class)->dispose (object);
641 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
643 if (qtdemux->posted_redirect) {
644 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
645 (_("This file contains no playable streams.")),
646 ("no known streams found, a redirect message has been posted"));
648 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
649 (_("This file contains no playable streams.")),
650 ("no known streams found"));
655 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
657 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
658 mem, size, 0, size, mem, free_func);
662 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
669 if (G_UNLIKELY (size == 0)) {
671 GstBuffer *tmp = NULL;
673 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
674 if (ret != GST_FLOW_OK)
677 gst_buffer_map (tmp, &map, GST_MAP_READ);
678 size = QT_UINT32 (map.data);
679 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
681 gst_buffer_unmap (tmp, &map);
682 gst_buffer_unref (tmp);
685 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
686 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
687 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
688 /* we're pulling header but already got most interesting bits,
689 * so never mind the rest (e.g. tags) (that much) */
690 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
694 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
695 (_("This file is invalid and cannot be played.")),
696 ("atom has bogus size %" G_GUINT64_FORMAT, size));
697 return GST_FLOW_ERROR;
701 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
703 if (G_UNLIKELY (flow != GST_FLOW_OK))
706 bsize = gst_buffer_get_size (*buf);
707 /* Catch short reads - we don't want any partial atoms */
708 if (G_UNLIKELY (bsize < size)) {
709 GST_WARNING_OBJECT (qtdemux,
710 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
711 gst_buffer_unref (*buf);
721 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
722 GstFormat dest_format, gint64 * dest_value)
725 QtDemuxStream *stream = gst_pad_get_element_private (pad);
726 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
729 if (stream->subtype != FOURCC_vide) {
734 switch (src_format) {
735 case GST_FORMAT_TIME:
736 switch (dest_format) {
737 case GST_FORMAT_BYTES:{
738 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
742 *dest_value = stream->samples[index].offset;
744 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
745 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
746 GST_TIME_ARGS (src_value), *dest_value);
754 case GST_FORMAT_BYTES:
755 switch (dest_format) {
756 case GST_FORMAT_TIME:{
758 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
765 QTSTREAMTIME_TO_GSTTIME (stream,
766 stream->samples[index].timestamp);
767 GST_DEBUG_OBJECT (qtdemux,
768 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
769 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
782 gst_object_unref (qtdemux);
789 gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
793 *duration = GST_CLOCK_TIME_NONE;
795 if (qtdemux->duration != 0) {
796 if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
797 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
804 gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
807 gboolean res = FALSE;
808 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
810 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
812 switch (GST_QUERY_TYPE (query)) {
813 case GST_QUERY_POSITION:{
816 gst_query_parse_position (query, &fmt, NULL);
817 if (fmt == GST_FORMAT_TIME
818 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
819 gst_query_set_position (query, GST_FORMAT_TIME,
820 qtdemux->segment.position);
825 case GST_QUERY_DURATION:{
828 gst_query_parse_duration (query, &fmt, NULL);
829 if (fmt == GST_FORMAT_TIME) {
830 /* First try to query upstream */
831 res = gst_pad_query_default (pad, parent, query);
834 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
835 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
842 case GST_QUERY_CONVERT:{
843 GstFormat src_fmt, dest_fmt;
844 gint64 src_value, dest_value = 0;
846 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
848 res = gst_qtdemux_src_convert (pad,
849 src_fmt, src_value, dest_fmt, &dest_value);
851 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
856 case GST_QUERY_FORMATS:
857 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
860 case GST_QUERY_SEEKING:{
864 /* try upstream first */
865 res = gst_pad_query_default (pad, parent, query);
868 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
869 if (fmt == GST_FORMAT_TIME) {
870 GstClockTime duration = GST_CLOCK_TIME_NONE;
872 gst_qtdemux_get_duration (qtdemux, &duration);
874 if (!qtdemux->pullbased) {
877 /* we might be able with help from upstream */
879 q = gst_query_new_seeking (GST_FORMAT_BYTES);
880 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
881 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
882 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
886 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
892 case GST_QUERY_SEGMENT:
897 format = qtdemux->segment.format;
900 gst_segment_to_stream_time (&qtdemux->segment, format,
901 qtdemux->segment.start);
902 if ((stop = qtdemux->segment.stop) == -1)
903 stop = qtdemux->segment.duration;
905 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
907 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
912 res = gst_pad_query_default (pad, parent, query);
920 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
922 if (G_LIKELY (stream->pad)) {
923 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
924 GST_DEBUG_PAD_NAME (stream->pad));
926 if (G_UNLIKELY (stream->pending_tags)) {
927 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
928 stream->pending_tags);
929 gst_pad_push_event (stream->pad,
930 gst_event_new_tag (stream->pending_tags));
931 stream->pending_tags = NULL;
934 if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
935 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
937 gst_pad_push_event (stream->pad,
938 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
939 stream->send_global_tags = FALSE;
944 /* push event on all source pads; takes ownership of the event */
946 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
949 gboolean has_valid_stream = FALSE;
950 GstEventType etype = GST_EVENT_TYPE (event);
952 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
953 GST_EVENT_TYPE_NAME (event));
955 for (n = 0; n < qtdemux->n_streams; n++) {
957 QtDemuxStream *stream = qtdemux->streams[n];
958 GST_DEBUG_OBJECT (qtdemux, "pushing on pad %i", n);
960 if ((pad = stream->pad)) {
961 has_valid_stream = TRUE;
963 if (etype == GST_EVENT_EOS) {
964 /* let's not send twice */
965 if (stream->sent_eos)
967 stream->sent_eos = TRUE;
970 gst_pad_push_event (pad, gst_event_ref (event));
974 gst_event_unref (event);
976 /* if it is EOS and there are no pads, post an error */
977 if (!has_valid_stream && etype == GST_EVENT_EOS) {
978 gst_qtdemux_post_no_playable_stream_error (qtdemux);
982 /* push a pending newsegment event, if any from the streaming thread */
984 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
986 if (qtdemux->pending_newsegment) {
987 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
988 qtdemux->pending_newsegment = NULL;
998 find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1000 if ((gint64) s1->timestamp + s1->pts_offset > *media_time)
1002 if ((gint64) s1->timestamp + s1->pts_offset == *media_time)
1008 /* find the index of the sample that includes the data for @media_time using a
1009 * binary search. Only to be called in optimized cases of linear search below.
1011 * Returns the index of the sample.
1014 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
1017 QtDemuxSample *result;
1020 /* convert media_time to mov format */
1022 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1024 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1025 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1026 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1028 if (G_LIKELY (result))
1029 index = result - str->samples;
1038 /* find the index of the sample that includes the data for @media_offset using a
1041 * Returns the index of the sample.
1044 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1045 QtDemuxStream * str, gint64 media_offset)
1047 QtDemuxSample *result = str->samples;
1050 if (result == NULL || str->n_samples == 0)
1053 if (media_offset == result->offset)
1057 while (index < str->n_samples - 1) {
1058 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1061 if (media_offset < result->offset)
1072 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1077 /* find the index of the sample that includes the data for @media_time using a
1078 * linear search, and keeping in mind that not all samples may have been parsed
1079 * yet. If possible, it will delegate to binary search.
1081 * Returns the index of the sample.
1084 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1085 GstClockTime media_time)
1089 QtDemuxSample *sample;
1091 /* convert media_time to mov format */
1093 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1095 sample = str->samples;
1096 if (mov_time == sample->timestamp + sample->pts_offset)
1099 /* use faster search if requested time in already parsed range */
1100 sample = str->samples + str->stbl_index;
1101 if (str->stbl_index >= 0 &&
1102 mov_time <= (sample->timestamp + sample->pts_offset))
1103 return gst_qtdemux_find_index (qtdemux, str, media_time);
1105 while (index < str->n_samples - 1) {
1106 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1109 sample = str->samples + index + 1;
1110 if (mov_time < (sample->timestamp + sample->pts_offset))
1120 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1125 /* find the index of the keyframe needed to decode the sample at @index
1128 * Returns the index of the keyframe.
1131 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1134 guint32 new_index = index;
1136 if (index >= str->n_samples) {
1137 new_index = str->n_samples;
1141 /* all keyframes, return index */
1142 if (str->all_keyframe) {
1147 /* else go back until we have a keyframe */
1149 if (str->samples[new_index].keyframe)
1159 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1160 "gave %u", index, new_index);
1165 /* find the segment for @time_position for @stream
1167 * Returns the index of the segment containing @time_position.
1168 * Returns the last segment and sets the @eos variable to TRUE
1169 * if the time is beyond the end. @eos may be NULL
1172 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1173 GstClockTime time_position)
1178 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1179 GST_TIME_ARGS (time_position));
1182 for (i = 0; i < stream->n_segments; i++) {
1183 QtDemuxSegment *segment = &stream->segments[i];
1185 GST_LOG_OBJECT (stream->pad,
1186 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1187 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1189 /* For the last segment we include stop_time in the last segment */
1190 if (i < stream->n_segments - 1) {
1191 if (segment->time <= time_position && time_position < segment->stop_time) {
1192 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1197 /* Last segment always matches */
1205 /* move the stream @str to the sample position @index.
1207 * Updates @str->sample_index and marks discontinuity if needed.
1210 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1213 /* no change needed */
1214 if (index == str->sample_index)
1217 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1220 /* position changed, we have a discont */
1221 str->sample_index = index;
1222 str->offset_in_sample = 0;
1223 /* Each time we move in the stream we store the position where we are
1225 str->from_sample = index;
1226 str->discont = TRUE;
1230 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1231 gboolean use_sparse, gint64 * key_time, gint64 * key_offset)
1234 gint64 min_byte_offset = -1;
1237 min_offset = desired_time;
1239 /* for each stream, find the index of the sample in the segment
1240 * and move back to the previous keyframe. */
1241 for (n = 0; n < qtdemux->n_streams; n++) {
1243 guint32 index, kindex;
1245 GstClockTime media_start;
1246 GstClockTime media_time;
1247 GstClockTime seg_time;
1248 QtDemuxSegment *seg;
1249 gboolean empty_segment = FALSE;
1251 str = qtdemux->streams[n];
1253 if (str->sparse && !use_sparse)
1256 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1257 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1259 /* get segment and time in the segment */
1260 seg = &str->segments[seg_idx];
1261 seg_time = desired_time - seg->time;
1263 while (QTSEGMENT_IS_EMPTY (seg)) {
1265 empty_segment = TRUE;
1266 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1269 if (seg_idx == str->n_segments)
1271 seg = &str->segments[seg_idx];
1274 if (seg_idx == str->n_segments) {
1275 /* FIXME track shouldn't have the last segment as empty, but if it
1276 * happens we better handle it */
1280 /* get the media time in the segment */
1281 media_start = seg->media_start + seg_time;
1283 /* get the index of the sample with media time */
1284 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1285 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1286 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1287 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1290 if (!empty_segment) {
1291 /* find previous keyframe */
1292 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);
1294 /* if the keyframe is at a different position, we need to update the
1295 * requested seek time */
1296 if (index != kindex) {
1299 /* get timestamp of keyframe */
1300 media_time = QTSAMPLE_DTS (str, &str->samples[kindex]);
1301 GST_DEBUG_OBJECT (qtdemux,
1302 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1303 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1304 str->samples[kindex].offset);
1306 /* keyframes in the segment get a chance to change the
1307 * desired_offset. keyframes out of the segment are
1309 if (media_time >= seg->media_start) {
1310 GstClockTime seg_time;
1312 /* this keyframe is inside the segment, convert back to
1314 seg_time = (media_time - seg->media_start) + seg->time;
1315 if (seg_time < min_offset)
1316 min_offset = seg_time;
1321 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1322 min_byte_offset = str->samples[index].offset;
1326 *key_time = min_offset;
1328 *key_offset = min_byte_offset;
1332 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1333 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1337 g_return_val_if_fail (format != NULL, FALSE);
1338 g_return_val_if_fail (cur != NULL, FALSE);
1339 g_return_val_if_fail (stop != NULL, FALSE);
1341 if (*format == GST_FORMAT_TIME)
1345 if (cur_type != GST_SEEK_TYPE_NONE)
1346 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1347 if (res && stop_type != GST_SEEK_TYPE_NONE)
1348 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1351 *format = GST_FORMAT_TIME;
1356 /* perform seek in push based mode:
1357 find BYTE position to move to based on time and delegate to upstream
1360 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1365 GstSeekType cur_type, stop_type;
1366 gint64 cur, stop, key_cur;
1369 gint64 original_stop;
1372 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1374 gst_event_parse_seek (event, &rate, &format, &flags,
1375 &cur_type, &cur, &stop_type, &stop);
1376 seqnum = gst_event_get_seqnum (event);
1378 /* only forward streaming and seeking is possible */
1380 goto unsupported_seek;
1382 /* convert to TIME if needed and possible */
1383 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1387 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1388 * the original stop position to use when upstream pushes the new segment
1390 original_stop = stop;
1393 /* find reasonable corresponding BYTE position,
1394 * also try to mind about keyframes, since we can not go back a bit for them
1396 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, &key_cur, &byte_cur);
1401 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1402 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1405 GST_OBJECT_LOCK (qtdemux);
1406 qtdemux->seek_offset = byte_cur;
1407 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1408 qtdemux->push_seek_start = cur;
1410 qtdemux->push_seek_start = key_cur;
1413 if (stop_type == GST_SEEK_TYPE_NONE) {
1414 qtdemux->push_seek_stop = qtdemux->segment.stop;
1416 qtdemux->push_seek_stop = original_stop;
1418 GST_OBJECT_UNLOCK (qtdemux);
1420 /* BYTE seek event */
1421 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1423 gst_event_set_seqnum (event, seqnum);
1424 res = gst_pad_push_event (qtdemux->sinkpad, event);
1431 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1437 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1442 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1447 /* perform the seek.
1449 * We set all segment_indexes in the streams to unknown and
1450 * adjust the time_position to the desired position. this is enough
1451 * to trigger a segment switch in the streaming thread to start
1452 * streaming from the desired position.
1454 * Keyframe seeking is a little more complicated when dealing with
1455 * segments. Ideally we want to move to the previous keyframe in
1456 * the segment but there might not be a keyframe in the segment. In
1457 * fact, none of the segments could contain a keyframe. We take a
1458 * practical approach: seek to the previous keyframe in the segment,
1459 * if there is none, seek to the beginning of the segment.
1461 * Called with STREAM_LOCK
1464 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1465 guint32 seqnum, GstSeekFlags flags)
1467 gint64 desired_offset;
1470 desired_offset = segment->position;
1472 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1473 GST_TIME_ARGS (desired_offset));
1475 /* may not have enough fragmented info to do this adjustment,
1476 * and we can't scan (and probably should not) at this time with
1477 * possibly flushing upstream */
1478 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1481 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, &min_offset, NULL);
1482 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1483 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1484 desired_offset = min_offset;
1487 /* and set all streams to the final position */
1488 gst_flow_combiner_reset (qtdemux->flowcombiner);
1489 for (n = 0; n < qtdemux->n_streams; n++) {
1490 QtDemuxStream *stream = qtdemux->streams[n];
1492 stream->time_position = desired_offset;
1493 stream->accumulated_base = 0;
1494 stream->sample_index = -1;
1495 stream->offset_in_sample = 0;
1496 stream->segment_index = -1;
1497 stream->sent_eos = FALSE;
1498 stream->segment_seqnum = seqnum;
1500 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1501 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1503 segment->position = desired_offset;
1504 segment->time = desired_offset;
1505 if (segment->rate >= 0) {
1506 segment->start = desired_offset;
1508 /* we stop at the end */
1509 if (segment->stop == -1)
1510 segment->stop = segment->duration;
1512 segment->stop = desired_offset;
1515 if (qtdemux->fragmented)
1516 qtdemux->fragmented_seek_pending = TRUE;
1521 /* do a seek in pull based mode */
1523 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1528 GstSeekType cur_type, stop_type;
1532 GstSegment seeksegment;
1534 GstEvent *flush_event;
1537 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1539 gst_event_parse_seek (event, &rate, &format, &flags,
1540 &cur_type, &cur, &stop_type, &stop);
1541 seqnum = gst_event_get_seqnum (event);
1543 /* we have to have a format as the segment format. Try to convert
1545 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1549 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1551 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1555 flush = flags & GST_SEEK_FLAG_FLUSH;
1557 /* stop streaming, either by flushing or by pausing the task */
1559 flush_event = gst_event_new_flush_start ();
1561 gst_event_set_seqnum (flush_event, seqnum);
1562 /* unlock upstream pull_range */
1563 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1564 /* make sure out loop function exits */
1565 gst_qtdemux_push_event (qtdemux, flush_event);
1567 /* non flushing seek, pause the task */
1568 gst_pad_pause_task (qtdemux->sinkpad);
1571 /* wait for streaming to finish */
1572 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1574 /* copy segment, we need this because we still need the old
1575 * segment when we close the current segment. */
1576 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1579 /* configure the segment with the seek variables */
1580 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1581 gst_segment_do_seek (&seeksegment, rate, format, flags,
1582 cur_type, cur, stop_type, stop, &update);
1585 /* now do the seek, this actually never returns FALSE */
1586 gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1588 /* prepare for streaming again */
1590 flush_event = gst_event_new_flush_stop (TRUE);
1592 gst_event_set_seqnum (flush_event, seqnum);
1594 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1595 gst_qtdemux_push_event (qtdemux, flush_event);
1598 /* commit the new segment */
1599 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1601 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1602 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1603 qtdemux->segment.format, qtdemux->segment.position);
1605 gst_message_set_seqnum (msg, seqnum);
1606 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1609 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1610 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1611 qtdemux->sinkpad, NULL);
1613 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1620 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1626 qtdemux_ensure_index (GstQTDemux * qtdemux)
1630 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1632 /* Build complete index */
1633 for (i = 0; i < qtdemux->n_streams; i++) {
1634 QtDemuxStream *stream = qtdemux->streams[i];
1636 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1644 GST_LOG_OBJECT (qtdemux,
1645 "Building complete index of stream %u for seeking failed!", i);
1651 gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1654 gboolean res = TRUE;
1655 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1657 switch (GST_EVENT_TYPE (event)) {
1658 case GST_EVENT_SEEK:
1660 #ifndef GST_DISABLE_GST_DEBUG
1661 GstClockTime ts = gst_util_get_timestamp ();
1664 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1665 /* seek should be handled by upstream, we might need to re-download fragments */
1666 GST_DEBUG_OBJECT (qtdemux,
1667 "let upstream handle seek for fragmented playback");
1671 /* Build complete index for seeking;
1672 * if not a fragmented file at least */
1673 if (!qtdemux->fragmented)
1674 if (!qtdemux_ensure_index (qtdemux))
1676 #ifndef GST_DISABLE_GST_DEBUG
1677 ts = gst_util_get_timestamp () - ts;
1678 GST_INFO_OBJECT (qtdemux,
1679 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1682 if (qtdemux->pullbased) {
1683 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1684 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1685 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1687 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
1688 && !qtdemux->fragmented) {
1689 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1691 GST_DEBUG_OBJECT (qtdemux,
1692 "ignoring seek in push mode in current state");
1695 gst_event_unref (event);
1698 case GST_EVENT_NAVIGATION:
1700 gst_event_unref (event);
1704 res = gst_pad_event_default (pad, parent, event);
1714 GST_ERROR_OBJECT (qtdemux, "Index failed");
1715 gst_event_unref (event);
1721 /* stream/index return sample that is min/max w.r.t. byte position,
1722 * time is min/max w.r.t. time of samples,
1723 * the latter need not be time of the former sample */
1725 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1726 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1729 gint64 time, min_time;
1730 QtDemuxStream *stream;
1736 for (n = 0; n < qtdemux->n_streams; ++n) {
1739 gboolean set_sample;
1741 str = qtdemux->streams[n];
1748 i = str->n_samples - 1;
1752 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1753 if (str->samples[i].size == 0)
1756 if (fw && (str->samples[i].offset < byte_pos))
1759 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1762 /* move stream to first available sample */
1764 gst_qtdemux_move_stream (qtdemux, str, i);
1768 /* avoid index from sparse streams since they might be far away */
1770 /* determine min/max time */
1771 time = QTSAMPLE_PTS (str, &str->samples[i]);
1772 if (min_time == -1 || (!fw && time > min_time) ||
1773 (fw && time < min_time)) {
1777 /* determine stream with leading sample, to get its position */
1779 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1780 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1788 /* no sample for this stream, mark eos */
1790 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1801 static QtDemuxStream *
1802 _create_stream (void)
1804 QtDemuxStream *stream;
1806 stream = g_new0 (QtDemuxStream, 1);
1807 /* new streams always need a discont */
1808 stream->discont = TRUE;
1809 /* we enable clipping for raw audio/video streams */
1810 stream->need_clip = FALSE;
1811 stream->need_process = FALSE;
1812 stream->segment_index = -1;
1813 stream->time_position = 0;
1814 stream->sample_index = -1;
1815 stream->offset_in_sample = 0;
1816 stream->new_stream = TRUE;
1817 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1818 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1819 stream->protected = FALSE;
1820 stream->protection_scheme_type = 0;
1821 stream->protection_scheme_version = 0;
1822 stream->protection_scheme_info = NULL;
1823 stream->n_samples_moof = 0;
1824 stream->duration_moof = 0;
1825 g_queue_init (&stream->protection_scheme_event_queue);
1830 gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1832 GstStructure *structure;
1833 const gchar *variant;
1834 const GstCaps *mediacaps = NULL;
1836 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1838 structure = gst_caps_get_structure (caps, 0);
1839 variant = gst_structure_get_string (structure, "variant");
1841 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1842 QtDemuxStream *stream;
1843 const GValue *value;
1845 demux->fragmented = TRUE;
1846 demux->mss_mode = TRUE;
1848 if (demux->n_streams > 1) {
1849 /* can't do this, we can only renegotiate for another mss format */
1853 value = gst_structure_get_value (structure, "media-caps");
1856 const GValue *timescale_v;
1858 /* TODO update when stream changes during playback */
1860 if (demux->n_streams == 0) {
1861 stream = _create_stream ();
1862 demux->streams[demux->n_streams] = stream;
1863 demux->n_streams = 1;
1865 stream = demux->streams[0];
1868 timescale_v = gst_structure_get_value (structure, "timescale");
1870 stream->timescale = g_value_get_uint64 (timescale_v);
1872 /* default mss timescale */
1873 stream->timescale = 10000000;
1875 demux->timescale = stream->timescale;
1877 mediacaps = gst_value_get_caps (value);
1878 if (!stream->caps || !gst_caps_is_equal_fixed (mediacaps, stream->caps)) {
1879 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1881 stream->new_caps = TRUE;
1883 gst_caps_replace (&stream->caps, (GstCaps *) mediacaps);
1884 structure = gst_caps_get_structure (mediacaps, 0);
1885 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
1886 stream->subtype = FOURCC_vide;
1888 gst_structure_get_int (structure, "width", &stream->width);
1889 gst_structure_get_int (structure, "height", &stream->height);
1890 gst_structure_get_fraction (structure, "framerate", &stream->fps_n,
1892 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1894 stream->subtype = FOURCC_soun;
1895 gst_structure_get_int (structure, "channels", &stream->n_channels);
1896 gst_structure_get_int (structure, "rate", &rate);
1897 stream->rate = rate;
1900 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1902 demux->mss_mode = FALSE;
1909 gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
1913 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
1914 gst_pad_stop_task (qtdemux->sinkpad);
1916 if (hard || qtdemux->upstream_format_is_time) {
1917 qtdemux->state = QTDEMUX_STATE_INITIAL;
1918 qtdemux->neededbytes = 16;
1919 qtdemux->todrop = 0;
1920 qtdemux->pullbased = FALSE;
1921 qtdemux->posted_redirect = FALSE;
1922 qtdemux->first_mdat = -1;
1923 qtdemux->header_size = 0;
1924 qtdemux->mdatoffset = -1;
1925 qtdemux->restoredata_offset = -1;
1926 if (qtdemux->mdatbuffer)
1927 gst_buffer_unref (qtdemux->mdatbuffer);
1928 if (qtdemux->restoredata_buffer)
1929 gst_buffer_unref (qtdemux->restoredata_buffer);
1930 qtdemux->mdatbuffer = NULL;
1931 qtdemux->restoredata_buffer = NULL;
1932 qtdemux->mdatleft = 0;
1933 if (qtdemux->comp_brands)
1934 gst_buffer_unref (qtdemux->comp_brands);
1935 qtdemux->comp_brands = NULL;
1936 qtdemux->last_moov_offset = -1;
1937 if (qtdemux->moov_node)
1938 g_node_destroy (qtdemux->moov_node);
1939 qtdemux->moov_node = NULL;
1940 qtdemux->moov_node_compressed = NULL;
1941 if (qtdemux->tag_list)
1942 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
1943 qtdemux->tag_list = NULL;
1945 if (qtdemux->element_index)
1946 gst_object_unref (qtdemux->element_index);
1947 qtdemux->element_index = NULL;
1949 qtdemux->major_brand = 0;
1950 if (qtdemux->pending_newsegment)
1951 gst_event_unref (qtdemux->pending_newsegment);
1952 qtdemux->pending_newsegment = NULL;
1953 qtdemux->upstream_format_is_time = FALSE;
1954 qtdemux->upstream_seekable = FALSE;
1955 qtdemux->upstream_size = 0;
1957 qtdemux->fragment_start = -1;
1958 qtdemux->fragment_start_offset = -1;
1959 qtdemux->duration = 0;
1960 qtdemux->moof_offset = 0;
1961 qtdemux->chapters_track_id = 0;
1962 qtdemux->have_group_id = FALSE;
1963 qtdemux->group_id = G_MAXUINT;
1965 if (qtdemux->protection_system_ids) {
1966 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
1967 qtdemux->protection_system_ids = NULL;
1969 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
1971 g_queue_clear (&qtdemux->protection_event_queue);
1973 qtdemux->offset = 0;
1974 gst_adapter_clear (qtdemux->adapter);
1975 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
1978 for (n = 0; n < qtdemux->n_streams; n++) {
1979 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
1980 qtdemux->streams[n] = NULL;
1982 qtdemux->n_streams = 0;
1983 qtdemux->n_video_streams = 0;
1984 qtdemux->n_audio_streams = 0;
1985 qtdemux->n_sub_streams = 0;
1986 qtdemux->exposed = FALSE;
1987 qtdemux->fragmented = FALSE;
1988 qtdemux->mss_mode = FALSE;
1989 gst_caps_replace (&qtdemux->media_caps, NULL);
1990 qtdemux->timescale = 0;
1991 qtdemux->got_moov = FALSE;
1992 qtdemux->pending_configure = FALSE;
1993 } else if (qtdemux->mss_mode) {
1994 gst_flow_combiner_reset (qtdemux->flowcombiner);
1995 for (n = 0; n < qtdemux->n_streams; n++)
1996 gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]);
1998 gst_flow_combiner_reset (qtdemux->flowcombiner);
1999 for (n = 0; n < qtdemux->n_streams; n++) {
2000 qtdemux->streams[n]->sent_eos = FALSE;
2001 qtdemux->streams[n]->segment_seqnum = 0;
2002 qtdemux->streams[n]->time_position = 0;
2003 qtdemux->streams[n]->accumulated_base = 0;
2005 if (!qtdemux->pending_newsegment) {
2006 qtdemux->pending_newsegment = gst_event_new_segment (&qtdemux->segment);
2012 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2015 GstQTDemux *demux = GST_QTDEMUX (parent);
2016 gboolean res = TRUE;
2018 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2020 switch (GST_EVENT_TYPE (event)) {
2021 case GST_EVENT_SEGMENT:
2024 QtDemuxStream *stream;
2027 GstEvent *segment_event;
2029 /* some debug output */
2030 gst_event_copy_segment (event, &segment);
2031 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2034 if (segment.format == GST_FORMAT_TIME) {
2035 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
2036 gst_event_replace (&demux->pending_newsegment, event);
2037 demux->upstream_format_is_time = TRUE;
2039 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2040 "not in time format");
2042 /* chain will send initial newsegment after pads have been added */
2043 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
2044 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2049 /* check if this matches a time seek we received previously
2050 * FIXME for backwards compatibility reasons we use the
2051 * seek_offset here to compare. In the future we might want to
2052 * change this to use the seqnum as it uniquely should identify
2053 * the segment that corresponds to the seek. */
2054 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2055 ", received segment offset %" G_GINT64_FORMAT,
2056 demux->seek_offset, segment.start);
2057 if (segment.format == GST_FORMAT_BYTES
2058 && demux->seek_offset == segment.start) {
2059 GST_OBJECT_LOCK (demux);
2060 offset = segment.start;
2062 segment.format = GST_FORMAT_TIME;
2063 segment.start = demux->push_seek_start;
2064 segment.stop = demux->push_seek_stop;
2065 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2066 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2067 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2068 GST_OBJECT_UNLOCK (demux);
2071 /* we only expect a BYTE segment, e.g. following a seek */
2072 if (segment.format == GST_FORMAT_BYTES) {
2073 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2074 offset = segment.start;
2076 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2077 NULL, (gint64 *) & segment.start);
2078 if ((gint64) segment.start < 0)
2081 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2082 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2083 NULL, (gint64 *) & segment.stop);
2084 /* keyframe seeking should already arrange for start >= stop,
2085 * but make sure in other rare cases */
2086 segment.stop = MAX (segment.stop, segment.start);
2088 } else if (segment.format == GST_FORMAT_TIME) {
2089 /* push all data on the adapter before starting this
2091 gst_qtdemux_process_adapter (demux, TRUE);
2093 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2097 /* accept upstream's notion of segment and distribute along */
2098 segment.format = GST_FORMAT_TIME;
2099 segment.position = segment.time = segment.start;
2100 segment.duration = demux->segment.duration;
2101 segment.base = gst_segment_to_running_time (&demux->segment,
2102 GST_FORMAT_TIME, demux->segment.position);
2104 gst_segment_copy_into (&segment, &demux->segment);
2105 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2106 segment_event = gst_event_new_segment (&segment);
2107 gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event));
2108 gst_qtdemux_push_event (demux, segment_event);
2110 /* clear leftover in current segment, if any */
2111 gst_adapter_clear (demux->adapter);
2113 /* set up streaming thread */
2114 demux->offset = offset;
2115 if (demux->upstream_format_is_time) {
2116 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2117 "set values to restart reading from a new atom");
2118 demux->neededbytes = 16;
2121 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2124 demux->todrop = stream->samples[idx].offset - offset;
2125 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2127 /* set up for EOS */
2128 demux->neededbytes = -1;
2133 gst_event_unref (event);
2137 case GST_EVENT_FLUSH_START:
2139 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2140 gst_event_unref (event);
2145 case GST_EVENT_FLUSH_STOP:
2149 dur = demux->segment.duration;
2150 gst_qtdemux_reset (demux, FALSE);
2151 demux->segment.duration = dur;
2153 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2154 gst_event_unref (event);
2160 /* If we are in push mode, and get an EOS before we've seen any streams,
2161 * then error out - we have nowhere to send the EOS */
2162 if (!demux->pullbased) {
2164 gboolean has_valid_stream = FALSE;
2165 for (i = 0; i < demux->n_streams; i++) {
2166 if (demux->streams[i]->pad != NULL) {
2167 has_valid_stream = TRUE;
2171 if (!has_valid_stream)
2172 gst_qtdemux_post_no_playable_stream_error (demux);
2174 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2175 (guint) gst_adapter_available (demux->adapter));
2176 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2182 case GST_EVENT_CAPS:{
2183 GstCaps *caps = NULL;
2185 gst_event_parse_caps (event, &caps);
2186 gst_qtdemux_setcaps (demux, caps);
2188 gst_event_unref (event);
2191 case GST_EVENT_PROTECTION:
2193 const gchar *system_id = NULL;
2195 gst_event_parse_protection (event, &system_id, NULL, NULL);
2196 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2198 gst_qtdemux_append_protection_system_id (demux, system_id);
2199 /* save the event for later, for source pads that have not been created */
2200 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2201 /* send it to all pads that already exist */
2202 gst_qtdemux_push_event (demux, event);
2210 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2218 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2220 GstQTDemux *demux = GST_QTDEMUX (element);
2222 GST_OBJECT_LOCK (demux);
2223 if (demux->element_index)
2224 gst_object_unref (demux->element_index);
2226 demux->element_index = gst_object_ref (index);
2228 demux->element_index = NULL;
2230 GST_OBJECT_UNLOCK (demux);
2231 /* object lock might be taken again */
2233 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2234 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2235 demux->element_index, demux->index_id);
2239 gst_qtdemux_get_index (GstElement * element)
2241 GstIndex *result = NULL;
2242 GstQTDemux *demux = GST_QTDEMUX (element);
2244 GST_OBJECT_LOCK (demux);
2245 if (demux->element_index)
2246 result = gst_object_ref (demux->element_index);
2247 GST_OBJECT_UNLOCK (demux);
2249 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2256 gst_qtdemux_stbl_free (QtDemuxStream * stream)
2258 g_free ((gpointer) stream->stco.data);
2259 stream->stco.data = NULL;
2260 g_free ((gpointer) stream->stsz.data);
2261 stream->stsz.data = NULL;
2262 g_free ((gpointer) stream->stsc.data);
2263 stream->stsc.data = NULL;
2264 g_free ((gpointer) stream->stts.data);
2265 stream->stts.data = NULL;
2266 g_free ((gpointer) stream->stss.data);
2267 stream->stss.data = NULL;
2268 g_free ((gpointer) stream->stps.data);
2269 stream->stps.data = NULL;
2270 g_free ((gpointer) stream->ctts.data);
2271 stream->ctts.data = NULL;
2275 gst_qtdemux_stream_flush_segments_data (GstQTDemux * qtdemux,
2276 QtDemuxStream * stream)
2278 g_free (stream->segments);
2279 stream->segments = NULL;
2280 stream->segment_index = -1;
2281 stream->accumulated_base = 0;
2285 gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux,
2286 QtDemuxStream * stream)
2288 g_free (stream->samples);
2289 stream->samples = NULL;
2290 gst_qtdemux_stbl_free (stream);
2293 g_free (stream->ra_entries);
2294 stream->ra_entries = NULL;
2295 stream->n_ra_entries = 0;
2297 stream->sample_index = -1;
2298 stream->stbl_index = -1;
2299 stream->n_samples = 0;
2300 stream->time_position = 0;
2302 stream->n_samples_moof = 0;
2303 stream->duration_moof = 0;
2307 gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream)
2309 if (stream->allocator)
2310 gst_object_unref (stream->allocator);
2311 while (stream->buffers) {
2312 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2313 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2315 if (stream->rgb8_palette) {
2316 gst_memory_unref (stream->rgb8_palette);
2317 stream->rgb8_palette = NULL;
2320 if (stream->pending_tags)
2321 gst_tag_list_unref (stream->pending_tags);
2322 stream->pending_tags = NULL;
2323 g_free (stream->redirect_uri);
2324 stream->redirect_uri = NULL;
2325 stream->sent_eos = FALSE;
2326 stream->sparse = FALSE;
2327 stream->protected = FALSE;
2328 if (stream->protection_scheme_info) {
2329 if (stream->protection_scheme_type == FOURCC_cenc) {
2330 QtDemuxCencSampleSetInfo *info =
2331 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2332 if (info->default_properties)
2333 gst_structure_free (info->default_properties);
2334 if (info->crypto_info)
2335 g_ptr_array_free (info->crypto_info, TRUE);
2337 g_free (stream->protection_scheme_info);
2338 stream->protection_scheme_info = NULL;
2340 stream->protection_scheme_type = 0;
2341 stream->protection_scheme_version = 0;
2342 g_queue_foreach (&stream->protection_scheme_event_queue,
2343 (GFunc) gst_event_unref, NULL);
2344 g_queue_clear (&stream->protection_scheme_event_queue);
2345 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
2346 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
2350 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
2352 gst_qtdemux_stream_clear (qtdemux, stream);
2354 gst_caps_unref (stream->caps);
2355 stream->caps = NULL;
2357 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
2358 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
2364 gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int i)
2366 g_assert (i >= 0 && i < qtdemux->n_streams && qtdemux->streams[i] != NULL);
2368 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[i]);
2369 qtdemux->streams[i] = qtdemux->streams[qtdemux->n_streams - 1];
2370 qtdemux->streams[qtdemux->n_streams - 1] = NULL;
2371 qtdemux->n_streams--;
2374 static GstStateChangeReturn
2375 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2377 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2378 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2380 switch (transition) {
2381 case GST_STATE_CHANGE_PAUSED_TO_READY:
2387 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2389 switch (transition) {
2390 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2391 gst_qtdemux_reset (qtdemux, TRUE);
2402 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2404 /* counts as header data */
2405 qtdemux->header_size += length;
2407 /* only consider at least a sufficiently complete ftyp atom */
2411 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2412 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2413 GST_FOURCC_ARGS (qtdemux->major_brand));
2414 if (qtdemux->comp_brands)
2415 gst_buffer_unref (qtdemux->comp_brands);
2416 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2417 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2422 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
2423 GstTagList * xmptaglist)
2425 /* Strip out bogus fields */
2427 if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
2428 gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
2429 gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
2431 gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
2434 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
2436 /* prioritize native tags using _KEEP mode */
2437 gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
2438 gst_tag_list_unref (xmptaglist);
2443 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2445 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2446 0x97, 0xA9, 0x42, 0xE8,
2447 0x9C, 0x71, 0x99, 0x94,
2448 0x91, 0xE3, 0xAF, 0xAC
2450 static const guint8 playready_uuid[] = {
2451 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2452 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2456 /* counts as header data */
2457 qtdemux->header_size += length;
2459 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2461 if (length <= offset + 16) {
2462 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2466 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2468 GstTagList *taglist;
2470 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
2471 length - offset - 16, NULL);
2472 taglist = gst_tag_list_from_xmp_buffer (buf);
2473 gst_buffer_unref (buf);
2475 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
2477 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
2479 const gunichar2 *s_utf16;
2482 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
2483 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
2484 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
2485 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
2489 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
2490 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
2493 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
2494 GST_READ_UINT32_LE (buffer + offset),
2495 GST_READ_UINT32_LE (buffer + offset + 4),
2496 GST_READ_UINT32_LE (buffer + offset + 8),
2497 GST_READ_UINT32_LE (buffer + offset + 12));
2502 qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2504 GstSidxParser sidx_parser;
2505 GstIsoffParserResult res;
2508 gst_isoff_qt_sidx_parser_init (&sidx_parser);
2511 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
2513 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
2514 if (res == GST_ISOFF_QT_PARSER_DONE) {
2515 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
2517 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
2520 /* caller verifies at least 8 bytes in buf */
2522 extract_initial_length_and_fourcc (const guint8 * data, guint size,
2523 guint64 * plength, guint32 * pfourcc)
2528 length = QT_UINT32 (data);
2529 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2530 fourcc = QT_FOURCC (data + 4);
2531 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
2534 length = G_MAXUINT64;
2535 } else if (length == 1 && size >= 16) {
2536 /* this means we have an extended size, which is the 64 bit value of
2537 * the next 8 bytes */
2538 length = QT_UINT64 (data + 8);
2539 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2549 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
2551 guint32 version = 0;
2552 GstClockTime duration = 0;
2554 if (!gst_byte_reader_get_uint32_be (br, &version))
2559 if (!gst_byte_reader_get_uint64_be (br, &duration))
2564 if (!gst_byte_reader_get_uint32_be (br, &dur))
2569 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
2570 qtdemux->duration = duration;
2576 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
2582 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
2583 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
2585 if (!stream->parsed_trex && qtdemux->moov_node) {
2587 GstByteReader trex_data;
2589 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
2591 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
2594 guint32 id = 0, dur = 0, size = 0, flags = 0, dummy = 0;
2596 /* skip version/flags */
2597 if (!gst_byte_reader_skip (&trex_data, 4))
2599 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
2601 if (id != stream->track_id)
2603 /* sample description index; ignore */
2604 if (!gst_byte_reader_get_uint32_be (&trex_data, &dummy))
2606 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2608 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
2610 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
2613 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
2614 "duration %d, size %d, flags 0x%x", stream->track_id,
2617 stream->parsed_trex = TRUE;
2618 stream->def_sample_duration = dur;
2619 stream->def_sample_size = size;
2620 stream->def_sample_flags = flags;
2623 /* iterate all siblings */
2624 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
2630 *ds_duration = stream->def_sample_duration;
2631 *ds_size = stream->def_sample_size;
2632 *ds_flags = stream->def_sample_flags;
2634 /* even then, above values are better than random ... */
2635 if (G_UNLIKELY (!stream->parsed_trex)) {
2636 GST_WARNING_OBJECT (qtdemux,
2637 "failed to find fragment defaults for stream %d", stream->track_id);
2644 /* This method should be called whenever a more accurate duration might
2645 * have been found. It will update all relevant variables if/where needed
2648 check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
2652 GstClockTime prevdur;
2654 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
2656 if (movdur > qtdemux->duration) {
2657 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
2658 GST_DEBUG_OBJECT (qtdemux,
2659 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
2660 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
2661 qtdemux->duration = movdur;
2662 GST_DEBUG_OBJECT (qtdemux,
2663 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
2664 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
2665 GST_TIME_ARGS (qtdemux->segment.stop));
2666 if (qtdemux->segment.duration == prevdur) {
2667 /* If the current segment has duration/stop identical to previous duration
2668 * update them also (because they were set at that point in time with
2669 * the wrong duration */
2670 /* We convert the value *from* the timescale version to avoid rounding errors */
2671 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
2672 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
2673 qtdemux->segment.duration = fixeddur;
2674 qtdemux->segment.stop = fixeddur;
2677 for (i = 0; i < qtdemux->n_streams; i++) {
2678 QtDemuxStream *stream = qtdemux->streams[i];
2680 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
2681 if (movdur > stream->duration) {
2682 GST_DEBUG_OBJECT (qtdemux,
2683 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
2684 GST_TIME_ARGS (duration));
2685 stream->duration = movdur;
2686 if (stream->dummy_segment) {
2687 /* Update all dummy values to new duration */
2688 stream->segments[0].stop_time = duration;
2689 stream->segments[0].duration = duration;
2690 stream->segments[0].media_stop = duration;
2698 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
2699 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
2700 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
2701 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts)
2703 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
2705 gint32 data_offset = 0;
2706 guint32 flags = 0, first_flags = 0, samples_count = 0;
2709 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
2710 QtDemuxSample *sample;
2711 gboolean ismv = FALSE;
2713 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
2714 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
2715 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
2716 d_sample_size, d_sample_flags, *base_offset, decode_ts);
2718 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
2719 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
2723 /* presence of stss or not can't really tell us much,
2724 * and flags and so on tend to be marginally reliable in these files */
2725 if (stream->subtype == FOURCC_soun) {
2726 GST_DEBUG_OBJECT (qtdemux,
2727 "sound track in fragmented file; marking all keyframes");
2728 stream->all_keyframe = TRUE;
2731 if (!gst_byte_reader_skip (trun, 1) ||
2732 !gst_byte_reader_get_uint24_be (trun, &flags))
2735 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
2738 if (flags & TR_DATA_OFFSET) {
2739 /* note this is really signed */
2740 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
2742 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
2743 /* default base offset = first byte of moof */
2744 if (*base_offset == -1) {
2745 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
2746 *base_offset = moof_offset;
2748 *running_offset = *base_offset + data_offset;
2750 /* if no offset at all, that would mean data starts at moof start,
2751 * which is a bit wrong and is ismv crappy way, so compensate
2752 * assuming data is in mdat following moof */
2753 if (*base_offset == -1) {
2754 *base_offset = moof_offset + moof_length + 8;
2755 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
2758 if (*running_offset == -1)
2759 *running_offset = *base_offset;
2762 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
2764 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
2765 data_offset, flags, samples_count);
2767 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2768 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
2769 GST_DEBUG_OBJECT (qtdemux,
2770 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
2771 flags ^= TR_FIRST_SAMPLE_FLAGS;
2773 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
2775 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
2779 /* FIXME ? spec says other bits should also be checked to determine
2780 * entry size (and prefix size for that matter) */
2782 dur_offset = size_offset = 0;
2783 if (flags & TR_SAMPLE_DURATION) {
2784 GST_LOG_OBJECT (qtdemux, "entry duration present");
2785 dur_offset = entry_size;
2788 if (flags & TR_SAMPLE_SIZE) {
2789 GST_LOG_OBJECT (qtdemux, "entry size present");
2790 size_offset = entry_size;
2793 if (flags & TR_SAMPLE_FLAGS) {
2794 GST_LOG_OBJECT (qtdemux, "entry flags present");
2795 flags_offset = entry_size;
2798 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2799 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
2800 ct_offset = entry_size;
2804 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
2806 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
2808 if (stream->n_samples + samples_count >=
2809 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
2812 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
2813 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
2814 (stream->n_samples + samples_count) *
2815 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
2817 /* create a new array of samples if it's the first sample parsed */
2818 if (stream->n_samples == 0) {
2819 g_assert (stream->samples == NULL);
2820 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
2821 /* or try to reallocate it with space enough to insert the new samples */
2823 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
2824 stream->n_samples + samples_count);
2825 if (stream->samples == NULL)
2828 if (qtdemux->fragment_start != -1) {
2829 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
2830 qtdemux->fragment_start = -1;
2832 if (stream->n_samples == 0) {
2833 if (decode_ts > 0) {
2834 timestamp = decode_ts;
2835 } else if (stream->pending_seek != NULL) {
2836 /* if we don't have a timestamp from a tfdt box, we'll use the one
2837 * from the mfra seek table */
2838 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
2839 GST_TIME_ARGS (stream->pending_seek->ts));
2841 /* FIXME: this is not fully correct, the timestamp refers to the random
2842 * access sample refered to in the tfra entry, which may not necessarily
2843 * be the first sample in the tfrag/trun (but hopefully/usually is) */
2844 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
2849 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
2850 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
2851 GST_TIME_ARGS (gst_ts));
2853 /* subsequent fragments extend stream */
2855 stream->samples[stream->n_samples - 1].timestamp +
2856 stream->samples[stream->n_samples - 1].duration;
2858 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
2859 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
2860 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
2864 sample = stream->samples + stream->n_samples;
2865 for (i = 0; i < samples_count; i++) {
2866 guint32 dur, size, sflags, ct;
2868 /* first read sample data */
2869 if (flags & TR_SAMPLE_DURATION) {
2870 dur = QT_UINT32 (data + dur_offset);
2872 dur = d_sample_duration;
2874 if (flags & TR_SAMPLE_SIZE) {
2875 size = QT_UINT32 (data + size_offset);
2877 size = d_sample_size;
2879 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2881 sflags = first_flags;
2883 sflags = d_sample_flags;
2885 } else if (flags & TR_SAMPLE_FLAGS) {
2886 sflags = QT_UINT32 (data + flags_offset);
2888 sflags = d_sample_flags;
2890 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2891 ct = QT_UINT32 (data + ct_offset);
2897 /* fill the sample information */
2898 sample->offset = *running_offset;
2899 sample->pts_offset = ct;
2900 sample->size = size;
2901 sample->timestamp = timestamp;
2902 sample->duration = dur;
2903 /* sample-is-difference-sample */
2904 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
2905 * now idea how it relates to bitfield other than massive LE/BE confusion */
2906 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
2907 *running_offset += size;
2909 stream->duration_moof += dur;
2913 /* Update total duration if needed */
2914 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
2916 stream->n_samples += samples_count;
2917 stream->n_samples_moof += samples_count;
2919 if (stream->pending_seek != NULL)
2920 stream->pending_seek = NULL;
2926 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
2931 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
2937 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
2938 "be larger than %uMB (broken file?)", stream->n_samples,
2939 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
2944 /* find stream with @id */
2945 static inline QtDemuxStream *
2946 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
2948 QtDemuxStream *stream;
2952 if (G_UNLIKELY (!id)) {
2953 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
2957 /* try to get it fast and simple */
2958 if (G_LIKELY (id <= qtdemux->n_streams)) {
2959 stream = qtdemux->streams[id - 1];
2960 if (G_LIKELY (stream->track_id == id))
2964 /* linear search otherwise */
2965 for (i = 0; i < qtdemux->n_streams; i++) {
2966 stream = qtdemux->streams[i];
2967 if (stream->track_id == id)
2970 if (qtdemux->mss_mode) {
2971 /* mss should have only 1 stream anyway */
2972 return qtdemux->streams[0];
2979 qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
2980 guint32 * fragment_number)
2982 if (!gst_byte_reader_skip (mfhd, 4))
2984 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
2989 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
2995 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
2996 QtDemuxStream ** stream, guint32 * default_sample_duration,
2997 guint32 * default_sample_size, guint32 * default_sample_flags,
2998 gint64 * base_offset)
3001 guint32 track_id = 0;
3003 if (!gst_byte_reader_skip (tfhd, 1) ||
3004 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3007 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3010 *stream = qtdemux_find_stream (qtdemux, track_id);
3011 if (G_UNLIKELY (!*stream))
3012 goto unknown_stream;
3014 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3015 *base_offset = qtdemux->moof_offset;
3017 if (flags & TF_BASE_DATA_OFFSET)
3018 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3021 /* obtain stream defaults */
3022 qtdemux_parse_trex (qtdemux, *stream,
3023 default_sample_duration, default_sample_size, default_sample_flags);
3025 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
3026 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
3027 if (!gst_byte_reader_skip (tfhd, 4))
3030 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3031 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3034 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3035 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3038 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3039 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3046 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3051 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
3057 qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3058 guint64 * decode_time)
3060 guint32 version = 0;
3062 if (!gst_byte_reader_get_uint32_be (br, &version))
3067 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3070 guint32 dec_time = 0;
3071 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3073 *decode_time = dec_time;
3076 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3083 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3088 /* Returns a pointer to a GstStructure containing the properties of
3089 * the stream sample identified by @sample_index. The caller must unref
3090 * the returned object after use. Returns NULL if unsuccessful. */
3091 static GstStructure *
3092 qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3093 QtDemuxStream * stream, guint sample_index)
3095 QtDemuxCencSampleSetInfo *info = NULL;
3097 g_return_val_if_fail (stream != NULL, NULL);
3098 g_return_val_if_fail (stream->protected, NULL);
3099 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3101 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3103 /* Currently, cenc properties for groups of samples are not supported, so
3104 * simply return a copy of the default sample properties */
3105 return gst_structure_copy (info->default_properties);
3108 /* Parses the sizes of sample auxiliary information contained within a stream,
3109 * as given in a saiz box. Returns array of sample_count guint8 size values,
3110 * or NULL on failure */
3112 qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3113 GstByteReader * br, guint32 * sample_count)
3117 guint8 default_info_size;
3119 g_return_val_if_fail (qtdemux != NULL, NULL);
3120 g_return_val_if_fail (stream != NULL, NULL);
3121 g_return_val_if_fail (br != NULL, NULL);
3122 g_return_val_if_fail (sample_count != NULL, NULL);
3124 if (!gst_byte_reader_get_uint32_be (br, &flags))
3128 /* aux_info_type and aux_info_type_parameter are ignored */
3129 if (!gst_byte_reader_skip (br, 8))
3133 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3135 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3137 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3139 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3142 if (default_info_size == 0) {
3143 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3147 info_sizes = g_new (guint8, *sample_count);
3148 memset (info_sizes, default_info_size, *sample_count);
3154 /* Parses the offset of sample auxiliary information contained within a stream,
3155 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3157 qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3158 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3163 guint32 aux_info_type = 0;
3164 guint32 aux_info_type_parameter = 0;
3165 guint32 entry_count;
3168 const guint8 *aux_info_type_data = NULL;
3170 g_return_val_if_fail (qtdemux != NULL, FALSE);
3171 g_return_val_if_fail (stream != NULL, FALSE);
3172 g_return_val_if_fail (br != NULL, FALSE);
3173 g_return_val_if_fail (offset != NULL, FALSE);
3175 if (!gst_byte_reader_get_uint8 (br, &version))
3178 if (!gst_byte_reader_get_uint24_be (br, &flags))
3183 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3185 aux_info_type = QT_FOURCC (aux_info_type_data);
3187 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3189 } else if (stream->protected) {
3190 aux_info_type = stream->protection_scheme_type;
3192 aux_info_type = stream->fourcc;
3196 *info_type = aux_info_type;
3197 if (info_type_parameter)
3198 *info_type_parameter = aux_info_type_parameter;
3200 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3201 "aux_info_type_parameter: %#06x",
3202 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3204 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3207 if (entry_count != 1) {
3208 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3213 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3215 *offset = (guint64) off_32;
3217 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3222 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3227 qtdemux_gst_structure_free (GstStructure * gststructure)
3230 gst_structure_free (gststructure);
3234 /* Parses auxiliary information relating to samples protected using Common
3235 * Encryption (cenc); the format of this information is defined in
3236 * ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
3238 qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3239 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3241 QtDemuxCencSampleSetInfo *ss_info = NULL;
3245 g_return_val_if_fail (qtdemux != NULL, FALSE);
3246 g_return_val_if_fail (stream != NULL, FALSE);
3247 g_return_val_if_fail (br != NULL, FALSE);
3248 g_return_val_if_fail (stream->protected, FALSE);
3249 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3251 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3253 if (ss_info->crypto_info) {
3254 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
3255 g_ptr_array_free (ss_info->crypto_info, TRUE);
3258 ss_info->crypto_info =
3259 g_ptr_array_new_full (sample_count,
3260 (GDestroyNotify) qtdemux_gst_structure_free);
3262 for (i = 0; i < sample_count; ++i) {
3263 GstStructure *properties;
3264 guint16 n_subsamples = 0;
3269 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3270 if (properties == NULL) {
3271 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3274 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3275 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3276 gst_structure_free (properties);
3279 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
3280 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3281 gst_structure_free (properties);
3284 buf = gst_buffer_new_wrapped (data, iv_size);
3285 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3286 gst_buffer_unref (buf);
3287 size = info_sizes[i];
3288 if (size > iv_size) {
3289 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3290 || !(n_subsamples > 0)) {
3291 gst_structure_free (properties);
3292 GST_ERROR_OBJECT (qtdemux,
3293 "failed to get subsample count for sample %u", i);
3296 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3297 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3298 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3300 gst_structure_free (properties);
3303 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3305 gst_structure_free (properties);
3308 gst_structure_set (properties,
3309 "subsample_count", G_TYPE_UINT, n_subsamples,
3310 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3311 gst_buffer_unref (buf);
3313 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3315 g_ptr_array_add (ss_info->crypto_info, properties);
3320 /* Converts a UUID in raw byte form to a string representation, as defined in
3321 * RFC 4122. The caller takes ownership of the returned string and is
3322 * responsible for freeing it after use. */
3324 qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
3326 const guint8 *uuid = (const guint8 *) uuid_bytes;
3328 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3329 "%02x%02x-%02x%02x%02x%02x%02x%02x",
3330 uuid[0], uuid[1], uuid[2], uuid[3],
3331 uuid[4], uuid[5], uuid[6], uuid[7],
3332 uuid[8], uuid[9], uuid[10], uuid[11],
3333 uuid[12], uuid[13], uuid[14], uuid[15]);
3336 /* Parses a Protection System Specific Header box (pssh), as defined in the
3337 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
3338 * information needed by a specific content protection system in order to
3339 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
3342 qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
3344 gchar *sysid_string;
3345 guint32 pssh_size = QT_UINT32 (node->data);
3346 GstBuffer *pssh = NULL;
3347 GstEvent *event = NULL;
3348 guint32 parent_box_type;
3351 if (G_UNLIKELY (pssh_size < 32U)) {
3352 GST_ERROR_OBJECT (qtdemux, "invalid box size");
3357 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
3359 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
3361 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
3362 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
3363 gst_buffer_get_size (pssh));
3365 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
3367 /* Push an event containing the pssh box onto the queues of all streams. */
3368 event = gst_event_new_protection (sysid_string, pssh,
3369 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
3370 for (i = 0; i < qtdemux->n_streams; ++i) {
3371 g_queue_push_tail (&qtdemux->streams[i]->protection_scheme_event_queue,
3372 gst_event_ref (event));
3374 g_free (sysid_string);
3375 gst_event_unref (event);
3376 gst_buffer_unref (pssh);
3381 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
3382 guint64 moof_offset, QtDemuxStream * stream)
3384 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
3385 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
3386 GNode *saiz_node, *saio_node, *pssh_node;
3387 GstByteReader saiz_data, saio_data;
3388 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
3389 gint64 base_offset, running_offset;
3392 /* NOTE @stream ignored */
3394 moof_node = g_node_new ((guint8 *) buffer);
3395 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
3396 qtdemux_node_dump (qtdemux, moof_node);
3398 /* Get fragment number from mfhd and check it's valid */
3400 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
3401 if (mfhd_node == NULL)
3403 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
3405 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
3407 /* unknown base_offset to start with */
3408 base_offset = running_offset = -1;
3409 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
3411 guint64 decode_time = 0;
3413 /* Fragment Header node */
3415 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
3419 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
3420 &ds_size, &ds_flags, &base_offset))
3423 /* The following code assumes at most a single set of sample auxiliary
3424 * data in the fragment (consisting of a saiz box and a corresponding saio
3425 * box); in theory, however, there could be multiple sets of sample
3426 * auxiliary data in a fragment. */
3428 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
3431 guint32 info_type = 0;
3433 guint32 info_type_parameter = 0;
3435 g_free (qtdemux->cenc_aux_info_sizes);
3437 qtdemux->cenc_aux_info_sizes =
3438 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
3439 &qtdemux->cenc_aux_sample_count);
3440 if (qtdemux->cenc_aux_info_sizes == NULL) {
3441 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
3445 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
3448 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
3449 g_free (qtdemux->cenc_aux_info_sizes);
3450 qtdemux->cenc_aux_info_sizes = NULL;
3454 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
3455 &info_type, &info_type_parameter, &offset))) {
3456 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
3457 g_free (qtdemux->cenc_aux_info_sizes);
3458 qtdemux->cenc_aux_info_sizes = NULL;
3461 if (base_offset > qtdemux->moof_offset)
3462 offset += (guint64) (base_offset - qtdemux->moof_offset);
3463 if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
3465 if (offset > length) {
3466 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
3467 qtdemux->cenc_aux_info_offset = offset;
3469 gst_byte_reader_init (&br, buffer + offset, length - offset);
3470 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
3471 qtdemux->cenc_aux_info_sizes,
3472 qtdemux->cenc_aux_sample_count)) {
3473 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
3474 g_free (qtdemux->cenc_aux_info_sizes);
3475 qtdemux->cenc_aux_info_sizes = NULL;
3483 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
3486 GstClockTime decode_time_ts;
3488 /* We'll use decode_time to interpolate timestamps
3489 * in case the input timestamps are missing */
3490 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
3492 decode_time_ts = QTSTREAMTIME_TO_GSTTIME (stream, decode_time);
3494 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
3495 " (%" GST_TIME_FORMAT ")", decode_time,
3496 GST_TIME_ARGS (decode_time_ts));
3498 /* Discard the fragment buffer timestamp info to avoid using it.
3499 * Rely on tfdt instead as it is more accurate than the timestamp
3500 * that is fetched from a manifest/playlist and is usually
3502 qtdemux->fragment_start = -1;
3505 if (G_UNLIKELY (!stream)) {
3506 /* we lost track of offset, we'll need to regain it,
3507 * but can delay complaining until later or avoid doing so altogether */
3511 if (G_UNLIKELY (base_offset < -1))
3514 if (qtdemux->upstream_format_is_time)
3515 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
3517 /* initialise moof sample data */
3518 stream->n_samples_moof = 0;
3519 stream->duration_moof = 0;
3521 /* Track Run node */
3523 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
3526 qtdemux_parse_trun (qtdemux, &trun_data, stream,
3527 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
3528 &running_offset, decode_time);
3529 /* iterate all siblings */
3530 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
3533 /* if no new base_offset provided for next traf,
3534 * base is end of current traf */
3535 base_offset = running_offset;
3536 running_offset = -1;
3538 if (stream->n_samples_moof && stream->duration_moof)
3539 stream->new_caps = TRUE;
3542 /* iterate all siblings */
3543 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
3546 /* parse any protection system info */
3547 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
3549 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
3550 qtdemux_parse_pssh (qtdemux, pssh_node);
3551 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
3554 g_node_destroy (moof_node);
3559 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
3564 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
3569 GST_DEBUG_OBJECT (qtdemux, "lost offset");
3574 g_node_destroy (moof_node);
3575 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
3576 (_("This file is corrupt and cannot be played.")), (NULL));
3582 /* might be used if some day we actually use mfra & co
3583 * for random access to fragments,
3584 * but that will require quite some modifications and much less relying
3585 * on a sample array */
3589 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
3591 QtDemuxStream *stream;
3592 guint32 ver_flags, track_id, len, num_entries, i;
3593 guint value_size, traf_size, trun_size, sample_size;
3594 guint64 time = 0, moof_offset = 0;
3596 GstBuffer *buf = NULL;
3601 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
3603 if (!gst_byte_reader_skip (&tfra, 8))
3606 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
3609 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
3610 || !gst_byte_reader_get_uint32_be (&tfra, &len)
3611 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
3614 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
3616 stream = qtdemux_find_stream (qtdemux, track_id);
3618 goto unknown_trackid;
3620 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
3621 sample_size = (len & 3) + 1;
3622 trun_size = ((len & 12) >> 2) + 1;
3623 traf_size = ((len & 48) >> 4) + 1;
3625 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
3626 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
3628 if (num_entries == 0)
3631 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
3632 value_size + value_size + traf_size + trun_size + sample_size))
3635 g_free (stream->ra_entries);
3636 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
3637 stream->n_ra_entries = num_entries;
3639 for (i = 0; i < num_entries; i++) {
3640 qt_atom_parser_get_offset (&tfra, value_size, &time);
3641 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
3642 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
3643 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
3644 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
3646 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
3648 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
3649 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
3651 stream->ra_entries[i].ts = time;
3652 stream->ra_entries[i].moof_offset = moof_offset;
3654 /* don't want to go through the entire file and read all moofs at startup */
3656 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
3657 if (ret != GST_FLOW_OK)
3659 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
3660 moof_offset, stream);
3661 gst_buffer_unref (buf);
3665 check_update_duration (qtdemux, time);
3672 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
3677 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
3682 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
3688 qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
3690 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
3691 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
3692 GstBuffer *mfro = NULL, *mfra = NULL;
3694 gboolean ret = FALSE;
3695 GNode *mfra_node, *tfra_node;
3696 guint64 mfra_offset = 0;
3697 guint32 fourcc, mfra_size;
3700 /* query upstream size in bytes */
3701 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
3702 goto size_query_failed;
3704 /* mfro box should be at the very end of the file */
3705 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
3706 if (flow != GST_FLOW_OK)
3709 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
3711 fourcc = QT_FOURCC (mfro_map.data + 4);
3712 if (fourcc != FOURCC_mfro)
3715 GST_INFO_OBJECT (qtdemux, "Found mfro box");
3716 if (mfro_map.size < 16)
3717 goto invalid_mfro_size;
3719 mfra_size = QT_UINT32 (mfro_map.data + 12);
3720 if (mfra_size >= len)
3721 goto invalid_mfra_size;
3723 mfra_offset = len - mfra_size;
3725 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
3726 mfra_offset, mfra_size);
3728 /* now get and parse mfra box */
3729 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
3730 if (flow != GST_FLOW_OK)
3733 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
3735 mfra_node = g_node_new ((guint8 *) mfra_map.data);
3736 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
3738 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
3741 qtdemux_parse_tfra (qtdemux, tfra_node);
3742 /* iterate all siblings */
3743 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
3745 g_node_destroy (mfra_node);
3747 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
3753 if (mfro_map.memory != NULL)
3754 gst_buffer_unmap (mfro, &mfro_map);
3755 gst_buffer_unref (mfro);
3758 if (mfra_map.memory != NULL)
3759 gst_buffer_unmap (mfra, &mfra_map);
3760 gst_buffer_unref (mfra);
3767 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
3772 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
3777 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
3782 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
3788 add_offset (guint64 offset, guint64 advance)
3790 /* Avoid 64-bit overflow by clamping */
3791 if (offset > G_MAXUINT64 - advance)
3793 return offset + advance;
3796 static GstFlowReturn
3797 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
3801 GstBuffer *buf = NULL;
3802 GstFlowReturn ret = GST_FLOW_OK;
3803 guint64 cur_offset = qtdemux->offset;
3806 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
3807 if (G_UNLIKELY (ret != GST_FLOW_OK))
3809 gst_buffer_map (buf, &map, GST_MAP_READ);
3810 if (G_LIKELY (map.size >= 8))
3811 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
3812 gst_buffer_unmap (buf, &map);
3813 gst_buffer_unref (buf);
3815 /* maybe we already got most we needed, so only consider this eof */
3816 if (G_UNLIKELY (length == 0)) {
3817 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
3818 (_("Invalid atom size.")),
3819 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
3820 GST_FOURCC_ARGS (fourcc)));
3827 /* record for later parsing when needed */
3828 if (!qtdemux->moof_offset) {
3829 qtdemux->moof_offset = qtdemux->offset;
3831 if (qtdemux_pull_mfro_mfra (qtdemux)) {
3834 qtdemux->offset += length; /* skip moof and keep going */
3836 if (qtdemux->got_moov) {
3837 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
3848 GST_LOG_OBJECT (qtdemux,
3849 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
3850 GST_FOURCC_ARGS (fourcc), cur_offset);
3851 qtdemux->offset = add_offset (qtdemux->offset, length);
3856 GstBuffer *moov = NULL;
3858 if (qtdemux->got_moov) {
3859 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
3860 qtdemux->offset = add_offset (qtdemux->offset, length);
3864 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
3865 if (ret != GST_FLOW_OK)
3867 gst_buffer_map (moov, &map, GST_MAP_READ);
3869 if (length != map.size) {
3870 /* Some files have a 'moov' atom at the end of the file which contains
3871 * a terminal 'free' atom where the body of the atom is missing.
3872 * Check for, and permit, this special case.
3874 if (map.size >= 8) {
3875 guint8 *final_data = map.data + (map.size - 8);
3876 guint32 final_length = QT_UINT32 (final_data);
3877 guint32 final_fourcc = QT_FOURCC (final_data + 4);
3879 if (final_fourcc == FOURCC_free
3880 && map.size + final_length - 8 == length) {
3881 /* Ok, we've found that special case. Allocate a new buffer with
3882 * that free atom actually present. */
3883 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
3884 gst_buffer_fill (newmoov, 0, map.data, map.size);
3885 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
3886 gst_buffer_unmap (moov, &map);
3887 gst_buffer_unref (moov);
3889 gst_buffer_map (moov, &map, GST_MAP_READ);
3894 if (length != map.size) {
3895 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
3896 (_("This file is incomplete and cannot be played.")),
3897 ("We got less than expected (received %" G_GSIZE_FORMAT
3898 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
3899 (guint) length, cur_offset));
3900 gst_buffer_unmap (moov, &map);
3901 gst_buffer_unref (moov);
3902 ret = GST_FLOW_ERROR;
3905 qtdemux->offset += length;
3907 qtdemux_parse_moov (qtdemux, map.data, length);
3908 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
3910 qtdemux_parse_tree (qtdemux);
3911 g_node_destroy (qtdemux->moov_node);
3912 gst_buffer_unmap (moov, &map);
3913 gst_buffer_unref (moov);
3914 qtdemux->moov_node = NULL;
3915 qtdemux->got_moov = TRUE;
3921 GstBuffer *ftyp = NULL;
3923 /* extract major brand; might come in handy for ISO vs QT issues */
3924 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
3925 if (ret != GST_FLOW_OK)
3927 qtdemux->offset += length;
3928 gst_buffer_map (ftyp, &map, GST_MAP_READ);
3929 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
3930 gst_buffer_unmap (ftyp, &map);
3931 gst_buffer_unref (ftyp);
3936 GstBuffer *uuid = NULL;
3938 /* uuid are extension atoms */
3939 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
3940 if (ret != GST_FLOW_OK)
3942 qtdemux->offset += length;
3943 gst_buffer_map (uuid, &map, GST_MAP_READ);
3944 qtdemux_parse_uuid (qtdemux, map.data, map.size);
3945 gst_buffer_unmap (uuid, &map);
3946 gst_buffer_unref (uuid);
3951 GstBuffer *sidx = NULL;
3952 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
3953 if (ret != GST_FLOW_OK)
3955 qtdemux->offset += length;
3956 gst_buffer_map (sidx, &map, GST_MAP_READ);
3957 qtdemux_parse_sidx (qtdemux, map.data, map.size);
3958 gst_buffer_unmap (sidx, &map);
3959 gst_buffer_unref (sidx);
3964 GstBuffer *unknown = NULL;
3966 GST_LOG_OBJECT (qtdemux,
3967 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
3968 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
3970 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
3971 if (ret != GST_FLOW_OK)
3973 gst_buffer_map (unknown, &map, GST_MAP_READ);
3974 GST_MEMDUMP ("Unknown tag", map.data, map.size);
3975 gst_buffer_unmap (unknown, &map);
3976 gst_buffer_unref (unknown);
3977 qtdemux->offset += length;
3983 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
3984 /* digested all data, show what we have */
3985 qtdemux_prepare_streams (qtdemux);
3986 ret = qtdemux_expose_streams (qtdemux);
3988 qtdemux->state = QTDEMUX_STATE_MOVIE;
3989 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
3996 /* Seeks to the previous keyframe of the indexed stream and
3997 * aligns other streams with respect to the keyframe timestamp
3998 * of indexed stream. Only called in case of Reverse Playback
4000 static GstFlowReturn
4001 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4004 guint32 seg_idx = 0, k_index = 0;
4005 guint32 ref_seg_idx, ref_k_index;
4006 GstClockTime k_pos = 0, last_stop = 0;
4007 QtDemuxSegment *seg = NULL;
4008 QtDemuxStream *ref_str = NULL;
4009 guint64 seg_media_start_mov; /* segment media start time in mov format */
4012 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4013 * and finally align all the other streams on that timestamp with their
4014 * respective keyframes */
4015 for (n = 0; n < qtdemux->n_streams; n++) {
4016 QtDemuxStream *str = qtdemux->streams[n];
4018 /* No candidate yet, take the first stream */
4024 /* So that stream has a segment, we prefer video streams */
4025 if (str->subtype == FOURCC_vide) {
4031 if (G_UNLIKELY (!ref_str)) {
4032 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4036 if (G_UNLIKELY (!ref_str->from_sample)) {
4037 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4041 /* So that stream has been playing from from_sample to to_sample. We will
4042 * get the timestamp of the previous sample and search for a keyframe before
4043 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4044 if (ref_str->subtype == FOURCC_vide) {
4045 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4046 ref_str->from_sample - 1);
4048 if (ref_str->from_sample >= 10)
4049 k_index = ref_str->from_sample - 10;
4055 ref_str->samples[k_index].timestamp +
4056 ref_str->samples[k_index].pts_offset;
4058 /* get current segment for that stream */
4059 seg = &ref_str->segments[ref_str->segment_index];
4060 /* Use segment start in original timescale for comparisons */
4061 seg_media_start_mov = seg->trak_media_start;
4063 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4064 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT "\n",
4065 k_index, target_ts, seg_media_start_mov,
4066 GST_TIME_ARGS (seg->media_start));
4068 /* Crawl back through segments to find the one containing this I frame */
4069 while (target_ts < seg_media_start_mov) {
4070 GST_DEBUG_OBJECT (qtdemux,
4071 "keyframe position (sample %u) is out of segment %u " " target %"
4072 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4073 ref_str->segment_index, target_ts, seg_media_start_mov);
4075 if (G_UNLIKELY (!ref_str->segment_index)) {
4076 /* Reached first segment, let's consider it's EOS */
4079 ref_str->segment_index--;
4080 seg = &ref_str->segments[ref_str->segment_index];
4081 /* Use segment start in original timescale for comparisons */
4082 seg_media_start_mov = seg->trak_media_start;
4084 /* Calculate time position of the keyframe and where we should stop */
4086 QTSTREAMTIME_TO_GSTTIME (ref_str,
4087 target_ts - seg->trak_media_start) + seg->time;
4089 QTSTREAMTIME_TO_GSTTIME (ref_str,
4090 ref_str->samples[ref_str->from_sample].timestamp -
4091 seg->trak_media_start) + seg->time;
4093 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4094 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4095 k_index, GST_TIME_ARGS (k_pos));
4097 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4098 qtdemux->segment.position = last_stop;
4099 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4100 GST_TIME_ARGS (last_stop));
4102 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4103 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4107 ref_seg_idx = ref_str->segment_index;
4108 ref_k_index = k_index;
4110 /* Align them all on this */
4111 for (n = 0; n < qtdemux->n_streams; n++) {
4113 GstClockTime seg_time = 0;
4114 QtDemuxStream *str = qtdemux->streams[n];
4116 /* aligning reference stream again might lead to backing up to yet another
4117 * keyframe (due to timestamp rounding issues),
4118 * potentially putting more load on downstream; so let's try to avoid */
4119 if (str == ref_str) {
4120 seg_idx = ref_seg_idx;
4121 seg = &str->segments[seg_idx];
4122 k_index = ref_k_index;
4123 GST_DEBUG_OBJECT (qtdemux, "reference stream %d segment %d, "
4124 "sample at index %d", n, ref_str->segment_index, k_index);
4126 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4127 GST_DEBUG_OBJECT (qtdemux,
4128 "stream %d align segment %d for keyframe pos %" GST_TIME_FORMAT, n,
4129 seg_idx, GST_TIME_ARGS (k_pos));
4131 /* get segment and time in the segment */
4132 seg = &str->segments[seg_idx];
4133 seg_time = k_pos - seg->time;
4135 /* get the media time in the segment.
4136 * No adjustment for empty "filler" segments */
4137 if (seg->media_start != GST_CLOCK_TIME_NONE)
4138 seg_time += seg->media_start;
4140 /* get the index of the sample with media time */
4141 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4142 GST_DEBUG_OBJECT (qtdemux,
4143 "stream %d sample for %" GST_TIME_FORMAT " at %u", n,
4144 GST_TIME_ARGS (seg_time), index);
4146 /* find previous keyframe */
4147 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index);
4150 /* Remember until where we want to go */
4151 str->to_sample = str->from_sample - 1;
4152 /* Define our time position */
4154 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4155 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4156 if (seg->media_start != GST_CLOCK_TIME_NONE)
4157 str->time_position -= seg->media_start;
4159 /* Now seek back in time */
4160 gst_qtdemux_move_stream (qtdemux, str, k_index);
4161 GST_DEBUG_OBJECT (qtdemux, "stream %d keyframe at %u, time position %"
4162 GST_TIME_FORMAT " playing from sample %u to %u", n, k_index,
4163 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4169 return GST_FLOW_EOS;
4172 /* activate the given segment number @seg_idx of @stream at time @offset.
4173 * @offset is an absolute global position over all the segments.
4175 * This will push out a NEWSEGMENT event with the right values and
4176 * position the stream index to the first decodable sample before
4180 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4181 guint32 seg_idx, GstClockTime offset)
4184 QtDemuxSegment *segment;
4185 guint32 index, kf_index;
4186 GstClockTime seg_time;
4187 GstClockTime start, stop, time;
4190 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
4191 seg_idx, GST_TIME_ARGS (offset));
4193 /* update the current segment */
4194 stream->segment_index = seg_idx;
4196 /* get the segment */
4197 segment = &stream->segments[seg_idx];
4199 if (G_UNLIKELY (offset < segment->time)) {
4200 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
4201 GST_TIME_ARGS (segment->time));
4205 /* segment lies beyond total indicated duration */
4206 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
4207 segment->time > qtdemux->segment.duration)) {
4208 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
4209 " < segment->time %" GST_TIME_FORMAT,
4210 GST_TIME_ARGS (qtdemux->segment.duration),
4211 GST_TIME_ARGS (segment->time));
4215 /* get time in this segment */
4216 seg_time = offset - segment->time;
4218 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4219 GST_TIME_ARGS (seg_time));
4221 if (G_UNLIKELY (seg_time > segment->duration)) {
4222 GST_LOG_OBJECT (stream->pad,
4223 "seg_time > segment->duration %" GST_TIME_FORMAT,
4224 GST_TIME_ARGS (segment->duration));
4225 seg_time = segment->duration;
4228 /* qtdemux->segment.stop is in outside-time-realm, whereas
4229 * segment->media_stop is in track-time-realm.
4231 * In order to compare the two, we need to bring segment.stop
4232 * into the track-time-realm */
4234 stop = qtdemux->segment.stop;
4235 if (stop == GST_CLOCK_TIME_NONE)
4236 stop = qtdemux->segment.duration;
4237 if (stop == GST_CLOCK_TIME_NONE)
4238 stop = segment->media_stop;
4241 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4243 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4244 start = segment->time + seg_time;
4246 stop = start - seg_time + segment->duration;
4247 } else if (qtdemux->segment.rate >= 0) {
4248 start = MIN (segment->media_start + seg_time, stop);
4251 if (segment->media_start >= qtdemux->segment.start) {
4252 time = segment->time;
4254 time = segment->time + (qtdemux->segment.start - segment->media_start);
4257 start = MAX (segment->media_start, qtdemux->segment.start);
4258 stop = MIN (segment->media_start + seg_time, stop);
4261 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
4262 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
4263 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
4265 /* combine global rate with that of the segment */
4266 rate = segment->rate * qtdemux->segment.rate;
4268 /* Copy flags from main segment */
4269 stream->segment.flags = qtdemux->segment.flags;
4271 /* update the segment values used for clipping */
4272 stream->segment.offset = qtdemux->segment.offset;
4273 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
4274 stream->segment.applied_rate = qtdemux->segment.applied_rate;
4275 stream->segment.rate = rate;
4276 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
4277 stream->cslg_shift);
4278 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
4279 stream->cslg_shift);
4280 stream->segment.time = time;
4281 stream->segment.position = stream->segment.start;
4283 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
4286 /* now prepare and send the segment */
4288 event = gst_event_new_segment (&stream->segment);
4289 if (stream->segment_seqnum) {
4290 gst_event_set_seqnum (event, stream->segment_seqnum);
4292 gst_pad_push_event (stream->pad, event);
4293 /* assume we can send more data now */
4294 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
4295 /* clear to send tags on this pad now */
4296 gst_qtdemux_push_tags (qtdemux, stream);
4299 /* in the fragmented case, we pick a fragment that starts before our
4300 * desired position and rely on downstream to wait for a keyframe
4301 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
4302 * tfra entries tells us which trun/sample the key unit is in, but we don't
4303 * make use of this additional information at the moment) */
4304 if (qtdemux->fragmented) {
4305 stream->to_sample = G_MAXUINT32;
4309 /* We don't need to look for a sample in push-based */
4310 if (!qtdemux->pullbased)
4313 /* and move to the keyframe before the indicated media time of the
4315 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
4316 if (qtdemux->segment.rate >= 0) {
4317 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
4318 stream->to_sample = G_MAXUINT32;
4319 GST_DEBUG_OBJECT (stream->pad,
4320 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4321 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
4322 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4324 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
4325 stream->to_sample = index;
4326 GST_DEBUG_OBJECT (stream->pad,
4327 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
4328 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
4329 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
4332 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
4333 "this is an empty segment");
4337 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
4338 * encountered an error and printed a message so we return appropriately */
4342 /* we're at the right spot */
4343 if (index == stream->sample_index) {
4344 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
4348 /* find keyframe of the target index */
4349 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
4352 /* indent does stupid stuff with stream->samples[].timestamp */
4354 /* if we move forwards, we don't have to go back to the previous
4355 * keyframe since we already sent that. We can also just jump to
4356 * the keyframe right before the target index if there is one. */
4357 if (index > stream->sample_index) {
4358 /* moving forwards check if we move past a keyframe */
4359 if (kf_index > stream->sample_index) {
4360 GST_DEBUG_OBJECT (stream->pad,
4361 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
4362 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
4363 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
4364 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
4366 GST_DEBUG_OBJECT (stream->pad,
4367 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
4368 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
4369 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
4372 GST_DEBUG_OBJECT (stream->pad,
4373 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
4374 GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
4375 GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
4376 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
4384 /* prepare to get the current sample of @stream, getting essential values.
4386 * This function will also prepare and send the segment when needed.
4388 * Return FALSE if the stream is EOS.
4393 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
4394 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
4395 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
4396 gboolean * keyframe)
4398 QtDemuxSample *sample;
4399 GstClockTime time_position;
4402 g_return_val_if_fail (stream != NULL, FALSE);
4404 time_position = stream->time_position;
4405 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
4408 seg_idx = stream->segment_index;
4409 if (G_UNLIKELY (seg_idx == -1)) {
4410 /* find segment corresponding to time_position if we are looking
4412 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
4415 /* different segment, activate it, sample_index will be set. */
4416 if (G_UNLIKELY (stream->segment_index != seg_idx))
4417 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
4419 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream->
4421 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
4423 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
4424 " prepare empty sample");
4427 *pts = *dts = time_position;
4428 *duration = seg->duration - (time_position - seg->time);
4435 if (stream->sample_index == -1)
4436 stream->sample_index = 0;
4438 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
4439 stream->sample_index, stream->n_samples);
4441 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
4442 if (!qtdemux->fragmented)
4445 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
4449 GST_OBJECT_LOCK (qtdemux);
4450 flow = qtdemux_add_fragmented_samples (qtdemux);
4451 GST_OBJECT_UNLOCK (qtdemux);
4453 if (flow != GST_FLOW_OK)
4456 while (stream->sample_index >= stream->n_samples);
4459 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
4460 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
4461 stream->sample_index);
4465 /* now get the info for the sample we're at */
4466 sample = &stream->samples[stream->sample_index];
4468 *dts = QTSAMPLE_DTS (stream, sample);
4469 *pts = QTSAMPLE_PTS (stream, sample);
4470 *offset = sample->offset;
4471 *size = sample->size;
4472 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
4473 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
4480 stream->time_position = GST_CLOCK_TIME_NONE;
4485 /* move to the next sample in @stream.
4487 * Moves to the next segment when needed.
4490 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
4492 QtDemuxSample *sample;
4493 QtDemuxSegment *segment;
4495 /* get current segment */
4496 segment = &stream->segments[stream->segment_index];
4498 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4499 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
4503 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
4504 /* Mark the stream as EOS */
4505 GST_DEBUG_OBJECT (qtdemux,
4506 "reached max allowed sample %u, mark EOS", stream->to_sample);
4507 stream->time_position = GST_CLOCK_TIME_NONE;
4511 /* move to next sample */
4512 stream->sample_index++;
4513 stream->offset_in_sample = 0;
4515 /* reached the last sample, we need the next segment */
4516 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
4519 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
4520 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
4521 stream->sample_index);
4525 /* get next sample */
4526 sample = &stream->samples[stream->sample_index];
4528 /* see if we are past the segment */
4529 if (G_UNLIKELY (QTSAMPLE_DTS (stream, sample) >= segment->media_stop))
4532 if (QTSAMPLE_DTS (stream, sample) >= segment->media_start) {
4533 /* inside the segment, update time_position, looks very familiar to
4534 * GStreamer segments, doesn't it? */
4535 stream->time_position =
4536 QTSAMPLE_DTS (stream, sample) - segment->media_start + segment->time;
4538 /* not yet in segment, time does not yet increment. This means
4539 * that we are still prerolling keyframes to the decoder so it can
4540 * decode the first sample of the segment. */
4541 stream->time_position = segment->time;
4545 /* move to the next segment */
4548 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
4550 if (stream->segment_index == stream->n_segments - 1) {
4551 /* are we at the end of the last segment, we're EOS */
4552 stream->time_position = GST_CLOCK_TIME_NONE;
4554 /* else we're only at the end of the current segment */
4555 stream->time_position = segment->stop_time;
4557 /* make sure we select a new segment */
4559 /* accumulate previous segments */
4560 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
4561 stream->accumulated_base +=
4562 (stream->segment.stop -
4563 stream->segment.start) / ABS (stream->segment.rate);
4565 stream->segment_index = -1;
4570 gst_qtdemux_sync_streams (GstQTDemux * demux)
4574 if (demux->n_streams <= 1)
4577 for (i = 0; i < demux->n_streams; i++) {
4578 QtDemuxStream *stream;
4579 GstClockTime end_time;
4581 stream = demux->streams[i];
4586 /* TODO advance time on subtitle streams here, if any some day */
4588 /* some clips/trailers may have unbalanced streams at the end,
4589 * so send EOS on shorter stream to prevent stalling others */
4591 /* do not mess with EOS if SEGMENT seeking */
4592 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
4595 if (demux->pullbased) {
4596 /* loop mode is sample time based */
4597 if (!STREAM_IS_EOS (stream))
4600 /* push mode is byte position based */
4601 if (stream->n_samples &&
4602 stream->samples[stream->n_samples - 1].offset >= demux->offset)
4606 if (stream->sent_eos)
4609 /* only act if some gap */
4610 end_time = stream->segments[stream->n_segments - 1].stop_time;
4611 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
4612 ", stream end: %" GST_TIME_FORMAT,
4613 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
4614 if (GST_CLOCK_TIME_IS_VALID (end_time)
4615 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
4616 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
4617 GST_PAD_NAME (stream->pad));
4618 stream->sent_eos = TRUE;
4619 gst_pad_push_event (stream->pad, gst_event_new_eos ());
4624 /* EOS and NOT_LINKED need to be combined. This means that we return:
4626 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
4627 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
4629 static GstFlowReturn
4630 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
4633 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
4636 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
4639 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
4641 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
4645 /* the input buffer metadata must be writable. Returns NULL when the buffer is
4646 * completely clipped
4648 * Should be used only with raw buffers */
4650 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
4653 guint64 start, stop, cstart, cstop, diff;
4654 GstClockTime pts, duration;
4656 gint num_rate, denom_rate;
4661 osize = size = gst_buffer_get_size (buf);
4664 /* depending on the type, setup the clip parameters */
4665 if (stream->subtype == FOURCC_soun) {
4666 frame_size = stream->bytes_per_frame;
4667 num_rate = GST_SECOND;
4668 denom_rate = (gint) stream->rate;
4670 } else if (stream->subtype == FOURCC_vide) {
4672 num_rate = stream->fps_n;
4673 denom_rate = stream->fps_d;
4678 if (frame_size <= 0)
4679 goto bad_frame_size;
4681 /* we can only clip if we have a valid pts */
4682 pts = GST_BUFFER_PTS (buf);
4683 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
4686 duration = GST_BUFFER_DURATION (buf);
4688 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
4690 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
4694 stop = start + duration;
4696 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
4697 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
4700 /* see if some clipping happened */
4701 diff = cstart - start;
4707 /* bring clipped time to samples and to bytes */
4708 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
4711 GST_DEBUG_OBJECT (qtdemux,
4712 "clipping start to %" GST_TIME_FORMAT " %"
4713 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
4719 diff = stop - cstop;
4724 /* bring clipped time to samples and then to bytes */
4725 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
4727 GST_DEBUG_OBJECT (qtdemux,
4728 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
4729 " bytes", GST_TIME_ARGS (cstop), diff);
4734 if (offset != 0 || size != osize)
4735 gst_buffer_resize (buf, offset, size);
4737 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
4738 GST_BUFFER_PTS (buf) = pts;
4739 GST_BUFFER_DURATION (buf) = duration;
4743 /* dropped buffer */
4746 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
4751 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
4756 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
4761 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
4762 gst_buffer_unref (buf);
4767 /* the input buffer metadata must be writable,
4768 * but time/duration etc not yet set and need not be preserved */
4770 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
4777 /* not many cases for now */
4778 if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
4779 /* send a one time dvd clut event */
4780 if (stream->pending_event && stream->pad)
4781 gst_pad_push_event (stream->pad, stream->pending_event);
4782 stream->pending_event = NULL;
4785 if (G_UNLIKELY (stream->subtype != FOURCC_text
4786 && stream->subtype != FOURCC_sbtl &&
4787 stream->subtype != FOURCC_subp)) {
4791 gst_buffer_map (buf, &map, GST_MAP_READ);
4793 /* empty buffer is sent to terminate previous subtitle */
4794 if (map.size <= 2) {
4795 gst_buffer_unmap (buf, &map);
4796 gst_buffer_unref (buf);
4799 if (stream->subtype == FOURCC_subp) {
4800 /* That's all the processing needed for subpictures */
4801 gst_buffer_unmap (buf, &map);
4805 nsize = GST_READ_UINT16_BE (map.data);
4806 nsize = MIN (nsize, map.size - 2);
4808 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
4811 /* takes care of UTF-8 validation or UTF-16 recognition,
4812 * no other encoding expected */
4813 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
4814 gst_buffer_unmap (buf, &map);
4816 gst_buffer_unref (buf);
4817 buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
4819 /* this should not really happen unless the subtitle is corrupted */
4820 gst_buffer_unref (buf);
4824 /* FIXME ? convert optional subsequent style info to markup */
4829 /* Sets a buffer's attributes properly and pushes it downstream.
4830 * Also checks for additional actions and custom processing that may
4831 * need to be done first.
4833 static GstFlowReturn
4834 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
4835 QtDemuxStream * stream, GstBuffer * buf,
4836 GstClockTime dts, GstClockTime pts, GstClockTime duration,
4837 gboolean keyframe, GstClockTime position, guint64 byte_position)
4839 GstFlowReturn ret = GST_FLOW_OK;
4841 /* offset the timestamps according to the edit list */
4843 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
4847 gst_buffer_map (buf, &map, GST_MAP_READ);
4848 url = g_strndup ((gchar *) map.data, map.size);
4849 gst_buffer_unmap (buf, &map);
4850 if (url != NULL && strlen (url) != 0) {
4851 /* we have RTSP redirect now */
4852 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4853 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
4854 gst_structure_new ("redirect",
4855 "new-location", G_TYPE_STRING, url, NULL)));
4856 qtdemux->posted_redirect = TRUE;
4858 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
4864 /* position reporting */
4865 if (qtdemux->segment.rate >= 0) {
4866 qtdemux->segment.position = position;
4867 gst_qtdemux_sync_streams (qtdemux);
4870 if (G_UNLIKELY (!stream->pad)) {
4871 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
4872 gst_buffer_unref (buf);
4876 /* send out pending buffers */
4877 while (stream->buffers) {
4878 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
4880 if (G_UNLIKELY (stream->discont)) {
4881 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
4882 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
4883 stream->discont = FALSE;
4885 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
4888 gst_pad_push (stream->pad, buffer);
4890 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
4893 /* we're going to modify the metadata */
4894 buf = gst_buffer_make_writable (buf);
4896 if (G_UNLIKELY (stream->need_process))
4897 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
4903 GST_BUFFER_DTS (buf) = dts;
4904 GST_BUFFER_PTS (buf) = pts;
4905 GST_BUFFER_DURATION (buf) = duration;
4906 GST_BUFFER_OFFSET (buf) = -1;
4907 GST_BUFFER_OFFSET_END (buf) = -1;
4909 if (G_UNLIKELY (stream->rgb8_palette))
4910 gst_buffer_append_memory (buf, gst_memory_ref (stream->rgb8_palette));
4912 if (G_UNLIKELY (stream->padding)) {
4913 gst_buffer_resize (buf, stream->padding, -1);
4916 if (G_UNLIKELY (qtdemux->element_index)) {
4917 GstClockTime stream_time;
4920 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
4922 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
4923 GST_LOG_OBJECT (qtdemux,
4924 "adding association %" GST_TIME_FORMAT "-> %"
4925 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
4926 gst_index_add_association (qtdemux->element_index,
4928 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
4929 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
4930 GST_FORMAT_BYTES, byte_position, NULL);
4935 if (stream->need_clip)
4936 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
4938 if (G_UNLIKELY (buf == NULL))
4941 if (G_UNLIKELY (stream->discont)) {
4942 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
4943 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
4944 stream->discont = FALSE;
4946 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
4950 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
4951 stream->on_keyframe = FALSE;
4953 stream->on_keyframe = TRUE;
4957 GST_LOG_OBJECT (qtdemux,
4958 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
4959 ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
4960 GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
4961 GST_PAD_NAME (stream->pad));
4963 if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
4964 GstStructure *crypto_info;
4965 QtDemuxCencSampleSetInfo *info =
4966 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4970 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
4971 gst_pad_push_event (stream->pad, event);
4974 index = stream->sample_index - (stream->n_samples - info->crypto_info->len);
4975 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
4976 /* steal structure from array */
4977 crypto_info = g_ptr_array_index (info->crypto_info, index);
4978 g_ptr_array_index (info->crypto_info, index) = NULL;
4979 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u]", index);
4980 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
4981 GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
4985 ret = gst_pad_push (stream->pad, buf);
4987 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
4988 /* mark position in stream, we'll need this to know when to send GAP event */
4989 stream->segment.position = pts + duration;
4996 static const QtDemuxRandomAccessEntry *
4997 gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4998 GstClockTime pos, gboolean after)
5000 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
5001 guint n_entries = stream->n_ra_entries;
5004 /* we assume the table is sorted */
5005 for (i = 0; i < n_entries; ++i) {
5006 if (entries[i].ts > pos)
5010 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
5011 * probably okay to assume that the index lists the very first fragment */
5018 return &entries[i - 1];
5022 gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
5024 const QtDemuxRandomAccessEntry *best_entry = NULL;
5027 GST_OBJECT_LOCK (qtdemux);
5029 g_assert (qtdemux->n_streams > 0);
5031 for (i = 0; i < qtdemux->n_streams; i++) {
5032 const QtDemuxRandomAccessEntry *entry;
5033 QtDemuxStream *stream;
5034 gboolean is_audio_or_video;
5036 stream = qtdemux->streams[i];
5038 g_free (stream->samples);
5039 stream->samples = NULL;
5040 stream->n_samples = 0;
5041 stream->stbl_index = -1; /* no samples have yet been parsed */
5042 stream->sample_index = -1;
5044 if (stream->ra_entries == NULL)
5047 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
5048 is_audio_or_video = TRUE;
5050 is_audio_or_video = FALSE;
5053 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
5054 stream->time_position, !is_audio_or_video);
5056 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
5057 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
5059 stream->pending_seek = entry;
5061 /* decide position to jump to just based on audio/video tracks, not subs */
5062 if (!is_audio_or_video)
5065 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
5069 if (best_entry == NULL) {
5070 GST_OBJECT_UNLOCK (qtdemux);
5074 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
5075 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
5076 GST_TIME_ARGS (qtdemux->streams[0]->time_position),
5077 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
5079 qtdemux->moof_offset = best_entry->moof_offset;
5081 qtdemux_add_fragmented_samples (qtdemux);
5083 GST_OBJECT_UNLOCK (qtdemux);
5087 static GstFlowReturn
5088 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
5090 GstFlowReturn ret = GST_FLOW_OK;
5091 GstBuffer *buf = NULL;
5092 QtDemuxStream *stream;
5093 GstClockTime min_time;
5095 GstClockTime dts = GST_CLOCK_TIME_NONE;
5096 GstClockTime pts = GST_CLOCK_TIME_NONE;
5097 GstClockTime duration = 0;
5098 gboolean keyframe = FALSE;
5099 guint sample_size = 0;
5105 gst_qtdemux_push_pending_newsegment (qtdemux);
5107 if (qtdemux->fragmented_seek_pending) {
5108 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
5109 gst_qtdemux_do_fragmented_seek (qtdemux);
5110 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
5111 qtdemux->fragmented_seek_pending = FALSE;
5114 /* Figure out the next stream sample to output, min_time is expressed in
5115 * global time and runs over the edit list segments. */
5116 min_time = G_MAXUINT64;
5118 for (i = 0; i < qtdemux->n_streams; i++) {
5119 GstClockTime position;
5121 stream = qtdemux->streams[i];
5122 position = stream->time_position;
5124 /* position of -1 is EOS */
5125 if (position != GST_CLOCK_TIME_NONE && position < min_time) {
5126 min_time = position;
5131 if (G_UNLIKELY (index == -1)) {
5132 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
5136 /* check for segment end */
5137 if (G_UNLIKELY (qtdemux->segment.stop != -1
5138 && ((qtdemux->segment.rate >= 0 && qtdemux->segment.stop <= min_time)
5139 || (qtdemux->segment.rate < 0
5140 && qtdemux->segment.start > min_time))
5141 && qtdemux->streams[index]->on_keyframe)) {
5142 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
5143 qtdemux->streams[index]->time_position = GST_CLOCK_TIME_NONE;
5147 /* gap events for subtitle streams */
5148 for (i = 0; i < qtdemux->n_streams; i++) {
5149 stream = qtdemux->streams[i];
5150 if (stream->pad && (stream->subtype == FOURCC_subp
5151 || stream->subtype == FOURCC_text
5152 || stream->subtype == FOURCC_sbtl)) {
5153 /* send one second gap events until the stream catches up */
5154 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
5155 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
5156 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
5157 stream->segment.position + GST_SECOND < min_time) {
5159 gst_event_new_gap (stream->segment.position, GST_SECOND);
5160 gst_pad_push_event (stream->pad, gap);
5161 stream->segment.position += GST_SECOND;
5166 stream = qtdemux->streams[index];
5167 if (stream->new_caps) {
5168 gst_qtdemux_configure_stream (qtdemux, stream);
5169 qtdemux_do_allocation (qtdemux, stream);
5172 /* fetch info for the current sample of this stream */
5173 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
5174 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
5177 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
5178 if (G_UNLIKELY (qtdemux->
5179 segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
5180 if (stream->subtype == FOURCC_vide && !keyframe) {
5181 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on stream %d", index);
5186 GST_DEBUG_OBJECT (qtdemux,
5187 "pushing from stream %d, empty %d offset %" G_GUINT64_FORMAT
5188 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
5189 ", duration %" GST_TIME_FORMAT, index, empty, offset, sample_size,
5190 GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
5192 if (G_UNLIKELY (empty)) {
5193 /* empty segment, push a gap and move to the next one */
5194 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
5195 stream->segment.position = pts + duration;
5199 /* hmm, empty sample, skip and move to next sample */
5200 if (G_UNLIKELY (sample_size <= 0))
5203 /* last pushed sample was out of boundary, goto next sample */
5204 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
5207 if (stream->max_buffer_size == 0 || sample_size <= stream->max_buffer_size) {
5210 GST_DEBUG_OBJECT (qtdemux,
5211 "size %d larger than stream max_buffer_size %d, trimming",
5212 sample_size, stream->max_buffer_size);
5214 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
5217 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
5220 if (stream->use_allocator) {
5221 /* if we have a per-stream allocator, use it */
5222 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
5225 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
5227 if (G_UNLIKELY (ret != GST_FLOW_OK))
5230 if (size != sample_size) {
5231 pts += gst_util_uint64_scale_int (GST_SECOND,
5232 stream->offset_in_sample / stream->bytes_per_frame, stream->timescale);
5233 dts += gst_util_uint64_scale_int (GST_SECOND,
5234 stream->offset_in_sample / stream->bytes_per_frame, stream->timescale);
5235 duration = gst_util_uint64_scale_int (GST_SECOND,
5236 size / stream->bytes_per_frame, stream->timescale);
5239 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
5240 dts, pts, duration, keyframe, min_time, offset);
5242 if (size != sample_size) {
5243 QtDemuxSample *sample = &stream->samples[stream->sample_index];
5244 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
5246 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
5247 sample->timestamp + stream->offset_in_sample / stream->bytes_per_frame);
5248 if (time_position >= segment->media_start) {
5249 /* inside the segment, update time_position, looks very familiar to
5250 * GStreamer segments, doesn't it? */
5251 stream->time_position = (time_position - segment->media_start) +
5254 /* not yet in segment, time does not yet increment. This means
5255 * that we are still prerolling keyframes to the decoder so it can
5256 * decode the first sample of the segment. */
5257 stream->time_position = segment->time;
5262 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
5263 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
5264 * we have no more data for the pad to push */
5265 if (ret == GST_FLOW_EOS)
5268 stream->offset_in_sample += size;
5269 if (stream->offset_in_sample >= sample_size) {
5270 gst_qtdemux_advance_sample (qtdemux, stream);
5275 gst_qtdemux_advance_sample (qtdemux, stream);
5283 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
5289 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
5290 /* EOS will be raised if all are EOS */
5297 gst_qtdemux_loop (GstPad * pad)
5299 GstQTDemux *qtdemux;
5303 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
5305 cur_offset = qtdemux->offset;
5306 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
5307 cur_offset, qtdemux->state);
5309 switch (qtdemux->state) {
5310 case QTDEMUX_STATE_INITIAL:
5311 case QTDEMUX_STATE_HEADER:
5312 ret = gst_qtdemux_loop_state_header (qtdemux);
5314 case QTDEMUX_STATE_MOVIE:
5315 ret = gst_qtdemux_loop_state_movie (qtdemux);
5316 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
5317 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
5325 /* if something went wrong, pause */
5326 if (ret != GST_FLOW_OK)
5330 gst_object_unref (qtdemux);
5336 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
5337 (NULL), ("streaming stopped, invalid state"));
5338 gst_pad_pause_task (pad);
5339 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5344 const gchar *reason = gst_flow_get_name (ret);
5346 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
5348 gst_pad_pause_task (pad);
5350 /* fatal errors need special actions */
5352 if (ret == GST_FLOW_EOS) {
5353 if (qtdemux->n_streams == 0) {
5354 /* we have no streams, post an error */
5355 gst_qtdemux_post_no_playable_stream_error (qtdemux);
5357 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
5360 if ((stop = qtdemux->segment.stop) == -1)
5361 stop = qtdemux->segment.duration;
5363 if (qtdemux->segment.rate >= 0) {
5364 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
5365 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5366 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
5367 GST_FORMAT_TIME, stop));
5368 gst_qtdemux_push_event (qtdemux,
5369 gst_event_new_segment_done (GST_FORMAT_TIME, stop));
5371 /* For Reverse Playback */
5372 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
5373 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
5374 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
5375 GST_FORMAT_TIME, qtdemux->segment.start));
5376 gst_qtdemux_push_event (qtdemux,
5377 gst_event_new_segment_done (GST_FORMAT_TIME,
5378 qtdemux->segment.start));
5381 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
5382 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5384 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
5385 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
5386 (NULL), ("streaming stopped, reason %s", reason));
5387 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
5396 * Returns if there are samples to be played.
5399 has_next_entry (GstQTDemux * demux)
5401 QtDemuxStream *stream;
5404 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
5406 for (i = 0; i < demux->n_streams; i++) {
5407 stream = demux->streams[i];
5409 if (stream->sample_index == -1) {
5410 stream->sample_index = 0;
5411 stream->offset_in_sample = 0;
5414 if (stream->sample_index >= stream->n_samples) {
5415 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
5418 GST_DEBUG_OBJECT (demux, "Found a sample");
5422 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
5429 * Returns the size of the first entry at the current offset.
5430 * If -1, there are none (which means EOS or empty file).
5433 next_entry_size (GstQTDemux * demux)
5435 QtDemuxStream *stream;
5438 guint64 smalloffs = (guint64) - 1;
5439 QtDemuxSample *sample;
5441 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
5444 for (i = 0; i < demux->n_streams; i++) {
5445 stream = demux->streams[i];
5447 if (stream->sample_index == -1) {
5448 stream->sample_index = 0;
5449 stream->offset_in_sample = 0;
5452 if (stream->sample_index >= stream->n_samples) {
5453 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
5457 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
5458 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
5459 stream->sample_index);
5463 sample = &stream->samples[stream->sample_index];
5465 GST_LOG_OBJECT (demux,
5466 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
5467 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
5468 sample->offset, sample->size);
5470 if (((smalloffs == -1)
5471 || (sample->offset < smalloffs)) && (sample->size)) {
5473 smalloffs = sample->offset;
5477 GST_LOG_OBJECT (demux,
5478 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
5479 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
5484 stream = demux->streams[smallidx];
5485 sample = &stream->samples[stream->sample_index];
5487 if (sample->offset >= demux->offset) {
5488 demux->todrop = sample->offset - demux->offset;
5489 return sample->size + demux->todrop;
5492 GST_DEBUG_OBJECT (demux,
5493 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
5498 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
5500 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
5502 gst_element_post_message (GST_ELEMENT_CAST (demux),
5503 gst_message_new_element (GST_OBJECT_CAST (demux),
5504 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
5508 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
5513 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
5516 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
5517 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
5518 GST_SEEK_TYPE_NONE, -1);
5520 /* store seqnum to drop flush events, they don't need to reach downstream */
5521 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
5522 res = gst_pad_push_event (demux->sinkpad, event);
5523 demux->offset_seek_seqnum = 0;
5528 /* check for seekable upstream, above and beyond a mere query */
5530 gst_qtdemux_check_seekability (GstQTDemux * demux)
5533 gboolean seekable = FALSE;
5534 gint64 start = -1, stop = -1;
5536 if (demux->upstream_size)
5539 query = gst_query_new_seeking (GST_FORMAT_BYTES);
5540 if (!gst_pad_peer_query (demux->sinkpad, query)) {
5541 GST_DEBUG_OBJECT (demux, "seeking query failed");
5545 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
5547 /* try harder to query upstream size if we didn't get it the first time */
5548 if (seekable && stop == -1) {
5549 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
5550 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
5553 /* if upstream doesn't know the size, it's likely that it's not seekable in
5554 * practice even if it technically may be seekable */
5555 if (seekable && (start != 0 || stop <= start)) {
5556 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
5561 gst_query_unref (query);
5563 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
5564 G_GUINT64_FORMAT ")", seekable, start, stop);
5565 demux->upstream_seekable = seekable;
5566 demux->upstream_size = seekable ? stop : -1;
5570 gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
5572 g_return_if_fail (bytes <= demux->todrop);
5574 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
5575 gst_adapter_flush (demux->adapter, bytes);
5576 demux->neededbytes -= bytes;
5577 demux->offset += bytes;
5578 demux->todrop -= bytes;
5582 gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
5584 if (G_UNLIKELY (demux->pending_newsegment)) {
5587 gst_qtdemux_push_pending_newsegment (demux);
5588 /* clear to send tags on all streams */
5589 for (i = 0; i < demux->n_streams; i++) {
5590 QtDemuxStream *stream;
5591 stream = demux->streams[i];
5592 gst_qtdemux_push_tags (demux, stream);
5593 if (stream->sparse) {
5594 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
5595 gst_pad_push_event (stream->pad,
5596 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
5603 gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
5604 QtDemuxStream * stream)
5608 /* Push any initial gap segments before proceeding to the
5610 for (i = 0; i < stream->n_segments; i++) {
5611 gst_qtdemux_activate_segment (demux, stream, i, stream->time_position);
5613 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
5614 GstClockTime ts, dur;
5617 ts = stream->time_position;
5619 stream->segments[i].duration - (stream->time_position -
5620 stream->segments[i].time);
5621 gap = gst_event_new_gap (ts, dur);
5622 stream->time_position += dur;
5624 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
5625 "segment: %" GST_PTR_FORMAT, gap);
5626 gst_pad_push_event (stream->pad, gap);
5628 /* Only support empty segment at the beginning followed by
5629 * one non-empty segment, this was checked when parsing the
5630 * edts atom, arriving here is unexpected */
5631 g_assert (i + 1 == stream->n_segments);
5637 static GstFlowReturn
5638 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
5642 demux = GST_QTDEMUX (parent);
5644 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
5647 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
5649 for (i = 0; i < demux->n_streams; i++) {
5650 demux->streams[i]->discont = TRUE;
5653 /* Reverse fragmented playback, need to flush all we have before
5654 * consuming a new fragment.
5655 * The samples array have the timestamps calculated by accumulating the
5656 * durations but this won't work for reverse playback of fragments as
5657 * the timestamps of a subsequent fragment should be smaller than the
5658 * previously received one. */
5659 if (demux->fragmented && demux->segment.rate < 0) {
5660 gst_qtdemux_process_adapter (demux, TRUE);
5661 for (i = 0; i < demux->n_streams; i++)
5662 gst_qtdemux_stream_flush_samples_data (demux, demux->streams[i]);
5666 gst_adapter_push (demux->adapter, inbuf);
5668 GST_DEBUG_OBJECT (demux,
5669 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
5670 demux->neededbytes, gst_adapter_available (demux->adapter));
5672 return gst_qtdemux_process_adapter (demux, FALSE);
5675 static GstFlowReturn
5676 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
5678 GstFlowReturn ret = GST_FLOW_OK;
5680 /* we never really mean to buffer that much */
5681 if (demux->neededbytes == -1) {
5685 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
5686 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
5688 GST_DEBUG_OBJECT (demux,
5689 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
5690 demux->state, demux->neededbytes, demux->offset);
5692 switch (demux->state) {
5693 case QTDEMUX_STATE_INITIAL:{
5698 gst_qtdemux_check_seekability (demux);
5700 data = gst_adapter_map (demux->adapter, demux->neededbytes);
5702 /* get fourcc/length, set neededbytes */
5703 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
5705 gst_adapter_unmap (demux->adapter);
5707 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
5708 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
5710 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
5711 (_("This file is invalid and cannot be played.")),
5712 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
5713 GST_FOURCC_ARGS (fourcc)));
5714 ret = GST_FLOW_ERROR;
5717 if (fourcc == FOURCC_mdat) {
5718 gint next_entry = next_entry_size (demux);
5719 if (demux->n_streams > 0 && (next_entry != -1 || !demux->fragmented)) {
5720 /* we have the headers, start playback */
5721 demux->state = QTDEMUX_STATE_MOVIE;
5722 demux->neededbytes = next_entry;
5723 demux->mdatleft = size;
5725 /* no headers yet, try to get them */
5728 guint64 old, target;
5731 old = demux->offset;
5732 target = old + size;
5734 /* try to jump over the atom with a seek */
5735 /* only bother if it seems worth doing so,
5736 * and avoids possible upstream/server problems */
5737 if (demux->upstream_seekable &&
5738 demux->upstream_size > 4 * (1 << 20)) {
5739 res = qtdemux_seek_offset (demux, target);
5741 GST_DEBUG_OBJECT (demux, "skipping seek");
5746 GST_DEBUG_OBJECT (demux, "seek success");
5747 /* remember the offset fo the first mdat so we can seek back to it
5748 * after we have the headers */
5749 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
5750 demux->first_mdat = old;
5751 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
5754 /* seek worked, continue reading */
5755 demux->offset = target;
5756 demux->neededbytes = 16;
5757 demux->state = QTDEMUX_STATE_INITIAL;
5759 /* seek failed, need to buffer */
5760 demux->offset = old;
5761 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
5762 /* there may be multiple mdat (or alike) buffers */
5764 if (demux->mdatbuffer)
5765 bs = gst_buffer_get_size (demux->mdatbuffer);
5768 if (size + bs > 10 * (1 << 20))
5770 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
5771 demux->neededbytes = size;
5772 if (!demux->mdatbuffer)
5773 demux->mdatoffset = demux->offset;
5776 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
5777 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
5778 (_("This file is invalid and cannot be played.")),
5779 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
5780 GST_FOURCC_ARGS (fourcc), size));
5781 ret = GST_FLOW_ERROR;
5784 /* this means we already started buffering and still no moov header,
5785 * let's continue buffering everything till we get moov */
5786 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
5787 || fourcc == FOURCC_moof))
5789 demux->neededbytes = size;
5790 demux->state = QTDEMUX_STATE_HEADER;
5794 case QTDEMUX_STATE_HEADER:{
5798 GST_DEBUG_OBJECT (demux, "In header");
5800 data = gst_adapter_map (demux->adapter, demux->neededbytes);
5802 /* parse the header */
5803 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
5805 if (fourcc == FOURCC_moov) {
5807 gboolean got_samples = FALSE;
5809 /* in usual fragmented setup we could try to scan for more
5810 * and end up at the the moov (after mdat) again */
5811 if (demux->got_moov && demux->n_streams > 0 &&
5813 || demux->last_moov_offset == demux->offset)) {
5814 GST_DEBUG_OBJECT (demux,
5815 "Skipping moov atom as we have (this) one already");
5817 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
5819 if (demux->got_moov && demux->fragmented) {
5820 GST_DEBUG_OBJECT (demux,
5821 "Got a second moov, clean up data from old one");
5822 if (demux->moov_node)
5823 g_node_destroy (demux->moov_node);
5824 demux->moov_node = NULL;
5825 demux->moov_node_compressed = NULL;
5827 /* prepare newsegment to send when streaming actually starts */
5828 if (!demux->pending_newsegment)
5829 demux->pending_newsegment =
5830 gst_event_new_segment (&demux->segment);
5833 demux->last_moov_offset = demux->offset;
5835 qtdemux_parse_moov (demux, data, demux->neededbytes);
5836 qtdemux_node_dump (demux, demux->moov_node);
5837 qtdemux_parse_tree (demux);
5838 qtdemux_prepare_streams (demux);
5840 for (n = 0; n < demux->n_streams; n++) {
5841 QtDemuxStream *stream = demux->streams[n];
5842 got_samples |= stream->stbl_index >= 0;
5844 if (!demux->fragmented || got_samples) {
5845 if (!demux->got_moov) {
5846 qtdemux_expose_streams (demux);
5848 for (n = 0; n < demux->n_streams; n++) {
5849 QtDemuxStream *stream = demux->streams[n];
5850 gst_qtdemux_configure_stream (demux, stream);
5853 gst_qtdemux_check_send_pending_segment (demux);
5854 demux->pending_configure = FALSE;
5856 demux->pending_configure = TRUE;
5859 demux->got_moov = TRUE;
5861 /* fragmented streams headers shouldn't contain edts atoms */
5862 if (!demux->fragmented) {
5863 for (n = 0; n < demux->n_streams; n++) {
5864 gst_qtdemux_stream_send_initial_gap_segments (demux,
5869 g_node_destroy (demux->moov_node);
5870 demux->moov_node = NULL;
5871 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
5873 } else if (fourcc == FOURCC_moof) {
5874 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
5876 GstClockTime prev_pts;
5877 guint64 prev_offset;
5880 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
5883 * The timestamp of the moof buffer is relevant as some scenarios
5884 * won't have the initial timestamp in the atoms. Whenever a new
5885 * buffer has started, we get that buffer's PTS and use it as a base
5886 * timestamp for the trun entries.
5888 * To keep track of the current buffer timestamp and starting point
5889 * we use gst_adapter_prev_pts that gives us the PTS and the distance
5890 * from the beggining of the buffer, with the distance and demux->offset
5891 * we know if it is still the same buffer or not.
5893 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
5894 prev_offset = demux->offset - dist;
5895 if (demux->fragment_start_offset == -1
5896 || prev_offset > demux->fragment_start_offset) {
5897 demux->fragment_start_offset = prev_offset;
5898 demux->fragment_start = prev_pts;
5899 GST_DEBUG_OBJECT (demux,
5900 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
5901 GST_TIME_FORMAT, demux->fragment_start_offset,
5902 GST_TIME_ARGS (demux->fragment_start));
5905 demux->moof_offset = demux->offset;
5906 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
5907 demux->offset, NULL)) {
5908 gst_adapter_unmap (demux->adapter);
5909 ret = GST_FLOW_ERROR;
5912 /* in MSS we need to expose the pads after the first moof as we won't get a moov
5913 * Also, fragmented format need to be exposed if a moov have no valid sample data */
5914 if (demux->mss_mode || demux->pending_configure) {
5915 if (!demux->exposed) {
5916 if (!demux->pending_newsegment) {
5918 gst_segment_init (&segment, GST_FORMAT_TIME);
5919 GST_DEBUG_OBJECT (demux, "new pending_newsegment");
5920 demux->pending_newsegment = gst_event_new_segment (&segment);
5922 qtdemux_expose_streams (demux);
5924 for (n = 0; n < demux->n_streams; n++) {
5925 QtDemuxStream *stream = demux->streams[n];
5926 gst_qtdemux_configure_stream (demux, stream);
5929 gst_qtdemux_check_send_pending_segment (demux);
5930 demux->pending_configure = FALSE;
5933 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
5935 } else if (fourcc == FOURCC_ftyp) {
5936 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
5937 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
5938 } else if (fourcc == FOURCC_uuid) {
5939 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
5940 qtdemux_parse_uuid (demux, data, demux->neededbytes);
5941 } else if (fourcc == FOURCC_sidx) {
5942 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
5943 qtdemux_parse_sidx (demux, data, demux->neededbytes);
5945 GST_WARNING_OBJECT (demux,
5946 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
5947 GST_FOURCC_ARGS (fourcc));
5948 /* Let's jump that one and go back to initial state */
5950 gst_adapter_unmap (demux->adapter);
5953 if (demux->mdatbuffer && demux->n_streams) {
5954 gsize remaining_data_size = 0;
5956 /* the mdat was before the header */
5957 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
5958 demux->n_streams, demux->mdatbuffer);
5959 /* restore our adapter/offset view of things with upstream;
5960 * put preceding buffered data ahead of current moov data.
5961 * This should also handle evil mdat, moov, mdat cases and alike */
5962 gst_adapter_flush (demux->adapter, demux->neededbytes);
5964 /* Store any remaining data after the mdat for later usage */
5965 remaining_data_size = gst_adapter_available (demux->adapter);
5966 if (remaining_data_size > 0) {
5967 g_assert (demux->restoredata_buffer == NULL);
5968 demux->restoredata_buffer =
5969 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
5970 demux->restoredata_offset = demux->offset + demux->neededbytes;
5971 GST_DEBUG_OBJECT (demux,
5972 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
5973 G_GUINT64_FORMAT, remaining_data_size,
5974 demux->restoredata_offset);
5977 gst_adapter_push (demux->adapter, demux->mdatbuffer);
5978 demux->mdatbuffer = NULL;
5979 demux->offset = demux->mdatoffset;
5980 demux->neededbytes = next_entry_size (demux);
5981 demux->state = QTDEMUX_STATE_MOVIE;
5982 demux->mdatleft = gst_adapter_available (demux->adapter);
5984 GST_DEBUG_OBJECT (demux, "Carrying on normally");
5985 gst_adapter_flush (demux->adapter, demux->neededbytes);
5987 /* only go back to the mdat if there are samples to play */
5988 if (demux->got_moov && demux->first_mdat != -1
5989 && has_next_entry (demux)) {
5992 /* we need to seek back */
5993 res = qtdemux_seek_offset (demux, demux->first_mdat);
5995 demux->offset = demux->first_mdat;
5997 GST_DEBUG_OBJECT (demux, "Seek back failed");
6000 demux->offset += demux->neededbytes;
6002 demux->neededbytes = 16;
6003 demux->state = QTDEMUX_STATE_INITIAL;
6008 case QTDEMUX_STATE_BUFFER_MDAT:{
6012 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
6014 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
6015 gst_buffer_extract (buf, 0, fourcc, 4);
6016 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
6017 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
6018 if (demux->mdatbuffer)
6019 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
6021 demux->mdatbuffer = buf;
6022 demux->offset += demux->neededbytes;
6023 demux->neededbytes = 16;
6024 demux->state = QTDEMUX_STATE_INITIAL;
6025 gst_qtdemux_post_progress (demux, 1, 1);
6029 case QTDEMUX_STATE_MOVIE:{
6030 QtDemuxStream *stream = NULL;
6031 QtDemuxSample *sample;
6033 GstClockTime dts, pts, duration;
6036 GST_DEBUG_OBJECT (demux,
6037 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
6039 if (demux->fragmented) {
6040 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
6042 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
6043 /* if needed data starts within this atom,
6044 * then it should not exceed this atom */
6045 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
6046 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
6047 (_("This file is invalid and cannot be played.")),
6048 ("sample data crosses atom boundary"));
6049 ret = GST_FLOW_ERROR;
6052 demux->mdatleft -= demux->neededbytes;
6054 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
6055 /* so we are dropping more than left in this atom */
6056 gst_qtdemux_drop_data (demux, demux->mdatleft);
6057 demux->mdatleft = 0;
6059 /* need to resume atom parsing so we do not miss any other pieces */
6060 demux->state = QTDEMUX_STATE_INITIAL;
6061 demux->neededbytes = 16;
6063 /* check if there was any stored post mdat data from previous buffers */
6064 if (demux->restoredata_buffer) {
6065 g_assert (gst_adapter_available (demux->adapter) == 0);
6067 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
6068 demux->restoredata_buffer = NULL;
6069 demux->offset = demux->restoredata_offset;
6076 if (demux->todrop) {
6077 if (demux->cenc_aux_info_offset > 0) {
6081 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
6082 data = gst_adapter_map (demux->adapter, demux->todrop);
6083 gst_byte_reader_init (&br, data + 8, demux->todrop);
6084 if (!qtdemux_parse_cenc_aux_info (demux, demux->streams[0], &br,
6085 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
6086 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
6087 ret = GST_FLOW_ERROR;
6088 gst_adapter_unmap (demux->adapter);
6089 g_free (demux->cenc_aux_info_sizes);
6090 demux->cenc_aux_info_sizes = NULL;
6093 demux->cenc_aux_info_offset = 0;
6094 g_free (demux->cenc_aux_info_sizes);
6095 demux->cenc_aux_info_sizes = NULL;
6096 gst_adapter_unmap (demux->adapter);
6098 gst_qtdemux_drop_data (demux, demux->todrop);
6102 /* initial newsegment sent here after having added pads,
6103 * possible others in sink_event */
6104 gst_qtdemux_check_send_pending_segment (demux);
6106 /* Figure out which stream this packet belongs to */
6107 for (i = 0; i < demux->n_streams; i++) {
6108 stream = demux->streams[i];
6109 if (stream->sample_index >= stream->n_samples)
6111 GST_LOG_OBJECT (demux,
6112 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
6113 " / size:%d)", i, stream->sample_index,
6114 stream->samples[stream->sample_index].offset,
6115 stream->samples[stream->sample_index].size);
6117 if (stream->samples[stream->sample_index].offset == demux->offset)
6121 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
6122 goto unknown_stream;
6124 if (stream->new_caps) {
6125 gst_qtdemux_configure_stream (demux, stream);
6128 /* Put data in a buffer, set timestamps, caps, ... */
6129 sample = &stream->samples[stream->sample_index];
6131 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
6132 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
6133 GST_FOURCC_ARGS (stream->fourcc));
6135 dts = QTSAMPLE_DTS (stream, sample);
6136 pts = QTSAMPLE_PTS (stream, sample);
6137 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
6138 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
6140 /* check for segment end */
6141 if (G_UNLIKELY (demux->segment.stop != -1
6142 && demux->segment.stop <= pts && stream->on_keyframe)) {
6143 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
6144 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
6146 /* skip this data, stream is EOS */
6147 gst_adapter_flush (demux->adapter, demux->neededbytes);
6149 /* check if all streams are eos */
6151 for (i = 0; i < demux->n_streams; i++) {
6152 if (!STREAM_IS_EOS (demux->streams[i])) {
6158 if (ret == GST_FLOW_EOS) {
6159 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
6166 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
6168 /* FIXME: should either be an assert or a plain check */
6169 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
6171 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
6172 dts, pts, duration, keyframe, dts, demux->offset);
6176 ret = gst_qtdemux_combine_flows (demux, stream, ret);
6177 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6178 goto non_ok_unlinked_flow;
6180 /* skip this data, stream is EOS */
6181 gst_adapter_flush (demux->adapter, demux->neededbytes);
6184 stream->sample_index++;
6185 stream->offset_in_sample = 0;
6187 /* update current offset and figure out size of next buffer */
6188 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
6189 demux->offset, demux->neededbytes);
6190 demux->offset += demux->neededbytes;
6191 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
6194 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
6195 if (demux->fragmented) {
6196 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
6197 /* there may be more to follow, only finish this atom */
6198 demux->todrop = demux->mdatleft;
6199 demux->neededbytes = demux->todrop;
6211 /* when buffering movie data, at least show user something is happening */
6212 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
6213 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
6214 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
6215 demux->neededbytes);
6222 non_ok_unlinked_flow:
6224 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
6225 gst_flow_get_name (ret));
6230 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
6231 ret = GST_FLOW_ERROR;
6236 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
6242 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
6243 (NULL), ("qtdemuxer invalid state %d", demux->state));
6244 ret = GST_FLOW_ERROR;
6249 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
6250 (NULL), ("no 'moov' atom within the first 10 MB"));
6251 ret = GST_FLOW_ERROR;
6257 qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
6262 query = gst_query_new_scheduling ();
6264 if (!gst_pad_peer_query (sinkpad, query)) {
6265 gst_query_unref (query);
6269 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
6270 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
6271 gst_query_unref (query);
6276 GST_DEBUG_OBJECT (sinkpad, "activating pull");
6277 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
6281 GST_DEBUG_OBJECT (sinkpad, "activating push");
6282 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
6287 qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
6288 GstPadMode mode, gboolean active)
6291 GstQTDemux *demux = GST_QTDEMUX (parent);
6294 case GST_PAD_MODE_PUSH:
6295 demux->pullbased = FALSE;
6298 case GST_PAD_MODE_PULL:
6300 demux->pullbased = TRUE;
6301 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
6304 res = gst_pad_stop_task (sinkpad);
6316 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size)
6318 return g_malloc (items * size);
6322 qtdemux_zfree (void *opaque, void *addr)
6328 qtdemux_inflate (void *z_buffer, guint z_length, guint length)
6334 z = g_new0 (z_stream, 1);
6335 z->zalloc = qtdemux_zalloc;
6336 z->zfree = qtdemux_zfree;
6339 z->next_in = z_buffer;
6340 z->avail_in = z_length;
6342 buffer = (guint8 *) g_malloc (length);
6343 ret = inflateInit (z);
6344 while (z->avail_in > 0) {
6345 if (z->avail_out == 0) {
6347 buffer = (guint8 *) g_realloc (buffer, length);
6348 z->next_out = buffer + z->total_out;
6349 z->avail_out = 1024;
6351 ret = inflate (z, Z_SYNC_FLUSH);
6355 if (ret != Z_STREAM_END) {
6356 g_warning ("inflate() returned %d", ret);
6362 #endif /* HAVE_ZLIB */
6365 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
6369 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
6371 /* counts as header data */
6372 qtdemux->header_size += length;
6374 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
6375 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
6377 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
6383 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
6384 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
6385 if (dcom == NULL || cmvd == NULL)
6386 goto invalid_compression;
6388 method = QT_FOURCC ((guint8 *) dcom->data + 8);
6392 guint uncompressed_length;
6393 guint compressed_length;
6396 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
6397 compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12;
6398 GST_LOG ("length = %u", uncompressed_length);
6401 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
6402 compressed_length, uncompressed_length);
6404 qtdemux->moov_node_compressed = qtdemux->moov_node;
6405 qtdemux->moov_node = g_node_new (buf);
6407 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
6408 uncompressed_length);
6411 #endif /* HAVE_ZLIB */
6413 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
6414 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
6421 invalid_compression:
6423 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
6429 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
6432 while (G_UNLIKELY (buf < end)) {
6436 if (G_UNLIKELY (buf + 4 > end)) {
6437 GST_LOG_OBJECT (qtdemux, "buffer overrun");
6440 len = QT_UINT32 (buf);
6441 if (G_UNLIKELY (len == 0)) {
6442 GST_LOG_OBJECT (qtdemux, "empty container");
6445 if (G_UNLIKELY (len < 8)) {
6446 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
6449 if (G_UNLIKELY (len > (end - buf))) {
6450 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
6451 (gint) (end - buf));
6455 child = g_node_new ((guint8 *) buf);
6456 g_node_append (node, child);
6457 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
6458 qtdemux_parse_node (qtdemux, child, buf, len);
6466 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
6469 int len = QT_UINT32 (xdxt->data);
6470 guint8 *buf = xdxt->data;
6471 guint8 *end = buf + len;
6474 /* skip size and type */
6482 size = QT_UINT32 (buf);
6483 type = QT_FOURCC (buf + 4);
6485 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
6487 if (buf + size > end || size <= 0)
6493 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
6494 GST_FOURCC_ARGS (type));
6498 buffer = gst_buffer_new_and_alloc (size);
6499 gst_buffer_fill (buffer, 0, buf, size);
6500 stream->buffers = g_slist_append (stream->buffers, buffer);
6501 GST_LOG_OBJECT (qtdemux, "parsing theora header");
6504 buffer = gst_buffer_new_and_alloc (size);
6505 gst_buffer_fill (buffer, 0, buf, size);
6506 stream->buffers = g_slist_append (stream->buffers, buffer);
6507 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
6510 buffer = gst_buffer_new_and_alloc (size);
6511 gst_buffer_fill (buffer, 0, buf, size);
6512 stream->buffers = g_slist_append (stream->buffers, buffer);
6513 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
6516 GST_WARNING_OBJECT (qtdemux,
6517 "unknown theora cookie %" GST_FOURCC_FORMAT,
6518 GST_FOURCC_ARGS (type));
6527 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
6531 guint32 node_length = 0;
6532 const QtNodeType *type;
6535 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
6537 if (G_UNLIKELY (length < 8))
6538 goto not_enough_data;
6540 node_length = QT_UINT32 (buffer);
6541 fourcc = QT_FOURCC (buffer + 4);
6543 /* ignore empty nodes */
6544 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
6547 type = qtdemux_type_get (fourcc);
6549 end = buffer + length;
6551 GST_LOG_OBJECT (qtdemux,
6552 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
6553 GST_FOURCC_ARGS (fourcc), node_length, type->name);
6555 if (node_length > length)
6556 goto broken_atom_size;
6558 if (type->flags & QT_FLAG_CONTAINER) {
6559 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
6564 if (node_length < 20) {
6565 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
6568 GST_DEBUG_OBJECT (qtdemux,
6569 "parsing stsd (sample table, sample description) atom");
6570 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
6571 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
6581 /* also read alac (or whatever) in stead of mp4a in the following,
6582 * since a similar layout is used in other cases as well */
6583 if (fourcc == FOURCC_mp4a)
6588 /* There are two things we might encounter here: a true mp4a atom, and
6589 an mp4a entry in an stsd atom. The latter is what we're interested
6590 in, and it looks like an atom, but isn't really one. The true mp4a
6591 atom is short, so we detect it based on length here. */
6592 if (length < min_size) {
6593 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
6594 GST_FOURCC_ARGS (fourcc));
6598 /* 'version' here is the sound sample description version. Types 0 and
6599 1 are documented in the QTFF reference, but type 2 is not: it's
6600 described in Apple header files instead (struct SoundDescriptionV2
6602 version = QT_UINT16 (buffer + 16);
6604 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
6605 GST_FOURCC_ARGS (fourcc), version);
6607 /* parse any esds descriptors */
6619 GST_WARNING_OBJECT (qtdemux,
6620 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
6621 GST_FOURCC_ARGS (fourcc), version);
6626 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
6643 /* codec_data is contained inside these atoms, which all have
6644 * the same format. */
6646 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
6647 GST_FOURCC_ARGS (fourcc));
6648 version = QT_UINT32 (buffer + 16);
6649 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
6650 if (1 || version == 0x00000000) {
6651 buf = buffer + 0x32;
6653 /* FIXME Quicktime uses PASCAL string while
6654 * the iso format uses C strings. Check the file
6655 * type before attempting to parse the string here. */
6656 tlen = QT_UINT8 (buf);
6657 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen);
6659 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf);
6660 /* the string has a reserved space of 32 bytes so skip
6661 * the remaining 31 */
6663 buf += 4; /* and 4 bytes reserved */
6665 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf);
6667 qtdemux_parse_container (qtdemux, node, buf, end);
6673 GST_MEMDUMP_OBJECT (qtdemux, "H264", buffer, end - buffer);
6674 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6679 GST_MEMDUMP_OBJECT (qtdemux, "avc1", buffer, end - buffer);
6680 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6685 GST_MEMDUMP_OBJECT (qtdemux, "avc3", buffer, end - buffer);
6686 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6691 GST_MEMDUMP_OBJECT (qtdemux, "H265", buffer, end - buffer);
6692 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6697 GST_MEMDUMP_OBJECT (qtdemux, "hvc1", buffer, end - buffer);
6698 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6703 GST_MEMDUMP_OBJECT (qtdemux, "hev1", buffer, end - buffer);
6704 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
6709 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
6714 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
6715 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
6720 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
6721 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
6722 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
6730 version = QT_UINT32 (buffer + 12);
6731 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
6738 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
6743 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
6748 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
6753 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
6758 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
6763 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
6767 if (!strcmp (type->name, "unknown"))
6768 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
6772 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
6773 GST_FOURCC_ARGS (fourcc));
6779 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6780 (_("This file is corrupt and cannot be played.")),
6781 ("Not enough data for an atom header, got only %u bytes", length));
6786 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6787 (_("This file is corrupt and cannot be played.")),
6788 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
6789 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
6796 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
6800 guint32 child_fourcc;
6802 for (child = g_node_first_child (node); child;
6803 child = g_node_next_sibling (child)) {
6804 buffer = (guint8 *) child->data;
6806 child_fourcc = QT_FOURCC (buffer + 4);
6808 if (G_UNLIKELY (child_fourcc == fourcc)) {
6816 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
6817 GstByteReader * parser)
6821 guint32 child_fourcc, child_len;
6823 for (child = g_node_first_child (node); child;
6824 child = g_node_next_sibling (child)) {
6825 buffer = (guint8 *) child->data;
6827 child_len = QT_UINT32 (buffer);
6828 child_fourcc = QT_FOURCC (buffer + 4);
6830 if (G_UNLIKELY (child_fourcc == fourcc)) {
6831 if (G_UNLIKELY (child_len < (4 + 4)))
6833 /* FIXME: must verify if atom length < parent atom length */
6834 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
6842 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
6843 GstByteReader * parser)
6847 guint32 child_fourcc, child_len;
6849 for (child = g_node_next_sibling (node); child;
6850 child = g_node_next_sibling (child)) {
6851 buffer = (guint8 *) child->data;
6853 child_fourcc = QT_FOURCC (buffer + 4);
6855 if (child_fourcc == fourcc) {
6857 child_len = QT_UINT32 (buffer);
6858 if (G_UNLIKELY (child_len < (4 + 4)))
6860 /* FIXME: must verify if atom length < parent atom length */
6861 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
6870 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
6872 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
6876 qtdemux_do_allocation (GstQTDemux * qtdemux, QtDemuxStream * stream)
6878 /* FIXME: This can only reliably work if demuxers have a
6879 * separate streaming thread per srcpad. This should be
6880 * done in a demuxer base class, which integrates parts
6883 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
6888 query = gst_query_new_allocation (stream->caps, FALSE);
6890 if (!gst_pad_peer_query (stream->pad, query)) {
6891 /* not a problem, just debug a little */
6892 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
6895 if (stream->allocator)
6896 gst_object_unref (stream->allocator);
6898 if (gst_query_get_n_allocation_params (query) > 0) {
6899 /* try the allocator */
6900 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
6902 stream->use_allocator = TRUE;
6904 stream->allocator = NULL;
6905 gst_allocation_params_init (&stream->params);
6906 stream->use_allocator = FALSE;
6908 gst_query_unref (query);
6913 gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
6914 QtDemuxStream * stream)
6917 const gchar *selected_system;
6919 g_return_val_if_fail (qtdemux != NULL, FALSE);
6920 g_return_val_if_fail (stream != NULL, FALSE);
6921 g_return_val_if_fail (gst_caps_get_size (stream->caps) == 1, FALSE);
6923 if (stream->protection_scheme_type != FOURCC_cenc) {
6924 GST_ERROR_OBJECT (qtdemux, "unsupported protection scheme");
6927 if (qtdemux->protection_system_ids == NULL) {
6928 GST_ERROR_OBJECT (qtdemux, "stream is protected using cenc, but no "
6929 "cenc protection system information has been found");
6932 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
6933 selected_system = gst_protection_select_system ((const gchar **)
6934 qtdemux->protection_system_ids->pdata);
6935 g_ptr_array_remove_index (qtdemux->protection_system_ids,
6936 qtdemux->protection_system_ids->len - 1);
6937 if (!selected_system) {
6938 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
6939 "suitable decryptor element has been found");
6943 s = gst_caps_get_structure (stream->caps, 0);
6944 if (!gst_structure_has_name (s, "application/x-cenc")) {
6945 gst_structure_set (s,
6946 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
6947 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
6949 gst_structure_set_name (s, "application/x-cenc");
6955 gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
6957 if (stream->subtype == FOURCC_vide) {
6958 /* fps is calculated base on the duration of the average framerate since
6959 * qt does not have a fixed framerate. */
6960 if ((stream->n_samples == 1) && (stream->first_duration == 0)) {
6965 if (stream->duration == 0 || stream->n_samples < 2) {
6966 stream->fps_n = stream->timescale;
6969 GstClockTime avg_duration;
6973 /* duration and n_samples can be updated for fragmented format
6974 * so, framerate of fragmented format is calculated using data in a moof */
6975 if (qtdemux->fragmented && stream->n_samples_moof > 0
6976 && stream->duration_moof > 0) {
6977 n_samples = stream->n_samples_moof;
6978 duration = stream->duration_moof;
6980 n_samples = stream->n_samples;
6981 duration = stream->duration;
6984 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
6985 /* stream->duration is guint64, timescale, n_samples are guint32 */
6987 gst_util_uint64_scale_round (duration -
6988 stream->first_duration, GST_SECOND,
6989 (guint64) (stream->timescale) * (n_samples - 1));
6991 GST_LOG_OBJECT (qtdemux,
6992 "Calculating avg sample duration based on stream (or moof) duration %"
6994 " minus first sample %u, leaving %d samples gives %"
6995 GST_TIME_FORMAT, duration, stream->first_duration,
6996 n_samples - 1, GST_TIME_ARGS (avg_duration));
6998 gst_video_guess_framerate (avg_duration, &stream->fps_n,
7001 GST_DEBUG_OBJECT (qtdemux,
7002 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
7003 stream->timescale, stream->fps_n, stream->fps_d);
7007 stream->caps = gst_caps_make_writable (stream->caps);
7009 gst_caps_set_simple (stream->caps,
7010 "width", G_TYPE_INT, stream->width,
7011 "height", G_TYPE_INT, stream->height,
7012 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
7014 /* calculate pixel-aspect-ratio using display width and height */
7015 GST_DEBUG_OBJECT (qtdemux,
7016 "video size %dx%d, target display size %dx%d", stream->width,
7017 stream->height, stream->display_width, stream->display_height);
7018 /* qt file might have pasp atom */
7019 if (stream->par_w > 0 && stream->par_h > 0) {
7020 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
7021 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
7022 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
7023 } else if (stream->display_width > 0 && stream->display_height > 0 &&
7024 stream->width > 0 && stream->height > 0) {
7027 /* calculate the pixel aspect ratio using the display and pixel w/h */
7028 n = stream->display_width * stream->height;
7029 d = stream->display_height * stream->width;
7032 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
7035 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
7036 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
7039 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
7040 guint par_w = 1, par_h = 1;
7042 if (stream->par_w > 0 && stream->par_h > 0) {
7043 par_w = stream->par_w;
7044 par_h = stream->par_h;
7047 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
7048 stream->width, stream->height, par_w, par_h)) {
7049 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
7052 gst_caps_set_simple (stream->caps,
7053 "multiview-mode", G_TYPE_STRING,
7054 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
7055 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
7056 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
7061 else if (stream->subtype == FOURCC_soun) {
7063 stream->caps = gst_caps_make_writable (stream->caps);
7064 if (stream->rate > 0)
7065 gst_caps_set_simple (stream->caps,
7066 "rate", G_TYPE_INT, (int) stream->rate, NULL);
7067 if (stream->n_channels > 0)
7068 gst_caps_set_simple (stream->caps,
7069 "channels", G_TYPE_INT, stream->n_channels, NULL);
7070 if (stream->n_channels > 2) {
7071 /* FIXME: Need to parse the 'chan' atom to get channel layouts
7072 * correctly; this is just the minimum we can do - assume
7073 * we don't actually have any channel positions. */
7074 gst_caps_set_simple (stream->caps,
7075 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
7081 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
7082 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
7083 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
7084 gst_pad_set_active (stream->pad, TRUE);
7086 gst_pad_use_fixed_caps (stream->pad);
7088 if (stream->protected) {
7089 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
7090 GST_ERROR_OBJECT (qtdemux,
7091 "Failed to configure protected stream caps.");
7096 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
7097 if (stream->new_stream) {
7100 GstStreamFlags stream_flags;
7103 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
7106 if (gst_event_parse_group_id (event, &qtdemux->group_id))
7107 qtdemux->have_group_id = TRUE;
7109 qtdemux->have_group_id = FALSE;
7110 gst_event_unref (event);
7111 } else if (!qtdemux->have_group_id) {
7112 qtdemux->have_group_id = TRUE;
7113 qtdemux->group_id = gst_util_group_id_next ();
7116 stream->new_stream = FALSE;
7118 gst_pad_create_stream_id_printf (stream->pad,
7119 GST_ELEMENT_CAST (qtdemux), "%03u", stream->track_id);
7120 event = gst_event_new_stream_start (stream_id);
7121 if (qtdemux->have_group_id)
7122 gst_event_set_group_id (event, qtdemux->group_id);
7123 stream_flags = GST_STREAM_FLAG_NONE;
7124 if (stream->disabled)
7125 stream_flags |= GST_STREAM_FLAG_UNSELECT;
7127 stream_flags |= GST_STREAM_FLAG_SPARSE;
7128 gst_event_set_stream_flags (event, stream_flags);
7129 gst_pad_push_event (stream->pad, event);
7132 gst_pad_set_caps (stream->pad, stream->caps);
7133 stream->new_caps = FALSE;
7139 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
7140 QtDemuxStream * stream, GstTagList * list)
7142 /* consistent default for push based mode */
7143 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
7145 if (stream->subtype == FOURCC_vide) {
7146 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
7149 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
7152 gst_qtdemux_configure_stream (qtdemux, stream);
7153 qtdemux->n_video_streams++;
7154 } else if (stream->subtype == FOURCC_soun) {
7155 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
7158 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
7160 gst_qtdemux_configure_stream (qtdemux, stream);
7161 qtdemux->n_audio_streams++;
7162 } else if (stream->subtype == FOURCC_strm) {
7163 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
7164 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
7165 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
7166 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
7169 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
7171 gst_qtdemux_configure_stream (qtdemux, stream);
7172 qtdemux->n_sub_streams++;
7173 } else if (stream->caps) {
7174 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
7177 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
7179 gst_qtdemux_configure_stream (qtdemux, stream);
7180 qtdemux->n_video_streams++;
7182 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
7189 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
7190 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
7191 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
7192 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
7194 if (stream->pending_tags)
7195 gst_tag_list_unref (stream->pending_tags);
7196 stream->pending_tags = list;
7198 /* global tags go on each pad anyway */
7199 stream->send_global_tags = TRUE;
7200 /* send upstream GST_EVENT_PROTECTION events that were received before
7201 this source pad was created */
7202 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
7203 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
7207 gst_tag_list_unref (list);
7211 /* find next atom with @fourcc starting at @offset */
7212 static GstFlowReturn
7213 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
7214 guint64 * length, guint32 fourcc)
7220 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
7221 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
7227 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
7228 if (G_UNLIKELY (ret != GST_FLOW_OK))
7230 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
7233 gst_buffer_unref (buf);
7236 gst_buffer_map (buf, &map, GST_MAP_READ);
7237 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
7238 gst_buffer_unmap (buf, &map);
7239 gst_buffer_unref (buf);
7241 if (G_UNLIKELY (*length == 0)) {
7242 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
7243 ret = GST_FLOW_ERROR;
7247 if (lfourcc == fourcc) {
7248 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
7252 GST_LOG_OBJECT (qtdemux,
7253 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
7254 GST_FOURCC_ARGS (fourcc), *offset);
7263 /* might simply have had last one */
7264 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
7269 /* should only do something in pull mode */
7270 /* call with OBJECT lock */
7271 static GstFlowReturn
7272 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
7274 guint64 length, offset;
7275 GstBuffer *buf = NULL;
7276 GstFlowReturn ret = GST_FLOW_OK;
7277 GstFlowReturn res = GST_FLOW_OK;
7280 offset = qtdemux->moof_offset;
7281 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
7284 GST_DEBUG_OBJECT (qtdemux, "no next moof");
7285 return GST_FLOW_EOS;
7288 /* best not do pull etc with lock held */
7289 GST_OBJECT_UNLOCK (qtdemux);
7291 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
7292 if (ret != GST_FLOW_OK)
7295 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
7296 if (G_UNLIKELY (ret != GST_FLOW_OK))
7298 gst_buffer_map (buf, &map, GST_MAP_READ);
7299 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
7300 gst_buffer_unmap (buf, &map);
7301 gst_buffer_unref (buf);
7306 gst_buffer_unmap (buf, &map);
7307 gst_buffer_unref (buf);
7311 /* look for next moof */
7312 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
7313 if (G_UNLIKELY (ret != GST_FLOW_OK))
7317 GST_OBJECT_LOCK (qtdemux);
7319 qtdemux->moof_offset = offset;
7325 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
7327 res = GST_FLOW_ERROR;
7332 /* maybe upstream temporarily flushing */
7333 if (ret != GST_FLOW_FLUSHING) {
7334 GST_DEBUG_OBJECT (qtdemux, "no next moof");
7337 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
7338 /* resume at current position next time */
7345 /* initialise bytereaders for stbl sub-atoms */
7347 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
7349 stream->stbl_index = -1; /* no samples have yet been parsed */
7350 stream->sample_index = -1;
7352 /* time-to-sample atom */
7353 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
7356 /* copy atom data into a new buffer for later use */
7357 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
7359 /* skip version + flags */
7360 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
7361 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
7363 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
7365 /* make sure there's enough data */
7366 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
7367 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
7368 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
7369 stream->n_sample_times);
7370 if (!stream->n_sample_times)
7374 /* sync sample atom */
7375 stream->stps_present = FALSE;
7376 if ((stream->stss_present =
7377 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
7378 &stream->stss) ? TRUE : FALSE) == TRUE) {
7379 /* copy atom data into a new buffer for later use */
7380 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
7382 /* skip version + flags */
7383 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
7384 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
7387 if (stream->n_sample_syncs) {
7388 /* make sure there's enough data */
7389 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
7393 /* partial sync sample atom */
7394 if ((stream->stps_present =
7395 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
7396 &stream->stps) ? TRUE : FALSE) == TRUE) {
7397 /* copy atom data into a new buffer for later use */
7398 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
7400 /* skip version + flags */
7401 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
7402 !gst_byte_reader_get_uint32_be (&stream->stps,
7403 &stream->n_sample_partial_syncs))
7406 /* if there are no entries, the stss table contains the real
7408 if (stream->n_sample_partial_syncs) {
7409 /* make sure there's enough data */
7410 if (!qt_atom_parser_has_chunks (&stream->stps,
7411 stream->n_sample_partial_syncs, 4))
7418 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
7421 /* copy atom data into a new buffer for later use */
7422 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
7424 /* skip version + flags */
7425 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
7426 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
7429 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
7432 if (!stream->n_samples)
7435 /* sample-to-chunk atom */
7436 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
7439 /* copy atom data into a new buffer for later use */
7440 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
7442 /* skip version + flags */
7443 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
7444 !gst_byte_reader_get_uint32_be (&stream->stsc,
7445 &stream->n_samples_per_chunk))
7448 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
7449 stream->n_samples_per_chunk);
7451 /* make sure there's enough data */
7452 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
7458 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
7459 stream->co_size = sizeof (guint32);
7460 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
7462 stream->co_size = sizeof (guint64);
7466 /* copy atom data into a new buffer for later use */
7467 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
7469 /* skip version + flags */
7470 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
7473 /* chunks_are_samples == TRUE means treat chunks as samples */
7474 stream->chunks_are_samples = stream->sample_size && !stream->sampled;
7475 if (stream->chunks_are_samples) {
7476 /* treat chunks as samples */
7477 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
7480 /* skip number of entries */
7481 if (!gst_byte_reader_skip (&stream->stco, 4))
7484 /* make sure there are enough data in the stsz atom */
7485 if (!stream->sample_size) {
7486 /* different sizes for each sample */
7487 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
7492 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
7493 stream->n_samples, (guint) sizeof (QtDemuxSample),
7494 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
7496 if (stream->n_samples >=
7497 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
7498 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
7499 "be larger than %uMB (broken file?)", stream->n_samples,
7500 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
7504 g_assert (stream->samples == NULL);
7505 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
7506 if (!stream->samples) {
7507 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
7512 /* composition time-to-sample */
7513 if ((stream->ctts_present =
7514 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
7515 &stream->ctts) ? TRUE : FALSE) == TRUE) {
7516 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
7518 /* copy atom data into a new buffer for later use */
7519 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
7521 /* skip version + flags */
7522 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
7523 || !gst_byte_reader_get_uint32_be (&stream->ctts,
7524 &stream->n_composition_times))
7527 /* make sure there's enough data */
7528 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
7532 /* This is optional, if missing we iterate the ctts */
7533 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
7534 if (!gst_byte_reader_skip (&cslg, 1 + 3)
7535 || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
7536 g_free ((gpointer) cslg.data);
7540 gint32 cslg_least = 0;
7541 guint num_entries, pos;
7544 pos = gst_byte_reader_get_pos (&stream->ctts);
7545 num_entries = stream->n_composition_times;
7547 stream->cslg_shift = 0;
7549 for (i = 0; i < num_entries; i++) {
7552 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
7553 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
7555 if (offset < cslg_least)
7556 cslg_least = offset;
7560 stream->cslg_shift = ABS (cslg_least);
7562 stream->cslg_shift = 0;
7564 /* reset the reader so we can generate sample table */
7565 gst_byte_reader_set_pos (&stream->ctts, pos);
7568 /* Ensure the cslg_shift value is consistent so we can use it
7569 * unconditionnally to produce TS and Segment */
7570 stream->cslg_shift = 0;
7577 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7578 (_("This file is corrupt and cannot be played.")), (NULL));
7583 gst_qtdemux_stbl_free (stream);
7584 if (!qtdemux->fragmented) {
7585 /* not quite good */
7586 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
7589 /* may pick up samples elsewhere */
7595 /* collect samples from the next sample to be parsed up to sample @n for @stream
7596 * by reading the info from @stbl
7598 * This code can be executed from both the streaming thread and the seeking
7599 * thread so it takes the object lock to protect itself
7602 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
7605 QtDemuxSample *samples, *first, *cur, *last;
7606 guint32 n_samples_per_chunk;
7609 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
7610 GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc),
7611 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
7613 n_samples = stream->n_samples;
7616 goto out_of_samples;
7618 GST_OBJECT_LOCK (qtdemux);
7619 if (n <= stream->stbl_index)
7620 goto already_parsed;
7622 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
7624 if (!stream->stsz.data) {
7625 /* so we already parsed and passed all the moov samples;
7626 * onto fragmented ones */
7627 g_assert (qtdemux->fragmented);
7631 /* pointer to the sample table */
7632 samples = stream->samples;
7634 /* starts from -1, moves to the next sample index to parse */
7635 stream->stbl_index++;
7637 /* keep track of the first and last sample to fill */
7638 first = &samples[stream->stbl_index];
7641 if (!stream->chunks_are_samples) {
7642 /* set the sample sizes */
7643 if (stream->sample_size == 0) {
7644 /* different sizes for each sample */
7645 for (cur = first; cur <= last; cur++) {
7646 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
7647 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
7648 (guint) (cur - samples), cur->size);
7651 /* samples have the same size */
7652 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
7653 for (cur = first; cur <= last; cur++)
7654 cur->size = stream->sample_size;
7658 n_samples_per_chunk = stream->n_samples_per_chunk;
7661 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
7664 if (stream->stsc_chunk_index >= stream->last_chunk
7665 || stream->stsc_chunk_index < stream->first_chunk) {
7666 stream->first_chunk =
7667 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
7668 stream->samples_per_chunk =
7669 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
7670 gst_byte_reader_skip_unchecked (&stream->stsc, 4);
7672 /* chunk numbers are counted from 1 it seems */
7673 if (G_UNLIKELY (stream->first_chunk == 0))
7676 --stream->first_chunk;
7678 /* the last chunk of each entry is calculated by taking the first chunk
7679 * of the next entry; except if there is no next, where we fake it with
7681 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
7682 stream->last_chunk = G_MAXUINT32;
7684 stream->last_chunk =
7685 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
7686 if (G_UNLIKELY (stream->last_chunk == 0))
7689 --stream->last_chunk;
7692 GST_LOG_OBJECT (qtdemux,
7693 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i,
7694 stream->first_chunk, stream->last_chunk, stream->samples_per_chunk);
7696 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
7699 if (stream->last_chunk != G_MAXUINT32) {
7700 if (!qt_atom_parser_peek_sub (&stream->stco,
7701 stream->first_chunk * stream->co_size,
7702 (stream->last_chunk - stream->first_chunk) * stream->co_size,
7707 stream->co_chunk = stream->stco;
7708 if (!gst_byte_reader_skip (&stream->co_chunk,
7709 stream->first_chunk * stream->co_size))
7713 stream->stsc_chunk_index = stream->first_chunk;
7716 last_chunk = stream->last_chunk;
7718 if (stream->chunks_are_samples) {
7719 cur = &samples[stream->stsc_chunk_index];
7721 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
7724 stream->stsc_chunk_index = j;
7729 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
7732 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
7733 "%" G_GUINT64_FORMAT, j, cur->offset);
7735 if (stream->samples_per_frame * stream->bytes_per_frame) {
7737 (stream->samples_per_chunk * stream->n_channels) /
7738 stream->samples_per_frame * stream->bytes_per_frame;
7740 cur->size = stream->samples_per_chunk;
7743 GST_DEBUG_OBJECT (qtdemux,
7744 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
7745 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
7746 stream->stco_sample_index)), cur->size);
7748 cur->timestamp = stream->stco_sample_index;
7749 cur->duration = stream->samples_per_chunk;
7750 cur->keyframe = TRUE;
7753 stream->stco_sample_index += stream->samples_per_chunk;
7755 stream->stsc_chunk_index = j;
7757 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
7758 guint32 samples_per_chunk;
7759 guint64 chunk_offset;
7761 if (!stream->stsc_sample_index
7762 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
7763 &stream->chunk_offset))
7766 samples_per_chunk = stream->samples_per_chunk;
7767 chunk_offset = stream->chunk_offset;
7769 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
7770 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
7771 G_GUINT64_FORMAT " and size %d",
7772 (guint) (cur - samples), chunk_offset, cur->size);
7774 cur->offset = chunk_offset;
7775 chunk_offset += cur->size;
7778 if (G_UNLIKELY (cur > last)) {
7780 stream->stsc_sample_index = k + 1;
7781 stream->chunk_offset = chunk_offset;
7782 stream->stsc_chunk_index = j;
7786 stream->stsc_sample_index = 0;
7788 stream->stsc_chunk_index = j;
7790 stream->stsc_index++;
7793 if (stream->chunks_are_samples)
7797 guint32 n_sample_times;
7799 n_sample_times = stream->n_sample_times;
7802 for (i = stream->stts_index; i < n_sample_times; i++) {
7803 guint32 stts_samples;
7804 gint32 stts_duration;
7807 if (stream->stts_sample_index >= stream->stts_samples
7808 || !stream->stts_sample_index) {
7810 stream->stts_samples =
7811 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
7812 stream->stts_duration =
7813 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
7815 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
7816 i, stream->stts_samples, stream->stts_duration);
7818 stream->stts_sample_index = 0;
7821 stts_samples = stream->stts_samples;
7822 stts_duration = stream->stts_duration;
7823 stts_time = stream->stts_time;
7825 for (j = stream->stts_sample_index; j < stts_samples; j++) {
7826 GST_DEBUG_OBJECT (qtdemux,
7827 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
7828 (guint) (cur - samples), j,
7829 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
7831 cur->timestamp = stts_time;
7832 cur->duration = stts_duration;
7834 /* avoid 32-bit wrap-around,
7835 * but still mind possible 'negative' duration */
7836 stts_time += (gint64) stts_duration;
7839 if (G_UNLIKELY (cur > last)) {
7841 stream->stts_time = stts_time;
7842 stream->stts_sample_index = j + 1;
7846 stream->stts_sample_index = 0;
7847 stream->stts_time = stts_time;
7848 stream->stts_index++;
7850 /* fill up empty timestamps with the last timestamp, this can happen when
7851 * the last samples do not decode and so we don't have timestamps for them.
7852 * We however look at the last timestamp to estimate the track length so we
7853 * need something in here. */
7854 for (; cur < last; cur++) {
7855 GST_DEBUG_OBJECT (qtdemux,
7856 "fill sample %d: timestamp %" GST_TIME_FORMAT,
7857 (guint) (cur - samples),
7858 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
7859 cur->timestamp = stream->stts_time;
7865 /* sample sync, can be NULL */
7866 if (stream->stss_present == TRUE) {
7867 guint32 n_sample_syncs;
7869 n_sample_syncs = stream->n_sample_syncs;
7871 if (!n_sample_syncs) {
7872 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
7873 stream->all_keyframe = TRUE;
7875 for (i = stream->stss_index; i < n_sample_syncs; i++) {
7876 /* note that the first sample is index 1, not 0 */
7879 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
7881 if (G_LIKELY (index > 0 && index <= n_samples)) {
7883 samples[index].keyframe = TRUE;
7884 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
7885 /* and exit if we have enough samples */
7886 if (G_UNLIKELY (index >= n)) {
7893 stream->stss_index = i;
7896 /* stps marks partial sync frames like open GOP I-Frames */
7897 if (stream->stps_present == TRUE) {
7898 guint32 n_sample_partial_syncs;
7900 n_sample_partial_syncs = stream->n_sample_partial_syncs;
7902 /* if there are no entries, the stss table contains the real
7904 if (n_sample_partial_syncs) {
7905 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
7906 /* note that the first sample is index 1, not 0 */
7909 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
7911 if (G_LIKELY (index > 0 && index <= n_samples)) {
7913 samples[index].keyframe = TRUE;
7914 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
7915 /* and exit if we have enough samples */
7916 if (G_UNLIKELY (index >= n)) {
7923 stream->stps_index = i;
7927 /* no stss, all samples are keyframes */
7928 stream->all_keyframe = TRUE;
7929 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
7934 /* composition time to sample */
7935 if (stream->ctts_present == TRUE) {
7936 guint32 n_composition_times;
7938 gint32 ctts_soffset;
7940 /* Fill in the pts_offsets */
7942 n_composition_times = stream->n_composition_times;
7944 for (i = stream->ctts_index; i < n_composition_times; i++) {
7945 if (stream->ctts_sample_index >= stream->ctts_count
7946 || !stream->ctts_sample_index) {
7947 stream->ctts_count =
7948 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
7949 stream->ctts_soffset =
7950 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
7951 stream->ctts_sample_index = 0;
7954 ctts_count = stream->ctts_count;
7955 ctts_soffset = stream->ctts_soffset;
7957 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
7958 cur->pts_offset = ctts_soffset;
7961 if (G_UNLIKELY (cur > last)) {
7963 stream->ctts_sample_index = j + 1;
7967 stream->ctts_sample_index = 0;
7968 stream->ctts_index++;
7972 stream->stbl_index = n;
7973 /* if index has been completely parsed, free data that is no-longer needed */
7974 if (n + 1 == stream->n_samples) {
7975 gst_qtdemux_stbl_free (stream);
7976 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
7977 if (qtdemux->pullbased) {
7978 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
7979 while (n + 1 == stream->n_samples)
7980 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
7984 GST_OBJECT_UNLOCK (qtdemux);
7991 GST_LOG_OBJECT (qtdemux,
7992 "Tried to parse up to sample %u but this sample has already been parsed",
7994 /* if fragmented, there may be more */
7995 if (qtdemux->fragmented && n == stream->stbl_index)
7997 GST_OBJECT_UNLOCK (qtdemux);
8003 GST_LOG_OBJECT (qtdemux,
8004 "Tried to parse up to sample %u but there are only %u samples", n + 1,
8006 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8007 (_("This file is corrupt and cannot be played.")), (NULL));
8012 GST_OBJECT_UNLOCK (qtdemux);
8013 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8014 (_("This file is corrupt and cannot be played.")), (NULL));
8019 /* collect all segment info for @stream.
8022 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
8026 /* accept edts if they contain gaps at start and there is only
8027 * one media segment */
8028 gboolean allow_pushbased_edts = TRUE;
8029 gint media_segments_count = 0;
8031 /* parse and prepare segment info from the edit list */
8032 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
8033 stream->n_segments = 0;
8034 stream->segments = NULL;
8035 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
8043 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
8044 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
8047 buffer = elst->data;
8049 n_segments = QT_UINT32 (buffer + 12);
8051 /* we might allocate a bit too much, at least allocate 1 segment */
8052 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
8054 /* segments always start from 0 */
8058 for (i = 0; i < n_segments; i++) {
8061 QtDemuxSegment *segment;
8063 GstClockTime media_start = GST_CLOCK_TIME_NONE;
8065 media_time = QT_UINT32 (buffer + 20 + i * 12);
8066 duration = QT_UINT32 (buffer + 16 + i * 12);
8068 if (media_time != G_MAXUINT32)
8069 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
8071 segment = &stream->segments[count++];
8073 /* time and duration expressed in global timescale */
8074 segment->time = stime;
8075 /* add non scaled values so we don't cause roundoff errors */
8078 stime = QTTIME_TO_GSTTIME (qtdemux, time);
8079 segment->duration = stime - segment->time;
8081 /* zero duration does not imply media_start == media_stop
8082 * but, only specify media_start.*/
8083 stime = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
8084 if (GST_CLOCK_TIME_IS_VALID (stime) && media_time != G_MAXUINT32
8085 && stime >= media_start) {
8086 segment->duration = stime - media_start;
8088 segment->duration = GST_CLOCK_TIME_NONE;
8091 segment->stop_time = stime;
8093 segment->trak_media_start = media_time;
8094 /* media_time expressed in stream timescale */
8095 if (media_time != G_MAXUINT32) {
8096 segment->media_start = media_start;
8097 segment->media_stop = segment->media_start + segment->duration;
8098 media_segments_count++;
8100 segment->media_start = GST_CLOCK_TIME_NONE;
8101 segment->media_stop = GST_CLOCK_TIME_NONE;
8103 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
8105 if (rate_int <= 1) {
8106 /* 0 is not allowed, some programs write 1 instead of the floating point
8108 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
8112 segment->rate = rate_int / 65536.0;
8115 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
8116 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
8117 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
8118 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
8119 i, GST_TIME_ARGS (segment->time),
8120 GST_TIME_ARGS (segment->duration),
8121 GST_TIME_ARGS (segment->media_start), media_time,
8122 GST_TIME_ARGS (segment->media_stop),
8123 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
8125 if (segment->stop_time > qtdemux->segment.stop) {
8126 GST_WARNING_OBJECT (qtdemux, "Segment %d "
8127 " extends to %" GST_TIME_FORMAT
8128 " past the end of the file duration %" GST_TIME_FORMAT
8129 " it will be truncated", i, GST_TIME_ARGS (segment->stop_time),
8130 GST_TIME_ARGS (qtdemux->segment.stop));
8131 qtdemux->segment.stop = segment->stop_time;
8134 GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
8135 stream->n_segments = count;
8136 if (media_segments_count != 1)
8137 allow_pushbased_edts = FALSE;
8141 /* push based does not handle segments, so act accordingly here,
8142 * and warn if applicable */
8143 if (!qtdemux->pullbased && !allow_pushbased_edts) {
8144 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
8145 /* remove and use default one below, we stream like it anyway */
8146 g_free (stream->segments);
8147 stream->segments = NULL;
8148 stream->n_segments = 0;
8151 /* no segments, create one to play the complete trak */
8152 if (stream->n_segments == 0) {
8153 GstClockTime stream_duration =
8154 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
8156 if (stream->segments == NULL)
8157 stream->segments = g_new (QtDemuxSegment, 1);
8159 /* represent unknown our way */
8160 if (stream_duration == 0)
8161 stream_duration = GST_CLOCK_TIME_NONE;
8163 stream->segments[0].time = 0;
8164 stream->segments[0].stop_time = stream_duration;
8165 stream->segments[0].duration = stream_duration;
8166 stream->segments[0].media_start = 0;
8167 stream->segments[0].media_stop = stream_duration;
8168 stream->segments[0].rate = 1.0;
8169 stream->segments[0].trak_media_start = 0;
8171 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
8172 GST_TIME_ARGS (stream_duration));
8173 stream->n_segments = 1;
8174 stream->dummy_segment = TRUE;
8176 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
8182 * Parses the stsd atom of a svq3 trak looking for
8183 * the SMI and gama atoms.
8186 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
8187 guint8 ** gamma, GstBuffer ** seqh)
8189 guint8 *_gamma = NULL;
8190 GstBuffer *_seqh = NULL;
8191 guint8 *stsd_data = stsd->data;
8192 guint32 length = QT_UINT32 (stsd_data);
8196 GST_WARNING_OBJECT (qtdemux, "stsd too short");
8202 version = QT_UINT16 (stsd_data);
8207 while (length > 8) {
8208 guint32 fourcc, size;
8210 size = QT_UINT32 (stsd_data);
8211 fourcc = QT_FOURCC (stsd_data + 4);
8212 data = stsd_data + 8;
8215 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
8216 "svq3 atom parsing");
8225 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
8226 " for gama atom, expected 12", size);
8231 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
8233 if (_seqh != NULL) {
8234 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
8235 " found, ignoring");
8237 seqh_size = QT_UINT32 (data + 4);
8238 if (seqh_size > 0) {
8239 _seqh = gst_buffer_new_and_alloc (seqh_size);
8240 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
8247 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
8248 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
8252 if (size <= length) {
8258 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
8261 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
8262 G_GUINT16_FORMAT, version);
8273 gst_buffer_unref (_seqh);
8278 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
8285 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
8286 * atom that might contain a 'data' atom with the rtsp uri.
8287 * This case was reported in bug #597497, some info about
8288 * the hndl atom can be found in TN1195
8290 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
8291 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
8294 guint32 dref_num_entries = 0;
8295 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
8296 gst_byte_reader_skip (&dref, 4) &&
8297 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
8300 /* search dref entries for hndl atom */
8301 for (i = 0; i < dref_num_entries; i++) {
8302 guint32 size = 0, type;
8303 guint8 string_len = 0;
8304 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
8305 qt_atom_parser_get_fourcc (&dref, &type)) {
8306 if (type == FOURCC_hndl) {
8307 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
8309 /* skip data reference handle bytes and the
8310 * following pascal string and some extra 4
8311 * bytes I have no idea what are */
8312 if (!gst_byte_reader_skip (&dref, 4) ||
8313 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
8314 !gst_byte_reader_skip (&dref, string_len + 4)) {
8315 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
8319 /* iterate over the atoms to find the data atom */
8320 while (gst_byte_reader_get_remaining (&dref) >= 8) {
8324 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
8325 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
8326 if (atom_type == FOURCC_data) {
8327 const guint8 *uri_aux = NULL;
8329 /* found the data atom that might contain the rtsp uri */
8330 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
8331 "hndl atom, interpreting it as an URI");
8332 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
8334 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
8335 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
8337 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
8338 "didn't contain a rtsp address");
8340 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
8345 /* skipping to the next entry */
8346 if (!gst_byte_reader_skip (&dref, atom_size - 8))
8349 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
8356 /* skip to the next entry */
8357 if (!gst_byte_reader_skip (&dref, size - 8))
8360 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
8363 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
8369 #define AMR_NB_ALL_MODES 0x81ff
8370 #define AMR_WB_ALL_MODES 0x83ff
8372 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
8374 /* The 'damr' atom is of the form:
8376 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
8377 * 32 b 8 b 16 b 8 b 8 b
8379 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
8380 * represents the highest mode used in the stream (and thus the maximum
8381 * bitrate), with a couple of special cases as seen below.
8384 /* Map of frame type ID -> bitrate */
8385 static const guint nb_bitrates[] = {
8386 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
8388 static const guint wb_bitrates[] = {
8389 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
8395 gst_buffer_map (buf, &map, GST_MAP_READ);
8397 if (map.size != 0x11) {
8398 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
8402 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
8403 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
8404 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
8408 mode_set = QT_UINT16 (map.data + 13);
8410 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
8411 max_mode = 7 + (wb ? 1 : 0);
8413 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
8414 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
8416 if (max_mode == -1) {
8417 GST_DEBUG ("No mode indication was found (mode set) = %x",
8422 gst_buffer_unmap (buf, &map);
8423 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
8426 gst_buffer_unmap (buf, &map);
8431 qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
8432 GstByteReader * reader, guint32 * matrix, const gchar * atom)
8435 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
8441 if (gst_byte_reader_get_remaining (reader) < 36)
8444 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
8445 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
8446 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
8447 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
8448 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
8449 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
8450 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
8451 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
8452 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
8454 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
8455 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
8456 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
8458 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
8459 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
8461 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
8462 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
8469 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
8470 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
8477 * This macro will only compare value abdegh, it expects cfi to have already
8480 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
8481 (m)[3] == (d << 16) && (m)[4] == (e << 16))
8483 /* only handle the cases where the last column has standard values */
8484 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
8485 const gchar *rotation_tag = NULL;
8487 /* no rotation needed */
8488 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
8490 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
8491 rotation_tag = "rotate-90";
8492 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
8493 rotation_tag = "rotate-180";
8494 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
8495 rotation_tag = "rotate-270";
8497 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
8500 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
8502 if (rotation_tag != NULL) {
8503 if (*taglist == NULL)
8504 *taglist = gst_tag_list_new_empty ();
8505 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
8506 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
8509 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
8513 /* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
8514 * protected streams (sinf, frma, schm and schi); if the protection scheme is
8515 * Common Encryption (cenc), the function will also parse the tenc box (defined
8516 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
8517 * (typically an enc[v|a|t|s] sample entry); the function will set
8518 * @original_fmt to the fourcc of the original unencrypted stream format.
8519 * Returns TRUE if successful; FALSE otherwise. */
8521 qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
8522 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
8529 g_return_val_if_fail (qtdemux != NULL, FALSE);
8530 g_return_val_if_fail (stream != NULL, FALSE);
8531 g_return_val_if_fail (container != NULL, FALSE);
8532 g_return_val_if_fail (original_fmt != NULL, FALSE);
8534 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
8535 if (G_UNLIKELY (!sinf)) {
8536 if (stream->protection_scheme_type == FOURCC_cenc) {
8537 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
8538 "mandatory for Common Encryption");
8544 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
8545 if (G_UNLIKELY (!frma)) {
8546 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
8550 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
8551 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
8552 GST_FOURCC_ARGS (*original_fmt));
8554 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
8556 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
8559 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
8560 stream->protection_scheme_version =
8561 QT_UINT32 ((const guint8 *) schm->data + 16);
8563 GST_DEBUG_OBJECT (qtdemux,
8564 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
8565 "protection_scheme_version: %#010x",
8566 GST_FOURCC_ARGS (stream->protection_scheme_type),
8567 stream->protection_scheme_version);
8569 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
8571 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
8574 if (stream->protection_scheme_type == FOURCC_cenc) {
8575 QtDemuxCencSampleSetInfo *info;
8577 const guint8 *tenc_data;
8578 guint32 isEncrypted;
8580 const guint8 *default_kid;
8583 if (G_UNLIKELY (!stream->protection_scheme_info))
8584 stream->protection_scheme_info =
8585 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
8587 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
8589 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
8591 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
8592 "which is mandatory for Common Encryption");
8595 tenc_data = (const guint8 *) tenc->data + 12;
8596 isEncrypted = QT_UINT24 (tenc_data);
8597 iv_size = QT_UINT8 (tenc_data + 3);
8598 default_kid = (tenc_data + 4);
8599 kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
8600 gst_buffer_fill (kid_buf, 0, default_kid, 16);
8601 if (info->default_properties)
8602 gst_structure_free (info->default_properties);
8603 info->default_properties =
8604 gst_structure_new ("application/x-cenc",
8605 "iv_size", G_TYPE_UINT, iv_size,
8606 "encrypted", G_TYPE_BOOLEAN, (isEncrypted == 1),
8607 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
8608 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
8609 "is_encrypted=%u, iv_size=%u", isEncrypted, iv_size);
8610 gst_buffer_unref (kid_buf);
8616 * With each track we associate a new QtDemuxStream that contains all the info
8618 * traks that do not decode to something (like strm traks) will not have a pad.
8621 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
8640 QtDemuxStream *stream = NULL;
8641 gboolean new_stream = FALSE;
8642 gchar *codec = NULL;
8643 const guint8 *stsd_data;
8644 guint16 lang_code; /* quicktime lang code or packed iso code */
8646 guint32 tkhd_flags = 0;
8647 guint8 tkhd_version = 0;
8649 guint value_size, stsd_len, len;
8653 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
8655 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
8656 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
8657 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
8660 /* pick between 64 or 32 bits */
8661 value_size = tkhd_version == 1 ? 8 : 4;
8662 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
8663 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
8666 if (!qtdemux->got_moov) {
8667 if (qtdemux_find_stream (qtdemux, track_id))
8668 goto existing_stream;
8669 stream = _create_stream ();
8670 stream->track_id = track_id;
8673 stream = qtdemux_find_stream (qtdemux, track_id);
8675 GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
8679 /* flush samples data from this track from previous moov */
8680 gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
8681 gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
8683 /* need defaults for fragments */
8684 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
8686 if (stream->pending_tags == NULL)
8687 stream->pending_tags = gst_tag_list_new_empty ();
8689 if ((tkhd_flags & 1) == 0)
8690 stream->disabled = TRUE;
8692 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
8693 tkhd_version, tkhd_flags, stream->track_id);
8695 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
8698 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
8699 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
8700 if (qtdemux->major_brand != FOURCC_mjp2 ||
8701 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
8705 len = QT_UINT32 ((guint8 *) mdhd->data);
8706 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
8707 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
8708 if (version == 0x01000000) {
8711 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
8712 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
8713 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
8717 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
8718 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
8719 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
8722 if (lang_code < 0x400) {
8723 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
8724 } else if (lang_code == 0x7fff) {
8725 stream->lang_id[0] = 0; /* unspecified */
8727 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
8728 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
8729 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
8730 stream->lang_id[3] = 0;
8733 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
8735 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
8737 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
8738 lang_code, stream->lang_id);
8740 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
8743 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
8744 /* chapters track reference */
8745 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
8747 gsize length = GST_READ_UINT32_BE (chap->data);
8748 if (qtdemux->chapters_track_id)
8749 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
8752 qtdemux->chapters_track_id =
8753 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
8758 /* fragmented files may have bogus duration in moov */
8759 if (!qtdemux->fragmented &&
8760 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
8761 guint64 tdur1, tdur2;
8763 /* don't overflow */
8764 tdur1 = stream->timescale * (guint64) qtdemux->duration;
8765 tdur2 = qtdemux->timescale * (guint64) stream->duration;
8768 * some of those trailers, nowadays, have prologue images that are
8769 * themselves vide tracks as well. I haven't really found a way to
8770 * identify those yet, except for just looking at their duration. */
8771 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
8772 GST_WARNING_OBJECT (qtdemux,
8773 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
8774 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
8775 "found, assuming preview image or something; skipping track",
8776 stream->duration, stream->timescale, qtdemux->duration,
8777 qtdemux->timescale);
8779 gst_qtdemux_stream_free (qtdemux, stream);
8784 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
8787 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
8788 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
8790 len = QT_UINT32 ((guint8 *) hdlr->data);
8792 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
8793 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
8794 GST_FOURCC_ARGS (stream->subtype));
8796 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
8799 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
8802 /*parse svmi header if existing */
8803 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
8805 len = QT_UINT32 ((guint8 *) svmi->data);
8806 version = QT_UINT32 ((guint8 *) svmi->data + 8);
8808 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
8809 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
8810 guint8 frame_type, frame_layout;
8812 /* MPEG-A stereo video */
8813 if (qtdemux->major_brand == FOURCC_ss02)
8814 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
8816 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
8817 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
8818 switch (frame_type) {
8820 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
8823 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
8826 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
8829 /* mode 3 is primary/secondary view sequence, ie
8830 * left/right views in separate tracks. See section 7.2
8831 * of ISO/IEC 23000-11:2009 */
8832 GST_FIXME_OBJECT (qtdemux,
8833 "Implement stereo video in separate streams");
8836 if ((frame_layout & 0x1) == 0)
8837 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
8839 GST_LOG_OBJECT (qtdemux,
8840 "StereoVideo: composition type: %u, is_left_first: %u",
8841 frame_type, frame_layout);
8842 stream->multiview_mode = mode;
8843 stream->multiview_flags = flags;
8848 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
8850 stsd_data = (const guint8 *) stsd->data;
8852 /* stsd should at least have one entry */
8853 stsd_len = QT_UINT32 (stsd_data);
8854 if (stsd_len < 24) {
8855 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
8856 if (stream->subtype == FOURCC_vivo) {
8858 gst_qtdemux_stream_free (qtdemux, stream);
8865 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
8867 /* and that entry should fit within stsd */
8868 len = QT_UINT32 (stsd_data + 16);
8869 if (len > stsd_len + 16)
8872 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
8873 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
8874 GST_FOURCC_ARGS (stream->fourcc));
8875 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
8877 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
8878 goto error_encrypted;
8880 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
8881 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
8882 stream->protected = TRUE;
8883 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
8884 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
8887 if (stream->subtype == FOURCC_vide) {
8888 guint32 w = 0, h = 0;
8890 gint depth, palette_size, palette_count;
8892 guint32 *palette_data = NULL;
8894 stream->sampled = TRUE;
8896 /* version 1 uses some 64-bit ints */
8897 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
8900 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
8903 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
8904 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
8907 stream->display_width = w >> 16;
8908 stream->display_height = h >> 16;
8910 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
8911 &stream->pending_tags);
8917 stream->width = QT_UINT16 (stsd_data + offset + 32);
8918 stream->height = QT_UINT16 (stsd_data + offset + 34);
8919 stream->fps_n = 0; /* this is filled in later */
8920 stream->fps_d = 0; /* this is filled in later */
8921 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
8922 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);
8924 /* if color_table_id is 0, ctab atom must follow; however some files
8925 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
8926 * if color table is not present we'll correct the value */
8927 if (stream->color_table_id == 0 &&
8928 (len < 90 || QT_FOURCC (stsd_data + offset + 86) != FOURCC_ctab)) {
8929 stream->color_table_id = -1;
8932 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
8933 stream->width, stream->height, stream->bits_per_sample,
8934 stream->color_table_id);
8936 depth = stream->bits_per_sample;
8938 /* more than 32 bits means grayscale */
8939 gray = (depth > 32);
8940 /* low 32 bits specify the depth */
8943 /* different number of palette entries is determined by depth. */
8945 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
8946 palette_count = (1 << depth);
8947 palette_size = palette_count * 4;
8949 if (stream->color_table_id) {
8950 switch (palette_count) {
8954 palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
8957 palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
8961 palette_data = g_memdup (ff_qt_grayscale_palette_16, palette_size);
8963 palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
8967 palette_data = g_memdup (ff_qt_grayscale_palette_256, palette_size);
8969 palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
8972 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
8973 (_("The video in this file might not play correctly.")),
8974 ("unsupported palette depth %d", depth));
8978 gint i, j, start, end;
8984 start = QT_UINT32 (stsd_data + offset + 86);
8985 palette_count = QT_UINT16 (stsd_data + offset + 90);
8986 end = QT_UINT16 (stsd_data + offset + 92);
8988 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
8989 start, end, palette_count);
8996 if (len < 94 + (end - start) * 8)
8999 /* palette is always the same size */
9000 palette_data = g_malloc0 (256 * 4);
9001 palette_size = 256 * 4;
9003 for (j = 0, i = start; i <= end; j++, i++) {
9006 a = QT_UINT16 (stsd_data + offset + 94 + (j * 8));
9007 r = QT_UINT16 (stsd_data + offset + 96 + (j * 8));
9008 g = QT_UINT16 (stsd_data + offset + 98 + (j * 8));
9009 b = QT_UINT16 (stsd_data + offset + 100 + (j * 8));
9011 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
9012 (g & 0xff00) | (b >> 8);
9017 gst_caps_unref (stream->caps);
9020 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
9021 if (G_UNLIKELY (!stream->caps)) {
9022 g_free (palette_data);
9023 goto unknown_stream;
9027 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9028 GST_TAG_VIDEO_CODEC, codec, NULL);
9037 if (stream->rgb8_palette)
9038 gst_memory_unref (stream->rgb8_palette);
9039 stream->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
9040 palette_data, palette_size, 0, palette_size, palette_data, g_free);
9042 s = gst_caps_get_structure (stream->caps, 0);
9044 /* non-raw video has a palette_data property. raw video has the palette as
9045 * an extra plane that we append to the output buffers before we push
9047 if (!gst_structure_has_name (s, "video/x-raw")) {
9050 palette = gst_buffer_new ();
9051 gst_buffer_append_memory (palette, stream->rgb8_palette);
9052 stream->rgb8_palette = NULL;
9054 gst_caps_set_simple (stream->caps, "palette_data",
9055 GST_TYPE_BUFFER, palette, NULL);
9056 gst_buffer_unref (palette);
9058 } else if (palette_count != 0) {
9059 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
9060 (NULL), ("Unsupported palette depth %d", depth));
9063 GST_LOG_OBJECT (qtdemux, "frame count: %u",
9064 QT_UINT16 (stsd_data + offset + 48));
9068 /* pick 'the' stsd child */
9069 if (!stream->protected)
9070 mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc);
9072 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_encv);
9075 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
9076 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
9080 const guint8 *pasp_data = (const guint8 *) pasp->data;
9082 stream->par_w = QT_UINT32 (pasp_data + 8);
9083 stream->par_h = QT_UINT32 (pasp_data + 12);
9090 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
9097 gint len = QT_UINT32 (stsd_data) - 0x66;
9098 const guint8 *avc_data = stsd_data + 0x66;
9101 while (len >= 0x8) {
9104 if (QT_UINT32 (avc_data) <= len)
9105 size = QT_UINT32 (avc_data) - 0x8;
9110 /* No real data, so break out */
9113 switch (QT_FOURCC (avc_data + 0x4)) {
9116 /* parse, if found */
9119 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
9121 /* First 4 bytes are the length of the atom, the next 4 bytes
9122 * are the fourcc, the next 1 byte is the version, and the
9123 * subsequent bytes are profile_tier_level structure like data. */
9124 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
9125 avc_data + 8 + 1, size - 1);
9126 buf = gst_buffer_new_and_alloc (size);
9127 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
9128 gst_caps_set_simple (stream->caps,
9129 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9130 gst_buffer_unref (buf);
9138 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
9140 /* First 4 bytes are the length of the atom, the next 4 bytes
9141 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
9142 * next 1 byte is the version, and the
9143 * subsequent bytes are sequence parameter set like data. */
9145 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
9147 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
9148 avc_data + 8 + 40 + 1, size - 1);
9150 buf = gst_buffer_new_and_alloc (size);
9151 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
9152 gst_caps_set_simple (stream->caps,
9153 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9154 gst_buffer_unref (buf);
9160 guint avg_bitrate, max_bitrate;
9162 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
9166 max_bitrate = QT_UINT32 (avc_data + 0xc);
9167 avg_bitrate = QT_UINT32 (avc_data + 0x10);
9169 if (!max_bitrate && !avg_bitrate)
9172 /* Some muxers seem to swap the average and maximum bitrates
9173 * (I'm looking at you, YouTube), so we swap for sanity. */
9174 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
9175 guint temp = avg_bitrate;
9177 avg_bitrate = max_bitrate;
9181 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
9182 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9183 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
9185 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
9186 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9187 GST_TAG_BITRATE, avg_bitrate, NULL);
9198 avc_data += size + 8;
9207 gint len = QT_UINT32 (stsd_data) - 0x66;
9208 const guint8 *hevc_data = stsd_data + 0x66;
9211 while (len >= 0x8) {
9214 if (QT_UINT32 (hevc_data) <= len)
9215 size = QT_UINT32 (hevc_data) - 0x8;
9220 /* No real data, so break out */
9223 switch (QT_FOURCC (hevc_data + 0x4)) {
9226 /* parse, if found */
9229 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
9231 /* First 4 bytes are the length of the atom, the next 4 bytes
9232 * are the fourcc, the next 1 byte is the version, and the
9233 * subsequent bytes are sequence parameter set like data. */
9234 gst_codec_utils_h265_caps_set_level_tier_and_profile
9235 (stream->caps, hevc_data + 8 + 1, size - 1);
9237 buf = gst_buffer_new_and_alloc (size);
9238 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
9239 gst_caps_set_simple (stream->caps,
9240 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9241 gst_buffer_unref (buf);
9248 hevc_data += size + 8;
9259 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
9260 GST_FOURCC_ARGS (fourcc));
9262 /* codec data might be in glbl extension atom */
9264 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
9270 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
9272 len = QT_UINT32 (data);
9275 buf = gst_buffer_new_and_alloc (len);
9276 gst_buffer_fill (buf, 0, data + 8, len);
9277 gst_caps_set_simple (stream->caps,
9278 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9279 gst_buffer_unref (buf);
9286 /* see annex I of the jpeg2000 spec */
9287 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
9289 const gchar *colorspace = NULL;
9291 guint32 ncomp_map = 0;
9292 gint32 *comp_map = NULL;
9293 guint32 nchan_def = 0;
9294 gint32 *chan_def = NULL;
9296 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
9297 /* some required atoms */
9298 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
9301 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
9305 /* number of components; redundant with info in codestream, but useful
9307 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
9308 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
9310 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
9312 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
9315 GST_DEBUG_OBJECT (qtdemux, "found colr");
9316 /* extract colour space info */
9317 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
9318 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
9320 colorspace = "sRGB";
9323 colorspace = "GRAY";
9326 colorspace = "sYUV";
9334 /* colr is required, and only values 16, 17, and 18 are specified,
9335 so error if we have no colorspace */
9338 /* extract component mapping */
9339 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
9341 guint32 cmap_len = 0;
9343 cmap_len = QT_UINT32 (cmap->data);
9344 if (cmap_len >= 8) {
9345 /* normal box, subtract off header */
9347 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
9348 if (cmap_len % 4 == 0) {
9349 ncomp_map = (cmap_len / 4);
9350 comp_map = g_new0 (gint32, ncomp_map);
9351 for (i = 0; i < ncomp_map; i++) {
9354 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
9355 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
9356 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
9357 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
9362 /* extract channel definitions */
9363 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
9365 guint32 cdef_len = 0;
9367 cdef_len = QT_UINT32 (cdef->data);
9368 if (cdef_len >= 10) {
9369 /* normal box, subtract off header and len */
9371 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
9372 if (cdef_len % 6 == 0) {
9373 nchan_def = (cdef_len / 6);
9374 chan_def = g_new0 (gint32, nchan_def);
9375 for (i = 0; i < nchan_def; i++)
9377 for (i = 0; i < nchan_def; i++) {
9378 guint16 cn, typ, asoc;
9379 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
9380 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
9381 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
9382 if (cn < nchan_def) {
9385 chan_def[cn] = asoc;
9388 chan_def[cn] = 0; /* alpha */
9391 chan_def[cn] = -typ;
9399 gst_caps_set_simple (stream->caps,
9400 "num-components", G_TYPE_INT, ncomp, NULL);
9401 gst_caps_set_simple (stream->caps,
9402 "colorspace", G_TYPE_STRING, colorspace, NULL);
9405 GValue arr = { 0, };
9406 GValue elt = { 0, };
9408 g_value_init (&arr, GST_TYPE_ARRAY);
9409 g_value_init (&elt, G_TYPE_INT);
9410 for (i = 0; i < ncomp_map; i++) {
9411 g_value_set_int (&elt, comp_map[i]);
9412 gst_value_array_append_value (&arr, &elt);
9414 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
9415 "component-map", &arr);
9416 g_value_unset (&elt);
9417 g_value_unset (&arr);
9422 GValue arr = { 0, };
9423 GValue elt = { 0, };
9425 g_value_init (&arr, GST_TYPE_ARRAY);
9426 g_value_init (&elt, G_TYPE_INT);
9427 for (i = 0; i < nchan_def; i++) {
9428 g_value_set_int (&elt, chan_def[i]);
9429 gst_value_array_append_value (&arr, &elt);
9431 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
9432 "channel-definitions", &arr);
9433 g_value_unset (&elt);
9434 g_value_unset (&arr);
9438 /* some optional atoms */
9439 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
9440 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
9442 /* indicate possible fields in caps */
9444 data = (guint8 *) field->data + 8;
9446 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
9447 (gint) * data, NULL);
9449 /* add codec_data if provided */
9454 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
9455 data = prefix->data;
9456 len = QT_UINT32 (data);
9459 buf = gst_buffer_new_and_alloc (len);
9460 gst_buffer_fill (buf, 0, data + 8, len);
9461 gst_caps_set_simple (stream->caps,
9462 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9463 gst_buffer_unref (buf);
9472 GstBuffer *seqh = NULL;
9473 guint8 *gamma_data = NULL;
9474 gint len = QT_UINT32 (stsd_data);
9476 qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
9478 gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
9479 QT_FP32 (gamma_data), NULL);
9482 /* sorry for the bad name, but we don't know what this is, other
9483 * than its own fourcc */
9484 gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
9488 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
9489 buf = gst_buffer_new_and_alloc (len);
9490 gst_buffer_fill (buf, 0, stsd_data, len);
9491 gst_caps_set_simple (stream->caps,
9492 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9493 gst_buffer_unref (buf);
9499 gst_caps_set_simple (stream->caps,
9500 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
9507 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
9508 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
9512 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
9516 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
9517 /* collect the headers and store them in a stream list so that we can
9518 * send them out first */
9519 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
9529 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
9530 ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
9533 ovc1_data = ovc1->data;
9534 ovc1_len = QT_UINT32 (ovc1_data);
9535 if (ovc1_len <= 198) {
9536 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
9539 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
9540 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
9541 gst_caps_set_simple (stream->caps,
9542 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9543 gst_buffer_unref (buf);
9548 gint len = QT_UINT32 (stsd_data) - 0x66;
9549 const guint8 *vc1_data = stsd_data + 0x66;
9555 if (QT_UINT32 (vc1_data) <= len)
9556 size = QT_UINT32 (vc1_data) - 8;
9561 /* No real data, so break out */
9564 switch (QT_FOURCC (vc1_data + 0x4)) {
9565 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
9569 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
9570 buf = gst_buffer_new_and_alloc (size);
9571 gst_buffer_fill (buf, 0, vc1_data + 8, size);
9572 gst_caps_set_simple (stream->caps,
9573 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9574 gst_buffer_unref (buf);
9581 vc1_data += size + 8;
9590 GST_INFO_OBJECT (qtdemux,
9591 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
9592 GST_FOURCC_ARGS (fourcc), stream->caps);
9594 } else if (stream->subtype == FOURCC_soun) {
9595 int version, samplesize;
9596 guint16 compression_id;
9597 gboolean amrwb = FALSE;
9600 /* sample description entry (16) + sound sample description v0 (20) */
9604 version = QT_UINT32 (stsd_data + offset);
9605 stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
9606 samplesize = QT_UINT16 (stsd_data + offset + 10);
9607 compression_id = QT_UINT16 (stsd_data + offset + 12);
9608 stream->rate = QT_FP32 (stsd_data + offset + 16);
9610 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
9611 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
9612 QT_UINT32 (stsd_data + offset + 4));
9613 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
9614 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
9615 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
9616 GST_LOG_OBJECT (qtdemux, "packet size: %d",
9617 QT_UINT16 (stsd_data + offset + 14));
9618 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
9620 if (compression_id == 0xfffe)
9621 stream->sampled = TRUE;
9623 /* first assume uncompressed audio */
9624 stream->bytes_per_sample = samplesize / 8;
9625 stream->samples_per_frame = stream->n_channels;
9626 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
9627 stream->samples_per_packet = stream->samples_per_frame;
9628 stream->bytes_per_packet = stream->bytes_per_sample;
9632 /* Yes, these have to be hard-coded */
9635 stream->samples_per_packet = 6;
9636 stream->bytes_per_packet = 1;
9637 stream->bytes_per_frame = 1 * stream->n_channels;
9638 stream->bytes_per_sample = 1;
9639 stream->samples_per_frame = 6 * stream->n_channels;
9644 stream->samples_per_packet = 3;
9645 stream->bytes_per_packet = 1;
9646 stream->bytes_per_frame = 1 * stream->n_channels;
9647 stream->bytes_per_sample = 1;
9648 stream->samples_per_frame = 3 * stream->n_channels;
9653 stream->samples_per_packet = 64;
9654 stream->bytes_per_packet = 34;
9655 stream->bytes_per_frame = 34 * stream->n_channels;
9656 stream->bytes_per_sample = 2;
9657 stream->samples_per_frame = 64 * stream->n_channels;
9663 stream->samples_per_packet = 1;
9664 stream->bytes_per_packet = 1;
9665 stream->bytes_per_frame = 1 * stream->n_channels;
9666 stream->bytes_per_sample = 1;
9667 stream->samples_per_frame = 1 * stream->n_channels;
9672 stream->samples_per_packet = 160;
9673 stream->bytes_per_packet = 33;
9674 stream->bytes_per_frame = 33 * stream->n_channels;
9675 stream->bytes_per_sample = 2;
9676 stream->samples_per_frame = 160 * stream->n_channels;
9683 if (version == 0x00010000) {
9684 /* sample description entry (16) + sound sample description v1 (20+16) */
9695 /* only parse extra decoding config for non-pcm audio */
9696 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
9697 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
9698 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
9699 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);
9701 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
9702 stream->samples_per_packet);
9703 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
9704 stream->bytes_per_packet);
9705 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
9706 stream->bytes_per_frame);
9707 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
9708 stream->bytes_per_sample);
9710 if (!stream->sampled && stream->bytes_per_packet) {
9711 stream->samples_per_frame = (stream->bytes_per_frame /
9712 stream->bytes_per_packet) * stream->samples_per_packet;
9713 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
9714 stream->samples_per_frame);
9719 } else if (version == 0x00020000) {
9726 /* sample description entry (16) + sound sample description v2 (56) */
9730 qtfp.val = QT_UINT64 (stsd_data + offset + 4);
9731 stream->rate = qtfp.fp;
9732 stream->n_channels = QT_UINT32 (stsd_data + offset + 12);
9734 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
9735 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
9736 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
9737 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
9738 QT_UINT32 (stsd_data + offset + 20));
9739 GST_LOG_OBJECT (qtdemux, "format flags: %X",
9740 QT_UINT32 (stsd_data + offset + 24));
9741 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
9742 QT_UINT32 (stsd_data + offset + 28));
9743 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
9744 QT_UINT32 (stsd_data + offset + 32));
9745 } else if (version != 0x00000) {
9746 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x", version);
9750 gst_caps_unref (stream->caps);
9752 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc,
9753 stsd_data + 32, len - 16, &codec);
9761 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
9763 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
9765 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
9767 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
9770 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
9771 gst_caps_set_simple (stream->caps,
9772 "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE", NULL);
9779 const guint8 *owma_data;
9780 const gchar *codec_name = NULL;
9784 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
9785 /* FIXME this should also be gst_riff_strf_auds,
9786 * but the latter one is actually missing bits-per-sample :( */
9791 gint32 nSamplesPerSec;
9792 gint32 nAvgBytesPerSec;
9794 gint16 wBitsPerSample;
9799 GST_DEBUG_OBJECT (qtdemux, "parse owma");
9800 owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
9803 owma_data = owma->data;
9804 owma_len = QT_UINT32 (owma_data);
9805 if (owma_len <= 54) {
9806 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
9809 wfex = (WAVEFORMATEX *) (owma_data + 36);
9810 buf = gst_buffer_new_and_alloc (owma_len - 54);
9811 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
9812 if (wfex->wFormatTag == 0x0161) {
9813 codec_name = "Windows Media Audio";
9815 } else if (wfex->wFormatTag == 0x0162) {
9816 codec_name = "Windows Media Audio 9 Pro";
9818 } else if (wfex->wFormatTag == 0x0163) {
9819 codec_name = "Windows Media Audio 9 Lossless";
9820 /* is that correct? gstffmpegcodecmap.c is missing it, but
9821 * fluendo codec seems to support it */
9825 gst_caps_set_simple (stream->caps,
9826 "codec_data", GST_TYPE_BUFFER, buf,
9827 "wmaversion", G_TYPE_INT, version,
9828 "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
9829 "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
9830 "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
9831 "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
9833 gst_buffer_unref (buf);
9837 codec = g_strdup (codec_name);
9843 gint len = QT_UINT32 (stsd_data) - offset;
9844 const guint8 *wfex_data = stsd_data + offset;
9845 const gchar *codec_name = NULL;
9847 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
9848 /* FIXME this should also be gst_riff_strf_auds,
9849 * but the latter one is actually missing bits-per-sample :( */
9854 gint32 nSamplesPerSec;
9855 gint32 nAvgBytesPerSec;
9857 gint16 wBitsPerSample;
9862 /* FIXME: unify with similar wavformatex parsing code above */
9863 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
9869 if (QT_UINT32 (wfex_data) <= len)
9870 size = QT_UINT32 (wfex_data) - 8;
9875 /* No real data, so break out */
9878 switch (QT_FOURCC (wfex_data + 4)) {
9879 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
9881 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
9886 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
9887 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
9888 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
9889 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
9890 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
9891 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
9892 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
9894 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
9895 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
9896 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
9897 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
9898 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
9899 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
9901 if (wfex.wFormatTag == 0x0161) {
9902 codec_name = "Windows Media Audio";
9904 } else if (wfex.wFormatTag == 0x0162) {
9905 codec_name = "Windows Media Audio 9 Pro";
9907 } else if (wfex.wFormatTag == 0x0163) {
9908 codec_name = "Windows Media Audio 9 Lossless";
9909 /* is that correct? gstffmpegcodecmap.c is missing it, but
9910 * fluendo codec seems to support it */
9914 gst_caps_set_simple (stream->caps,
9915 "wmaversion", G_TYPE_INT, version,
9916 "block_align", G_TYPE_INT, wfex.nBlockAlign,
9917 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
9918 "width", G_TYPE_INT, wfex.wBitsPerSample,
9919 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
9921 if (size > wfex.cbSize) {
9924 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
9925 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
9926 size - wfex.cbSize);
9927 gst_caps_set_simple (stream->caps,
9928 "codec_data", GST_TYPE_BUFFER, buf, NULL);
9929 gst_buffer_unref (buf);
9931 GST_WARNING_OBJECT (qtdemux, "no codec data");
9936 codec = g_strdup (codec_name);
9944 wfex_data += size + 8;
9951 const guint8 *opus_data;
9952 guint8 *channel_mapping = NULL;
9955 guint8 channel_mapping_family;
9956 guint8 stream_count;
9957 guint8 coupled_count;
9960 opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
9961 opus_data = opus->data;
9963 channels = GST_READ_UINT8 (opus_data + 45);
9964 rate = GST_READ_UINT32_LE (opus_data + 48);
9965 channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
9966 stream_count = GST_READ_UINT8 (opus_data + 55);
9967 coupled_count = GST_READ_UINT8 (opus_data + 56);
9970 channel_mapping = g_malloc (channels * sizeof (guint8));
9971 for (i = 0; i < channels; i++)
9972 channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
9975 stream->caps = gst_codec_utils_opus_create_caps (rate, channels,
9976 channel_mapping_family, stream_count, coupled_count,
9988 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9989 GST_TAG_AUDIO_CODEC, codec, NULL);
9993 /* some bitrate info may have ended up in caps */
9994 s = gst_caps_get_structure (stream->caps, 0);
9995 gst_structure_get_int (s, "bitrate", &bitrate);
9997 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
9998 GST_TAG_BITRATE, bitrate, NULL);
10001 if (stream->protected && fourcc == FOURCC_mp4a)
10002 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_enca);
10004 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
10009 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
10011 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
10013 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
10017 /* If the fourcc's bottom 16 bits gives 'sm', then the top
10018 16 bits is a byte-swapped wave-style codec identifier,
10019 and we can find a WAVE header internally to a 'wave' atom here.
10020 This can more clearly be thought of as 'ms' as the top 16 bits, and a
10021 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
10024 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
10025 if (len < offset + 20) {
10026 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
10028 guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
10029 const guint8 *data = stsd_data + offset + 16;
10031 GNode *waveheadernode;
10033 wavenode = g_node_new ((guint8 *) data);
10034 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
10035 const guint8 *waveheader;
10038 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
10039 if (waveheadernode) {
10040 waveheader = (const guint8 *) waveheadernode->data;
10041 headerlen = QT_UINT32 (waveheader);
10043 if (headerlen > 8) {
10044 gst_riff_strf_auds *header = NULL;
10045 GstBuffer *headerbuf;
10051 headerbuf = gst_buffer_new_and_alloc (headerlen);
10052 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
10054 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
10055 headerbuf, &header, &extra)) {
10056 gst_caps_unref (stream->caps);
10057 /* FIXME: Need to do something with the channel reorder map */
10058 stream->caps = gst_riff_create_audio_caps (header->format, NULL,
10059 header, extra, NULL, NULL, NULL);
10062 gst_buffer_unref (extra);
10067 GST_DEBUG ("Didn't find waveheadernode for this codec");
10069 g_node_destroy (wavenode);
10072 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
10076 /* FIXME: what is in the chunk? */
10079 gint len = QT_UINT32 (stsd_data);
10081 /* seems to be always = 116 = 0x74 */
10087 gint len = QT_UINT32 (stsd_data);
10090 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
10092 gst_buffer_fill (buf, 0, stsd_data + 0x4C, len - 0x4C);
10093 gst_caps_set_simple (stream->caps,
10094 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10095 gst_buffer_unref (buf);
10097 gst_caps_set_simple (stream->caps,
10098 "samplesize", G_TYPE_INT, samplesize, NULL);
10103 GNode *alac, *wave = NULL;
10105 /* apparently, m4a has this atom appended directly in the stsd entry,
10106 * while mov has it in a wave atom */
10107 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
10109 /* alac now refers to stsd entry atom */
10110 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
10112 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
10114 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
10117 const guint8 *alac_data = alac->data;
10118 gint len = QT_UINT32 (alac->data);
10122 GST_DEBUG_OBJECT (qtdemux,
10123 "discarding alac atom with unexpected len %d", len);
10125 /* codec-data contains alac atom size and prefix,
10126 * ffmpeg likes it that way, not quite gst-ish though ...*/
10127 buf = gst_buffer_new_and_alloc (len);
10128 gst_buffer_fill (buf, 0, alac->data, len);
10129 gst_caps_set_simple (stream->caps,
10130 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10131 gst_buffer_unref (buf);
10133 stream->bytes_per_frame = QT_UINT32 (alac_data + 12);
10134 stream->n_channels = QT_UINT8 (alac_data + 21);
10135 stream->rate = QT_UINT32 (alac_data + 32);
10138 gst_caps_set_simple (stream->caps,
10139 "samplesize", G_TYPE_INT, samplesize, NULL);
10147 gint len = QT_UINT32 (stsd_data);
10150 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
10153 gst_buffer_fill (buf, 0, stsd_data + 0x34, len - 0x34);
10155 /* If we have enough data, let's try to get the 'damr' atom. See
10156 * the 3GPP container spec (26.244) for more details. */
10157 if ((len - 0x34) > 8 &&
10158 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
10159 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10160 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
10163 gst_caps_set_simple (stream->caps,
10164 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10165 gst_buffer_unref (buf);
10171 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
10172 gint len = QT_UINT32 (stsd_data);
10175 guint16 sound_version = QT_UINT16 (stsd_data + 32);
10177 if (sound_version == 1) {
10178 guint16 channels = QT_UINT16 (stsd_data + 40);
10179 guint32 time_scale = QT_UINT32 (stsd_data + 46);
10180 guint8 codec_data[2];
10182 gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */
10184 gint sample_rate_index =
10185 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
10187 /* build AAC codec data */
10188 codec_data[0] = profile << 3;
10189 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
10190 codec_data[1] = (sample_rate_index & 0x01) << 7;
10191 codec_data[1] |= (channels & 0xF) << 3;
10193 buf = gst_buffer_new_and_alloc (2);
10194 gst_buffer_fill (buf, 0, codec_data, 2);
10195 gst_caps_set_simple (stream->caps,
10196 "codec_data", GST_TYPE_BUFFER, buf, NULL);
10197 gst_buffer_unref (buf);
10203 GST_INFO_OBJECT (qtdemux,
10204 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
10208 GST_INFO_OBJECT (qtdemux,
10209 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
10210 GST_FOURCC_ARGS (fourcc), stream->caps);
10212 } else if (stream->subtype == FOURCC_strm) {
10213 if (fourcc == FOURCC_rtsp) {
10214 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
10216 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
10217 GST_FOURCC_ARGS (fourcc));
10218 goto unknown_stream;
10220 stream->sampled = TRUE;
10221 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
10222 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
10224 stream->sampled = TRUE;
10225 stream->sparse = TRUE;
10228 qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
10230 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10231 GST_TAG_SUBTITLE_CODEC, codec, NULL);
10236 /* hunt for sort-of codec data */
10240 GNode *mp4s = NULL;
10241 GNode *esds = NULL;
10243 /* look for palette in a stsd->mp4s->esds sub-atom */
10244 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
10246 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
10247 if (esds == NULL) {
10249 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
10253 gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
10257 GST_INFO_OBJECT (qtdemux,
10258 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
10261 GST_INFO_OBJECT (qtdemux,
10262 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
10263 GST_FOURCC_ARGS (fourcc), stream->caps);
10265 /* everything in 1 sample */
10266 stream->sampled = TRUE;
10269 qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec);
10271 if (stream->caps == NULL)
10272 goto unknown_stream;
10275 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10276 GST_TAG_SUBTITLE_CODEC, codec, NULL);
10282 /* promote to sampled format */
10283 if (stream->fourcc == FOURCC_samr) {
10284 /* force mono 8000 Hz for AMR */
10285 stream->sampled = TRUE;
10286 stream->n_channels = 1;
10287 stream->rate = 8000;
10288 } else if (stream->fourcc == FOURCC_sawb) {
10289 /* force mono 16000 Hz for AMR-WB */
10290 stream->sampled = TRUE;
10291 stream->n_channels = 1;
10292 stream->rate = 16000;
10293 } else if (stream->fourcc == FOURCC_mp4a) {
10294 stream->sampled = TRUE;
10297 /* collect sample information */
10298 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
10299 goto samples_failed;
10301 if (qtdemux->fragmented) {
10304 /* need all moov samples as basis; probably not many if any at all */
10305 /* prevent moof parsing taking of at this time */
10306 offset = qtdemux->moof_offset;
10307 qtdemux->moof_offset = 0;
10308 if (stream->n_samples &&
10309 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
10310 qtdemux->moof_offset = offset;
10311 goto samples_failed;
10313 qtdemux->moof_offset = 0;
10314 /* movie duration more reliable in this case (e.g. mehd) */
10315 if (qtdemux->segment.duration &&
10316 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
10318 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
10321 /* configure segments */
10322 if (!qtdemux_parse_segments (qtdemux, stream, trak))
10323 goto segments_failed;
10325 /* add some language tag, if useful */
10326 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
10327 strcmp (stream->lang_id, "und")) {
10328 const gchar *lang_code;
10330 /* convert ISO 639-2 code to ISO 639-1 */
10331 lang_code = gst_tag_get_language_code (stream->lang_id);
10332 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10333 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
10336 /* Check for UDTA tags */
10337 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
10338 qtdemux_parse_udta (qtdemux, stream->pending_tags, udta);
10341 /* now we are ready to add the stream */
10342 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
10343 goto too_many_streams;
10345 if (!qtdemux->got_moov) {
10346 qtdemux->streams[qtdemux->n_streams] = stream;
10347 qtdemux->n_streams++;
10348 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
10356 GST_INFO_OBJECT (qtdemux, "skip disabled track");
10358 gst_qtdemux_stream_free (qtdemux, stream);
10363 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10364 (_("This file is corrupt and cannot be played.")), (NULL));
10366 gst_qtdemux_stream_free (qtdemux, stream);
10371 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
10373 gst_qtdemux_stream_free (qtdemux, stream);
10379 /* we posted an error already */
10380 /* free stbl sub-atoms */
10381 gst_qtdemux_stbl_free (stream);
10383 gst_qtdemux_stream_free (qtdemux, stream);
10388 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
10391 gst_qtdemux_stream_free (qtdemux, stream);
10396 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
10397 GST_FOURCC_ARGS (stream->subtype));
10399 gst_qtdemux_stream_free (qtdemux, stream);
10404 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
10405 (_("This file contains too many streams. Only playing first %d"),
10406 GST_QTDEMUX_MAX_STREAMS), (NULL));
10411 /* If we can estimate the overall bitrate, and don't have information about the
10412 * stream bitrate for exactly one stream, this guesses the stream bitrate as
10413 * the overall bitrate minus the sum of the bitrates of all other streams. This
10414 * should be useful for the common case where we have one audio and one video
10415 * stream and can estimate the bitrate of one, but not the other. */
10417 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
10419 QtDemuxStream *stream = NULL;
10420 gint64 size, sys_bitrate, sum_bitrate = 0;
10421 GstClockTime duration;
10425 if (qtdemux->fragmented)
10428 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
10430 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
10432 GST_DEBUG_OBJECT (qtdemux,
10433 "Size in bytes of the stream not known - bailing");
10437 /* Subtract the header size */
10438 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
10439 size, qtdemux->header_size);
10441 if (size < qtdemux->header_size)
10444 size = size - qtdemux->header_size;
10446 if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
10447 duration == GST_CLOCK_TIME_NONE) {
10448 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
10452 for (i = 0; i < qtdemux->n_streams; i++) {
10453 switch (qtdemux->streams[i]->subtype) {
10456 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
10457 qtdemux->streams[i]->caps);
10458 /* retrieve bitrate, prefer avg then max */
10460 if (qtdemux->streams[i]->pending_tags) {
10461 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10462 GST_TAG_MAXIMUM_BITRATE, &bitrate);
10463 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
10464 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10465 GST_TAG_NOMINAL_BITRATE, &bitrate);
10466 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
10467 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
10468 GST_TAG_BITRATE, &bitrate);
10469 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
10472 sum_bitrate += bitrate;
10475 GST_DEBUG_OBJECT (qtdemux,
10476 ">1 stream with unknown bitrate - bailing");
10479 stream = qtdemux->streams[i];
10483 /* For other subtypes, we assume no significant impact on bitrate */
10489 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
10493 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
10495 if (sys_bitrate < sum_bitrate) {
10496 /* This can happen, since sum_bitrate might be derived from maximum
10497 * bitrates and not average bitrates */
10498 GST_DEBUG_OBJECT (qtdemux,
10499 "System bitrate less than sum bitrate - bailing");
10503 bitrate = sys_bitrate - sum_bitrate;
10504 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
10505 ", Stream bitrate = %u", sys_bitrate, bitrate);
10507 if (!stream->pending_tags)
10508 stream->pending_tags = gst_tag_list_new_empty ();
10510 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
10511 GST_TAG_BITRATE, bitrate, NULL);
10514 static GstFlowReturn
10515 qtdemux_prepare_streams (GstQTDemux * qtdemux)
10518 GstFlowReturn ret = GST_FLOW_OK;
10520 GST_DEBUG_OBJECT (qtdemux, "prepare streams");
10522 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
10523 QtDemuxStream *stream = qtdemux->streams[i];
10524 guint32 sample_num = 0;
10526 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
10527 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
10529 if (qtdemux->fragmented) {
10530 /* need all moov samples first */
10531 GST_OBJECT_LOCK (qtdemux);
10532 while (stream->n_samples == 0)
10533 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
10535 GST_OBJECT_UNLOCK (qtdemux);
10537 /* discard any stray moof */
10538 qtdemux->moof_offset = 0;
10541 /* prepare braking */
10542 if (ret != GST_FLOW_ERROR)
10545 /* in pull mode, we should have parsed some sample info by now;
10546 * and quite some code will not handle no samples.
10547 * in push mode, we'll just have to deal with it */
10548 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
10549 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
10550 gst_qtdemux_remove_stream (qtdemux, i);
10555 /* parse the initial sample for use in setting the frame rate cap */
10556 while (sample_num == 0 && sample_num < stream->n_samples) {
10557 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
10561 if (stream->n_samples > 0 && stream->stbl_index >= 0) {
10562 stream->first_duration = stream->samples[0].duration;
10563 GST_LOG_OBJECT (qtdemux, "stream %d first duration %u",
10564 stream->track_id, stream->first_duration);
10571 static GstFlowReturn
10572 qtdemux_expose_streams (GstQTDemux * qtdemux)
10575 GstFlowReturn ret = GST_FLOW_OK;
10576 GSList *oldpads = NULL;
10579 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
10581 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
10582 QtDemuxStream *stream = qtdemux->streams[i];
10583 GstPad *oldpad = stream->pad;
10586 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
10587 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
10589 if ((stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) &&
10590 stream->track_id == qtdemux->chapters_track_id) {
10591 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
10592 so that it doesn't look like a subtitle track */
10593 gst_qtdemux_remove_stream (qtdemux, i);
10598 /* now we have all info and can expose */
10599 list = stream->pending_tags;
10600 stream->pending_tags = NULL;
10602 oldpads = g_slist_prepend (oldpads, oldpad);
10603 gst_qtdemux_add_stream (qtdemux, stream, list);
10606 gst_qtdemux_guess_bitrate (qtdemux);
10608 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
10610 for (iter = oldpads; iter; iter = g_slist_next (iter)) {
10611 GstPad *oldpad = iter->data;
10613 gst_pad_push_event (oldpad, gst_event_new_eos ());
10614 gst_pad_set_active (oldpad, FALSE);
10615 gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad);
10616 gst_flow_combiner_remove_pad (qtdemux->flowcombiner, oldpad);
10617 gst_object_unref (oldpad);
10620 /* check if we should post a redirect in case there is a single trak
10621 * and it is a redirecting trak */
10622 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
10625 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
10626 "an external content");
10627 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
10628 gst_structure_new ("redirect",
10629 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
10631 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
10632 qtdemux->posted_redirect = TRUE;
10635 for (i = 0; i < qtdemux->n_streams; i++) {
10636 QtDemuxStream *stream = qtdemux->streams[i];
10638 qtdemux_do_allocation (qtdemux, stream);
10641 qtdemux->exposed = TRUE;
10645 /* check if major or compatible brand is 3GP */
10646 static inline gboolean
10647 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
10650 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
10652 } else if (qtdemux->comp_brands != NULL) {
10656 gboolean res = FALSE;
10658 gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
10661 while (size >= 4) {
10662 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
10667 gst_buffer_unmap (qtdemux->comp_brands, &map);
10674 /* check if tag is a spec'ed 3GP tag keyword storing a string */
10675 static inline gboolean
10676 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
10678 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
10679 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
10680 || fourcc == FOURCC_albm;
10684 qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
10685 const char *tag, const char *dummy, GNode * node)
10687 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
10691 gdouble longitude, latitude, altitude;
10694 len = QT_UINT32 (node->data);
10701 /* TODO: language code skipped */
10703 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
10706 /* do not alarm in trivial case, but bail out otherwise */
10707 if (*(data + offset) != 0) {
10708 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
10712 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
10713 GST_TAG_GEO_LOCATION_NAME, name, NULL);
10714 offset += strlen (name);
10718 if (len < offset + 2 + 4 + 4 + 4)
10721 /* +1 +1 = skip null-terminator and location role byte */
10723 /* table in spec says unsigned, semantics say negative has meaning ... */
10724 longitude = QT_SFP32 (data + offset);
10727 latitude = QT_SFP32 (data + offset);
10730 altitude = QT_SFP32 (data + offset);
10732 /* one invalid means all are invalid */
10733 if (longitude >= -180.0 && longitude <= 180.0 &&
10734 latitude >= -90.0 && latitude <= 90.0) {
10735 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
10736 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
10737 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
10738 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
10741 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
10748 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
10755 qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
10756 const char *tag, const char *dummy, GNode * node)
10762 len = QT_UINT32 (node->data);
10766 y = QT_UINT16 ((guint8 *) node->data + 12);
10768 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
10771 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
10773 date = g_date_new_dmy (1, 1, y);
10774 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
10775 g_date_free (date);
10779 qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
10780 const char *tag, const char *dummy, GNode * node)
10783 char *tag_str = NULL;
10788 len = QT_UINT32 (node->data);
10793 entity = (guint8 *) node->data + offset;
10794 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
10795 GST_DEBUG_OBJECT (qtdemux,
10796 "classification info: %c%c%c%c invalid classification entity",
10797 entity[0], entity[1], entity[2], entity[3]);
10802 table = QT_UINT16 ((guint8 *) node->data + offset);
10804 /* Language code skipped */
10808 /* Tag format: "XXXX://Y[YYYY]/classification info string"
10809 * XXXX: classification entity, fixed length 4 chars.
10810 * Y[YYYY]: classification table, max 5 chars.
10812 tag_str = g_strdup_printf ("----://%u/%s",
10813 table, (char *) node->data + offset);
10815 /* memcpy To be sure we're preserving byte order */
10816 memcpy (tag_str, entity, 4);
10817 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
10819 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
10828 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
10834 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
10835 const char *tag, const char *dummy, GNode * node)
10837 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
10843 gboolean ret = TRUE;
10844 const gchar *charset = NULL;
10846 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
10848 len = QT_UINT32 (data->data);
10849 type = QT_UINT32 ((guint8 *) data->data + 8);
10850 if (type == 0x00000001 && len > 16) {
10851 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
10854 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
10855 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
10858 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
10862 len = QT_UINT32 (node->data);
10863 type = QT_UINT32 ((guint8 *) node->data + 4);
10864 if ((type >> 24) == 0xa9) {
10868 /* Type starts with the (C) symbol, so the next data is a list
10869 * of (string size(16), language code(16), string) */
10871 str_len = QT_UINT16 ((guint8 *) node->data + 8);
10872 lang_code = QT_UINT16 ((guint8 *) node->data + 10);
10874 /* the string + fourcc + size + 2 16bit fields,
10875 * means that there are more tags in this atom */
10876 if (len > str_len + 8 + 4) {
10877 /* TODO how to represent the same tag in different languages? */
10878 GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
10879 "text alternatives, reading only first one");
10883 len = str_len + 8 + 4; /* remove trailing strings that we don't use */
10884 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
10886 if (lang_code < 0x800) { /* MAC encoded string */
10889 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
10890 QT_FOURCC ((guint8 *) node->data + 4))) {
10891 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
10893 /* we go for 3GP style encoding if major brands claims so,
10894 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
10895 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
10896 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
10897 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
10899 /* 16-bit Language code is ignored here as well */
10900 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
10907 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
10908 ret = FALSE; /* may have to fallback */
10911 GError *err = NULL;
10913 s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
10914 charset, NULL, NULL, &err);
10916 GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
10917 " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
10919 g_error_free (err);
10922 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
10923 len - offset, env_vars);
10926 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
10927 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
10931 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
10938 qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
10939 const char *tag, const char *dummy, GNode * node)
10941 qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
10945 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
10946 const char *tag, const char *dummy, GNode * node)
10948 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
10950 char *s, *t, *k = NULL;
10955 /* first try normal string tag if major brand not 3GP */
10956 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
10957 if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
10958 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
10959 * let's try it 3gpp way after minor safety check */
10961 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
10967 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
10971 len = QT_UINT32 (data);
10975 count = QT_UINT8 (data + 14);
10977 for (; count; count--) {
10980 if (offset + 1 > len)
10982 slen = QT_UINT8 (data + offset);
10984 if (offset + slen > len)
10986 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
10989 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
10991 t = g_strjoin (",", k, s, NULL);
10999 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
11006 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
11007 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
11016 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
11022 qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
11023 const char *tag1, const char *tag2, GNode * node)
11030 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11032 len = QT_UINT32 (data->data);
11033 type = QT_UINT32 ((guint8 *) data->data + 8);
11034 if (type == 0x00000000 && len >= 22) {
11035 n1 = QT_UINT16 ((guint8 *) data->data + 18);
11036 n2 = QT_UINT16 ((guint8 *) data->data + 20);
11038 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
11039 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
11042 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
11043 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
11050 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
11051 const char *tag1, const char *dummy, GNode * node)
11058 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11060 len = QT_UINT32 (data->data);
11061 type = QT_UINT32 ((guint8 *) data->data + 8);
11062 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
11063 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
11064 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
11065 n1 = QT_UINT16 ((guint8 *) data->data + 16);
11067 /* do not add bpm=0 */
11068 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
11069 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
11077 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
11078 const char *tag1, const char *dummy, GNode * node)
11085 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11087 len = QT_UINT32 (data->data);
11088 type = QT_UINT32 ((guint8 *) data->data + 8);
11089 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
11090 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
11091 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
11092 num = QT_UINT32 ((guint8 *) data->data + 16);
11094 /* do not add num=0 */
11095 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
11096 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
11103 qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
11104 const char *tag1, const char *dummy, GNode * node)
11111 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11113 len = QT_UINT32 (data->data);
11114 type = QT_UINT32 ((guint8 *) data->data + 8);
11115 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
11116 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
11118 gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
11119 len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
11120 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
11121 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
11122 gst_sample_unref (sample);
11129 qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
11130 const char *tag, const char *dummy, GNode * node)
11137 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11139 len = QT_UINT32 (data->data);
11140 type = QT_UINT32 ((guint8 *) data->data + 8);
11141 if (type == 0x00000001 && len > 16) {
11142 guint y, m = 1, d = 1;
11145 s = g_strndup ((char *) data->data + 16, len - 16);
11146 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
11147 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
11148 if (ret >= 1 && y > 1500 && y < 3000) {
11151 date = g_date_new_dmy (d, m, y);
11152 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
11153 g_date_free (date);
11155 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
11163 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
11164 const char *tag, const char *dummy, GNode * node)
11168 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11170 /* re-route to normal string tag if major brand says so
11171 * or no data atom and compatible brand suggests so */
11172 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
11173 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
11174 qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
11179 guint len, type, n;
11181 len = QT_UINT32 (data->data);
11182 type = QT_UINT32 ((guint8 *) data->data + 8);
11183 if (type == 0x00000000 && len >= 18) {
11184 n = QT_UINT16 ((guint8 *) data->data + 16);
11186 const gchar *genre;
11188 genre = gst_tag_id3_genre_get (n - 1);
11189 if (genre != NULL) {
11190 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
11191 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
11199 qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
11200 const gchar * tag, guint8 * data, guint32 datasize)
11205 /* make a copy to have \0 at the end */
11206 datacopy = g_strndup ((gchar *) data, datasize);
11208 /* convert the str to double */
11209 if (sscanf (datacopy, "%lf", &value) == 1) {
11210 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
11211 gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
11213 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
11221 qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
11222 const char *tag, const char *tag_bis, GNode * node)
11231 const gchar *meanstr;
11232 const gchar *namestr;
11234 /* checking the whole ---- atom size for consistency */
11235 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
11236 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
11240 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
11242 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
11246 meansize = QT_UINT32 (mean->data);
11247 if (meansize <= 12) {
11248 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
11251 meanstr = ((gchar *) mean->data) + 12;
11254 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
11256 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
11260 namesize = QT_UINT32 (name->data);
11261 if (namesize <= 12) {
11262 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
11265 namestr = ((gchar *) name->data) + 12;
11273 * uint24 - data type
11277 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
11279 GST_WARNING_OBJECT (demux, "No data atom in this tag");
11282 datasize = QT_UINT32 (data->data);
11283 if (datasize <= 16) {
11284 GST_WARNING_OBJECT (demux, "Data atom too small");
11287 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
11289 if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
11290 (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
11291 static const struct
11293 const gchar name[28];
11294 const gchar tag[28];
11297 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
11298 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
11299 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
11300 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
11301 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
11302 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
11303 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
11304 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
11308 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
11309 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
11310 switch (gst_tag_get_type (tags[i].tag)) {
11311 case G_TYPE_DOUBLE:
11312 qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
11313 ((guint8 *) data->data) + 16, datasize - 16);
11315 case G_TYPE_STRING:
11316 qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
11325 if (i == G_N_ELEMENTS (tags))
11335 #ifndef GST_DISABLE_GST_DEBUG
11337 gchar *namestr_dbg;
11338 gchar *meanstr_dbg;
11340 meanstr_dbg = g_strndup (meanstr, meansize);
11341 namestr_dbg = g_strndup (namestr, namesize);
11343 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
11344 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
11346 g_free (namestr_dbg);
11347 g_free (meanstr_dbg);
11354 qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
11355 const char *tag_bis, GNode * node)
11360 GstTagList *id32_taglist = NULL;
11362 GST_LOG_OBJECT (demux, "parsing ID32");
11365 len = GST_READ_UINT32_BE (data);
11367 /* need at least full box and language tag */
11371 buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
11372 gst_buffer_fill (buf, 0, data + 14, len - 14);
11374 id32_taglist = gst_tag_list_from_id3v2_tag (buf);
11375 if (id32_taglist) {
11376 GST_LOG_OBJECT (demux, "parsing ok");
11377 gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
11378 gst_tag_list_unref (id32_taglist);
11380 GST_LOG_OBJECT (demux, "parsing failed");
11383 gst_buffer_unref (buf);
11386 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
11387 const char *tag, const char *tag_bis, GNode * node);
11390 FOURCC_pcst -> if media is a podcast -> bool
11391 FOURCC_cpil -> if media is part of a compilation -> bool
11392 FOURCC_pgap -> if media is part of a gapless context -> bool
11393 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
11396 static const struct
11399 const gchar *gst_tag;
11400 const gchar *gst_tag_bis;
11401 const GstQTDemuxAddTagFunc func;
11404 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
11405 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
11406 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
11407 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
11408 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
11409 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
11410 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
11411 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
11412 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
11413 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
11414 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
11415 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
11416 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
11417 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11418 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11419 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
11420 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
11421 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
11422 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
11423 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
11424 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
11425 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
11426 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
11427 qtdemux_tag_add_num}, {
11428 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
11429 qtdemux_tag_add_num}, {
11430 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
11431 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
11432 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
11433 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
11434 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
11435 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
11436 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
11437 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
11438 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
11439 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
11440 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
11441 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
11442 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
11443 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
11444 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
11445 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
11446 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
11447 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
11448 qtdemux_tag_add_classification}, {
11449 FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
11450 FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
11451 FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
11453 /* This is a special case, some tags are stored in this
11454 * 'reverse dns naming', according to:
11455 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
11458 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
11459 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
11460 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
11463 struct _GstQtDemuxTagList
11466 GstTagList *taglist;
11468 typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
11471 qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
11477 const gchar *style;
11482 GstQTDemux *demux = qtdemuxtaglist->demux;
11483 GstTagList *taglist = qtdemuxtaglist->taglist;
11486 len = QT_UINT32 (data);
11487 buf = gst_buffer_new_and_alloc (len);
11488 gst_buffer_fill (buf, 0, data, len);
11490 /* heuristic to determine style of tag */
11491 if (QT_FOURCC (data + 4) == FOURCC_____ ||
11492 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
11494 else if (demux->major_brand == FOURCC_qt__)
11495 style = "quicktime";
11496 /* fall back to assuming iso/3gp tag style */
11500 /* santize the name for the caps. */
11501 for (i = 0; i < 4; i++) {
11502 guint8 d = data[4 + i];
11503 if (g_ascii_isalnum (d))
11504 ndata[i] = g_ascii_tolower (d);
11509 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
11510 ndata[0], ndata[1], ndata[2], ndata[3]);
11511 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
11513 s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
11514 sample = gst_sample_new (buf, NULL, NULL, s);
11515 gst_buffer_unref (buf);
11516 g_free (media_type);
11518 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
11521 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
11522 GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
11524 gst_sample_unref (sample);
11528 qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
11535 GstQtDemuxTagList demuxtaglist;
11537 demuxtaglist.demux = qtdemux;
11538 demuxtaglist.taglist = taglist;
11540 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
11541 if (meta != NULL) {
11542 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
11543 if (ilst == NULL) {
11544 GST_LOG_OBJECT (qtdemux, "no ilst");
11549 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
11553 while (i < G_N_ELEMENTS (add_funcs)) {
11554 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
11558 len = QT_UINT32 (node->data);
11560 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
11561 GST_FOURCC_ARGS (add_funcs[i].fourcc));
11563 add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
11564 add_funcs[i].gst_tag_bis, node);
11566 g_node_destroy (node);
11572 /* parsed nodes have been removed, pass along remainder as blob */
11573 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
11574 (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
11576 /* parse up XMP_ node if existing */
11577 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
11578 if (xmp_ != NULL) {
11580 GstTagList *xmptaglist;
11582 buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
11583 QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
11584 xmptaglist = gst_tag_list_from_xmp_buffer (buf);
11585 gst_buffer_unref (buf);
11587 qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
11589 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
11595 GstStructure *structure; /* helper for sort function */
11597 guint min_req_bitrate;
11598 guint min_req_qt_version;
11602 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
11604 GstQtReference *ref_a = (GstQtReference *) a;
11605 GstQtReference *ref_b = (GstQtReference *) b;
11607 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
11608 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
11610 /* known bitrates go before unknown; higher bitrates go first */
11611 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
11614 /* sort the redirects and post a message for the application.
11617 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
11619 GstQtReference *best;
11622 GValue list_val = { 0, };
11625 g_assert (references != NULL);
11627 references = g_list_sort (references, qtdemux_redirects_sort_func);
11629 best = (GstQtReference *) references->data;
11631 g_value_init (&list_val, GST_TYPE_LIST);
11633 for (l = references; l != NULL; l = l->next) {
11634 GstQtReference *ref = (GstQtReference *) l->data;
11635 GValue struct_val = { 0, };
11637 ref->structure = gst_structure_new ("redirect",
11638 "new-location", G_TYPE_STRING, ref->location, NULL);
11640 if (ref->min_req_bitrate > 0) {
11641 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
11642 ref->min_req_bitrate, NULL);
11645 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
11646 g_value_set_boxed (&struct_val, ref->structure);
11647 gst_value_list_append_value (&list_val, &struct_val);
11648 g_value_unset (&struct_val);
11649 /* don't free anything here yet, since we need best->structure below */
11652 g_assert (best != NULL);
11653 s = gst_structure_copy (best->structure);
11655 if (g_list_length (references) > 1) {
11656 gst_structure_set_value (s, "locations", &list_val);
11659 g_value_unset (&list_val);
11661 for (l = references; l != NULL; l = l->next) {
11662 GstQtReference *ref = (GstQtReference *) l->data;
11664 gst_structure_free (ref->structure);
11665 g_free (ref->location);
11668 g_list_free (references);
11670 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
11671 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
11672 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
11673 qtdemux->posted_redirect = TRUE;
11676 /* look for redirect nodes, collect all redirect information and
11680 qtdemux_parse_redirects (GstQTDemux * qtdemux)
11682 GNode *rmra, *rmda, *rdrf;
11684 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
11686 GList *redirects = NULL;
11688 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
11690 GstQtReference ref = { NULL, NULL, 0, 0 };
11691 GNode *rmdr, *rmvc;
11693 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
11694 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
11695 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
11696 ref.min_req_bitrate);
11699 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
11700 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
11701 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
11703 #ifndef GST_DISABLE_GST_DEBUG
11704 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
11706 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
11708 GST_LOG_OBJECT (qtdemux,
11709 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
11710 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
11711 bitmask, check_type);
11712 if (package == FOURCC_qtim && check_type == 0) {
11713 ref.min_req_qt_version = version;
11717 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
11723 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
11724 if (ref_len > 20) {
11725 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
11726 ref_data = (guint8 *) rdrf->data + 20;
11727 if (ref_type == FOURCC_alis) {
11728 guint record_len, record_version, fn_len;
11730 if (ref_len > 70) {
11731 /* MacOSX alias record, google for alias-layout.txt */
11732 record_len = QT_UINT16 (ref_data + 4);
11733 record_version = QT_UINT16 (ref_data + 4 + 2);
11734 fn_len = QT_UINT8 (ref_data + 50);
11735 if (record_len > 50 && record_version == 2 && fn_len > 0) {
11736 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
11739 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
11742 } else if (ref_type == FOURCC_url_) {
11743 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
11745 GST_DEBUG_OBJECT (qtdemux,
11746 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
11747 GST_FOURCC_ARGS (ref_type));
11749 if (ref.location != NULL) {
11750 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
11752 g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
11754 GST_WARNING_OBJECT (qtdemux,
11755 "Failed to extract redirect location from rdrf atom");
11758 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
11762 /* look for others */
11763 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
11766 if (redirects != NULL) {
11767 qtdemux_process_redirects (qtdemux, redirects);
11773 static GstTagList *
11774 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
11778 if (tags == NULL) {
11779 tags = gst_tag_list_new_empty ();
11780 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
11783 if (qtdemux->major_brand == FOURCC_mjp2)
11784 fmt = "Motion JPEG 2000";
11785 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
11787 else if (qtdemux->major_brand == FOURCC_qt__)
11789 else if (qtdemux->fragmented)
11792 fmt = "ISO MP4/M4A";
11794 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
11795 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
11797 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
11803 /* we have read th complete moov node now.
11804 * This function parses all of the relevant info, creates the traks and
11805 * prepares all data structures for playback
11808 qtdemux_parse_tree (GstQTDemux * qtdemux)
11814 GstClockTime duration;
11816 guint64 creation_time;
11817 GstDateTime *datetime = NULL;
11820 /* make sure we have a usable taglist */
11821 if (!qtdemux->tag_list) {
11822 qtdemux->tag_list = gst_tag_list_new_empty ();
11823 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
11825 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
11828 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
11829 if (mvhd == NULL) {
11830 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
11831 return qtdemux_parse_redirects (qtdemux);
11834 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
11835 if (version == 1) {
11836 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
11837 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
11838 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
11839 } else if (version == 0) {
11840 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
11841 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
11842 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
11844 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
11848 /* Moving qt creation time (secs since 1904) to unix time */
11849 if (creation_time != 0) {
11850 /* Try to use epoch first as it should be faster and more commonly found */
11851 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
11854 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
11855 /* some data cleansing sanity */
11856 g_get_current_time (&now);
11857 if (now.tv_sec + 24 * 3600 < creation_time) {
11858 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
11860 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
11863 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
11864 GDateTime *dt, *dt_local;
11866 dt = g_date_time_add_seconds (base_dt, creation_time);
11867 dt_local = g_date_time_to_local (dt);
11868 datetime = gst_date_time_new_from_g_date_time (dt_local);
11870 g_date_time_unref (base_dt);
11871 g_date_time_unref (dt);
11875 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
11876 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
11878 gst_date_time_unref (datetime);
11881 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
11882 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
11884 /* check for fragmented file and get some (default) data */
11885 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
11888 GstByteReader mehd_data;
11890 /* let track parsing or anyone know weird stuff might happen ... */
11891 qtdemux->fragmented = TRUE;
11893 /* compensate for total duration */
11894 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
11896 qtdemux_parse_mehd (qtdemux, &mehd_data);
11899 /* set duration in the segment info */
11900 gst_qtdemux_get_duration (qtdemux, &duration);
11902 qtdemux->segment.duration = duration;
11903 /* also do not exceed duration; stop is set that way post seek anyway,
11904 * and segment activation falls back to duration,
11905 * whereas loop only checks stop, so let's align this here as well */
11906 qtdemux->segment.stop = duration;
11909 /* parse all traks */
11910 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
11912 qtdemux_parse_trak (qtdemux, trak);
11913 /* iterate all siblings */
11914 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
11917 if (!qtdemux->tag_list) {
11918 GST_DEBUG_OBJECT (qtdemux, "new tag list");
11919 qtdemux->tag_list = gst_tag_list_new_empty ();
11920 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
11922 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
11926 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
11928 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
11930 GST_LOG_OBJECT (qtdemux, "No udta node found.");
11933 /* maybe also some tags in meta box */
11934 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
11936 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
11937 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
11939 GST_LOG_OBJECT (qtdemux, "No meta node found.");
11942 /* parse any protection system info */
11943 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
11945 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
11946 qtdemux_parse_pssh (qtdemux, pssh);
11947 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
11950 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
11955 /* taken from ffmpeg */
11957 read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
11969 len = (len << 7) | (c & 0x7f);
11977 /* this can change the codec originally present in @list */
11979 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
11980 GNode * esds, GstTagList * list)
11982 int len = QT_UINT32 (esds->data);
11983 guint8 *ptr = esds->data;
11984 guint8 *end = ptr + len;
11986 guint8 *data_ptr = NULL;
11988 guint8 object_type_id = 0;
11989 const char *codec_name = NULL;
11990 GstCaps *caps = NULL;
11992 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
11994 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
11996 while (ptr + 1 < end) {
11997 tag = QT_UINT8 (ptr);
11998 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
12000 len = read_descr_size (ptr, end, &ptr);
12001 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
12003 /* Check the stated amount of data is available for reading */
12004 if (len < 0 || ptr + len > end)
12008 case ES_DESCRIPTOR_TAG:
12009 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
12010 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
12013 case DECODER_CONFIG_DESC_TAG:{
12014 guint max_bitrate, avg_bitrate;
12016 object_type_id = QT_UINT8 (ptr);
12017 max_bitrate = QT_UINT32 (ptr + 5);
12018 avg_bitrate = QT_UINT32 (ptr + 9);
12019 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
12020 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
12021 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
12022 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
12023 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
12024 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12025 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
12026 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
12028 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12029 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
12030 avg_bitrate, NULL);
12035 case DECODER_SPECIFIC_INFO_TAG:
12036 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
12037 if (object_type_id == 0xe0 && len == 0x40) {
12043 GST_DEBUG_OBJECT (qtdemux,
12044 "Have VOBSUB palette. Creating palette event");
12045 /* move to decConfigDescr data and read palette */
12047 for (i = 0; i < 16; i++) {
12048 clut[i] = QT_UINT32 (data);
12052 s = gst_structure_new ("application/x-gst-dvd", "event",
12053 G_TYPE_STRING, "dvd-spu-clut-change",
12054 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
12055 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
12056 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
12057 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
12058 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
12059 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
12060 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
12061 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
12064 /* store event and trigger custom processing */
12065 stream->pending_event =
12066 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
12068 /* Generic codec_data handler puts it on the caps */
12075 case SL_CONFIG_DESC_TAG:
12076 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
12080 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
12082 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
12088 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
12089 * in use, and should also be used to override some other parameters for some
12091 switch (object_type_id) {
12092 case 0x20: /* MPEG-4 */
12093 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
12094 * profile_and_level_indication */
12095 if (data_ptr != NULL && data_len >= 5 &&
12096 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
12097 gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
12098 data_ptr + 4, data_len - 4);
12100 break; /* Nothing special needed here */
12101 case 0x21: /* H.264 */
12102 codec_name = "H.264 / AVC";
12103 caps = gst_caps_new_simple ("video/x-h264",
12104 "stream-format", G_TYPE_STRING, "avc",
12105 "alignment", G_TYPE_STRING, "au", NULL);
12107 case 0x40: /* AAC (any) */
12108 case 0x66: /* AAC Main */
12109 case 0x67: /* AAC LC */
12110 case 0x68: /* AAC SSR */
12111 /* Override channels and rate based on the codec_data, as it's often
12113 /* Only do so for basic setup without HE-AAC extension */
12114 if (data_ptr && data_len == 2) {
12115 guint channels, rateindex, rate;
12117 /* FIXME: add gst_codec_utils_aac_get_{channels|sample_rate}()? */
12118 channels = (data_ptr[1] & 0x7f) >> 3;
12119 if (channels > 0 && channels < 7) {
12120 stream->n_channels = channels;
12121 } else if (channels == 7) {
12122 stream->n_channels = 8;
12125 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7);
12126 rate = gst_codec_utils_aac_get_sample_rate_from_index (rateindex);
12128 stream->rate = rate;
12131 /* Set level and profile if possible */
12132 if (data_ptr != NULL && data_len >= 2) {
12133 gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
12134 data_ptr, data_len);
12137 case 0x60: /* MPEG-2, various profiles */
12143 codec_name = "MPEG-2 video";
12144 caps = gst_caps_new_simple ("video/mpeg",
12145 "mpegversion", G_TYPE_INT, 2,
12146 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12148 case 0x69: /* MPEG-2 BC audio */
12149 case 0x6B: /* MPEG-1 audio */
12150 caps = gst_caps_new_simple ("audio/mpeg",
12151 "mpegversion", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
12152 codec_name = "MPEG-1 audio";
12154 case 0x6A: /* MPEG-1 */
12155 codec_name = "MPEG-1 video";
12156 caps = gst_caps_new_simple ("video/mpeg",
12157 "mpegversion", G_TYPE_INT, 1,
12158 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12160 case 0x6C: /* MJPEG */
12162 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12164 codec_name = "Motion-JPEG";
12166 case 0x6D: /* PNG */
12168 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
12170 codec_name = "PNG still images";
12172 case 0x6E: /* JPEG2000 */
12173 codec_name = "JPEG-2000";
12174 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
12176 case 0xA4: /* Dirac */
12177 codec_name = "Dirac";
12178 caps = gst_caps_new_empty_simple ("video/x-dirac");
12180 case 0xA5: /* AC3 */
12181 codec_name = "AC-3 audio";
12182 caps = gst_caps_new_simple ("audio/x-ac3",
12183 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12185 case 0xA9: /* AC3 */
12186 codec_name = "DTS audio";
12187 caps = gst_caps_new_simple ("audio/x-dts",
12188 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12190 case 0xE1: /* QCELP */
12191 /* QCELP, the codec_data is a riff tag (little endian) with
12192 * 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). */
12193 caps = gst_caps_new_empty_simple ("audio/qcelp");
12194 codec_name = "QCELP";
12200 /* If we have a replacement caps, then change our caps for this stream */
12202 gst_caps_unref (stream->caps);
12203 stream->caps = caps;
12206 if (codec_name && list)
12207 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
12208 GST_TAG_AUDIO_CODEC, codec_name, NULL);
12210 /* Add the codec_data attribute to caps, if we have it */
12214 buffer = gst_buffer_new_and_alloc (data_len);
12215 gst_buffer_fill (buffer, 0, data_ptr, data_len);
12217 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
12218 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
12220 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
12222 gst_buffer_unref (buffer);
12227 #define _codec(name) \
12229 if (codec_name) { \
12230 *codec_name = g_strdup (name); \
12235 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
12236 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
12238 GstCaps *caps = NULL;
12239 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
12242 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
12243 _codec ("PNG still images");
12244 caps = gst_caps_new_empty_simple ("image/png");
12247 _codec ("JPEG still images");
12249 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12252 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
12253 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
12254 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
12255 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
12256 _codec ("Motion-JPEG");
12258 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
12261 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
12262 _codec ("Motion-JPEG format B");
12263 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
12266 _codec ("JPEG-2000");
12267 /* override to what it should be according to spec, avoid palette_data */
12268 stream->bits_per_sample = 24;
12269 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
12272 _codec ("Sorensen video v.3");
12273 caps = gst_caps_new_simple ("video/x-svq",
12274 "svqversion", G_TYPE_INT, 3, NULL);
12276 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
12277 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
12278 _codec ("Sorensen video v.1");
12279 caps = gst_caps_new_simple ("video/x-svq",
12280 "svqversion", G_TYPE_INT, 1, NULL);
12282 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
12283 caps = gst_caps_new_empty_simple ("video/x-raw");
12284 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
12285 _codec ("Windows Raw RGB");
12291 bps = QT_UINT16 (stsd_data + 98);
12294 format = GST_VIDEO_FORMAT_RGB15;
12297 format = GST_VIDEO_FORMAT_RGB16;
12300 format = GST_VIDEO_FORMAT_RGB;
12303 format = GST_VIDEO_FORMAT_ARGB;
12311 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
12312 format = GST_VIDEO_FORMAT_I420;
12314 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
12315 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
12316 format = GST_VIDEO_FORMAT_I420;
12319 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
12320 format = GST_VIDEO_FORMAT_UYVY;
12322 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
12323 format = GST_VIDEO_FORMAT_v308;
12325 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
12326 format = GST_VIDEO_FORMAT_v216;
12329 format = GST_VIDEO_FORMAT_v210;
12331 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
12332 format = GST_VIDEO_FORMAT_r210;
12334 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
12335 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
12336 format = GST_VIDEO_FORMAT_v410;
12339 /* Packed YUV 4:4:4:4 8 bit in 32 bits
12340 * but different order than AYUV
12341 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
12342 format = GST_VIDEO_FORMAT_v408;
12345 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
12346 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
12347 _codec ("MPEG-1 video");
12348 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
12349 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12351 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
12352 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
12353 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
12354 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
12355 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
12356 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
12357 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
12358 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
12359 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
12360 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
12361 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
12362 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
12363 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
12364 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
12365 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
12366 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
12367 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
12368 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
12369 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
12370 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
12371 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
12372 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
12373 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
12374 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
12375 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
12376 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
12377 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
12378 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
12379 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
12380 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
12381 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
12382 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
12383 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
12384 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
12385 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
12386 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
12387 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
12388 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
12389 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
12390 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
12391 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
12392 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
12393 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
12394 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
12395 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
12396 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
12397 _codec ("MPEG-2 video");
12398 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
12399 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12401 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
12402 _codec ("GIF still images");
12403 caps = gst_caps_new_empty_simple ("image/gif");
12406 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
12408 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
12410 /* ffmpeg uses the height/width props, don't know why */
12411 caps = gst_caps_new_simple ("video/x-h263",
12412 "variant", G_TYPE_STRING, "itu", NULL);
12416 _codec ("MPEG-4 video");
12417 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
12418 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12420 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
12421 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
12422 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
12423 caps = gst_caps_new_simple ("video/x-msmpeg",
12424 "msmpegversion", G_TYPE_INT, 43, NULL);
12426 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
12428 caps = gst_caps_new_simple ("video/x-divx",
12429 "divxversion", G_TYPE_INT, 3, NULL);
12431 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
12432 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
12434 caps = gst_caps_new_simple ("video/x-divx",
12435 "divxversion", G_TYPE_INT, 4, NULL);
12437 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
12439 caps = gst_caps_new_simple ("video/x-divx",
12440 "divxversion", G_TYPE_INT, 5, NULL);
12443 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
12445 caps = gst_caps_new_simple ("video/x-ffv",
12446 "ffvversion", G_TYPE_INT, 1, NULL);
12449 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
12450 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
12451 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
12452 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
12454 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
12455 caps = gst_caps_new_simple ("video/mpeg",
12456 "mpegversion", G_TYPE_INT, 4, NULL);
12460 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
12461 _codec ("Cinepak");
12462 caps = gst_caps_new_empty_simple ("video/x-cinepak");
12464 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
12465 _codec ("Apple QuickDraw");
12466 caps = gst_caps_new_empty_simple ("video/x-qdrw");
12468 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
12469 _codec ("Apple video");
12470 caps = gst_caps_new_empty_simple ("video/x-apple-video");
12474 _codec ("H.264 / AVC");
12475 caps = gst_caps_new_simple ("video/x-h264",
12476 "stream-format", G_TYPE_STRING, "avc",
12477 "alignment", G_TYPE_STRING, "au", NULL);
12480 _codec ("H.264 / AVC");
12481 caps = gst_caps_new_simple ("video/x-h264",
12482 "stream-format", G_TYPE_STRING, "avc3",
12483 "alignment", G_TYPE_STRING, "au", NULL);
12487 _codec ("H.265 / HEVC");
12488 caps = gst_caps_new_simple ("video/x-h265",
12489 "stream-format", G_TYPE_STRING, "hvc1",
12490 "alignment", G_TYPE_STRING, "au", NULL);
12493 _codec ("H.265 / HEVC");
12494 caps = gst_caps_new_simple ("video/x-h265",
12495 "stream-format", G_TYPE_STRING, "hev1",
12496 "alignment", G_TYPE_STRING, "au", NULL);
12499 _codec ("Run-length encoding");
12500 caps = gst_caps_new_simple ("video/x-rle",
12501 "layout", G_TYPE_STRING, "quicktime", NULL);
12504 _codec ("Run-length encoding");
12505 caps = gst_caps_new_simple ("video/x-rle",
12506 "layout", G_TYPE_STRING, "microsoft", NULL);
12508 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
12509 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
12510 _codec ("Indeo Video 3");
12511 caps = gst_caps_new_simple ("video/x-indeo",
12512 "indeoversion", G_TYPE_INT, 3, NULL);
12514 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
12515 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
12516 _codec ("Intel Video 4");
12517 caps = gst_caps_new_simple ("video/x-indeo",
12518 "indeoversion", G_TYPE_INT, 4, NULL);
12522 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
12523 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
12524 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
12525 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
12526 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
12527 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
12528 _codec ("DV Video");
12529 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
12530 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12532 case FOURCC_dv5n: /* DVCPRO50 NTSC */
12533 case FOURCC_dv5p: /* DVCPRO50 PAL */
12534 _codec ("DVCPro50 Video");
12535 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
12536 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12538 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
12539 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
12540 _codec ("DVCProHD Video");
12541 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
12542 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
12544 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
12545 _codec ("Apple Graphics (SMC)");
12546 caps = gst_caps_new_empty_simple ("video/x-smc");
12548 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
12550 caps = gst_caps_new_empty_simple ("video/x-vp3");
12552 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
12553 _codec ("VP6 Flash");
12554 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
12558 caps = gst_caps_new_empty_simple ("video/x-theora");
12559 /* theora uses one byte of padding in the data stream because it does not
12560 * allow 0 sized packets while theora does */
12561 stream->padding = 1;
12565 caps = gst_caps_new_empty_simple ("video/x-dirac");
12567 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
12568 _codec ("TIFF still images");
12569 caps = gst_caps_new_empty_simple ("image/tiff");
12571 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
12572 _codec ("Apple Intermediate Codec");
12573 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
12575 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
12576 _codec ("AVID DNxHD");
12577 caps = gst_caps_from_string ("video/x-dnxhd");
12580 _codec ("On2 VP8");
12581 caps = gst_caps_from_string ("video/x-vp8");
12584 _codec ("Apple ProRes LT");
12586 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
12590 _codec ("Apple ProRes HQ");
12592 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
12596 _codec ("Apple ProRes");
12598 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12602 _codec ("Apple ProRes Proxy");
12604 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12608 _codec ("Apple ProRes 4444");
12610 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
12616 caps = gst_caps_new_simple ("video/x-wmv",
12617 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
12619 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
12622 char *s, fourstr[5];
12624 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12625 s = g_strdup_printf ("video/x-gst-fourcc-%s", g_strstrip (fourstr));
12626 caps = gst_caps_new_empty_simple (s);
12632 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
12635 gst_video_info_init (&info);
12636 gst_video_info_set_format (&info, format, stream->width, stream->height);
12638 caps = gst_video_info_to_caps (&info);
12639 *codec_name = gst_pb_utils_get_codec_description (caps);
12641 /* enable clipping for raw video streams */
12642 stream->need_clip = TRUE;
12649 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
12650 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
12653 const GstStructure *s;
12656 GstAudioFormat format = 0;
12659 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
12661 depth = stream->bytes_per_packet * 8;
12664 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
12666 /* 8-bit audio is unsigned */
12668 format = GST_AUDIO_FORMAT_U8;
12669 /* otherwise it's signed and big-endian just like 'twos' */
12671 endian = G_BIG_ENDIAN;
12678 endian = G_LITTLE_ENDIAN;
12681 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
12683 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
12687 caps = gst_caps_new_simple ("audio/x-raw",
12688 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
12689 "layout", G_TYPE_STRING, "interleaved", NULL);
12692 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
12693 _codec ("Raw 64-bit floating-point audio");
12694 caps = gst_caps_new_simple ("audio/x-raw",
12695 "format", G_TYPE_STRING, "F64BE",
12696 "layout", G_TYPE_STRING, "interleaved", NULL);
12698 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
12699 _codec ("Raw 32-bit floating-point audio");
12700 caps = gst_caps_new_simple ("audio/x-raw",
12701 "format", G_TYPE_STRING, "F32BE",
12702 "layout", G_TYPE_STRING, "interleaved", NULL);
12705 _codec ("Raw 24-bit PCM audio");
12706 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
12708 caps = gst_caps_new_simple ("audio/x-raw",
12709 "format", G_TYPE_STRING, "S24BE",
12710 "layout", G_TYPE_STRING, "interleaved", NULL);
12712 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
12713 _codec ("Raw 32-bit PCM audio");
12714 caps = gst_caps_new_simple ("audio/x-raw",
12715 "format", G_TYPE_STRING, "S32BE",
12716 "layout", G_TYPE_STRING, "interleaved", NULL);
12719 _codec ("Mu-law audio");
12720 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
12723 _codec ("A-law audio");
12724 caps = gst_caps_new_empty_simple ("audio/x-alaw");
12728 _codec ("Microsoft ADPCM");
12729 /* Microsoft ADPCM-ACM code 2 */
12730 caps = gst_caps_new_simple ("audio/x-adpcm",
12731 "layout", G_TYPE_STRING, "microsoft", NULL);
12735 _codec ("DVI/IMA ADPCM");
12736 caps = gst_caps_new_simple ("audio/x-adpcm",
12737 "layout", G_TYPE_STRING, "dvi", NULL);
12741 _codec ("DVI/Intel IMA ADPCM");
12742 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
12743 caps = gst_caps_new_simple ("audio/x-adpcm",
12744 "layout", G_TYPE_STRING, "quicktime", NULL);
12748 /* MPEG layer 3, CBR only (pre QT4.1) */
12750 _codec ("MPEG-1 layer 3");
12751 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
12752 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
12753 "mpegversion", G_TYPE_INT, 1, NULL);
12756 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
12757 _codec ("EAC-3 audio");
12758 caps = gst_caps_new_simple ("audio/x-eac3",
12759 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12760 stream->sampled = TRUE;
12762 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
12764 _codec ("AC-3 audio");
12765 caps = gst_caps_new_simple ("audio/x-ac3",
12766 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12767 stream->sampled = TRUE;
12769 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
12770 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
12771 _codec ("DTS audio");
12772 caps = gst_caps_new_simple ("audio/x-dts",
12773 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12774 stream->sampled = TRUE;
12776 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
12777 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
12778 _codec ("DTS-HD audio");
12779 caps = gst_caps_new_simple ("audio/x-dts",
12780 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
12781 stream->sampled = TRUE;
12785 caps = gst_caps_new_simple ("audio/x-mace",
12786 "maceversion", G_TYPE_INT, 3, NULL);
12790 caps = gst_caps_new_simple ("audio/x-mace",
12791 "maceversion", G_TYPE_INT, 6, NULL);
12793 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
12795 caps = gst_caps_new_empty_simple ("application/ogg");
12797 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
12798 _codec ("DV audio");
12799 caps = gst_caps_new_empty_simple ("audio/x-dv");
12802 _codec ("MPEG-4 AAC audio");
12803 caps = gst_caps_new_simple ("audio/mpeg",
12804 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
12805 "stream-format", G_TYPE_STRING, "raw", NULL);
12807 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
12808 _codec ("QDesign Music");
12809 caps = gst_caps_new_empty_simple ("audio/x-qdm");
12812 _codec ("QDesign Music v.2");
12813 /* FIXME: QDesign music version 2 (no constant) */
12814 if (FALSE && data) {
12815 caps = gst_caps_new_simple ("audio/x-qdm2",
12816 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
12817 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
12818 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
12820 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
12824 _codec ("GSM audio");
12825 caps = gst_caps_new_empty_simple ("audio/x-gsm");
12828 _codec ("AMR audio");
12829 caps = gst_caps_new_empty_simple ("audio/AMR");
12832 _codec ("AMR-WB audio");
12833 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
12836 _codec ("Quicktime IMA ADPCM");
12837 caps = gst_caps_new_simple ("audio/x-adpcm",
12838 "layout", G_TYPE_STRING, "quicktime", NULL);
12841 _codec ("Apple lossless audio");
12842 caps = gst_caps_new_empty_simple ("audio/x-alac");
12844 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
12845 _codec ("QualComm PureVoice");
12846 caps = gst_caps_from_string ("audio/qcelp");
12851 caps = gst_caps_new_empty_simple ("audio/x-wma");
12855 caps = gst_caps_new_empty_simple ("audio/x-opus");
12857 case GST_MAKE_FOURCC ('l', 'p', 'c', 'm'):
12862 GstAudioFormat format;
12865 FLAG_IS_FLOAT = 0x1,
12866 FLAG_IS_BIG_ENDIAN = 0x2,
12867 FLAG_IS_SIGNED = 0x4,
12868 FLAG_IS_PACKED = 0x8,
12869 FLAG_IS_ALIGNED_HIGH = 0x10,
12870 FLAG_IS_NON_INTERLEAVED = 0x20
12872 _codec ("Raw LPCM audio");
12874 if (data && len >= 56) {
12875 depth = QT_UINT32 (data + 40);
12876 flags = QT_UINT32 (data + 44);
12877 width = QT_UINT32 (data + 48) * 8 / stream->n_channels;
12879 if ((flags & FLAG_IS_FLOAT) == 0) {
12884 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
12885 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
12886 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
12887 caps = gst_caps_new_simple ("audio/x-raw",
12888 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
12889 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
12890 "non-interleaved" : "interleaved", NULL);
12895 if (flags & FLAG_IS_BIG_ENDIAN)
12896 format = GST_AUDIO_FORMAT_F64BE;
12898 format = GST_AUDIO_FORMAT_F64LE;
12900 if (flags & FLAG_IS_BIG_ENDIAN)
12901 format = GST_AUDIO_FORMAT_F32BE;
12903 format = GST_AUDIO_FORMAT_F32LE;
12905 caps = gst_caps_new_simple ("audio/x-raw",
12906 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
12907 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
12908 "non-interleaved" : "interleaved", NULL);
12912 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
12916 char *s, fourstr[5];
12918 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12919 s = g_strdup_printf ("audio/x-gst-fourcc-%s", g_strstrip (fourstr));
12920 caps = gst_caps_new_empty_simple (s);
12927 GstCaps *templ_caps =
12928 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
12929 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
12930 gst_caps_unref (caps);
12931 gst_caps_unref (templ_caps);
12932 caps = intersection;
12935 /* enable clipping for raw audio streams */
12936 s = gst_caps_get_structure (caps, 0);
12937 name = gst_structure_get_name (s);
12938 if (g_str_has_prefix (name, "audio/x-raw")) {
12939 stream->need_clip = TRUE;
12940 stream->max_buffer_size = 4096 * stream->bytes_per_frame;
12941 GST_DEBUG ("setting max buffer size to %d", stream->max_buffer_size);
12947 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
12948 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
12952 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
12956 _codec ("DVD subtitle");
12957 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
12958 stream->need_process = TRUE;
12961 _codec ("Quicktime timed text");
12964 _codec ("3GPP timed text");
12966 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
12968 /* actual text piece needs to be extracted */
12969 stream->need_process = TRUE;
12972 _codec ("XML subtitles");
12973 caps = gst_caps_new_empty_simple ("application/ttml+xml");
12977 char *s, fourstr[5];
12979 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
12980 s = g_strdup_printf ("text/x-gst-fourcc-%s", g_strstrip (fourstr));
12981 caps = gst_caps_new_empty_simple (s);
12990 qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
12991 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
12997 _codec ("MPEG 1 video");
12998 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
12999 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
13009 gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
13010 const gchar * system_id)
13014 if (!qtdemux->protection_system_ids)
13015 qtdemux->protection_system_ids =
13016 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
13017 /* Check whether we already have an entry for this system ID. */
13018 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
13019 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
13020 if (g_ascii_strcasecmp (system_id, id) == 0) {
13024 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
13025 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,