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>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 * SECTION:element-qtdemux
28 * Demuxes a .mov file into raw or compressed audio and/or video streams.
30 * This element supports both push and pull-based scheduling, depending on the
31 * capabilities of the upstream elements.
34 * <title>Example launch line</title>
36 * gst-launch filesrc location=test.mov ! qtdemux name=demux demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
37 * ]| Play (parse and decode) a .mov file and try to output it to
38 * an automatically detected soundcard and videosink. If the MOV file contains
39 * compressed audio or video data, this will only work if you have the
40 * right decoder elements/plugins installed.
43 * Last reviewed on 2006-12-29 (0.10.5)
50 #include "gst/gst-i18n-plugin.h"
52 #include <glib/gprintf.h>
53 #include <gst/tag/tag.h>
55 #include "qtatomparser.h"
56 #include "qtdemux_types.h"
57 #include "qtdemux_dump.h"
58 #include "qtdemux_fourcc.h"
59 #include "qtdemux_lang.h"
61 #include "qtpalette.h"
63 #include "gst/riff/riff-media.h"
64 #include "gst/riff/riff-read.h"
66 #include <gst/pbutils/pbutils.h>
76 /* max. size considered 'sane' for non-mdat atoms */
77 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
79 /* if the sample index is larger than this, something is likely wrong */
80 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
82 /* For converting qt creation times to unix epoch times */
83 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
84 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
85 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
86 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
88 #ifdef QTDEMUX_MODIFICATION
89 /*Modification: Added macro for default value of buffer-size property */
90 #define QTDEMUX_MAX_BUFFER_SIZE (100*1024*1024)
93 #ifdef QTDEMUX_MODIFICATION
94 /*Modification: Added new properties considering different cases of file formats */
103 GST_DEBUG_CATEGORY (qtdemux_debug);
105 /*typedef struct _QtNode QtNode; */
106 typedef struct _QtDemuxSegment QtDemuxSegment;
107 typedef struct _QtDemuxSample QtDemuxSample;
116 struct _QtDemuxSample
119 gint32 pts_offset; /* Add this value to timestamp to get the pts */
121 guint64 timestamp; /* DTS In mov time */
122 guint32 duration; /* In mov time */
123 gboolean keyframe; /* TRUE when this packet is a keyframe */
126 /* timestamp is the DTS */
127 #define QTSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\
128 GST_SECOND, (stream)->timescale)
129 /* timestamp + offset is the PTS */
130 #define QTSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \
131 (sample)->pts_offset, GST_SECOND, (stream)->timescale)
132 /* timestamp + duration - dts is the duration */
133 #define QTSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
134 (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
135 /* timestamp + offset + duration - pts is the duration */
136 #define QTSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
137 (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
139 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
142 * Quicktime has tracks and segments. A track is a continuous piece of
143 * multimedia content. The track is not always played from start to finish but
144 * instead, pieces of the track are 'cut out' and played in sequence. This is
145 * what the segments do.
147 * Inside the track we have keyframes (K) and delta frames. The track has its
148 * own timing, which starts from 0 and extends to end. The position in the track
149 * is called the media_time.
151 * The segments now describe the pieces that should be played from this track
152 * and are basically tupples of media_time/duration/rate entries. We can have
153 * multiple segments and they are all played after one another. An example:
155 * segment 1: media_time: 1 second, duration: 1 second, rate 1
156 * segment 2: media_time: 3 second, duration: 2 second, rate 2
158 * To correctly play back this track, one must play: 1 second of media starting
159 * from media_time 1 followed by 2 seconds of media starting from media_time 3
162 * Each of the segments will be played at a specific time, the first segment at
163 * time 0, the second one after the duration of the first one, etc.. Note that
164 * the time in resulting playback is not identical to the media_time of the
167 * Visually, assuming the track has 4 second of media_time:
170 * .-----------------------------------------------------------.
171 * track: | K.....K.........K........K.......K.......K...........K... |
172 * '-----------------------------------------------------------'
174 * .------------^ ^ .----------^ ^
175 * / .-------------' / .------------------'
177 * .--------------. .--------------.
178 * | segment 1 | | segment 2 |
179 * '--------------' '--------------'
181 * The challenge here is to cut out the right pieces of the track for each of
182 * the playback segments. This fortunatly can easily be done with the SEGMENT
183 * events of gstreamer.
185 * For playback of segment 1, we need to provide the decoder with the keyframe
186 * (a), in the above figure, but we must instruct it only to output the decoded
187 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
188 * position set to the time of the segment: 0.
190 * We then proceed to push data from keyframe (a) to frame (b). The decoder
191 * decodes but clips all before media_time 1.
193 * After finishing a segment, we push out a new SEGMENT event with the clipping
194 * boundaries of the new data.
196 * This is a good usecase for the GStreamer accumulated SEGMENT events.
199 struct _QtDemuxSegment
201 /* global time and duration, all gst time */
205 /* media time of trak, all gst time */
211 struct _QtDemuxStream
214 #ifdef QTDEMUX_MODIFICATION
215 /*Modification: Added new variables for trickplay index table generation */
219 guint32 total_samples_bet_2_keyframes;
220 guint64 avg_duration_bet_2_keyframes;
221 guint32 samples_to_show_bet_kframes;
228 /* if the stream has a redirect URI in its headers, we store it here */
235 guint64 duration; /* in timescale */
239 gchar lang_id[4]; /* ISO 639-2T language code */
243 QtDemuxSample *samples;
244 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
245 guint32 min_duration; /* duration in timescale of first sample, used for figuring out
246 the framerate, in timescale units */
248 /* if we use chunks or samples */
260 /* Numerator/denominator framerate */
263 guint16 bits_per_sample;
264 guint16 color_table_id;
269 guint samples_per_packet;
270 guint samples_per_frame;
271 guint bytes_per_packet;
272 guint bytes_per_sample;
273 guint bytes_per_frame;
276 /* when a discontinuity is pending */
279 /* list of buffers to push first */
282 /* if we need to clip this buffer. This is only needed for uncompressed
286 /* buffer needs some custom processing, e.g. subtitles */
287 gboolean need_process;
289 /* current position */
290 guint32 segment_index;
291 guint32 sample_index;
292 guint64 time_position; /* in gst time */
294 /* the Gst segment we are processing out, used for clipping */
297 /* last GstFlowReturn */
298 GstFlowReturn last_ret;
300 /* quicktime segments */
302 QtDemuxSegment *segments;
307 GstTagList *pending_tags;
308 gboolean send_global_tags;
310 GstEvent *pending_event;
320 gboolean chunks_are_chunks;
324 GstByteReader co_chunk;
326 guint32 current_chunk;
328 guint32 samples_per_chunk;
329 guint32 stco_sample_index;
331 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
334 guint32 n_samples_per_chunk;
335 guint32 stsc_chunk_index;
336 guint32 stsc_sample_index;
337 guint64 chunk_offset;
340 guint32 stts_samples;
341 guint32 n_sample_times;
342 guint32 stts_sample_index;
344 guint32 stts_duration;
346 gboolean stss_present;
347 guint32 n_sample_syncs;
350 gboolean stps_present;
351 guint32 n_sample_partial_syncs;
354 gboolean ctts_present;
355 guint32 n_composition_times;
357 guint32 ctts_sample_index;
362 gboolean parsed_trex;
363 guint32 def_sample_duration;
364 guint32 def_sample_size;
365 guint32 def_sample_flags;
370 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
371 QTDEMUX_STATE_HEADER, /* Parsing the header */
372 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
373 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
376 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
377 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
378 guint32 fourcc, GstByteReader * parser);
379 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
380 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
381 guint32 fourcc, GstByteReader * parser);
383 static GstStaticPadTemplate gst_qtdemux_sink_template =
384 GST_STATIC_PAD_TEMPLATE ("sink",
387 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
391 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
392 GST_STATIC_PAD_TEMPLATE ("video_%02d",
395 GST_STATIC_CAPS_ANY);
397 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
398 GST_STATIC_PAD_TEMPLATE ("audio_%02d",
401 GST_STATIC_CAPS_ANY);
403 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
404 GST_STATIC_PAD_TEMPLATE ("subtitle_%02d",
407 GST_STATIC_CAPS_ANY);
409 GST_BOILERPLATE (GstQTDemux, gst_qtdemux, GstQTDemux, GST_TYPE_ELEMENT);
411 static void gst_qtdemux_dispose (GObject * object);
414 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
417 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
418 QtDemuxStream * str, gint64 media_offset);
419 #ifdef QTDEMUX_MODIFICATION
420 /*Modification: Added the property methods as new properties are added */
421 static void gst_qtdemux_set_property (GObject * object, guint prop_id,
422 const GValue * value, GParamSpec * pspec);
423 static void gst_qtdemux_get_property (GObject * object, guint prop_id,
424 GValue * value, GParamSpec * pspec);
427 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
428 static GstIndex *gst_qtdemux_get_index (GstElement * element);
429 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
430 GstStateChange transition);
431 static gboolean qtdemux_sink_activate (GstPad * sinkpad);
432 static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
433 static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
435 static void gst_qtdemux_loop (GstPad * pad);
436 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
437 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
439 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
440 const guint8 * buffer, guint length);
441 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
442 const guint8 * buffer, guint length);
443 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
445 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
446 QtDemuxStream * stream, GNode * esds, GstTagList * list);
447 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
448 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
449 gchar ** codec_name);
450 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
451 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
452 gchar ** codec_name);
453 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
454 QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
455 gchar ** codec_name);
456 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
457 QtDemuxStream * stream, guint32 n);
458 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
462 gst_qtdemux_base_init (gpointer klass)
464 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
466 gst_element_class_add_pad_template (element_class,
467 gst_static_pad_template_get (&gst_qtdemux_sink_template));
468 gst_element_class_add_pad_template (element_class,
469 gst_static_pad_template_get (&gst_qtdemux_videosrc_template));
470 gst_element_class_add_pad_template (element_class,
471 gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
472 gst_element_class_add_pad_template (element_class,
473 gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
474 gst_element_class_set_details_simple (element_class, "QuickTime demuxer",
476 "Demultiplex a QuickTime file into audio and video streams",
477 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
479 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
483 gst_qtdemux_class_init (GstQTDemuxClass * klass)
485 GObjectClass *gobject_class;
486 GstElementClass *gstelement_class;
488 gobject_class = (GObjectClass *) klass;
489 gstelement_class = (GstElementClass *) klass;
491 parent_class = g_type_class_peek_parent (klass);
493 gobject_class->dispose = gst_qtdemux_dispose;
495 #ifdef QTDEMUX_MODIFICATION
496 gobject_class->set_property = gst_qtdemux_set_property;
497 gobject_class->get_property = gst_qtdemux_get_property;
500 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
502 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
503 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
505 #ifdef QTDEMUX_MODIFICATION
506 g_object_class_install_property (gobject_class, PROP_MAX_BUFFER_SIZE,
507 g_param_spec_uint ("buffer-size", "buffer-size",
508 "Maximum buffer size for mdat atom buffering incase it comes before the moov atom",
509 1, G_MAXUINT, QTDEMUX_MAX_BUFFER_SIZE,
510 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
512 g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
513 g_param_spec_string ("temp-location", "temp-location",
514 "Location for the mdat atom buffering incase it comes before the moov atom",
516 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
521 gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
524 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
525 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
526 gst_pad_set_activatepull_function (qtdemux->sinkpad,
527 qtdemux_sink_activate_pull);
528 gst_pad_set_activatepush_function (qtdemux->sinkpad,
529 qtdemux_sink_activate_push);
530 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
531 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
532 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
534 qtdemux->state = QTDEMUX_STATE_INITIAL;
535 qtdemux->pullbased = FALSE;
536 qtdemux->posted_redirect = FALSE;
537 qtdemux->neededbytes = 16;
539 qtdemux->adapter = gst_adapter_new ();
541 qtdemux->first_mdat = -1;
542 qtdemux->got_moov = FALSE;
543 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
544 qtdemux->mdatbuffer = NULL;
545 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
546 #ifdef QTDEMUX_MODIFICATION
547 /* Modification: Initialization of new properties with default values*/
548 qtdemux->filename = g_strdup("/tmp/qtdemux");
549 qtdemux->file = NULL;
550 qtdemux->ofile = NULL;
551 qtdemux->filesize = 0;
552 qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
557 gst_qtdemux_dispose (GObject * object)
559 GstQTDemux *qtdemux = GST_QTDEMUX (object);
561 if (qtdemux->adapter) {
562 g_object_unref (G_OBJECT (qtdemux->adapter));
563 qtdemux->adapter = NULL;
566 #ifdef QTDEMUX_MODIFICATION
567 if (qtdemux->filename)
568 g_free (qtdemux->filename);
571 G_OBJECT_CLASS (parent_class)->dispose (object);
574 #ifdef QTDEMUX_MODIFICATION
575 /*Modification: Added method implementations for new properties added */
577 gst_qtdemux_set_property (GObject * object, guint prop_id,
578 const GValue * value, GParamSpec * pspec)
582 demux = GST_QTDEMUX (object);
585 case PROP_MAX_BUFFER_SIZE:
586 demux->maxbuffersize = g_value_get_uint (value);
589 case PROP_TEMP_LOCATION:
591 g_free (demux->filename);
592 demux->filename = g_strdup (g_value_get_string (value));
596 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
602 #ifdef QTDEMUX_MODIFICATION
603 /*Modification: Added method implementations for new properties added */
605 gst_qtdemux_get_property (GObject * object, guint prop_id,
606 GValue * value, GParamSpec * pspec)
610 demux = GST_QTDEMUX (object);
613 case PROP_MAX_BUFFER_SIZE:
614 g_value_set_uint (value, demux->maxbuffersize);
617 case PROP_TEMP_LOCATION:
618 g_value_set_string (value, demux->filename);
622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
630 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
632 if (qtdemux->posted_redirect) {
633 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
634 (_("This file contains no playable streams.")),
635 ("no known streams found, a redirect message has been posted"));
637 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
638 (_("This file contains no playable streams.")),
639 ("no known streams found"));
644 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
650 if (G_UNLIKELY (size == 0)) {
652 GstBuffer *tmp = NULL;
654 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
655 if (ret != GST_FLOW_OK)
658 size = QT_UINT32 (GST_BUFFER_DATA (tmp));
659 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
661 gst_buffer_unref (tmp);
666 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
667 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
668 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
669 /* we're pulling header but already got most interesting bits,
670 * so never mind the rest (e.g. tags) (that much) */
671 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
673 return GST_FLOW_UNEXPECTED;
675 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
676 (_("This file is invalid and cannot be played.")),
677 ("atom has bogus size %" G_GUINT64_FORMAT, size));
678 return GST_FLOW_ERROR;
682 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
684 if (G_UNLIKELY (flow != GST_FLOW_OK))
687 /* Catch short reads - we don't want any partial atoms */
688 if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) {
689 GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT,
690 GST_BUFFER_SIZE (*buf), size);
691 gst_buffer_unref (*buf);
693 return GST_FLOW_UNEXPECTED;
701 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
702 GstFormat dest_format, gint64 * dest_value)
705 QtDemuxStream *stream = gst_pad_get_element_private (pad);
706 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
709 if (stream->subtype != FOURCC_vide) {
714 switch (src_format) {
715 case GST_FORMAT_TIME:
716 switch (dest_format) {
717 case GST_FORMAT_BYTES:{
718 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
722 *dest_value = stream->samples[index].offset;
724 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
725 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
726 GST_TIME_ARGS (src_value), *dest_value);
734 case GST_FORMAT_BYTES:
735 switch (dest_format) {
736 case GST_FORMAT_TIME:{
738 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
745 gst_util_uint64_scale (stream->samples[index].timestamp,
746 GST_SECOND, stream->timescale);
747 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Offset->Time :%"
748 G_GUINT64_FORMAT "->%" GST_TIME_FORMAT,
749 src_value, GST_TIME_ARGS (*dest_value));
762 gst_object_unref (qtdemux);
768 static const GstQueryType *
769 gst_qtdemux_get_src_query_types (GstPad * pad)
771 static const GstQueryType src_types[] = {
784 gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration)
788 *duration = GST_CLOCK_TIME_NONE;
790 if (qtdemux->duration != 0) {
791 if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
792 *duration = gst_util_uint64_scale (qtdemux->duration,
793 GST_SECOND, qtdemux->timescale);
800 gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
802 gboolean res = FALSE;
803 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
805 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
807 switch (GST_QUERY_TYPE (query)) {
808 case GST_QUERY_POSITION:
809 if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) {
810 gst_query_set_position (query, GST_FORMAT_TIME,
811 qtdemux->segment.last_stop);
815 case GST_QUERY_DURATION:{
818 gst_query_parse_duration (query, &fmt, NULL);
819 if (fmt == GST_FORMAT_TIME) {
820 gint64 duration = -1;
822 gst_qtdemux_get_duration (qtdemux, &duration);
824 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
830 case GST_QUERY_CONVERT:{
831 GstFormat src_fmt, dest_fmt;
832 gint64 src_value, dest_value = 0;
834 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
836 res = gst_qtdemux_src_convert (pad,
837 src_fmt, src_value, dest_fmt, &dest_value);
839 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
844 case GST_QUERY_FORMATS:
845 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
848 case GST_QUERY_SEEKING:{
852 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
853 if (fmt == GST_FORMAT_TIME) {
854 gint64 duration = -1;
856 gst_qtdemux_get_duration (qtdemux, &duration);
858 if (!qtdemux->pullbased) {
861 /* we might be able with help from upstream */
863 q = gst_query_new_seeking (GST_FORMAT_BYTES);
864 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
865 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
866 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
870 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
876 res = gst_pad_query_default (pad, query);
880 gst_object_unref (qtdemux);
886 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
888 if (G_LIKELY (stream->pad)) {
889 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
890 GST_DEBUG_PAD_NAME (stream->pad));
892 if (G_UNLIKELY (stream->pending_tags)) {
893 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
894 stream->pending_tags);
895 gst_pad_push_event (stream->pad,
896 gst_event_new_tag (stream->pending_tags));
897 stream->pending_tags = NULL;
900 if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
901 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
903 gst_pad_push_event (stream->pad,
904 gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
905 stream->send_global_tags = FALSE;
910 /* push event on all source pads; takes ownership of the event */
912 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
915 gboolean has_valid_stream = FALSE;
916 GstEventType etype = GST_EVENT_TYPE (event);
918 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
919 GST_EVENT_TYPE_NAME (event));
921 for (n = 0; n < qtdemux->n_streams; n++) {
923 QtDemuxStream *stream = qtdemux->streams[n];
925 if ((pad = stream->pad)) {
926 has_valid_stream = TRUE;
928 if (etype == GST_EVENT_EOS) {
929 /* let's not send twice */
930 if (stream->sent_eos)
932 stream->sent_eos = TRUE;
935 gst_pad_push_event (pad, gst_event_ref (event));
939 gst_event_unref (event);
941 /* if it is EOS and there are no pads, post an error */
942 if (!has_valid_stream && etype == GST_EVENT_EOS) {
943 gst_qtdemux_post_no_playable_stream_error (qtdemux);
947 /* push a pending newsegment event, if any from the streaming thread */
949 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
951 if (qtdemux->pending_newsegment) {
952 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
953 qtdemux->pending_newsegment = NULL;
963 find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data)
965 if (s1->timestamp > *media_time)
971 /* find the index of the sample that includes the data for @media_time using a
972 * binary search. Only to be called in optimized cases of linear search below.
974 * Returns the index of the sample.
977 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
980 QtDemuxSample *result;
983 /* convert media_time to mov format */
984 media_time = gst_util_uint64_scale (media_time, str->timescale, GST_SECOND);
986 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
987 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
988 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
990 if (G_LIKELY (result))
991 index = result - str->samples;
995 #ifdef QTDEMUX_MODIFICATION
996 /*Modification: check duplicated timestamp */
1000 if (str->samples[index-1].timestamp == media_time)
1013 #ifdef QTDEMUX_MODIFICATION
1014 /*Modification: Added function to find next key frame in support of index-table generation*/
1017 /* find the index of the keyframe needed to decode the sample at @index
1020 * Returns the index of the keyframe.
1023 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1026 gint32 new_index = index;
1028 if (index == str->n_samples) {
1030 if (!str->samples[new_index].keyframe)
1035 else if (index > str->n_samples)
1044 /* all keyframes, return index */
1045 if (str->all_keyframe) {
1050 /* else go back until we have a keyframe */
1052 if (str->samples[new_index].keyframe)
1055 if (new_index >= str->n_samples) {
1064 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1065 "gave %d", index, new_index);
1072 /* find the index of the sample that includes the data for @media_offset using a
1075 * Returns the index of the sample.
1078 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1079 QtDemuxStream * str, gint64 media_offset)
1081 QtDemuxSample *result = str->samples;
1084 if (result == NULL || str->n_samples == 0)
1087 if (media_offset == result->offset)
1091 while (index < str->n_samples - 1) {
1092 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1095 if (media_offset < result->offset)
1106 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1111 /* find the index of the sample that includes the data for @media_time using a
1112 * linear search, and keeping in mind that not all samples may have been parsed
1113 * yet. If possible, it will delegate to binary search.
1115 * Returns the index of the sample.
1118 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1124 /* convert media_time to mov format */
1126 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1128 if (mov_time == str->samples[0].timestamp)
1131 /* use faster search if requested time in already parsed range */
1132 if (str->stbl_index >= 0 &&
1133 mov_time <= str->samples[str->stbl_index].timestamp)
1134 return gst_qtdemux_find_index (qtdemux, str, media_time);
1136 while (index < str->n_samples - 1) {
1137 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1140 if (mov_time < str->samples[index + 1].timestamp)
1150 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1155 /* find the index of the keyframe needed to decode the sample at @index
1158 * Returns the index of the keyframe.
1161 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1164 guint32 new_index = index;
1166 if (index >= str->n_samples) {
1167 new_index = str->n_samples;
1171 /* all keyframes, return index */
1172 if (str->all_keyframe) {
1177 /* else go back until we have a keyframe */
1179 if (str->samples[new_index].keyframe)
1189 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1190 "gave %u", index, new_index);
1195 /* find the segment for @time_position for @stream
1197 * Returns -1 if the segment cannot be found.
1200 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1201 guint64 time_position)
1206 GST_LOG_OBJECT (qtdemux, "finding segment for %" GST_TIME_FORMAT,
1207 GST_TIME_ARGS (time_position));
1209 /* find segment corresponding to time_position if we are looking
1212 for (i = 0; i < stream->n_segments; i++) {
1213 QtDemuxSegment *segment = &stream->segments[i];
1215 GST_LOG_OBJECT (qtdemux,
1216 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1217 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1219 /* For the last segment we include stop_time in the last segment */
1220 if (i < stream->n_segments - 1) {
1221 if (segment->time <= time_position && time_position < segment->stop_time) {
1222 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1227 if (segment->time <= time_position && time_position <= segment->stop_time) {
1228 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1237 /* move the stream @str to the sample position @index.
1239 * Updates @str->sample_index and marks discontinuity if needed.
1242 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1245 /* no change needed */
1246 if (index == str->sample_index)
1249 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1252 /* position changed, we have a discont */
1253 str->sample_index = index;
1254 /* Each time we move in the stream we store the position where we are
1256 str->from_sample = index;
1257 str->discont = TRUE;
1261 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1262 gint64 * key_time, gint64 * key_offset)
1265 gint64 min_byte_offset = -1;
1268 min_offset = desired_time;
1270 /* for each stream, find the index of the sample in the segment
1271 * and move back to the previous keyframe. */
1272 for (n = 0; n < qtdemux->n_streams; n++) {
1274 guint32 index, kindex;
1276 guint64 media_start;
1279 QtDemuxSegment *seg;
1281 str = qtdemux->streams[n];
1283 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1284 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1286 /* segment not found, continue with normal flow */
1290 /* get segment and time in the segment */
1291 seg = &str->segments[seg_idx];
1292 seg_time = desired_time - seg->time;
1294 /* get the media time in the segment */
1295 media_start = seg->media_start + seg_time;
1297 /* get the index of the sample with media time */
1298 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1299 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
1300 GST_TIME_ARGS (media_start), index);
1302 /* find previous keyframe */
1303 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);
1305 /* if the keyframe is at a different position, we need to update the
1306 * requested seek time */
1307 if (index != kindex) {
1310 /* get timestamp of keyframe */
1312 gst_util_uint64_scale (str->samples[kindex].timestamp, GST_SECOND,
1314 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT,
1315 kindex, GST_TIME_ARGS (media_time));
1317 /* keyframes in the segment get a chance to change the
1318 * desired_offset. keyframes out of the segment are
1320 if (media_time >= seg->media_start) {
1323 /* this keyframe is inside the segment, convert back to
1325 seg_time = (media_time - seg->media_start) + seg->time;
1326 if (seg_time < min_offset)
1327 min_offset = seg_time;
1331 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1332 min_byte_offset = str->samples[index].offset;
1336 *key_time = min_offset;
1338 *key_offset = min_byte_offset;
1342 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1343 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1348 g_return_val_if_fail (format != NULL, FALSE);
1349 g_return_val_if_fail (cur != NULL, FALSE);
1350 g_return_val_if_fail (stop != NULL, FALSE);
1352 if (*format == GST_FORMAT_TIME)
1355 fmt = GST_FORMAT_TIME;
1357 if (cur_type != GST_SEEK_TYPE_NONE)
1358 res = gst_pad_query_convert (pad, *format, *cur, &fmt, cur);
1359 if (res && stop_type != GST_SEEK_TYPE_NONE)
1360 res = gst_pad_query_convert (pad, *format, *stop, &fmt, stop);
1363 *format = GST_FORMAT_TIME;
1368 /* perform seek in push based mode:
1369 find BYTE position to move to based on time and delegate to upstream
1372 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1377 GstSeekType cur_type, stop_type;
1382 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1384 gst_event_parse_seek (event, &rate, &format, &flags,
1385 &cur_type, &cur, &stop_type, &stop);
1387 /* FIXME, always play to the end */
1390 /* only forward streaming and seeking is possible */
1392 goto unsupported_seek;
1394 /* convert to TIME if needed and possible */
1395 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1399 /* find reasonable corresponding BYTE position,
1400 * also try to mind about keyframes, since we can not go back a bit for them
1402 gst_qtdemux_adjust_seek (qtdemux, cur, NULL, &byte_cur);
1407 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1408 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1411 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1412 GST_DEBUG_OBJECT (qtdemux,
1413 "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %"
1414 G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
1415 GST_OBJECT_LOCK (qtdemux);
1416 qtdemux->requested_seek_time = cur;
1417 qtdemux->seek_offset = byte_cur;
1418 GST_OBJECT_UNLOCK (qtdemux);
1421 /* BYTE seek event */
1422 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
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)
1466 gint64 desired_offset;
1469 desired_offset = segment->last_stop;
1471 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1472 GST_TIME_ARGS (desired_offset));
1474 if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
1477 gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL);
1478 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1479 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1480 desired_offset = min_offset;
1483 /* and set all streams to the final position */
1484 for (n = 0; n < qtdemux->n_streams; n++) {
1485 QtDemuxStream *stream = qtdemux->streams[n];
1487 stream->time_position = desired_offset;
1488 stream->sample_index = -1;
1489 stream->segment_index = -1;
1490 stream->last_ret = GST_FLOW_OK;
1491 stream->sent_eos = FALSE;
1492 #ifdef QTDEMUX_MODIFICATION
1493 /*initialization of rate params */ //Kishore
1494 stream->next_kindex=0;
1495 stream->prev_kindex=0;
1496 stream->total_samples_bet_2_keyframes=0;
1499 segment->last_stop = desired_offset;
1500 segment->time = desired_offset;
1502 /* we stop at the end */
1503 if (segment->stop == -1)
1504 segment->stop = segment->duration;
1509 /* do a seek in pull based mode */
1511 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1516 GstSeekType cur_type, stop_type;
1520 GstSegment seeksegment;
1524 GST_INFO_OBJECT (qtdemux, "doing pull based seek with event");
1526 gst_event_parse_seek (event, &rate, &format, &flags,
1527 &cur_type, &cur, &stop_type, &stop);
1529 /* we have to have a format as the segment format. Try to convert
1531 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1535 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1537 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1541 flush = flags & GST_SEEK_FLAG_FLUSH;
1543 /* stop streaming, either by flushing or by pausing the task */
1545 /* unlock upstream pull_range */
1546 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
1547 /* make sure out loop function exits */
1548 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
1550 /* non flushing seek, pause the task */
1551 gst_pad_pause_task (qtdemux->sinkpad);
1554 /* wait for streaming to finish */
1555 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1557 /* copy segment, we need this because we still need the old
1558 * segment when we close the current segment. */
1559 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1562 /* configure the segment with the seek variables */
1563 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1564 gst_segment_set_seek (&seeksegment, rate, format, flags,
1565 cur_type, cur, stop_type, stop, &update);
1568 /* now do the seek, this actually never returns FALSE */
1569 gst_qtdemux_perform_seek (qtdemux, &seeksegment);
1571 /* prepare for streaming again */
1573 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
1574 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ());
1575 } else if (qtdemux->segment_running) {
1576 /* we are running the current segment and doing a non-flushing seek,
1577 * close the segment first based on the last_stop. */
1578 GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT
1579 " to %" G_GINT64_FORMAT, qtdemux->segment.start,
1580 qtdemux->segment.last_stop);
1582 if (qtdemux->segment.rate >= 0) {
1583 /* FIXME, rate is the product of the global rate and the (quicktime)
1585 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1586 qtdemux->segment.rate, qtdemux->segment.format,
1587 qtdemux->segment.start, qtdemux->segment.last_stop,
1588 qtdemux->segment.time);
1589 } else { /* For Reverse Playback */
1592 if ((stop = qtdemux->segment.stop) == -1)
1593 stop = qtdemux->segment.duration;
1594 /* for reverse playback, we played from stop to last_stop. */
1595 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1596 qtdemux->segment.rate, qtdemux->segment.format,
1597 qtdemux->segment.last_stop, stop, qtdemux->segment.last_stop);
1601 /* commit the new segment */
1602 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1604 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1605 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1606 gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1607 qtdemux->segment.format, qtdemux->segment.last_stop));
1610 /* restart streaming, NEWSEGMENT will be sent from the streaming
1612 qtdemux->segment_running = TRUE;
1613 for (i = 0; i < qtdemux->n_streams; i++)
1614 qtdemux->streams[i]->last_ret = GST_FLOW_OK;
1616 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1619 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1626 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1632 qtdemux_ensure_index (GstQTDemux * qtdemux)
1636 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1638 /* Build complete index */
1639 for (i = 0; i < qtdemux->n_streams; i++) {
1640 QtDemuxStream *stream = qtdemux->streams[i];
1642 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1650 GST_LOG_OBJECT (qtdemux,
1651 "Building complete index of stream %u for seeking failed!", i);
1657 gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
1659 gboolean res = TRUE;
1660 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
1662 switch (GST_EVENT_TYPE (event)) {
1663 case GST_EVENT_SEEK:
1665 #ifndef GST_DISABLE_GST_DEBUG
1666 GstClockTime ts = gst_util_get_timestamp ();
1668 /* Build complete index for seeking;
1669 * if not a fragmented file at least */
1670 if (!qtdemux->fragmented)
1671 if (!qtdemux_ensure_index (qtdemux))
1673 #ifndef GST_DISABLE_GST_DEBUG
1674 ts = gst_util_get_timestamp () - ts;
1675 GST_INFO_OBJECT (qtdemux,
1676 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1679 if (qtdemux->pullbased) {
1680 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1681 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
1682 !qtdemux->fragmented) {
1683 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1685 GST_DEBUG_OBJECT (qtdemux,
1686 "ignoring seek in push mode in current state");
1689 gst_event_unref (event);
1692 case GST_EVENT_NAVIGATION:
1694 gst_event_unref (event);
1697 res = gst_pad_event_default (pad, event);
1701 gst_object_unref (qtdemux);
1709 GST_ERROR_OBJECT (qtdemux, "Index failed");
1710 gst_event_unref (event);
1716 /* stream/index return sample that is min/max w.r.t. byte position,
1717 * time is min/max w.r.t. time of samples,
1718 * the latter need not be time of the former sample */
1720 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1721 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1724 gint64 time, min_time;
1725 QtDemuxStream *stream;
1731 for (n = 0; n < qtdemux->n_streams; ++n) {
1734 gboolean set_sample;
1736 str = qtdemux->streams[n];
1743 i = str->n_samples - 1;
1746 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1747 if (str->samples[i].size &&
1748 ((fw && (str->samples[i].offset >= byte_pos)) ||
1750 (str->samples[i].offset + str->samples[i].size <=
1752 /* move stream to first available sample */
1754 gst_qtdemux_move_stream (qtdemux, str, i);
1757 /* determine min/max time */
1758 time = str->samples[i].timestamp + str->samples[i].pts_offset;
1759 time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);
1760 if (min_time == -1 || (!fw && time > min_time) ||
1761 (fw && time < min_time)) {
1764 /* determine stream with leading sample, to get its position */
1766 && (str->samples[i].offset < stream->samples[index].offset))
1768 && (str->samples[i].offset > stream->samples[index].offset))) {
1775 /* no sample for this stream, mark eos */
1777 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1789 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
1791 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
1794 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
1796 switch (GST_EVENT_TYPE (event)) {
1797 case GST_EVENT_NEWSEGMENT:
1800 gdouble rate, arate;
1801 gint64 start, stop, time, offset = 0;
1802 QtDemuxStream *stream;
1807 /* some debug output */
1808 gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
1809 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
1810 &start, &stop, &time);
1811 gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
1813 GST_DEBUG_OBJECT (demux,
1814 "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
1817 /* chain will send initial newsegment after pads have been added */
1818 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
1819 GST_DEBUG_OBJECT (demux, "still starting, eating event");
1823 /* we only expect a BYTE segment, e.g. following a seek */
1824 if (format == GST_FORMAT_BYTES) {
1826 gint64 requested_seek_time;
1827 guint64 seek_offset;
1831 GST_OBJECT_LOCK (demux);
1832 requested_seek_time = demux->requested_seek_time;
1833 seek_offset = demux->seek_offset;
1834 demux->requested_seek_time = -1;
1835 demux->seek_offset = -1;
1836 GST_OBJECT_UNLOCK (demux);
1838 if (offset == seek_offset) {
1839 start = requested_seek_time;
1841 gst_qtdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,
1843 start = MAX (start, 0);
1847 gst_qtdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,
1849 /* keyframe seeking should already arrange for start >= stop,
1850 * but make sure in other rare cases */
1851 stop = MAX (stop, start);
1854 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
1858 /* accept upstream's notion of segment and distribute along */
1859 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
1860 GST_FORMAT_TIME, start, stop, start);
1861 GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, "
1862 "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
1863 "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
1864 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
1866 gst_qtdemux_push_event (demux,
1867 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
1868 start, stop, start));
1870 /* clear leftover in current segment, if any */
1871 gst_adapter_clear (demux->adapter);
1872 /* set up streaming thread */
1873 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);
1874 demux->offset = offset;
1876 demux->todrop = stream->samples[idx].offset - offset;
1877 demux->neededbytes = demux->todrop + stream->samples[idx].size;
1879 /* set up for EOS */
1880 demux->neededbytes = -1;
1884 gst_event_unref (event);
1889 case GST_EVENT_FLUSH_STOP:
1893 /* clean up, force EOS if no more info follows */
1894 gst_adapter_clear (demux->adapter);
1896 demux->neededbytes = -1;
1897 /* reset flow return, e.g. following seek */
1898 for (i = 0; i < demux->n_streams; i++) {
1899 demux->streams[i]->last_ret = GST_FLOW_OK;
1900 demux->streams[i]->sent_eos = FALSE;
1905 /* If we are in push mode, and get an EOS before we've seen any streams,
1906 * then error out - we have nowhere to send the EOS */
1907 if (!demux->pullbased) {
1909 gboolean has_valid_stream = FALSE;
1910 for (i = 0; i < demux->n_streams; i++) {
1911 if (demux->streams[i]->pad != NULL) {
1912 has_valid_stream = TRUE;
1916 if (!has_valid_stream)
1917 gst_qtdemux_post_no_playable_stream_error (demux);
1924 res = gst_pad_event_default (demux->sinkpad, event);
1931 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
1933 GstQTDemux *demux = GST_QTDEMUX (element);
1935 GST_OBJECT_LOCK (demux);
1936 if (demux->element_index)
1937 gst_object_unref (demux->element_index);
1939 demux->element_index = gst_object_ref (index);
1941 demux->element_index = NULL;
1943 GST_OBJECT_UNLOCK (demux);
1944 /* object lock might be taken again */
1946 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
1947 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT, demux->element_index);
1951 gst_qtdemux_get_index (GstElement * element)
1953 GstIndex *result = NULL;
1954 GstQTDemux *demux = GST_QTDEMUX (element);
1956 GST_OBJECT_LOCK (demux);
1957 if (demux->element_index)
1958 result = gst_object_ref (demux->element_index);
1959 GST_OBJECT_UNLOCK (demux);
1961 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
1967 gst_qtdemux_stbl_free (QtDemuxStream * stream)
1969 g_free ((gpointer) stream->stco.data);
1970 stream->stco.data = NULL;
1971 g_free ((gpointer) stream->stsz.data);
1972 stream->stsz.data = NULL;
1973 g_free ((gpointer) stream->stsc.data);
1974 stream->stsc.data = NULL;
1975 g_free ((gpointer) stream->stts.data);
1976 stream->stts.data = NULL;
1977 g_free ((gpointer) stream->stss.data);
1978 stream->stss.data = NULL;
1979 g_free ((gpointer) stream->stps.data);
1980 stream->stps.data = NULL;
1981 g_free ((gpointer) stream->ctts.data);
1982 stream->ctts.data = NULL;
1986 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
1988 while (stream->buffers) {
1989 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
1990 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
1993 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
1994 g_free (stream->samples);
1996 gst_caps_unref (stream->caps);
1997 g_free (stream->segments);
1998 if (stream->pending_tags)
1999 gst_tag_list_free (stream->pending_tags);
2000 g_free (stream->redirect_uri);
2001 /* free stbl sub-atoms */
2002 gst_qtdemux_stbl_free (stream);
2006 static GstStateChangeReturn
2007 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2009 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2010 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2012 switch (transition) {
2013 case GST_STATE_CHANGE_PAUSED_TO_READY:
2019 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2021 switch (transition) {
2022 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2025 qtdemux->state = QTDEMUX_STATE_INITIAL;
2026 qtdemux->neededbytes = 16;
2027 qtdemux->todrop = 0;
2028 qtdemux->pullbased = FALSE;
2029 qtdemux->posted_redirect = FALSE;
2030 qtdemux->offset = 0;
2031 qtdemux->first_mdat = -1;
2032 qtdemux->got_moov = FALSE;
2033 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
2034 if (qtdemux->mdatbuffer)
2035 gst_buffer_unref (qtdemux->mdatbuffer);
2036 qtdemux->mdatbuffer = NULL;
2037 if (qtdemux->comp_brands)
2038 gst_buffer_unref (qtdemux->comp_brands);
2039 qtdemux->comp_brands = NULL;
2040 if (qtdemux->tag_list)
2041 gst_tag_list_free (qtdemux->tag_list);
2042 qtdemux->tag_list = NULL;
2043 if (qtdemux->element_index)
2044 gst_object_unref (qtdemux->element_index);
2045 qtdemux->element_index = NULL;
2046 gst_adapter_clear (qtdemux->adapter);
2047 #ifdef QTDEMUX_MODIFICATION
2048 if (qtdemux->file) {
2049 fclose (qtdemux->file);
2050 fclose (qtdemux->ofile);
2052 remove (qtdemux->filename);
2055 for (n = 0; n < qtdemux->n_streams; n++) {
2056 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2057 qtdemux->streams[n] = NULL;
2059 qtdemux->major_brand = 0;
2060 qtdemux->n_streams = 0;
2061 qtdemux->n_video_streams = 0;
2062 qtdemux->n_audio_streams = 0;
2063 qtdemux->n_sub_streams = 0;
2064 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2065 qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE;
2066 qtdemux->seek_offset = 0;
2077 qtdemux_post_global_tags (GstQTDemux * qtdemux)
2079 if (qtdemux->tag_list) {
2080 /* all header tags ready and parsed, push them */
2081 GST_INFO_OBJECT (qtdemux, "posting global tags: %" GST_PTR_FORMAT,
2083 /* post now, send event on pads later */
2084 gst_element_post_message (GST_ELEMENT (qtdemux),
2085 gst_message_new_tag (GST_OBJECT (qtdemux),
2086 gst_tag_list_copy (qtdemux->tag_list)));
2091 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2093 /* only consider at least a sufficiently complete ftyp atom */
2097 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2098 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2099 GST_FOURCC_ARGS (qtdemux->major_brand));
2100 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2101 memcpy (GST_BUFFER_DATA (buf), buffer + 16, GST_BUFFER_SIZE (buf));
2106 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist)
2108 /* Strip out bogus fields */
2110 gst_tag_list_remove_tag (taglist, GST_TAG_VIDEO_CODEC);
2112 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, taglist);
2114 if (qtdemux->tag_list) {
2115 /* prioritize native tags using _KEEP mode */
2116 gst_tag_list_insert (qtdemux->tag_list, taglist, GST_TAG_MERGE_KEEP);
2117 gst_tag_list_free (taglist);
2119 qtdemux->tag_list = taglist;
2124 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2126 static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2127 0x97, 0xA9, 0x42, 0xE8,
2128 0x9C, 0x71, 0x99, 0x94,
2129 0x91, 0xE3, 0xAF, 0xAC
2133 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2135 if (length <= offset + 16) {
2136 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2140 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2142 GstTagList *taglist;
2144 buf = gst_buffer_new ();
2145 GST_BUFFER_DATA (buf) = (guint8 *) buffer + offset + 16;
2146 GST_BUFFER_SIZE (buf) = length - offset - 16;
2148 taglist = gst_tag_list_from_xmp_buffer (buf);
2149 gst_buffer_unref (buf);
2151 qtdemux_handle_xmp_taglist (qtdemux, taglist);
2154 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid");
2158 /* caller verifies at least 8 bytes in buf */
2160 extract_initial_length_and_fourcc (const guint8 * data, guint size,
2161 guint64 * plength, guint32 * pfourcc)
2166 length = QT_UINT32 (data);
2167 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2168 fourcc = QT_FOURCC (data + 4);
2169 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
2172 length = G_MAXUINT32;
2173 } else if (length == 1 && size >= 16) {
2174 /* this means we have an extended size, which is the 64 bit value of
2175 * the next 8 bytes */
2176 length = QT_UINT64 (data + 8);
2177 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2187 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
2189 guint32 version = 0;
2190 guint64 duration = 0;
2192 if (!gst_byte_reader_get_uint32_be (br, &version))
2197 if (!gst_byte_reader_get_uint64_be (br, &duration))
2202 if (!gst_byte_reader_get_uint32_be (br, &dur))
2207 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
2208 qtdemux->duration = duration;
2214 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
2220 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
2221 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
2223 if (!stream->parsed_trex && qtdemux->moov_node) {
2225 GstByteReader trex_data;
2227 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
2229 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
2232 guint32 id = 0, dur = 0, size = 0, flags = 0;
2234 /* skip version/flags */
2235 if (!gst_byte_reader_skip (&trex_data, 4))
2237 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
2239 if (id != stream->track_id)
2241 /* sample description index; ignore */
2242 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2244 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2246 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
2248 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
2251 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
2252 "duration %d, size %d, flags 0x%x", stream->track_id,
2255 stream->parsed_trex = TRUE;
2256 stream->def_sample_duration = dur;
2257 stream->def_sample_size = size;
2258 stream->def_sample_flags = flags;
2261 /* iterate all siblings */
2262 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
2268 *ds_duration = stream->def_sample_duration;
2269 *ds_size = stream->def_sample_size;
2270 *ds_size = stream->def_sample_size;
2272 /* even then, above values are better than random ... */
2273 if (G_UNLIKELY (!stream->parsed_trex)) {
2274 GST_WARNING_OBJECT (qtdemux,
2275 "failed to find fragment defaults for stream %d", stream->track_id);
2283 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
2284 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
2285 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
2286 gint64 * base_offset, gint64 * running_offset)
2289 gint32 data_offset = 0;
2290 guint32 flags = 0, first_flags = 0, samples_count = 0;
2293 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
2294 QtDemuxSample *sample;
2295 gboolean ismv = FALSE;
2297 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
2298 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,
2299 stream->track_id, d_sample_duration, d_sample_size, d_sample_flags,
2302 if (!gst_byte_reader_skip (trun, 1) ||
2303 !gst_byte_reader_get_uint24_be (trun, &flags))
2306 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
2309 if (flags & TR_DATA_OFFSET) {
2310 /* note this is really signed */
2311 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
2313 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
2314 /* default base offset = first byte of moof */
2315 if (*base_offset == -1) {
2316 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
2317 *base_offset = moof_offset;
2319 *running_offset = *base_offset + data_offset;
2321 /* if no offset at all, that would mean data starts at moof start,
2322 * which is a bit wrong and is ismv crappy way, so compensate
2323 * assuming data is in mdat following moof */
2324 if (*base_offset == -1) {
2325 *base_offset = moof_offset + moof_length + 8;
2326 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
2329 if (*running_offset == -1)
2330 *running_offset = *base_offset;
2333 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
2335 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
2336 data_offset, flags, samples_count);
2338 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2339 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
2340 GST_DEBUG_OBJECT (qtdemux,
2341 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
2342 flags ^= TR_FIRST_SAMPLE_FLAGS;
2344 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
2346 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
2350 /* FIXME ? spec says other bits should also be checked to determine
2351 * entry size (and prefix size for that matter) */
2353 dur_offset = size_offset = 0;
2354 if (flags & TR_SAMPLE_DURATION) {
2355 GST_LOG_OBJECT (qtdemux, "entry duration present");
2356 dur_offset = entry_size;
2359 if (flags & TR_SAMPLE_SIZE) {
2360 GST_LOG_OBJECT (qtdemux, "entry size present");
2361 size_offset = entry_size;
2364 if (flags & TR_SAMPLE_FLAGS) {
2365 GST_LOG_OBJECT (qtdemux, "entry flags present");
2366 flags_offset = entry_size;
2369 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2370 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
2371 ct_offset = entry_size;
2375 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
2377 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
2379 if (stream->n_samples >=
2380 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
2383 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u (%lu MB)",
2384 stream->n_samples, (stream->n_samples * sizeof (QtDemuxSample)) >> 20);
2386 /* create a new array of samples if it's the first sample parsed */
2387 if (stream->n_samples == 0)
2388 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
2389 /* or try to reallocate it with space enough to insert the new samples */
2391 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
2392 stream->n_samples + samples_count);
2393 if (stream->samples == NULL)
2396 if (G_UNLIKELY (stream->n_samples == 0)) {
2397 /* the timestamp of the first sample is also provided by the tfra entry
2398 * but we shouldn't rely on it as it is at the end of files */
2401 /* subsequent fragments extend stream */
2403 stream->samples[stream->n_samples - 1].timestamp +
2404 stream->samples[stream->n_samples - 1].duration;
2406 sample = stream->samples + stream->n_samples;
2407 for (i = 0; i < samples_count; i++) {
2408 guint32 dur, size, sflags, ct;
2410 /* first read sample data */
2411 if (flags & TR_SAMPLE_DURATION) {
2412 dur = QT_UINT32 (data + dur_offset);
2414 dur = d_sample_duration;
2416 if (flags & TR_SAMPLE_SIZE) {
2417 size = QT_UINT32 (data + size_offset);
2419 size = d_sample_size;
2421 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2423 sflags = first_flags;
2425 sflags = d_sample_flags;
2427 } else if (flags & TR_SAMPLE_FLAGS) {
2428 sflags = QT_UINT32 (data + flags_offset);
2430 sflags = d_sample_flags;
2432 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2433 ct = QT_UINT32 (data + ct_offset);
2439 /* fill the sample information */
2440 sample->offset = *running_offset;
2441 sample->pts_offset = ct;
2442 sample->size = size;
2443 sample->timestamp = timestamp;
2444 sample->duration = dur;
2445 /* sample-is-difference-sample */
2446 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
2447 * now idea how it relates to bitfield other than massive LE/BE confusion */
2448 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
2449 *running_offset += size;
2454 stream->n_samples += samples_count;
2460 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
2465 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
2471 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
2472 "be larger than %uMB (broken file?)", stream->n_samples,
2473 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
2478 /* find stream with @id */
2479 static inline QtDemuxStream *
2480 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
2482 QtDemuxStream *stream;
2486 if (G_UNLIKELY (!id)) {
2487 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
2491 /* try to get it fast and simple */
2492 if (G_LIKELY (id <= qtdemux->n_streams)) {
2493 stream = qtdemux->streams[id - 1];
2494 if (G_LIKELY (stream->track_id == id))
2498 /* linear search otherwise */
2499 for (i = 0; i < qtdemux->n_streams; i++) {
2500 stream = qtdemux->streams[i];
2501 if (stream->track_id == id)
2509 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
2510 QtDemuxStream ** stream, guint32 * default_sample_duration,
2511 guint32 * default_sample_size, guint32 * default_sample_flags,
2512 gint64 * base_offset)
2515 guint32 track_id = 0;
2517 if (!gst_byte_reader_skip (tfhd, 1) ||
2518 !gst_byte_reader_get_uint24_be (tfhd, &flags))
2521 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
2524 *stream = qtdemux_find_stream (qtdemux, track_id);
2525 if (G_UNLIKELY (!*stream))
2526 goto unknown_stream;
2528 if (flags & TF_BASE_DATA_OFFSET)
2529 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
2532 /* obtain stream defaults */
2533 qtdemux_parse_trex (qtdemux, *stream,
2534 default_sample_duration, default_sample_size, default_sample_flags);
2536 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
2537 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
2538 if (!gst_byte_reader_skip (tfhd, 4))
2541 if (flags & TF_DEFAULT_SAMPLE_DURATION)
2542 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
2545 if (flags & TF_DEFAULT_SAMPLE_SIZE)
2546 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
2549 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
2550 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
2557 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
2562 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
2568 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
2569 guint64 moof_offset, QtDemuxStream * stream)
2571 GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
2572 GstByteReader trun_data, tfhd_data;
2573 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
2574 gint64 base_offset, running_offset;
2576 /* NOTE @stream ignored */
2578 moof_node = g_node_new ((guint8 *) buffer);
2579 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
2580 qtdemux_node_dump (qtdemux, moof_node);
2582 /* unknown base_offset to start with */
2583 base_offset = running_offset = -1;
2584 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
2586 /* Fragment Header node */
2588 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
2592 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
2593 &ds_size, &ds_flags, &base_offset))
2595 if (G_UNLIKELY (!stream)) {
2596 /* we lost track of offset, we'll need to regain it,
2597 * but can delay complaining until later or avoid doing so altogether */
2601 if (G_UNLIKELY (base_offset < -1))
2603 /* Track Run node */
2605 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
2608 qtdemux_parse_trun (qtdemux, &trun_data, stream,
2609 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
2611 /* iterate all siblings */
2612 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
2615 /* if no new base_offset provided for next traf,
2616 * base is end of current traf */
2617 base_offset = running_offset;
2618 running_offset = -1;
2620 /* iterate all siblings */
2621 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
2623 g_node_destroy (moof_node);
2628 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
2633 GST_DEBUG_OBJECT (qtdemux, "lost offset");
2638 g_node_destroy (moof_node);
2639 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2640 (_("This file is corrupt and cannot be played.")), (NULL));
2645 /* might be used if some day we actually use mfra & co
2646 * for random access to fragments,
2647 * but that will require quite some modifications and much less relying
2648 * on a sample array */
2651 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node,
2652 QtDemuxStream * stream)
2654 guint64 time = 0, moof_offset = 0;
2655 guint32 ver_flags, track_id, len, num_entries, i;
2656 guint value_size, traf_size, trun_size, sample_size;
2657 GstBuffer *buf = NULL;
2661 gst_byte_reader_init (&tfra, (guint8 *) tfra_node->data + (4 + 4),
2662 QT_UINT32 ((guint8 *) tfra_node->data) - (4 + 4));
2664 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
2667 if (!(gst_byte_reader_get_uint32_be (&tfra, &track_id) &&
2668 gst_byte_reader_get_uint32_be (&tfra, &len) &&
2669 gst_byte_reader_get_uint32_be (&tfra, &num_entries)))
2672 GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?",
2673 track_id, stream->track_id);
2674 if (track_id != stream->track_id) {
2678 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
2679 sample_size = (len & 3) + 1;
2680 trun_size = ((len & 12) >> 2) + 1;
2681 traf_size = ((len & 48) >> 4) + 1;
2683 if (num_entries == 0)
2686 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
2687 value_size + value_size + traf_size + trun_size + sample_size))
2690 for (i = 0; i < num_entries; i++) {
2691 qt_atom_parser_get_offset (&tfra, value_size, &time);
2692 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
2693 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
2694 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
2695 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
2697 GST_LOG_OBJECT (qtdemux,
2698 "fragment time: %" GST_TIME_FORMAT " moof_offset: %u",
2699 GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND,
2700 stream->timescale)), moof_offset);
2702 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
2703 if (ret != GST_FLOW_OK)
2705 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
2706 moof_offset, stream);
2707 gst_buffer_unref (buf);
2715 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2716 (_("This file is corrupt and cannot be played.")), (NULL));
2721 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
2727 qtdemux_parse_mfra (GstQTDemux * qtdemux, QtDemuxStream * stream)
2730 GNode *mfra_node, *tfra_node;
2733 if (!qtdemux->mfra_offset)
2736 ret = gst_qtdemux_pull_atom (qtdemux, qtdemux->mfra_offset, 0, &buffer);
2737 if (ret != GST_FLOW_OK)
2740 mfra_node = g_node_new ((guint8 *) GST_BUFFER_DATA (buffer));
2741 qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer),
2742 GST_BUFFER_SIZE (buffer));
2744 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
2747 qtdemux_parse_tfra (qtdemux, tfra_node, stream);
2748 /* iterate all siblings */
2749 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
2751 g_node_destroy (mfra_node);
2752 gst_buffer_unref (buffer);
2758 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2759 (_("This file is corrupt and cannot be played.")), (NULL));
2764 static GstFlowReturn
2765 qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset,
2766 guint32 * mfro_size)
2768 GstFlowReturn ret = GST_FLOW_ERROR;
2769 GstBuffer *mfro = NULL;
2772 GstFormat fmt = GST_FORMAT_BYTES;
2774 if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &fmt, &len)) {
2775 GST_DEBUG_OBJECT (qtdemux, "upstream size not available; "
2776 "can not locate mfro");
2780 ret = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
2781 if (ret != GST_FLOW_OK)
2784 fourcc = QT_FOURCC (GST_BUFFER_DATA (mfro) + 4);
2785 if (fourcc != FOURCC_mfro)
2788 GST_INFO_OBJECT (qtdemux, "Found mfro atom: fragmented mp4 container");
2789 if (GST_BUFFER_SIZE (mfro) >= 16) {
2790 GST_DEBUG_OBJECT (qtdemux, "parsing 'mfro' atom");
2791 *mfro_size = QT_UINT32 (GST_BUFFER_DATA (mfro) + 12);
2792 if (*mfro_size >= len) {
2793 GST_WARNING_OBJECT (qtdemux, "mfro.size is invalid");
2794 ret = GST_FLOW_ERROR;
2797 *mfra_offset = len - *mfro_size;
2802 gst_buffer_unref (mfro);
2808 qtdemux_parse_fragmented (GstQTDemux * qtdemux)
2811 guint32 mfra_size = 0;
2812 guint64 mfra_offset = 0;
2815 qtdemux->fragmented = FALSE;
2817 /* We check here if it is a fragmented mp4 container */
2818 ret = qtdemux_parse_mfro (qtdemux, &mfra_offset, &mfra_size);
2819 if (ret == GST_FLOW_OK && mfra_size != 0 && mfra_offset != 0) {
2820 qtdemux->fragmented = TRUE;
2821 GST_DEBUG_OBJECT (qtdemux,
2822 "mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset);
2823 qtdemux->mfra_offset = mfra_offset;
2828 static GstFlowReturn
2829 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
2833 GstBuffer *buf = NULL;
2834 GstFlowReturn ret = GST_FLOW_OK;
2835 guint64 cur_offset = qtdemux->offset;
2837 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
2838 if (G_UNLIKELY (ret != GST_FLOW_OK))
2840 if (G_LIKELY (GST_BUFFER_SIZE (buf) >= 8))
2841 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf),
2842 GST_BUFFER_SIZE (buf), &length, &fourcc);
2843 gst_buffer_unref (buf);
2845 /* maybe we already got most we needed, so only consider this eof */
2846 if (G_UNLIKELY (length == 0)) {
2847 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
2848 (_("Invalid atom size.")),
2849 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
2850 GST_FOURCC_ARGS (fourcc)));
2851 ret = GST_FLOW_UNEXPECTED;
2857 /* record for later parsing when needed */
2858 if (!qtdemux->moof_offset) {
2859 qtdemux->moof_offset = qtdemux->offset;
2868 GST_LOG_OBJECT (qtdemux,
2869 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
2870 GST_FOURCC_ARGS (fourcc), cur_offset);
2871 qtdemux->offset += length;
2878 if (qtdemux->got_moov) {
2879 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
2880 qtdemux->offset += length;
2884 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
2885 if (ret != GST_FLOW_OK)
2887 if (length != GST_BUFFER_SIZE (moov)) {
2888 /* Some files have a 'moov' atom at the end of the file which contains
2889 * a terminal 'free' atom where the body of the atom is missing.
2890 * Check for, and permit, this special case.
2892 if (GST_BUFFER_SIZE (moov) >= 8) {
2893 guint8 *final_data = GST_BUFFER_DATA (moov) +
2894 (GST_BUFFER_SIZE (moov) - 8);
2895 guint32 final_length = QT_UINT32 (final_data);
2896 guint32 final_fourcc = QT_FOURCC (final_data + 4);
2897 if (final_fourcc == FOURCC_free &&
2898 GST_BUFFER_SIZE (moov) + final_length - 8 == length) {
2899 /* Ok, we've found that special case. Allocate a new buffer with
2900 * that free atom actually present. */
2901 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
2902 gst_buffer_copy_metadata (newmoov, moov,
2903 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2904 GST_BUFFER_COPY_CAPS);
2905 memcpy (GST_BUFFER_DATA (newmoov), GST_BUFFER_DATA (moov),
2906 GST_BUFFER_SIZE (moov));
2907 memset (GST_BUFFER_DATA (newmoov) + GST_BUFFER_SIZE (moov), 0,
2909 gst_buffer_unref (moov);
2915 if (length != GST_BUFFER_SIZE (moov)) {
2916 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2917 (_("This file is incomplete and cannot be played.")),
2918 ("We got less than expected (received %u, wanted %u, offset %"
2919 G_GUINT64_FORMAT ")",
2920 GST_BUFFER_SIZE (moov), (guint) length, cur_offset));
2921 ret = GST_FLOW_ERROR;
2924 qtdemux->offset += length;
2926 qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
2927 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
2929 qtdemux_parse_tree (qtdemux);
2930 g_node_destroy (qtdemux->moov_node);
2931 gst_buffer_unref (moov);
2932 qtdemux->moov_node = NULL;
2933 qtdemux->got_moov = TRUE;
2941 /* extract major brand; might come in handy for ISO vs QT issues */
2942 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
2943 if (ret != GST_FLOW_OK)
2945 qtdemux->offset += length;
2946 qtdemux_parse_ftyp (qtdemux, GST_BUFFER_DATA (ftyp),
2947 GST_BUFFER_SIZE (ftyp));
2948 gst_buffer_unref (ftyp);
2955 /* uuid are extension atoms */
2956 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
2957 if (ret != GST_FLOW_OK)
2959 qtdemux->offset += length;
2960 qtdemux_parse_uuid (qtdemux, GST_BUFFER_DATA (uuid),
2961 GST_BUFFER_SIZE (uuid));
2962 gst_buffer_unref (uuid);
2969 GST_LOG_OBJECT (qtdemux,
2970 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
2971 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
2973 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
2974 if (ret != GST_FLOW_OK)
2976 GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown),
2977 GST_BUFFER_SIZE (unknown));
2978 gst_buffer_unref (unknown);
2979 qtdemux->offset += length;
2985 if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) {
2986 /* digested all data, show what we have */
2987 ret = qtdemux_expose_streams (qtdemux);
2989 /* Only post, event on pads is done after newsegment */
2990 qtdemux_post_global_tags (qtdemux);
2992 qtdemux->state = QTDEMUX_STATE_MOVIE;
2993 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
3000 /* Seeks to the previous keyframe of the indexed stream and
3001 * aligns other streams with respect to the keyframe timestamp
3002 * of indexed stream. Only called in case of Reverse Playback
3004 static GstFlowReturn
3005 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
3008 guint32 seg_idx = 0, k_index = 0;
3009 guint32 ref_seg_idx, ref_k_index;
3010 guint64 k_pos = 0, last_stop = 0;
3011 QtDemuxSegment *seg = NULL;
3012 QtDemuxStream *ref_str = NULL;
3013 guint64 seg_media_start_mov; /* segment media start time in mov format */
3015 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
3016 * and finally align all the other streams on that timestamp with their
3017 * respective keyframes */
3018 for (n = 0; n < qtdemux->n_streams; n++) {
3019 QtDemuxStream *str = qtdemux->streams[n];
3021 seg_idx = gst_qtdemux_find_segment (qtdemux, str,
3022 qtdemux->segment.last_stop);
3024 /* segment not found, continue with normal flow */
3028 /* No candidate yet, take that one */
3034 /* So that stream has a segment, we prefer video streams */
3035 if (str->subtype == FOURCC_vide) {
3041 if (G_UNLIKELY (!ref_str)) {
3042 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
3046 if (G_UNLIKELY (!ref_str->from_sample)) {
3047 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
3051 /* So that stream has been playing from from_sample to to_sample. We will
3052 * get the timestamp of the previous sample and search for a keyframe before
3053 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
3054 if (ref_str->subtype == FOURCC_vide) {
3055 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
3056 ref_str->from_sample - 1);
3058 if (ref_str->from_sample >= 10)
3059 k_index = ref_str->from_sample - 10;
3064 /* get current segment for that stream */
3065 seg = &ref_str->segments[ref_str->segment_index];
3066 /* convert seg->media_start to mov format time for timestamp comparison */
3067 seg_media_start_mov =
3068 gst_util_uint64_scale (seg->media_start, ref_str->timescale, GST_SECOND);
3069 /* Crawl back through segments to find the one containing this I frame */
3070 while (ref_str->samples[k_index].timestamp < seg_media_start_mov) {
3071 GST_DEBUG_OBJECT (qtdemux, "keyframe position is out of segment %u",
3072 ref_str->segment_index);
3073 if (G_UNLIKELY (!ref_str->segment_index)) {
3074 /* Reached first segment, let's consider it's EOS */
3077 ref_str->segment_index--;
3078 seg = &ref_str->segments[ref_str->segment_index];
3079 /* convert seg->media_start to mov format time for timestamp comparison */
3080 seg_media_start_mov =
3081 gst_util_uint64_scale (seg->media_start, ref_str->timescale,
3084 /* Calculate time position of the keyframe and where we should stop */
3086 (gst_util_uint64_scale (ref_str->samples[k_index].timestamp, GST_SECOND,
3087 ref_str->timescale) - seg->media_start) + seg->time;
3089 gst_util_uint64_scale (ref_str->samples[ref_str->from_sample].timestamp,
3090 GST_SECOND, ref_str->timescale);
3091 last_stop = (last_stop - seg->media_start) + seg->time;
3093 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
3094 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
3095 k_index, GST_TIME_ARGS (k_pos));
3097 /* Set last_stop with the keyframe timestamp we pushed of that stream */
3098 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, last_stop);
3099 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
3100 GST_TIME_ARGS (last_stop));
3102 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
3103 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
3107 ref_seg_idx = ref_str->segment_index;
3108 ref_k_index = k_index;
3110 /* Align them all on this */
3111 for (n = 0; n < qtdemux->n_streams; n++) {
3113 guint64 media_start = 0, seg_time = 0;
3114 QtDemuxStream *str = qtdemux->streams[n];
3116 /* aligning reference stream again might lead to backing up to yet another
3117 * keyframe (due to timestamp rounding issues),
3118 * potentially putting more load on downstream; so let's try to avoid */
3119 if (str == ref_str) {
3120 seg_idx = ref_seg_idx;
3121 seg = &str->segments[seg_idx];
3122 k_index = ref_k_index;
3123 GST_DEBUG_OBJECT (qtdemux, "reference stream segment %d, "
3124 "sample at index %d", ref_str->segment_index, k_index);
3126 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
3127 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
3129 /* segment not found, continue with normal flow */
3133 /* get segment and time in the segment */
3134 seg = &str->segments[seg_idx];
3135 seg_time = k_pos - seg->time;
3137 /* get the media time in the segment */
3138 media_start = seg->media_start + seg_time;
3140 /* get the index of the sample with media time */
3141 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
3142 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
3143 GST_TIME_ARGS (media_start), index);
3145 /* find previous keyframe */
3146 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index);
3149 /* Remember until where we want to go */
3150 str->to_sample = str->from_sample - 1;
3151 /* Define our time position */
3152 str->time_position =
3153 (gst_util_uint64_scale (str->samples[k_index].timestamp, GST_SECOND,
3154 str->timescale) - seg->media_start) + seg->time;
3155 /* Now seek back in time */
3156 gst_qtdemux_move_stream (qtdemux, str, k_index);
3157 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u, time position %"
3158 GST_TIME_FORMAT " playing from sample %u to %u", k_index,
3159 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
3165 return GST_FLOW_UNEXPECTED;
3168 /* activate the given segment number @seg_idx of @stream at time @offset.
3169 * @offset is an absolute global position over all the segments.
3171 * This will push out a NEWSEGMENT event with the right values and
3172 * position the stream index to the first decodable sample before
3176 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
3177 guint32 seg_idx, guint64 offset)
3180 QtDemuxSegment *segment;
3181 guint32 index, kf_index;
3183 guint64 start, stop, time;
3186 GST_LOG_OBJECT (qtdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,
3189 /* update the current segment */
3190 stream->segment_index = seg_idx;
3192 /* get the segment */
3193 segment = &stream->segments[seg_idx];
3195 if (G_UNLIKELY (offset < segment->time)) {
3196 GST_WARNING_OBJECT (qtdemux, "offset < segment->time %" G_GUINT64_FORMAT,
3201 /* segment lies beyond total indicated duration */
3202 if (G_UNLIKELY (qtdemux->segment.duration != -1 &&
3203 segment->time > qtdemux->segment.duration)) {
3204 GST_WARNING_OBJECT (qtdemux, "file duration %" G_GINT64_FORMAT
3205 " < segment->time %" G_GUINT64_FORMAT, qtdemux->segment.duration,
3210 /* get time in this segment */
3211 seg_time = offset - segment->time;
3213 GST_LOG_OBJECT (qtdemux, "seg_time %" GST_TIME_FORMAT,
3214 GST_TIME_ARGS (seg_time));
3216 if (G_UNLIKELY (seg_time > segment->duration)) {
3217 GST_LOG_OBJECT (qtdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,
3218 GST_TIME_ARGS (segment->duration));
3222 /* qtdemux->segment.stop is in outside-time-realm, whereas
3223 * segment->media_stop is in track-time-realm.
3225 * In order to compare the two, we need to bring segment.stop
3226 * into the track-time-realm */
3228 if (qtdemux->segment.stop == -1)
3229 stop = segment->media_stop;
3232 MIN (segment->media_stop,
3233 qtdemux->segment.stop - segment->time + segment->media_start);
3238 if (qtdemux->segment.rate >= 0) {
3239 start = MIN (segment->media_start + seg_time, stop);
3242 start = segment->media_start;
3243 stop = MIN (segment->media_start + seg_time, stop);
3244 time = segment->time;
3247 GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT
3248 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
3249 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
3251 GST_INFO("segment->rate:%f, qtdemux->segment.rate:%f", segment->rate, qtdemux->segment.rate);
3252 /* combine global rate with that of the segment */
3253 rate = segment->rate * qtdemux->segment.rate;
3255 /* update the segment values used for clipping */
3256 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
3257 gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
3260 /* now prepare and send the segment */
3262 event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
3264 gst_pad_push_event (stream->pad, event);
3265 /* assume we can send more data now */
3266 stream->last_ret = GST_FLOW_OK;
3267 /* clear to send tags on this pad now */
3268 gst_qtdemux_push_tags (qtdemux, stream);
3271 /* and move to the keyframe before the indicated media time of the
3273 if (qtdemux->segment.rate >= 0) {
3274 index = gst_qtdemux_find_index (qtdemux, stream, start);
3275 stream->to_sample = stream->n_samples;
3276 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3277 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
3278 GST_TIME_ARGS (stream->samples[index].timestamp));
3280 index = gst_qtdemux_find_index (qtdemux, stream, stop);
3281 #ifdef QTDEMUX_MODIFICATION
3282 stream->to_sample = 0; //changed from index to 0 for the new design
3284 stream->to_sample = index;
3287 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3288 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
3289 GST_TIME_ARGS (stream->samples[index].timestamp));
3292 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
3293 * encountered an error and printed a message so we return appropriately */
3297 /* we're at the right spot */
3298 if (index == stream->sample_index) {
3299 GST_DEBUG_OBJECT (qtdemux, "we are at the right index");
3303 #ifdef QTDEMUX_MODIFICATION
3304 /* Modification: Added trickplay functionality*/
3305 /* find keyframe of the target index */
3306 if (qtdemux->segment.rate > 1.0)
3308 kf_index = index;//gst_qtdemux_find_next_keyframe (qtdemux, stream, index);
3309 if(stream->subtype != FOURCC_vide)
3314 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
3317 /* indent does stupid stuff with stream->samples[].timestamp */
3319 /* if we move forwards, we don't have to go back to the previous
3320 * keyframe since we already sent that. We can also just jump to
3321 * the keyframe right before the target index if there is one. */
3322 if (index > stream->sample_index) {
3323 /* moving forwards check if we move past a keyframe */
3324 if (kf_index > stream->sample_index) {
3325 GST_DEBUG_OBJECT (qtdemux,
3326 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3327 GST_TIME_ARGS (gst_util_uint64_scale (
3328 stream->samples[kf_index].timestamp,
3329 GST_SECOND, stream->timescale)));
3330 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3332 GST_DEBUG_OBJECT (qtdemux,
3333 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT
3334 " already sent", kf_index,
3335 GST_TIME_ARGS (gst_util_uint64_scale (
3336 stream->samples[kf_index].timestamp,
3337 GST_SECOND, stream->timescale)));
3340 GST_DEBUG_OBJECT (qtdemux,
3341 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3342 GST_TIME_ARGS (gst_util_uint64_scale (
3343 stream->samples[kf_index].timestamp,
3344 GST_SECOND, stream->timescale)));
3345 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3353 #ifdef QTDEMUX_MODIFICATION
3354 /*Modification: Added function to update the sample in support of index-table generation*/
3356 // Added by Kishore for rate
3357 /* move to the next sample in @stream.
3359 * Moves to the next segment when needed.
3362 gst_qtdemux_update_sample (GstQTDemux * qtdemux, QtDemuxStream * stream, int index)
3364 QtDemuxSample *sample;
3365 QtDemuxSegment *segment;
3367 if (qtdemux->segment.rate>1.0)
3369 if (G_UNLIKELY (index > stream->to_sample)) {
3370 /* Mark the stream as EOS */
3371 GST_DEBUG_OBJECT (qtdemux, "reached max allowed sample %u, mark EOS",
3373 stream->time_position = -1;
3376 }else if (qtdemux->segment.rate<0)
3378 if (G_UNLIKELY (index < stream->to_sample)) {
3379 /* Mark the stream as EOS */
3380 GST_DEBUG_OBJECT (qtdemux, "reached max allowed sample %u, mark EOS",
3382 stream->time_position = -1;
3387 /* move to next sample */
3388 stream->sample_index = index;
3391 /* get current segment */
3392 segment = &stream->segments[stream->segment_index];
3394 /* reached the last sample, we need the next segment */
3395 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3398 /* get next sample */
3399 sample = &stream->samples[stream->sample_index];
3401 /* see if we are past the segment */
3402 if (G_UNLIKELY (sample->timestamp >= segment->media_stop))
3405 if (sample->timestamp >= segment->media_start) {
3406 /* inside the segment, update time_position, looks very familiar to
3407 * GStreamer segments, doesn't it? */
3408 stream->time_position =
3409 (sample->timestamp - segment->media_start) + segment->time;
3411 /* not yet in segment, time does not yet increment. This means
3412 * that we are still prerolling keyframes to the decoder so it can
3413 * decode the first sample of the segment. */
3414 stream->time_position = segment->time;
3418 last_stop = QTSAMPLE_DTS (stream,sample);
3421 last_stop = (last_stop - segment->media_start) + segment->time;
3423 /* Set last_stop with the keyframe timestamp we pushed of that stream */
3424 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, last_stop);
3425 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
3426 GST_TIME_ARGS (last_stop));
3430 /* move to the next segment */
3433 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
3435 if (stream->segment_index == stream->n_segments - 1) {
3436 /* are we at the end of the last segment, we're EOS */
3437 stream->time_position = -1;
3439 /* else we're only at the end of the current segment */
3440 stream->time_position = segment->stop_time;
3442 /* make sure we select a new segment */
3443 stream->segment_index = -1;
3448 /* prepare to get the current sample of @stream, getting essential values.
3450 * This function will also prepare and send the segment when needed.
3452 * Return FALSE if the stream is EOS.
3455 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
3456 QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
3457 guint64 * duration, gboolean * keyframe)
3459 QtDemuxSample *sample;
3460 guint64 time_position;
3462 #ifdef QTDEMUX_MODIFICATION
3463 guint32 index=0, kindex=0;
3467 g_return_val_if_fail (stream != NULL, FALSE);
3469 time_position = stream->time_position;
3470 if (G_UNLIKELY (time_position == -1))
3473 seg_idx = stream->segment_index;
3474 if (G_UNLIKELY (seg_idx == -1)) {
3475 /* find segment corresponding to time_position if we are looking
3477 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
3479 /* nothing found, we're really eos */
3484 /* different segment, activate it, sample_index will be set. */
3485 if (G_UNLIKELY (stream->segment_index != seg_idx))
3486 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
3488 #ifdef QTDEMUX_MODIFICATION
3489 /*Modification: Added Trickplay functionality*/
3491 if (qtdemux->segment.rate>1.0)
3493 /* ***********Kishore***********
3494 finding the number of frames & duration between I frame to I frame
3495 Skipping according to the rate
3497 if(qtdemux->segment.rate>=2.0 || stream->subtype == FOURCC_vide)
3500 GST_DEBUG_OBJECT (qtdemux, "current index:%d, next key index:%d", stream->sample_index, stream->next_kindex);
3501 if(!stream->next_kindex)
3503 stream->next_kindex = stream->prev_kindex = stream->sample_index;
3507 //stream->next_kindex = gst_qtdemux_find_next_keyframe (qtdemux, stream, stream->next_kindex+1);
3508 stream->next_kindex = gst_qtdemux_find_index_linear (qtdemux, stream, stream->next_kindex+1);
3509 if(stream->next_kindex<0)
3511 stream->prev_kindex=stream->next_kindex=0;
3514 if(qtdemux->segment.rate==4.0)
3515 usleep(1); //needs to be removed kishore
3516 GST_DEBUG_OBJECT (qtdemux, "finding next key index:current index:%d, next key index:%d", stream->sample_index, stream->next_kindex);
3517 stream->total_samples_bet_2_keyframes = stream->next_kindex-stream->prev_kindex;
3518 stream->avg_duration_bet_2_keyframes=(stream->samples[stream->next_kindex].timestamp-stream->samples[stream->prev_kindex].timestamp)/stream->total_samples_bet_2_keyframes;
3519 GST_DEBUG_OBJECT (qtdemux, "avg duration:%"GST_TIME_FORMAT, GST_TIME_ARGS(stream->avg_duration_bet_2_keyframes));
3520 stream->samples_to_show_bet_kframes = stream->total_samples_bet_2_keyframes/qtdemux->segment.rate;
3521 if(stream->samples_to_show_bet_kframes!=0)
3527 stream->discont = TRUE;
3532 if(!stream->samples_to_show_bet_kframes)
3534 gst_qtdemux_update_sample (qtdemux, stream, stream->next_kindex);
3535 stream->next_kindex=0;
3536 stream->samples_to_show_bet_kframes=0;
3537 stream->discont = TRUE;
3541 stream->samples_to_show_bet_kframes--;
3542 sample = &stream->samples[stream->prev_kindex];
3543 *timestamp = (sample->timestamp + sample->pts_offset) + (stream->sample_index-stream->prev_kindex)*qtdemux->segment.rate*stream->avg_duration_bet_2_keyframes;
3547 else if (qtdemux->segment.rate<0)
3549 /* ***********Kishore***********
3550 only I frames Displayed
3552 gdouble minusone = -1;
3553 sample = &stream->samples[stream->sample_index];
3555 if((time_position - (minusone *qtdemux->segment.rate)*sample->duration)>0)
3556 time_position -= (minusone *qtdemux->segment.rate)*sample->duration;
3560 GST_DEBUG_OBJECT (qtdemux, "time:%"GST_TIME_FORMAT,
3561 GST_TIME_ARGS(time_position));
3563 index = gst_qtdemux_find_index_linear (qtdemux, stream, time_position);
3566 kindex = gst_qtdemux_find_keyframe (qtdemux, stream, index);
3568 gst_qtdemux_update_sample (qtdemux, stream, kindex);
3570 stream->discont = TRUE;
3574 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
3575 stream->sample_index, stream->n_samples);
3577 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3580 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3581 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3582 stream->sample_index);
3586 /* now get the info for the sample we're at */
3587 sample = &stream->samples[stream->sample_index];
3589 #ifdef QTDEMUX_MODIFICATION
3590 /*Modification: Changed because timestamp is modified for 0<rate<64 for video/audio */
3592 *timestamp = QTSAMPLE_PTS (stream, sample);
3594 *timestamp = sample->timestamp + sample->pts_offset;
3597 *offset = sample->offset;
3598 *size = sample->size;
3599 *duration = QTSAMPLE_DUR_PTS (stream, sample, *timestamp);
3600 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
3607 stream->time_position = -1;
3613 #ifdef QTDEMUX_MODIFICATION
3614 /*Modification: Added function to get previous sample in support of index-table generation*/
3616 /* Added by Kishore */
3617 /* move to the previous sample in @stream.
3619 * Moves to the previous segment when needed.
3622 gst_qtdemux_previous_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
3624 QtDemuxSample *sample;
3625 QtDemuxSegment *segment;
3627 if (G_UNLIKELY (stream->sample_index <= stream->to_sample)) {
3628 /* Mark the stream as EOS */
3629 GST_DEBUG_OBJECT (qtdemux, "reached max allowed sample %u, mark EOS",
3631 stream->time_position = -1;
3634 /* move to next sample */
3635 if(stream->subtype == FOURCC_vide)
3636 stream->sample_index--;
3638 stream->sample_index-=10;
3640 /* get current segment */
3641 segment = &stream->segments[stream->segment_index];
3643 /* reached the last sample, we need the next segment */
3644 if (G_UNLIKELY (stream->sample_index < 0))
3647 /* get next sample */
3648 sample = &stream->samples[stream->sample_index];
3650 /* see if we are past the segment */
3651 if (G_UNLIKELY (sample->timestamp < 0))
3654 if (G_UNLIKELY (gst_util_uint64_scale (sample->timestamp,
3655 GST_SECOND, stream->timescale) >= segment->media_start))
3658 /* inside the segment, update time_position, looks very familiar to
3659 * GStreamer segments, doesn't it? */
3661 stream->time_position =
3662 (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3663 stream->timescale) - segment->media_start) + segment->time;
3668 /* not yet in segment, time does not yet increment. This means
3669 * that we are still prerolling keyframes to the decoder so it can
3670 * decode the first sample of the segment. */
3671 stream->time_position = segment->time;
3678 /* move to the next segment */
3681 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
3683 if (stream->segment_index == stream->n_segments - 1) {
3684 /* are we at the end of the last segment, we're EOS */
3685 stream->time_position = -1;
3687 /* else we're only at the end of the current segment */
3688 stream->time_position = segment->stop_time;
3690 /* make sure we select a new segment */
3691 stream->segment_index = -1;
3695 /* move to the next sample in @stream.
3697 * Moves to the next segment when needed.
3700 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
3702 QtDemuxSample *sample;
3703 QtDemuxSegment *segment;
3705 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
3706 /* Mark the stream as EOS */
3707 GST_DEBUG_OBJECT (qtdemux,
3708 "reached max allowed sample %u, mark EOS", stream->to_sample);
3709 stream->time_position = -1;
3713 /* move to next sample */
3714 stream->sample_index++;
3716 /* get current segment */
3717 segment = &stream->segments[stream->segment_index];
3719 /* reached the last sample, we need the next segment */
3720 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3723 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3724 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3725 stream->sample_index);
3729 /* get next sample */
3730 sample = &stream->samples[stream->sample_index];
3732 /* see if we are past the segment */
3733 if (G_UNLIKELY (gst_util_uint64_scale (sample->timestamp,
3734 GST_SECOND, stream->timescale) >= segment->media_stop))
3737 if (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3738 stream->timescale) >= segment->media_start) {
3739 /* inside the segment, update time_position, looks very familiar to
3740 * GStreamer segments, doesn't it? */
3741 stream->time_position =
3742 (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3743 stream->timescale) - segment->media_start) + segment->time;
3745 /* not yet in segment, time does not yet increment. This means
3746 * that we are still prerolling keyframes to the decoder so it can
3747 * decode the first sample of the segment. */
3748 stream->time_position = segment->time;
3752 /* move to the next segment */
3755 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
3757 if (stream->segment_index == stream->n_segments - 1) {
3758 /* are we at the end of the last segment, we're EOS */
3759 stream->time_position = -1;
3761 /* else we're only at the end of the current segment */
3762 stream->time_position = segment->stop_time;
3764 /* make sure we select a new segment */
3765 stream->segment_index = -1;
3770 gst_qtdemux_sync_streams (GstQTDemux * demux)
3774 if (demux->n_streams <= 1)
3777 for (i = 0; i < demux->n_streams; i++) {
3778 QtDemuxStream *stream;
3779 GstClockTime end_time;
3781 stream = demux->streams[i];
3786 /* TODO advance time on subtitle streams here, if any some day */
3788 /* some clips/trailers may have unbalanced streams at the end,
3789 * so send EOS on shorter stream to prevent stalling others */
3791 /* do not mess with EOS if SEGMENT seeking */
3792 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
3795 if (demux->pullbased) {
3796 /* loop mode is sample time based */
3797 if (stream->time_position != -1)
3800 /* push mode is byte position based */
3801 if (stream->n_samples &&
3802 stream->samples[stream->n_samples - 1].offset >= demux->offset)
3806 if (stream->sent_eos)
3809 /* only act if some gap */
3810 end_time = stream->segments[stream->n_segments - 1].stop_time;
3811 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
3812 ", stream end: %" GST_TIME_FORMAT,
3813 GST_TIME_ARGS (demux->segment.last_stop), GST_TIME_ARGS (end_time));
3814 if (end_time + 2 * GST_SECOND < demux->segment.last_stop) {
3815 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
3816 GST_PAD_NAME (stream->pad));
3817 stream->sent_eos = TRUE;
3818 gst_pad_push_event (stream->pad, gst_event_new_eos ());
3823 /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return:
3825 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
3826 * GST_FLOW_UNEXPECTED: when all pads UNEXPECTED or NOT_LINKED.
3828 static GstFlowReturn
3829 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
3833 gboolean unexpected = FALSE, not_linked = TRUE;
3835 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
3837 /* store the value */
3838 stream->last_ret = ret;
3840 /* any other error that is not-linked or eos can be returned right away */
3841 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3844 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
3845 for (i = 0; i < demux->n_streams; i++) {
3846 QtDemuxStream *ostream = demux->streams[i];
3848 ret = ostream->last_ret;
3850 /* no unexpected or unlinked, return */
3851 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3854 /* we check to see if we have at least 1 unexpected or all unlinked */
3855 unexpected |= (ret == GST_FLOW_UNEXPECTED);
3856 not_linked &= (ret == GST_FLOW_NOT_LINKED);
3859 /* when we get here, we all have unlinked or unexpected */
3861 ret = GST_FLOW_NOT_LINKED;
3862 else if (unexpected)
3863 ret = GST_FLOW_UNEXPECTED;
3865 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
3869 /* the input buffer metadata must be writable. Returns NULL when the buffer is
3870 * completely cliped */
3872 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3875 gint64 start, stop, cstart, cstop, diff;
3876 GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
3879 gint num_rate, denom_rate;
3883 data = GST_BUFFER_DATA (buf);
3884 size = GST_BUFFER_SIZE (buf);
3886 /* depending on the type, setup the clip parameters */
3887 if (stream->subtype == FOURCC_soun) {
3888 frame_size = stream->bytes_per_frame;
3889 num_rate = GST_SECOND;
3890 denom_rate = (gint) stream->rate;
3892 } else if (stream->subtype == FOURCC_vide) {
3894 num_rate = stream->fps_n;
3895 denom_rate = stream->fps_d;
3900 /* we can only clip if we have a valid timestamp */
3901 timestamp = GST_BUFFER_TIMESTAMP (buf);
3902 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
3905 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) {
3906 duration = GST_BUFFER_DURATION (buf);
3909 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
3913 stop = start + duration;
3915 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
3916 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
3919 /* see if some clipping happened */
3920 diff = cstart - start;
3926 /* bring clipped time to samples and to bytes */
3927 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3930 GST_DEBUG_OBJECT (qtdemux,
3931 "clipping start to %" GST_TIME_FORMAT " %"
3932 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
3938 diff = stop - cstop;
3943 /* bring clipped time to samples and then to bytes */
3944 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3946 GST_DEBUG_OBJECT (qtdemux,
3947 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
3948 " bytes", GST_TIME_ARGS (cstop), diff);
3953 GST_BUFFER_TIMESTAMP (buf) = timestamp;
3954 GST_BUFFER_DURATION (buf) = duration;
3955 GST_BUFFER_SIZE (buf) = size;
3956 GST_BUFFER_DATA (buf) = data;
3960 /* dropped buffer */
3963 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
3968 GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer");
3973 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
3974 gst_buffer_unref (buf);
3979 /* the input buffer metadata must be writable,
3980 * but time/duration etc not yet set and need not be preserved */
3982 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3986 guint size, nsize = 0;
3989 data = GST_BUFFER_DATA (buf);
3990 size = GST_BUFFER_SIZE (buf);
3992 /* not many cases for now */
3993 if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
3994 /* send a one time dvd clut event */
3995 if (stream->pending_event && stream->pad)
3996 gst_pad_push_event (stream->pad, stream->pending_event);
3997 stream->pending_event = NULL;
3998 /* no further processing needed */
3999 stream->need_process = FALSE;
4002 if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
4006 if (G_LIKELY (size >= 2)) {
4007 nsize = GST_READ_UINT16_BE (data);
4008 nsize = MIN (nsize, size - 2);
4011 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%d", nsize, size);
4013 /* takes care of UTF-8 validation or UTF-16 recognition,
4014 * no other encoding expected */
4015 str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
4017 gst_buffer_unref (buf);
4018 buf = gst_buffer_new ();
4019 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;
4020 GST_BUFFER_SIZE (buf) = strlen (str);
4022 /* may be 0-size subtitle, which is also sent to keep pipeline going */
4023 GST_BUFFER_DATA (buf) = data + 2;
4024 GST_BUFFER_SIZE (buf) = nsize;
4027 /* FIXME ? convert optional subsequent style info to markup */
4032 /* Sets a buffer's attributes properly and pushes it downstream.
4033 * Also checks for additional actions and custom processing that may
4034 * need to be done first.
4037 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
4038 QtDemuxStream * stream, GstBuffer * buf,
4039 guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
4040 guint64 byte_position)
4042 GstFlowReturn ret = GST_FLOW_OK;
4044 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
4047 url = g_strndup ((gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
4048 if (url != NULL && strlen (url) != 0) {
4049 /* we have RTSP redirect now */
4050 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4051 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
4052 gst_structure_new ("redirect",
4053 "new-location", G_TYPE_STRING, url, NULL)));
4054 qtdemux->posted_redirect = TRUE;
4056 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
4062 /* position reporting */
4063 if (qtdemux->segment.rate >= 0) {
4064 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, position);
4065 gst_qtdemux_sync_streams (qtdemux);
4068 if (G_UNLIKELY (!stream->pad)) {
4069 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
4070 gst_buffer_unref (buf);
4074 /* send out pending buffers */
4075 while (stream->buffers) {
4076 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
4078 if (G_UNLIKELY (stream->discont)) {
4079 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
4080 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
4081 stream->discont = FALSE;
4083 gst_buffer_set_caps (buffer, stream->caps);
4085 gst_pad_push (stream->pad, buffer);
4087 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
4090 /* we're going to modify the metadata */
4091 buf = gst_buffer_make_metadata_writable (buf);
4093 if (G_UNLIKELY (stream->need_process))
4094 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
4096 GST_BUFFER_TIMESTAMP (buf) = timestamp;
4097 GST_BUFFER_DURATION (buf) = duration;
4098 GST_BUFFER_OFFSET (buf) = -1;
4099 GST_BUFFER_OFFSET_END (buf) = -1;
4101 if (G_UNLIKELY (stream->padding)) {
4102 GST_BUFFER_DATA (buf) += stream->padding;
4103 GST_BUFFER_SIZE (buf) -= stream->padding;
4106 if (G_UNLIKELY (qtdemux->element_index)) {
4107 GstClockTime stream_time;
4110 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
4112 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
4113 GST_LOG_OBJECT (qtdemux,
4114 "adding association %" GST_TIME_FORMAT "-> %"
4115 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
4116 gst_index_add_association (qtdemux->element_index,
4118 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
4119 GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_TIME, stream_time,
4120 GST_FORMAT_BYTES, byte_position, NULL);
4124 if (stream->need_clip)
4125 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
4127 if (G_UNLIKELY (buf == NULL))
4130 if (G_UNLIKELY (stream->discont)) {
4131 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
4132 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
4133 stream->discont = FALSE;
4137 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
4139 gst_buffer_set_caps (buf, stream->caps);
4141 GST_LOG_OBJECT (qtdemux,
4142 "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
4143 GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
4144 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
4146 ret = gst_pad_push (stream->pad, buf);
4152 static GstFlowReturn
4153 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
4155 GstFlowReturn ret = GST_FLOW_OK;
4156 GstBuffer *buf = NULL;
4157 QtDemuxStream *stream;
4160 guint64 timestamp = GST_CLOCK_TIME_NONE;
4161 guint64 duration = 0;
4162 gboolean keyframe = FALSE;
4166 #ifdef QTDEMUX_MODIFICATION
4167 guint64 max_time = 0;
4170 gst_qtdemux_push_pending_newsegment (qtdemux);
4172 /* Figure out the next stream sample to output, min_time is expressed in
4173 * global time and runs over the edit list segments. */
4174 min_time = G_MAXUINT64;
4177 for (i = 0; i < qtdemux->n_streams; i++) {
4180 stream = qtdemux->streams[i];
4181 position = stream->time_position;
4182 #ifdef QTDEMUX_MODIFICATION
4183 if (qtdemux->segment.rate>0)
4186 /* position of -1 is EOS */
4187 if (position != -1 && position < min_time) {
4188 min_time = position;
4191 #ifdef QTDEMUX_MODIFICATION
4194 if (position != -1 && position > max_time) {
4195 max_time = position;
4202 if (G_UNLIKELY (index == -1)) {
4203 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
4207 #ifdef QTDEMUX_MODIFICATION
4208 if (qtdemux->segment.rate>0)
4211 /* check for segment end */
4212 if (G_UNLIKELY (qtdemux->segment.stop != -1
4213 && qtdemux->segment.stop < min_time)) {
4214 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
4216 #ifdef QTDEMUX_MODIFICATION
4221 if (G_UNLIKELY (qtdemux->segment.stop != -1
4222 && qtdemux->segment.stop < max_time)) {
4223 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
4229 stream = qtdemux->streams[index];
4231 /* fetch info for the current sample of this stream */
4232 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
4233 &size, ×tamp, &duration, &keyframe)))
4236 GST_LOG_OBJECT (qtdemux,
4237 "pushing from stream %d, offset %" G_GUINT64_FORMAT
4238 ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
4239 index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
4241 /* hmm, empty sample, skip and move to next sample */
4242 if (G_UNLIKELY (size <= 0))
4245 /* last pushed sample was out of boundary, goto next sample */
4246 if (G_UNLIKELY (stream->last_ret == GST_FLOW_UNEXPECTED))
4249 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
4252 ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
4253 if (G_UNLIKELY (ret != GST_FLOW_OK))
4256 #ifdef QTDEMUX_MODIFICATION
4257 if (qtdemux->segment.rate>0)
4259 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
4260 timestamp, duration, keyframe, min_time, offset);
4261 #ifdef QTDEMUX_MODIFICATION
4263 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
4264 timestamp, duration, keyframe, max_time, offset);
4267 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
4268 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
4269 * we have no more data for the pad to push */
4270 if (ret == GST_FLOW_UNEXPECTED)
4274 #ifdef QTDEMUX_MODIFICATION
4275 if(qtdemux->segment.rate<0)
4276 gst_qtdemux_previous_sample (qtdemux, stream);
4279 gst_qtdemux_advance_sample (qtdemux, stream);
4287 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
4288 ret = GST_FLOW_UNEXPECTED;
4293 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
4294 /* EOS will be raised if all are EOS */
4301 gst_qtdemux_loop (GstPad * pad)
4303 GstQTDemux *qtdemux;
4307 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
4309 cur_offset = qtdemux->offset;
4310 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
4311 cur_offset, qtdemux->state);
4313 switch (qtdemux->state) {
4314 case QTDEMUX_STATE_INITIAL:
4315 case QTDEMUX_STATE_HEADER:
4316 ret = gst_qtdemux_loop_state_header (qtdemux);
4318 case QTDEMUX_STATE_MOVIE:
4319 ret = gst_qtdemux_loop_state_movie (qtdemux);
4320 #ifndef QTDEMUX_MODIFICATION
4321 // not used now, Kishore
4322 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_UNEXPECTED) {
4323 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
4332 /* if something went wrong, pause */
4333 if (ret != GST_FLOW_OK)
4337 gst_object_unref (qtdemux);
4343 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4344 (NULL), ("streaming stopped, invalid state"));
4345 qtdemux->segment_running = FALSE;
4346 gst_pad_pause_task (pad);
4347 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4352 const gchar *reason = gst_flow_get_name (ret);
4354 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
4356 qtdemux->segment_running = FALSE;
4357 gst_pad_pause_task (pad);
4359 /* fatal errors need special actions */
4360 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
4362 if (ret == GST_FLOW_UNEXPECTED) {
4363 if (qtdemux->n_streams == 0) {
4364 /* we have no streams, post an error */
4365 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
4366 (_("This file contains no playable streams.")),
4367 ("no known streams found"));
4369 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4372 /* FIXME: I am not sure this is the right fix. If the sinks are
4373 * supposed to detect the segment is complete and accumulate
4374 * automatically, it does not seem to work here. Need more work */
4375 qtdemux->segment_running = TRUE;
4377 if ((stop = qtdemux->segment.stop) == -1)
4378 stop = qtdemux->segment.duration;
4380 if (qtdemux->segment.rate >= 0) {
4381 GST_INFO_OBJECT (qtdemux, "Sending segment done, at end of segment");
4382 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4383 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4384 GST_FORMAT_TIME, stop));
4386 /* For Reverse Playback */
4387 GST_INFO_OBJECT (qtdemux,
4388 "Sending segment done, at start of segment");
4389 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4390 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4391 GST_FORMAT_TIME, qtdemux->segment.start));
4394 GST_INFO_OBJECT (qtdemux, "Sending EOS at end of segment");
4395 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4398 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4399 (NULL), ("streaming stopped, reason %s", reason));
4400 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4410 * Returns the size of the first entry at the current offset.
4411 * If -1, there are none (which means EOS or empty file).
4414 next_entry_size (GstQTDemux * demux)
4416 QtDemuxStream *stream;
4419 guint64 smalloffs = (guint64) - 1;
4420 QtDemuxSample *sample;
4422 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
4425 for (i = 0; i < demux->n_streams; i++) {
4426 stream = demux->streams[i];
4428 if (stream->sample_index == -1)
4429 stream->sample_index = 0;
4431 if (stream->sample_index >= stream->n_samples) {
4432 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
4436 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
4437 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
4438 stream->sample_index);
4442 sample = &stream->samples[stream->sample_index];
4444 GST_LOG_OBJECT (demux,
4445 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4446 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
4447 sample->offset, sample->size);
4449 if (((smalloffs == -1)
4450 || (sample->offset < smalloffs)) && (sample->size)) {
4452 smalloffs = sample->offset;
4456 GST_LOG_OBJECT (demux,
4457 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
4458 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
4463 stream = demux->streams[smallidx];
4464 sample = &stream->samples[stream->sample_index];
4466 if (sample->offset >= demux->offset) {
4467 demux->todrop = sample->offset - demux->offset;
4468 return sample->size + demux->todrop;
4471 GST_DEBUG_OBJECT (demux,
4472 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
4477 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
4479 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
4481 gst_element_post_message (GST_ELEMENT_CAST (demux),
4482 gst_message_new_element (GST_OBJECT_CAST (demux),
4483 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
4487 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
4492 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
4495 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
4496 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
4497 GST_SEEK_TYPE_NONE, -1);
4499 res = gst_pad_push_event (demux->sinkpad, event);
4504 /* FIXME, unverified after edit list updates */
4505 static GstFlowReturn
4506 gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
4509 GstFlowReturn ret = GST_FLOW_OK;
4511 demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad));
4513 #ifdef QTDEMUX_MODIFICATION
4514 /*Modification: Added consideration of demuxer state */
4516 if (demux->file && demux->state == QTDEMUX_STATE_MOVIE) {
4517 if (fwrite (GST_BUFFER_DATA (inbuf), GST_BUFFER_SIZE (inbuf),
4518 1, demux->file) != 1) {
4519 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4520 (_("Could not write to temporary buffer file.")),
4521 ("File Write Failure: '%s'",
4523 return GST_FLOW_ERROR;
4525 demux->filesize += GST_BUFFER_SIZE (inbuf);
4527 gst_buffer_unref (inbuf);
4529 gst_adapter_push (demux->adapter, inbuf);
4532 gst_adapter_push (demux->adapter, inbuf);
4535 /* we never really mean to buffer that much */
4536 if (demux->neededbytes == -1)
4539 GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
4540 inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
4542 #ifdef QTDEMUX_MODIFICATION
4543 /*Modification: Added consideration of demuxer state and file size*/
4545 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes
4546 || (demux->file && demux->state == QTDEMUX_STATE_MOVIE &&
4547 demux->filesize >= demux->neededbytes))
4548 && ret == GST_FLOW_OK) {
4550 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
4551 (ret == GST_FLOW_OK)) {
4554 GST_DEBUG_OBJECT (demux,
4555 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
4556 demux->state, demux->neededbytes, demux->offset);
4558 switch (demux->state) {
4559 case QTDEMUX_STATE_INITIAL:{
4564 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4566 /* get fourcc/length, set neededbytes */
4567 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
4569 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
4570 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
4572 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4573 (_("This file is invalid and cannot be played.")),
4574 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
4575 GST_FOURCC_ARGS (fourcc)));
4576 ret = GST_FLOW_ERROR;
4579 if (fourcc == FOURCC_mdat) {
4580 if (demux->n_streams > 0) {
4581 /* we have the headers, start playback */
4582 demux->state = QTDEMUX_STATE_MOVIE;
4583 demux->neededbytes = next_entry_size (demux);
4584 demux->mdatleft = size;
4586 /* Only post, event on pads is done after newsegment */
4587 qtdemux_post_global_tags (demux);
4590 /* no headers yet, try to get them */
4593 guint64 old, target;
4596 old = demux->offset;
4597 target = old + size;
4599 /* try to jump over the atom with a seek */
4600 res = qtdemux_seek_offset (demux, target);
4603 GST_DEBUG_OBJECT (demux, "seek success");
4604 /* remember the offset fo the first mdat so we can seek back to it
4605 * after we have the headers */
4606 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
4607 demux->first_mdat = old;
4608 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
4611 /* seek worked, continue reading */
4612 demux->offset = target;
4613 demux->neededbytes = 16;
4614 demux->state = QTDEMUX_STATE_INITIAL;
4616 /* seek failed, need to buffer */
4617 demux->offset = old;
4618 GST_DEBUG_OBJECT (demux, "seek failed");
4619 /* there may be multiple mdat (or alike) buffers */
4621 if (demux->mdatbuffer)
4622 bs = GST_BUFFER_SIZE (demux->mdatbuffer);
4623 #ifdef QTDEMUX_MODIFICATION
4624 else if (demux->file)
4625 bs = demux->filesize;
4629 #ifdef QTDEMUX_MODIFICATION
4630 if (size + bs > demux->maxbuffersize)
4632 if (size + bs > 10 * (1 << 20))
4635 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
4636 demux->neededbytes = size;
4637 #ifdef QTDEMUX_MODIFICATION
4638 if ((demux->filename && demux->file == NULL) || !demux->mdatbuffer)
4640 if (!demux->mdatbuffer)
4642 demux->mdatoffset = demux->offset;
4645 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
4646 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4647 (_("This file is invalid and cannot be played.")),
4648 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
4649 GST_FOURCC_ARGS (fourcc), size));
4650 ret = GST_FLOW_ERROR;
4653 /* this means we already started buffering and still no moov header,
4654 * let's continue buffering everything till we get moov */
4655 #ifdef QTDEMUX_MODIFICATION
4656 if ((demux->mdatbuffer || demux->file) && (fourcc != FOURCC_moov))
4658 if (demux->mdatbuffer && (fourcc != FOURCC_moov))
4661 demux->neededbytes = size;
4662 demux->state = QTDEMUX_STATE_HEADER;
4666 case QTDEMUX_STATE_HEADER:{
4670 GST_DEBUG_OBJECT (demux, "In header");
4672 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4674 /* parse the header */
4675 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
4677 if (fourcc == FOURCC_moov) {
4678 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
4680 demux->got_moov = TRUE;
4682 /* prepare newsegment to send when streaming actually starts */
4683 if (!demux->pending_newsegment) {
4684 demux->pending_newsegment =
4685 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
4686 0, GST_CLOCK_TIME_NONE, 0);
4689 qtdemux_parse_moov (demux, data, demux->neededbytes);
4690 qtdemux_node_dump (demux, demux->moov_node);
4691 qtdemux_parse_tree (demux);
4692 qtdemux_expose_streams (demux);
4694 g_node_destroy (demux->moov_node);
4695 demux->moov_node = NULL;
4696 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
4697 } else if (fourcc == FOURCC_moof) {
4698 if (demux->got_moov && demux->fragmented) {
4699 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
4700 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
4701 demux->offset, NULL)) {
4702 ret = GST_FLOW_ERROR;
4706 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
4708 } else if (fourcc == FOURCC_ftyp) {
4709 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
4710 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
4711 } else if (fourcc == FOURCC_uuid) {
4712 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
4713 qtdemux_parse_uuid (demux, data, demux->neededbytes);
4715 GST_WARNING_OBJECT (demux,
4716 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
4717 GST_FOURCC_ARGS (fourcc));
4718 /* Let's jump that one and go back to initial state */
4721 #ifdef QTDEMUX_MODIFICATION
4722 if ((demux->mdatbuffer || demux->file) && demux->n_streams) {
4724 if (demux->mdatbuffer && demux->n_streams) {
4728 /* the mdat was before the header */
4729 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
4730 demux->n_streams, demux->mdatbuffer);
4731 /* restore our adapter/offset view of things with upstream;
4732 * put preceding buffered data ahead of current moov data.
4733 * This should also handle evil mdat, moov, mdat cases and alike */
4734 #ifdef QTDEMUX_MODIFICATION
4735 if (demux->file == NULL) {
4737 buf = gst_adapter_take_buffer (demux->adapter,
4738 gst_adapter_available (demux->adapter));
4739 gst_adapter_clear (demux->adapter);
4740 demux->mdatbuffer = NULL;
4741 #ifdef QTDEMUX_MODIFICATION
4743 buf = gst_adapter_take_buffer (demux->adapter,
4744 gst_adapter_available (demux->adapter));
4745 gst_adapter_clear (demux->adapter);
4746 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4747 1, demux->file) != 1) {
4748 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4749 (_("Could not write to temporary buffer file.")),
4750 ("File Write Failure: '%s'",
4752 ret = GST_FLOW_ERROR;
4755 demux->filesize += GST_BUFFER_SIZE (buf);
4757 gst_buffer_unref (buf);
4760 demux->offset = demux->mdatoffset;
4761 demux->neededbytes = next_entry_size (demux);
4762 demux->state = QTDEMUX_STATE_MOVIE;
4763 demux->mdatleft = gst_adapter_available (demux->adapter);
4765 /* Only post, event on pads is done after newsegment */
4766 qtdemux_post_global_tags (demux);
4768 #ifdef QTDEMUX_MODIFICATION
4770 demux->ofile = fopen (demux->filename, "rb");
4771 if (demux->ofile == NULL) {
4772 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4773 (_("Could not open temporary buffer file.")),
4774 ("File Open Failure %d: '%s'", __LINE__,
4776 ret = GST_FLOW_ERROR;
4783 GST_DEBUG_OBJECT (demux, "Carrying on normally");
4784 gst_adapter_flush (demux->adapter, demux->neededbytes);
4786 if (demux->got_moov && demux->first_mdat != -1) {
4789 /* we need to seek back */
4790 res = qtdemux_seek_offset (demux, demux->first_mdat);
4792 demux->offset = demux->first_mdat;
4794 GST_DEBUG_OBJECT (demux, "Seek back failed");
4797 demux->offset += demux->neededbytes;
4799 demux->neededbytes = 16;
4800 demux->state = QTDEMUX_STATE_INITIAL;
4805 case QTDEMUX_STATE_BUFFER_MDAT:{
4808 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
4810 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4811 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
4812 GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4)));
4814 #ifdef QTDEMUX_MODIFICATION
4815 if (demux->filename == NULL) {
4817 if (demux->mdatbuffer)
4818 demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
4820 demux->mdatbuffer = buf;
4821 #ifdef QTDEMUX_MODIFICATION
4826 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, demux->file) != 1) {
4827 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4828 (_("Could not write to temporary buffer file.")),
4829 ("File Write Failure: '%s'",
4831 ret = GST_FLOW_ERROR;
4834 demux->filesize += GST_BUFFER_SIZE (buf);
4836 gst_buffer_unref (buf);
4838 demux->file = fopen (demux->filename, "wb");
4839 if (demux->file == NULL) {
4840 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4841 (_("Could not create temporary buffer file.")),
4842 ("File Open Failure: '%s'",
4844 ret = GST_FLOW_ERROR;
4852 demux->offset += demux->neededbytes;
4853 demux->neededbytes = 16;
4854 demux->state = QTDEMUX_STATE_INITIAL;
4855 gst_qtdemux_post_progress (demux, 1, 1);
4859 case QTDEMUX_STATE_MOVIE:{
4861 QtDemuxStream *stream = NULL;
4862 QtDemuxSample *sample;
4864 guint64 timestamp, duration, position;
4867 GST_DEBUG_OBJECT (demux,
4868 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
4870 if (demux->fragmented) {
4871 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
4873 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
4874 /* if needed data starts within this atom,
4875 * then it should not exceed this atom */
4876 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
4877 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4878 (_("This file is invalid and cannot be played.")),
4879 ("sample data crosses atom boundary"));
4880 ret = GST_FLOW_ERROR;
4883 demux->mdatleft -= demux->neededbytes;
4885 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
4886 /* so we are dropping more than left in this atom */
4887 demux->todrop -= demux->mdatleft;
4888 demux->neededbytes -= demux->mdatleft;
4889 demux->mdatleft = 0;
4890 /* need to resume atom parsing so we do not miss any other pieces */
4891 demux->state = QTDEMUX_STATE_INITIAL;
4892 demux->neededbytes = 16;
4897 if (demux->todrop) {
4898 GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);
4899 #ifdef QTDEMUX_MODIFICATION
4900 if (demux->file == NULL)
4902 gst_adapter_flush (demux->adapter, demux->todrop);
4903 #ifdef QTDEMUX_MODIFICATION
4905 fseek (demux->ofile, (long) demux->todrop, SEEK_CUR);
4906 demux->filesize -= demux->todrop;
4910 demux->neededbytes -= demux->todrop;
4911 demux->offset += demux->todrop;
4915 /* initial newsegment sent here after having added pads,
4916 * possible others in sink_event */
4917 if (G_UNLIKELY (demux->pending_newsegment)) {
4918 gst_qtdemux_push_event (demux, demux->pending_newsegment);
4919 demux->pending_newsegment = NULL;
4920 /* clear to send tags on all streams */
4921 for (i = 0; i < demux->n_streams; i++) {
4922 gst_qtdemux_push_tags (demux, demux->streams[i]);
4926 /* Figure out which stream this is packet belongs to */
4927 for (i = 0; i < demux->n_streams; i++) {
4928 stream = demux->streams[i];
4929 if (stream->sample_index >= stream->n_samples)
4931 GST_LOG_OBJECT (demux,
4932 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4933 " / size:%d)", i, stream->sample_index,
4934 stream->samples[stream->sample_index].offset,
4935 stream->samples[stream->sample_index].size);
4937 if (stream->samples[stream->sample_index].offset == demux->offset)
4941 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
4942 goto unknown_stream;
4944 /* Put data in a buffer, set timestamps, caps, ... */
4945 #ifdef QTDEMUX_MODIFICATION
4946 if (demux->file == NULL) {
4947 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4948 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4951 outbuf = gst_buffer_new_and_alloc (demux->neededbytes);
4952 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4954 if (fread (GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf),
4955 1, demux->ofile) != 1) {
4956 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4957 (_("Could not read from temporary buffer file.")),
4958 ("File Read Failure: '%s'",
4960 ret = GST_FLOW_ERROR;
4963 demux->filesize -= demux->neededbytes;
4966 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4967 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
4968 GST_FOURCC_ARGS (stream->fourcc));
4970 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4973 sample = &stream->samples[stream->sample_index];
4975 position = QTSAMPLE_DTS (stream, sample);
4976 timestamp = QTSAMPLE_PTS (stream, sample);
4977 duration = QTSAMPLE_DUR_DTS (stream, sample, position);
4978 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
4980 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
4981 timestamp, duration, keyframe, position, demux->offset);
4984 ret = gst_qtdemux_combine_flows (demux, stream, ret);
4986 stream->sample_index++;
4988 /* update current offset and figure out size of next buffer */
4989 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
4990 demux->offset, demux->neededbytes);
4991 demux->offset += demux->neededbytes;
4992 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
4995 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
4996 if (demux->fragmented) {
4997 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
4998 /* there may be more to follow, only finish this atom */
4999 demux->todrop = demux->mdatleft;
5000 demux->neededbytes = demux->todrop;
5012 /* when buffering movie data, at least show user something is happening */
5013 #ifdef QTDEMUX_MODIFICATION
5014 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT) {
5018 available = demux->filesize;
5020 available = gst_adapter_available (demux->adapter);
5022 if (available <= demux->neededbytes) {
5023 gst_qtdemux_post_progress (demux, available,
5024 demux->neededbytes);
5028 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
5029 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
5030 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
5031 demux->neededbytes);
5036 gst_object_unref (demux);
5043 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
5044 ret = GST_FLOW_ERROR;
5049 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
5050 ret = GST_FLOW_UNEXPECTED;
5055 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
5056 (NULL), ("qtdemuxer invalid state %d", demux->state));
5057 ret = GST_FLOW_ERROR;
5062 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
5063 (NULL), ("no 'moov' atom within the first 10 MB"));
5064 ret = GST_FLOW_ERROR;
5070 qtdemux_sink_activate (GstPad * sinkpad)
5072 if (gst_pad_check_pull_range (sinkpad))
5073 return gst_pad_activate_pull (sinkpad, TRUE);
5075 return gst_pad_activate_push (sinkpad, TRUE);
5079 qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
5081 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
5084 demux->pullbased = TRUE;
5085 demux->segment_running = TRUE;
5086 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
5089 demux->segment_running = FALSE;
5090 return gst_pad_stop_task (sinkpad);
5095 qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
5097 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
5099 demux->pullbased = FALSE;
5106 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size)
5108 return g_malloc (items * size);
5112 qtdemux_zfree (void *opaque, void *addr)
5118 qtdemux_inflate (void *z_buffer, guint z_length, guint length)
5124 z = g_new0 (z_stream, 1);
5125 z->zalloc = qtdemux_zalloc;
5126 z->zfree = qtdemux_zfree;
5129 z->next_in = z_buffer;
5130 z->avail_in = z_length;
5132 buffer = (guint8 *) g_malloc (length);
5133 ret = inflateInit (z);
5134 while (z->avail_in > 0) {
5135 if (z->avail_out == 0) {
5137 buffer = (guint8 *) g_realloc (buffer, length);
5138 z->next_out = buffer + z->total_out;
5139 z->avail_out = 1024;
5141 ret = inflate (z, Z_SYNC_FLUSH);
5145 if (ret != Z_STREAM_END) {
5146 g_warning ("inflate() returned %d", ret);
5152 #endif /* HAVE_ZLIB */
5155 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
5159 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
5161 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
5162 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
5164 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
5170 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
5171 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
5172 if (dcom == NULL || cmvd == NULL)
5173 goto invalid_compression;
5175 method = QT_FOURCC ((guint8 *) dcom->data + 8);
5178 case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{
5179 guint uncompressed_length;
5180 guint compressed_length;
5183 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
5184 compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12;
5185 GST_LOG ("length = %u", uncompressed_length);
5188 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
5189 compressed_length, uncompressed_length);
5191 qtdemux->moov_node_compressed = qtdemux->moov_node;
5192 qtdemux->moov_node = g_node_new (buf);
5194 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
5195 uncompressed_length);
5198 #endif /* HAVE_ZLIB */
5200 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
5201 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
5208 invalid_compression:
5210 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
5216 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
5219 while (G_UNLIKELY (buf < end)) {
5223 if (G_UNLIKELY (buf + 4 > end)) {
5224 GST_LOG_OBJECT (qtdemux, "buffer overrun");
5227 len = QT_UINT32 (buf);
5228 if (G_UNLIKELY (len == 0)) {
5229 GST_LOG_OBJECT (qtdemux, "empty container");
5232 if (G_UNLIKELY (len < 8)) {
5233 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
5236 if (G_UNLIKELY (len > (end - buf))) {
5237 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
5238 (gint) (end - buf));
5242 child = g_node_new ((guint8 *) buf);
5243 g_node_append (node, child);
5244 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
5245 qtdemux_parse_node (qtdemux, child, buf, len);
5253 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
5256 int len = QT_UINT32 (xdxt->data);
5257 guint8 *buf = xdxt->data;
5258 guint8 *end = buf + len;
5261 /* skip size and type */
5269 size = QT_UINT32 (buf);
5270 type = QT_FOURCC (buf + 4);
5272 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
5274 if (buf + size > end || size <= 0)
5280 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
5281 GST_FOURCC_ARGS (type));
5285 buffer = gst_buffer_new_and_alloc (size);
5286 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5287 stream->buffers = g_slist_append (stream->buffers, buffer);
5288 GST_LOG_OBJECT (qtdemux, "parsing theora header");
5291 buffer = gst_buffer_new_and_alloc (size);
5292 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5293 stream->buffers = g_slist_append (stream->buffers, buffer);
5294 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
5297 buffer = gst_buffer_new_and_alloc (size);
5298 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5299 stream->buffers = g_slist_append (stream->buffers, buffer);
5300 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
5303 GST_WARNING_OBJECT (qtdemux,
5304 "unknown theora cookie %" GST_FOURCC_FORMAT,
5305 GST_FOURCC_ARGS (type));
5314 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
5318 guint32 node_length = 0;
5319 const QtNodeType *type;
5322 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
5324 if (G_UNLIKELY (length < 8))
5325 goto not_enough_data;
5327 node_length = QT_UINT32 (buffer);
5328 fourcc = QT_FOURCC (buffer + 4);
5330 /* ignore empty nodes */
5331 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
5334 type = qtdemux_type_get (fourcc);
5336 end = buffer + length;
5338 GST_LOG_OBJECT (qtdemux,
5339 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
5340 GST_FOURCC_ARGS (fourcc), node_length, type->name);
5342 if (node_length > length)
5343 goto broken_atom_size;
5345 if (type->flags & QT_FLAG_CONTAINER) {
5346 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
5351 if (node_length < 20) {
5352 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
5355 GST_DEBUG_OBJECT (qtdemux,
5356 "parsing stsd (sample table, sample description) atom");
5357 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
5367 /* also read alac (or whatever) in stead of mp4a in the following,
5368 * since a similar layout is used in other cases as well */
5369 if (fourcc == FOURCC_mp4a)
5374 /* There are two things we might encounter here: a true mp4a atom, and
5375 an mp4a entry in an stsd atom. The latter is what we're interested
5376 in, and it looks like an atom, but isn't really one. The true mp4a
5377 atom is short, so we detect it based on length here. */
5378 if (length < min_size) {
5379 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
5380 GST_FOURCC_ARGS (fourcc));
5384 /* 'version' here is the sound sample description version. Types 0 and
5385 1 are documented in the QTFF reference, but type 2 is not: it's
5386 described in Apple header files instead (struct SoundDescriptionV2
5388 version = QT_UINT16 (buffer + 16);
5390 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
5391 GST_FOURCC_ARGS (fourcc), version);
5393 /* parse any esds descriptors */
5405 GST_WARNING_OBJECT (qtdemux,
5406 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
5407 GST_FOURCC_ARGS (fourcc), version);
5412 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5424 GST_DEBUG_OBJECT (qtdemux, "parsing in mp4v");
5425 version = QT_UINT32 (buffer + 16);
5426 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
5427 if (1 || version == 0x00000000) {
5428 buf = buffer + 0x32;
5430 /* FIXME Quicktime uses PASCAL string while
5431 * the iso format uses C strings. Check the file
5432 * type before attempting to parse the string here. */
5433 tlen = QT_UINT8 (buf);
5434 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen);
5436 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf);
5437 /* the string has a reserved space of 32 bytes so skip
5438 * the remaining 31 */
5440 buf += 4; /* and 4 bytes reserved */
5442 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf);
5444 qtdemux_parse_container (qtdemux, node, buf, end);
5450 GST_MEMDUMP_OBJECT (qtdemux, "avc1", buffer, end - buffer);
5451 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
5456 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
5461 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
5462 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
5470 version = QT_UINT32 (buffer + 12);
5471 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
5478 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
5483 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5488 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
5492 if (!strcmp (type->name, "unknown"))
5493 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
5497 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
5498 GST_FOURCC_ARGS (fourcc));
5504 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5505 (_("This file is corrupt and cannot be played.")),
5506 ("Not enough data for an atom header, got only %u bytes", length));
5511 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5512 (_("This file is corrupt and cannot be played.")),
5513 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
5514 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
5521 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
5525 guint32 child_fourcc;
5527 for (child = g_node_first_child (node); child;
5528 child = g_node_next_sibling (child)) {
5529 buffer = (guint8 *) child->data;
5531 child_fourcc = QT_FOURCC (buffer + 4);
5533 if (G_UNLIKELY (child_fourcc == fourcc)) {
5541 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
5542 GstByteReader * parser)
5546 guint32 child_fourcc, child_len;
5548 for (child = g_node_first_child (node); child;
5549 child = g_node_next_sibling (child)) {
5550 buffer = (guint8 *) child->data;
5552 child_len = QT_UINT32 (buffer);
5553 child_fourcc = QT_FOURCC (buffer + 4);
5555 if (G_UNLIKELY (child_fourcc == fourcc)) {
5556 if (G_UNLIKELY (child_len < (4 + 4)))
5558 /* FIXME: must verify if atom length < parent atom length */
5559 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5567 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
5568 GstByteReader * parser)
5572 guint32 child_fourcc, child_len;
5574 for (child = g_node_next_sibling (node); child;
5575 child = g_node_next_sibling (child)) {
5576 buffer = (guint8 *) child->data;
5578 child_fourcc = QT_FOURCC (buffer + 4);
5580 if (child_fourcc == fourcc) {
5582 child_len = QT_UINT32 (buffer);
5583 if (G_UNLIKELY (child_len < (4 + 4)))
5585 /* FIXME: must verify if atom length < parent atom length */
5586 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5595 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
5597 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
5601 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
5602 QtDemuxStream * stream, GstTagList * list)
5604 /* consistent default for push based mode */
5605 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
5606 gst_segment_set_newsegment (&stream->segment, FALSE, 1.0, GST_FORMAT_TIME,
5607 0, GST_CLOCK_TIME_NONE, 0);
5609 if (stream->subtype == FOURCC_vide) {
5610 gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
5613 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
5616 /* fps is calculated base on the duration of the first frames since
5617 * qt does not have a fixed framerate. */
5618 if ((stream->n_samples == 1) && (stream->min_duration == 0)) {
5623 stream->fps_n = stream->timescale;
5624 if (stream->min_duration == 0)
5627 stream->fps_d = stream->min_duration;
5632 gint depth, palette_count;
5633 const guint32 *palette_data = NULL;
5635 gst_caps_set_simple (stream->caps,
5636 "width", G_TYPE_INT, stream->width,
5637 "height", G_TYPE_INT, stream->height,
5638 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
5640 /* calculate pixel-aspect-ratio using display width and height */
5641 GST_DEBUG_OBJECT (qtdemux,
5642 "video size %dx%d, target display size %dx%d", stream->width,
5643 stream->height, stream->display_width, stream->display_height);
5645 if (stream->display_width > 0 && stream->display_height > 0 &&
5646 stream->width > 0 && stream->height > 0) {
5649 /* calculate the pixel aspect ratio using the display and pixel w/h */
5650 n = stream->display_width * stream->height;
5651 d = stream->display_height * stream->width;
5654 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
5655 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5656 GST_TYPE_FRACTION, n, d, NULL);
5659 /* qt file might have pasp atom */
5660 if (stream->par_w > 0 && stream->par_h > 0) {
5661 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
5662 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5663 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
5666 depth = stream->bits_per_sample;
5668 /* more than 32 bits means grayscale */
5669 gray = (depth > 32);
5670 /* low 32 bits specify the depth */
5673 /* different number of palette entries is determined by depth. */
5675 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
5676 palette_count = (1 << depth);
5678 switch (palette_count) {
5682 palette_data = ff_qt_default_palette_2;
5685 palette_data = ff_qt_default_palette_4;
5689 palette_data = ff_qt_grayscale_palette_16;
5691 palette_data = ff_qt_default_palette_16;
5695 palette_data = ff_qt_grayscale_palette_256;
5697 palette_data = ff_qt_default_palette_256;
5700 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5701 (_("The video in this file might not play correctly.")),
5702 ("unsupported palette depth %d", depth));
5708 /* make sure it's not writable. We leave MALLOCDATA to NULL so that we
5709 * don't free any of the buffer data. */
5710 palette = gst_buffer_new ();
5711 GST_BUFFER_FLAG_SET (palette, GST_BUFFER_FLAG_READONLY);
5712 GST_BUFFER_DATA (palette) = (guint8 *) palette_data;
5713 GST_BUFFER_SIZE (palette) = sizeof (guint32) * palette_count;
5715 gst_caps_set_simple (stream->caps, "palette_data",
5716 GST_TYPE_BUFFER, palette, NULL);
5717 gst_buffer_unref (palette);
5718 } else if (palette_count != 0) {
5719 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
5720 (NULL), ("Unsupported palette depth %d. Ignoring stream.", depth));
5722 gst_object_unref (stream->pad);
5726 qtdemux->n_video_streams++;
5727 } else if (stream->subtype == FOURCC_soun) {
5728 gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
5731 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
5734 gst_caps_set_simple (stream->caps,
5735 "rate", G_TYPE_INT, (int) stream->rate,
5736 "channels", G_TYPE_INT, stream->n_channels, NULL);
5738 qtdemux->n_audio_streams++;
5739 } else if (stream->subtype == FOURCC_strm) {
5740 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
5741 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
5742 gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams);
5745 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
5747 qtdemux->n_sub_streams++;
5749 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5754 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
5756 gst_pad_use_fixed_caps (stream->pad);
5757 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
5758 gst_pad_set_query_type_function (stream->pad,
5759 gst_qtdemux_get_src_query_types);
5760 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
5762 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
5763 gst_pad_set_caps (stream->pad, stream->caps);
5765 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
5766 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
5767 gst_pad_set_active (stream->pad, TRUE);
5768 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
5769 if (stream->pending_tags)
5770 gst_tag_list_free (stream->pending_tags);
5771 stream->pending_tags = list;
5773 /* post now, send event on pad later */
5774 GST_DEBUG_OBJECT (qtdemux, "Posting tags %" GST_PTR_FORMAT, list);
5775 gst_element_post_message (GST_ELEMENT (qtdemux),
5776 gst_message_new_tag_full (GST_OBJECT (qtdemux), stream->pad,
5777 gst_tag_list_copy (list)));
5779 /* global tags go on each pad anyway */
5780 stream->send_global_tags = TRUE;
5786 /* find next atom with @fourcc starting at @offset */
5787 static GstFlowReturn
5788 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
5789 guint64 * length, guint32 fourcc)
5795 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
5796 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
5799 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
5800 if (G_UNLIKELY (ret != GST_FLOW_OK))
5802 if (G_LIKELY (GST_BUFFER_SIZE (buf) != 16)) {
5804 ret = GST_FLOW_UNEXPECTED;
5805 gst_buffer_unref (buf);
5808 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), 16, length,
5810 gst_buffer_unref (buf);
5812 if (G_UNLIKELY (*length == 0)) {
5813 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
5814 ret = GST_FLOW_ERROR;
5818 if (lfourcc == fourcc) {
5819 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
5823 GST_LOG_OBJECT (qtdemux,
5824 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5825 GST_FOURCC_ARGS (fourcc), *offset);
5834 /* might simply have had last one */
5835 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
5840 /* should only do something in pull mode */
5841 /* call with OBJECT lock */
5842 static GstFlowReturn
5843 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
5845 guint64 length, offset;
5846 GstBuffer *buf = NULL;
5847 GstFlowReturn ret = GST_FLOW_OK;
5848 GstFlowReturn res = TRUE;
5850 offset = qtdemux->moof_offset;
5851 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
5854 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5855 return GST_FLOW_UNEXPECTED;
5858 /* best not do pull etc with lock held */
5859 GST_OBJECT_UNLOCK (qtdemux);
5861 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5862 if (ret != GST_FLOW_OK)
5865 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
5866 if (G_UNLIKELY (ret != GST_FLOW_OK))
5868 if (!qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf),
5869 GST_BUFFER_SIZE (buf), offset, NULL)) {
5870 gst_buffer_unref (buf);
5875 gst_buffer_unref (buf);
5879 /* look for next moof */
5880 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5881 if (G_UNLIKELY (ret != GST_FLOW_OK))
5885 GST_OBJECT_LOCK (qtdemux);
5887 qtdemux->moof_offset = offset;
5893 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
5895 res = GST_FLOW_ERROR;
5900 /* maybe upstream temporarily flushing */
5901 if (ret != GST_FLOW_WRONG_STATE) {
5902 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5905 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
5906 /* resume at current position next time */
5913 /* initialise bytereaders for stbl sub-atoms */
5915 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
5917 stream->stbl_index = -1; /* no samples have yet been parsed */
5919 /* time-to-sample atom */
5920 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
5923 /* copy atom data into a new buffer for later use */
5924 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
5926 /* skip version + flags */
5927 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
5928 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
5930 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
5932 /* make sure there's enough data */
5933 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 2 * 4))
5936 /* sync sample atom */
5937 stream->stps_present = FALSE;
5938 if ((stream->stss_present =
5939 !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
5940 &stream->stss) ? TRUE : FALSE) == TRUE) {
5941 /* copy atom data into a new buffer for later use */
5942 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
5944 /* skip version + flags */
5945 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
5946 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
5949 if (stream->n_sample_syncs) {
5950 /* make sure there's enough data */
5951 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
5955 /* partial sync sample atom */
5956 if ((stream->stps_present =
5957 !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
5958 &stream->stps) ? TRUE : FALSE) == TRUE) {
5959 /* copy atom data into a new buffer for later use */
5960 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
5962 /* skip version + flags */
5963 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
5964 !gst_byte_reader_get_uint32_be (&stream->stps,
5965 &stream->n_sample_partial_syncs))
5968 /* if there are no entries, the stss table contains the real
5970 if (stream->n_sample_partial_syncs) {
5971 /* make sure there's enough data */
5972 if (!qt_atom_parser_has_chunks (&stream->stps,
5973 stream->n_sample_partial_syncs, 4))
5980 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
5983 /* copy atom data into a new buffer for later use */
5984 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
5986 /* skip version + flags */
5987 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
5988 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
5991 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
5994 if (!stream->n_samples)
5997 /* sample-to-chunk atom */
5998 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
6001 /* copy atom data into a new buffer for later use */
6002 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
6004 /* skip version + flags */
6005 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
6006 !gst_byte_reader_get_uint32_be (&stream->stsc,
6007 &stream->n_samples_per_chunk))
6010 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
6011 stream->n_samples_per_chunk);
6013 /* make sure there's enough data */
6014 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
6020 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
6021 stream->co_size = sizeof (guint32);
6022 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
6024 stream->co_size = sizeof (guint64);
6028 /* copy atom data into a new buffer for later use */
6029 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
6031 /* skip version + flags */
6032 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
6035 /* chunks_are_chunks == 0 means treat chunks as samples */
6036 stream->chunks_are_chunks = !stream->sample_size || stream->sampled;
6037 if (stream->chunks_are_chunks) {
6038 /* skip number of entries */
6039 if (!gst_byte_reader_skip (&stream->stco, 4))
6042 /* make sure there are enough data in the stsz atom */
6043 if (!stream->sample_size) {
6044 /* different sizes for each sample */
6045 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
6049 /* treat chunks as samples */
6050 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
6054 GST_DEBUG_OBJECT (qtdemux,
6055 "allocating n_samples %u * %" G_GSIZE_FORMAT " = (%u MB)",
6056 stream->n_samples, sizeof (QtDemuxSample),
6057 (guint) (stream->n_samples * sizeof (QtDemuxSample)) >> 20);
6059 if (stream->n_samples >=
6060 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
6061 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
6062 "be larger than %uMB (broken file?)", stream->n_samples,
6063 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
6067 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
6068 if (!stream->samples) {
6069 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
6075 /* composition time-to-sample */
6076 if ((stream->ctts_present =
6077 !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
6078 &stream->ctts) ? TRUE : FALSE) == TRUE) {
6079 /* copy atom data into a new buffer for later use */
6080 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
6082 /* skip version + flags */
6083 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
6084 || !gst_byte_reader_get_uint32_be (&stream->ctts,
6085 &stream->n_composition_times))
6088 /* make sure there's enough data */
6089 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
6098 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6099 (_("This file is corrupt and cannot be played.")), (NULL));
6104 gst_qtdemux_stbl_free (stream);
6105 if (!qtdemux->fragmented) {
6106 /* not quite good */
6107 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
6110 /* may pick up samples elsewhere */
6116 /* collect samples from the next sample to be parsed up to sample @n for @stream
6117 * by reading the info from @stbl
6119 * This code can be executed from both the streaming thread and the seeking
6120 * thread so it takes the object lock to protect itself
6123 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
6126 QtDemuxSample *samples, *first, *cur, *last;
6127 guint32 n_samples_per_chunk;
6130 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
6131 GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc),
6132 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
6134 n_samples = stream->n_samples;
6137 goto out_of_samples;
6139 GST_OBJECT_LOCK (qtdemux);
6140 if (n <= stream->stbl_index)
6141 goto already_parsed;
6143 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
6145 if (!stream->stsz.data) {
6146 /* so we already parsed and passed all the moov samples;
6147 * onto fragmented ones */
6148 g_assert (qtdemux->fragmented);
6152 /* pointer to the sample table */
6153 samples = stream->samples;
6155 /* starts from -1, moves to the next sample index to parse */
6156 stream->stbl_index++;
6158 /* keep track of the first and last sample to fill */
6159 first = &samples[stream->stbl_index];
6162 if (stream->chunks_are_chunks) {
6163 /* set the sample sizes */
6164 if (stream->sample_size == 0) {
6165 /* different sizes for each sample */
6166 for (cur = first; cur <= last; cur++) {
6167 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
6168 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
6169 (guint) (cur - samples), cur->size);
6172 /* samples have the same size */
6173 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
6174 for (cur = first; cur <= last; cur++)
6175 cur->size = stream->sample_size;
6179 n_samples_per_chunk = stream->n_samples_per_chunk;
6182 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
6185 if (stream->stsc_chunk_index >= stream->last_chunk
6186 || stream->stsc_chunk_index < stream->first_chunk) {
6187 stream->first_chunk =
6188 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
6189 stream->samples_per_chunk =
6190 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
6191 gst_byte_reader_skip_unchecked (&stream->stsc, 4);
6193 /* chunk numbers are counted from 1 it seems */
6194 if (G_UNLIKELY (stream->first_chunk == 0))
6197 --stream->first_chunk;
6199 /* the last chunk of each entry is calculated by taking the first chunk
6200 * of the next entry; except if there is no next, where we fake it with
6202 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
6203 stream->last_chunk = G_MAXUINT32;
6205 stream->last_chunk =
6206 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
6207 if (G_UNLIKELY (stream->last_chunk == 0))
6210 --stream->last_chunk;
6213 GST_LOG_OBJECT (qtdemux,
6214 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i,
6215 stream->first_chunk, stream->last_chunk, stream->samples_per_chunk);
6217 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
6220 if (stream->last_chunk != G_MAXUINT32) {
6221 if (!qt_atom_parser_peek_sub (&stream->stco,
6222 stream->first_chunk * stream->co_size,
6223 (stream->last_chunk - stream->first_chunk) * stream->co_size,
6228 stream->co_chunk = stream->stco;
6229 if (!gst_byte_reader_skip (&stream->co_chunk,
6230 stream->first_chunk * stream->co_size))
6234 stream->stsc_chunk_index = stream->first_chunk;
6237 last_chunk = stream->last_chunk;
6239 if (stream->chunks_are_chunks) {
6240 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
6241 guint32 samples_per_chunk;
6242 guint64 chunk_offset;
6244 if (!stream->stsc_sample_index
6245 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
6246 &stream->chunk_offset))
6249 samples_per_chunk = stream->samples_per_chunk;
6250 chunk_offset = stream->chunk_offset;
6252 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
6253 GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %"
6254 G_GUINT64_FORMAT, (guint) (cur - samples), stream->chunk_offset);
6256 cur->offset = chunk_offset;
6257 chunk_offset += cur->size;
6260 if (G_UNLIKELY (cur > last)) {
6262 stream->stsc_sample_index = k + 1;
6263 stream->chunk_offset = chunk_offset;
6264 stream->stsc_chunk_index = j;
6268 stream->stsc_sample_index = 0;
6270 stream->stsc_chunk_index = j;
6272 cur = &samples[stream->stsc_chunk_index];
6274 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
6277 stream->stsc_chunk_index = j;
6282 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
6285 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
6286 "%" G_GUINT64_FORMAT, j, cur->offset);
6288 if (stream->samples_per_frame * stream->bytes_per_frame) {
6290 (stream->samples_per_chunk * stream->n_channels) /
6291 stream->samples_per_frame * stream->bytes_per_frame;
6293 cur->size = stream->samples_per_chunk;
6296 GST_DEBUG_OBJECT (qtdemux,
6297 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
6298 j, GST_TIME_ARGS (gst_util_uint64_scale (stream->stco_sample_index,
6299 GST_SECOND, stream->timescale)), cur->size);
6301 cur->timestamp = stream->stco_sample_index;
6302 cur->duration = stream->samples_per_chunk;
6303 cur->keyframe = TRUE;
6306 stream->stco_sample_index += stream->samples_per_chunk;
6308 stream->stsc_chunk_index = j;
6310 stream->stsc_index++;
6313 if (!stream->chunks_are_chunks)
6317 guint32 n_sample_times;
6319 n_sample_times = stream->n_sample_times;
6322 for (i = stream->stts_index; i < n_sample_times; i++) {
6323 guint32 stts_samples;
6324 guint32 stts_duration;
6327 if (stream->stts_sample_index >= stream->stts_samples
6328 || !stream->stts_sample_index) {
6330 stream->stts_samples =
6331 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
6332 stream->stts_duration =
6333 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
6335 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
6336 i, stream->stts_samples, stream->stts_duration);
6338 stream->stts_sample_index = 0;
6341 stts_samples = stream->stts_samples;
6342 stts_duration = stream->stts_duration;
6343 stts_time = stream->stts_time;
6345 for (j = stream->stts_sample_index; j < stts_samples; j++) {
6346 GST_DEBUG_OBJECT (qtdemux,
6347 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
6348 (guint) (cur - samples), j,
6349 GST_TIME_ARGS (gst_util_uint64_scale (stts_time, GST_SECOND,
6350 stream->timescale)));
6352 cur->timestamp = stts_time;
6353 cur->duration = stts_duration;
6355 stts_time += stts_duration;
6358 if (G_UNLIKELY (cur > last)) {
6360 stream->stts_time = stts_time;
6361 stream->stts_sample_index = j + 1;
6365 stream->stts_sample_index = 0;
6366 stream->stts_time = stts_time;
6367 stream->stts_index++;
6369 /* fill up empty timestamps with the last timestamp, this can happen when
6370 * the last samples do not decode and so we don't have timestamps for them.
6371 * We however look at the last timestamp to estimate the track length so we
6372 * need something in here. */
6373 for (; cur < last; cur++) {
6374 GST_DEBUG_OBJECT (qtdemux,
6375 "fill sample %d: timestamp %" GST_TIME_FORMAT,
6376 (guint) (cur - samples),
6377 GST_TIME_ARGS (gst_util_uint64_scale (stream->stts_time, GST_SECOND,
6378 stream->timescale)));
6379 cur->timestamp = stream->stts_time;
6385 /* sample sync, can be NULL */
6386 if (stream->stss_present == TRUE) {
6387 guint32 n_sample_syncs;
6389 n_sample_syncs = stream->n_sample_syncs;
6391 if (!n_sample_syncs) {
6392 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
6393 stream->all_keyframe = TRUE;
6395 for (i = stream->stss_index; i < n_sample_syncs; i++) {
6396 /* note that the first sample is index 1, not 0 */
6399 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
6401 if (G_LIKELY (index > 0 && index <= n_samples)) {
6403 samples[index].keyframe = TRUE;
6404 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
6405 /* and exit if we have enough samples */
6406 if (G_UNLIKELY (index >= n)) {
6413 stream->stss_index = i;
6416 /* stps marks partial sync frames like open GOP I-Frames */
6417 if (stream->stps_present == TRUE) {
6418 guint32 n_sample_partial_syncs;
6420 n_sample_partial_syncs = stream->n_sample_partial_syncs;
6422 /* if there are no entries, the stss table contains the real
6424 if (n_sample_partial_syncs) {
6425 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
6426 /* note that the first sample is index 1, not 0 */
6429 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
6431 if (G_LIKELY (index > 0 && index <= n_samples)) {
6433 samples[index].keyframe = TRUE;
6434 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
6435 /* and exit if we have enough samples */
6436 if (G_UNLIKELY (index >= n)) {
6443 stream->stps_index = i;
6447 /* no stss, all samples are keyframes */
6448 stream->all_keyframe = TRUE;
6449 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
6454 /* composition time to sample */
6455 if (stream->ctts_present == TRUE) {
6456 guint32 n_composition_times;
6458 gint32 ctts_soffset;
6460 /* Fill in the pts_offsets */
6462 n_composition_times = stream->n_composition_times;
6464 for (i = stream->ctts_index; i < n_composition_times; i++) {
6465 if (stream->ctts_sample_index >= stream->ctts_count
6466 || !stream->ctts_sample_index) {
6467 stream->ctts_count =
6468 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
6469 stream->ctts_soffset =
6470 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
6471 stream->ctts_sample_index = 0;
6474 ctts_count = stream->ctts_count;
6475 ctts_soffset = stream->ctts_soffset;
6477 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
6478 cur->pts_offset = ctts_soffset;
6481 if (G_UNLIKELY (cur > last)) {
6483 stream->ctts_sample_index = j + 1;
6487 stream->ctts_sample_index = 0;
6488 stream->ctts_index++;
6492 stream->stbl_index = n;
6493 /* if index has been completely parsed, free data that is no-longer needed */
6494 if (n + 1 == stream->n_samples) {
6495 gst_qtdemux_stbl_free (stream);
6496 GST_DEBUG_OBJECT (qtdemux,
6497 "parsed all available samples; checking for more");
6498 while (n + 1 == stream->n_samples)
6499 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
6502 GST_OBJECT_UNLOCK (qtdemux);
6509 GST_LOG_OBJECT (qtdemux,
6510 "Tried to parse up to sample %u but this sample has already been parsed",
6512 /* if fragmented, there may be more */
6513 if (qtdemux->fragmented && n == stream->stbl_index)
6515 GST_OBJECT_UNLOCK (qtdemux);
6521 GST_LOG_OBJECT (qtdemux,
6522 "Tried to parse up to sample %u but there are only %u samples", n + 1,
6524 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6525 (_("This file is corrupt and cannot be played.")), (NULL));
6530 GST_OBJECT_UNLOCK (qtdemux);
6531 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6532 (_("This file is corrupt and cannot be played.")), (NULL));
6537 /* collect all segment info for @stream.
6540 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
6545 /* parse and prepare segment info from the edit list */
6546 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
6547 stream->n_segments = 0;
6548 stream->segments = NULL;
6549 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
6553 guint64 time, stime;
6556 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
6557 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
6560 buffer = elst->data;
6562 n_segments = QT_UINT32 (buffer + 12);
6564 /* we might allocate a bit too much, at least allocate 1 segment */
6565 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
6567 /* segments always start from 0 */
6571 for (i = 0; i < n_segments; i++) {
6574 QtDemuxSegment *segment;
6577 media_time = QT_UINT32 (buffer + 20 + i * 12);
6579 /* -1 media time is an empty segment, just ignore it */
6580 if (media_time == G_MAXUINT32)
6583 duration = QT_UINT32 (buffer + 16 + i * 12);
6585 segment = &stream->segments[count++];
6587 /* time and duration expressed in global timescale */
6588 segment->time = stime;
6589 /* add non scaled values so we don't cause roundoff errors */
6591 stime = gst_util_uint64_scale (time, GST_SECOND, qtdemux->timescale);
6592 segment->stop_time = stime;
6593 segment->duration = stime - segment->time;
6594 /* media_time expressed in stream timescale */
6595 segment->media_start =
6596 gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale);
6597 segment->media_stop = segment->media_start + segment->duration;
6598 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
6600 if (rate_int <= 1) {
6601 /* 0 is not allowed, some programs write 1 instead of the floating point
6603 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
6607 segment->rate = rate_int / 65536.0;
6610 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
6611 ", duration %" GST_TIME_FORMAT ", media_time %" GST_TIME_FORMAT
6612 ", rate %g, (%d)", i, GST_TIME_ARGS (segment->time),
6613 GST_TIME_ARGS (segment->duration),
6614 GST_TIME_ARGS (segment->media_start), segment->rate, rate_int);
6616 GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count);
6617 stream->n_segments = count;
6621 /* push based does not handle segments, so act accordingly here,
6622 * and warn if applicable */
6623 if (!qtdemux->pullbased) {
6624 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
6625 /* remove and use default one below, we stream like it anyway */
6626 g_free (stream->segments);
6627 stream->segments = NULL;
6628 stream->n_segments = 0;
6631 /* no segments, create one to play the complete trak */
6632 if (stream->n_segments == 0) {
6633 GstClockTime stream_duration =
6634 gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale);
6636 if (stream->segments == NULL)
6637 stream->segments = g_new (QtDemuxSegment, 1);
6639 /* represent unknown our way */
6640 if (stream_duration == 0)
6641 stream_duration = -1;
6643 stream->segments[0].time = 0;
6644 stream->segments[0].stop_time = stream_duration;
6645 stream->segments[0].duration = stream_duration;
6646 stream->segments[0].media_start = 0;
6647 stream->segments[0].media_stop = stream_duration;
6648 stream->segments[0].rate = 1.0;
6650 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
6651 GST_TIME_ARGS (stream_duration));
6652 stream->n_segments = 1;
6654 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
6660 * Parses the stsd atom of a svq3 trak looking for
6661 * the SMI and gama atoms.
6664 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
6665 guint8 ** gamma, GstBuffer ** seqh)
6667 guint8 *_gamma = NULL;
6668 GstBuffer *_seqh = NULL;
6669 guint8 *stsd_data = stsd->data;
6670 guint32 length = QT_UINT32 (stsd_data);
6674 GST_WARNING_OBJECT (qtdemux, "stsd too short");
6680 version = QT_UINT16 (stsd_data);
6685 while (length > 8) {
6686 guint32 fourcc, size;
6688 size = QT_UINT32 (stsd_data);
6689 fourcc = QT_FOURCC (stsd_data + 4);
6690 data = stsd_data + 8;
6697 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
6698 " for gama atom, expected 12", size);
6703 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
6705 if (_seqh != NULL) {
6706 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
6707 " found, ignoring");
6709 seqh_size = QT_UINT32 (data + 4);
6710 if (seqh_size > 0) {
6711 _seqh = gst_buffer_new_and_alloc (seqh_size);
6712 memcpy (GST_BUFFER_DATA (_seqh), data + 8, seqh_size);
6719 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
6720 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
6724 if (size <= length) {
6730 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
6733 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
6734 G_GUINT16_FORMAT, version);
6745 gst_buffer_unref (_seqh);
6750 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
6758 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
6759 * atom that might contain a 'data' atom with the rtsp uri.
6760 * This case was reported in bug #597497, some info about
6761 * the hndl atom can be found in TN1195
6763 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
6764 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
6767 guint32 dref_num_entries = 0;
6768 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
6769 gst_byte_reader_skip (&dref, 4) &&
6770 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
6774 /* search dref entries for hndl atom */
6775 for (i = 0; i < dref_num_entries; i++) {
6776 guint32 size = 0, type;
6777 guint8 string_len = 0;
6778 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
6779 qt_atom_parser_get_fourcc (&dref, &type)) {
6780 if (type == FOURCC_hndl) {
6781 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
6783 /* skip data reference handle bytes and the
6784 * following pascal string and some extra 4
6785 * bytes I have no idea what are */
6786 if (!gst_byte_reader_skip (&dref, 4) ||
6787 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
6788 !gst_byte_reader_skip (&dref, string_len + 4)) {
6789 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
6793 /* iterate over the atoms to find the data atom */
6794 while (gst_byte_reader_get_remaining (&dref) >= 8) {
6798 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
6799 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
6800 if (atom_type == FOURCC_data) {
6801 const guint8 *uri_aux = NULL;
6803 /* found the data atom that might contain the rtsp uri */
6804 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
6805 "hndl atom, interpreting it as an URI");
6806 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
6808 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
6809 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
6811 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
6812 "didn't contain a rtsp address");
6814 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
6819 /* skipping to the next entry */
6820 gst_byte_reader_skip (&dref, atom_size - 8);
6822 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
6829 /* skip to the next entry */
6830 gst_byte_reader_skip (&dref, size - 8);
6832 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
6835 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
6842 less_than (gconstpointer a, gconstpointer b)
6844 const guint32 *av = a, *bv = b;
6850 * With each track we associate a new QtDemuxStream that contains all the info
6852 * traks that do not decode to something (like strm traks) will not have a pad.
6855 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
6870 QtDemuxStream *stream;
6871 GstTagList *list = NULL;
6872 gchar *codec = NULL;
6873 const guint8 *stsd_data;
6874 guint16 lang_code; /* quicktime lang code or packed iso code */
6876 guint32 tkhd_flags = 0;
6877 guint8 tkhd_version = 0;
6879 guint value_size, len;
6881 stream = g_new0 (QtDemuxStream, 1);
6882 /* new streams always need a discont */
6883 stream->discont = TRUE;
6884 /* we enable clipping for raw audio/video streams */
6885 stream->need_clip = FALSE;
6886 stream->need_process = FALSE;
6887 stream->segment_index = -1;
6888 stream->time_position = 0;
6889 stream->sample_index = -1;
6890 stream->last_ret = GST_FLOW_OK;
6892 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
6893 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
6894 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
6897 /* pick between 64 or 32 bits */
6898 value_size = tkhd_version == 1 ? 8 : 4;
6899 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
6900 !gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id))
6903 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
6904 tkhd_version, tkhd_flags, stream->track_id);
6906 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
6909 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
6910 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
6911 if (qtdemux->major_brand != FOURCC_mjp2 ||
6912 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
6916 len = QT_UINT32 ((guint8 *) mdhd->data);
6917 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
6918 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
6919 if (version == 0x01000000) {
6922 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
6923 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
6924 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
6928 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
6929 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
6930 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
6933 if (lang_code < 0x800) {
6934 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
6936 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
6937 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
6938 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
6939 stream->lang_id[3] = 0;
6942 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
6944 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
6946 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
6947 lang_code, stream->lang_id);
6949 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
6952 /* fragmented files may have bogus duration in moov */
6953 if (!qtdemux->fragmented &&
6954 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
6955 guint64 tdur1, tdur2;
6957 /* don't overflow */
6958 tdur1 = stream->timescale * (guint64) qtdemux->duration;
6959 tdur2 = qtdemux->timescale * (guint64) stream->duration;
6962 * some of those trailers, nowadays, have prologue images that are
6963 * themselves vide tracks as well. I haven't really found a way to
6964 * identify those yet, except for just looking at their duration. */
6965 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
6966 GST_WARNING_OBJECT (qtdemux,
6967 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
6968 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
6969 "found, assuming preview image or something; skipping track",
6970 stream->duration, stream->timescale, qtdemux->duration,
6971 qtdemux->timescale);
6977 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
6980 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
6981 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
6983 len = QT_UINT32 ((guint8 *) hdlr->data);
6985 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
6986 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
6987 GST_FOURCC_ARGS (stream->subtype));
6989 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
6992 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
6996 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
6998 stsd_data = (const guint8 *) stsd->data;
7000 /* stsd should at least have one entry */
7001 len = QT_UINT32 (stsd_data);
7005 /* and that entry should fit within stsd */
7006 len = QT_UINT32 (stsd_data + 16);
7007 if (len > QT_UINT32 (stsd_data) + 16)
7009 GST_LOG_OBJECT (qtdemux, "stsd len: %d", len);
7011 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
7012 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
7013 GST_FOURCC_ARGS (stream->fourcc));
7015 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) ||
7016 ((fourcc & 0xFFFFFF00) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)))
7017 goto error_encrypted;
7019 if (stream->subtype == FOURCC_vide) {
7020 guint32 w = 0, h = 0;
7022 stream->sampled = TRUE;
7024 /* version 1 uses some 64-bit ints */
7025 if (!gst_byte_reader_skip (&tkhd, 56 + value_size)
7026 || !gst_byte_reader_get_uint32_be (&tkhd, &w)
7027 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
7030 stream->display_width = w >> 16;
7031 stream->display_height = h >> 16;
7037 stream->width = QT_UINT16 (stsd_data + offset + 32);
7038 stream->height = QT_UINT16 (stsd_data + offset + 34);
7039 stream->fps_n = 0; /* this is filled in later */
7040 stream->fps_d = 0; /* this is filled in later */
7041 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
7042 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);
7044 GST_LOG_OBJECT (qtdemux, "frame count: %u",
7045 QT_UINT16 (stsd_data + offset + 48));
7048 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
7050 list = gst_tag_list_new ();
7051 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7052 GST_TAG_VIDEO_CODEC, codec, NULL);
7059 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4v);
7061 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_MP4V);
7062 /* H264 is MPEG-4 after all,
7063 * and qt seems to put MPEG-4 stuff in there as well */
7065 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_avc1);
7067 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
7068 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
7072 const guint8 *pasp_data = (const guint8 *) pasp->data;
7074 stream->par_w = QT_UINT32 (pasp_data + 8);
7075 stream->par_h = QT_UINT32 (pasp_data + 12);
7082 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
7083 #ifdef QTDEMUX_MODIFICATION
7084 /* If the caps modified to H263 from MPEG-4, update the fourcc variable */
7085 fourcc = stream->fourcc;
7091 gint len = QT_UINT32 (stsd_data) - 0x66;
7092 const guint8 *avc_data = stsd_data + 0x66;
7095 while (len >= 0x8) {
7098 if (QT_UINT32 (avc_data) <= len)
7099 size = QT_UINT32 (avc_data) - 0x8;
7104 /* No real data, so break out */
7107 switch (QT_FOURCC (avc_data + 0x4)) {
7110 /* parse, if found */
7113 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
7115 /* First 4 bytes are the length of the atom, the next 4 bytes
7116 * are the fourcc, the next 1 byte is the version, and the
7117 * subsequent bytes are sequence parameter set like data. */
7118 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
7119 avc_data + 8 + 1, size - 1);
7121 buf = gst_buffer_new_and_alloc (size);
7122 memcpy (GST_BUFFER_DATA (buf), avc_data + 0x8, size);
7123 gst_caps_set_simple (stream->caps,
7124 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7125 gst_buffer_unref (buf);
7131 guint avg_bitrate, max_bitrate;
7133 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
7137 max_bitrate = QT_UINT32 (avc_data + 0xc);
7138 avg_bitrate = QT_UINT32 (avc_data + 0x10);
7140 if (!max_bitrate && !avg_bitrate)
7143 /* Some muxers seem to swap the average and maximum bitrates
7144 * (I'm looking at you, YouTube), so we swap for sanity. */
7145 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
7146 guint temp = avg_bitrate;
7148 avg_bitrate = max_bitrate;
7153 list = gst_tag_list_new ();
7155 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
7156 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7157 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
7159 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
7160 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7161 GST_TAG_BITRATE, avg_bitrate, NULL);
7172 avc_data += size + 8;
7184 GST_DEBUG_OBJECT (qtdemux, "found mp4v");
7186 /* codec data might be in glbl extension atom */
7187 glbl = qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl);
7193 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
7195 len = QT_UINT32 (data);
7198 buf = gst_buffer_new_and_alloc (len);
7199 memcpy (GST_BUFFER_DATA (buf), data + 8, len);
7200 gst_caps_set_simple (stream->caps,
7201 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7202 gst_buffer_unref (buf);
7209 /* see annex I of the jpeg2000 spec */
7210 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
7214 guint32 ncomp_map = 0;
7215 gint32 *comp_map = NULL;
7216 guint32 nchan_def = 0;
7217 gint32 *chan_def = NULL;
7219 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
7220 /* some required atoms */
7221 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
7224 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
7228 /* number of components; redundant with info in codestream, but useful
7230 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
7231 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
7233 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
7235 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
7238 GST_DEBUG_OBJECT (qtdemux, "found colr");
7239 /* extract colour space info */
7240 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
7241 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
7243 fourcc = GST_MAKE_FOURCC ('s', 'R', 'G', 'B');
7246 fourcc = GST_MAKE_FOURCC ('G', 'R', 'A', 'Y');
7249 fourcc = GST_MAKE_FOURCC ('s', 'Y', 'U', 'V');
7256 /* colr is required, and only values 16, 17, and 18 are specified,
7257 so error if we have no fourcc */
7260 /* extract component mapping */
7261 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
7263 guint32 cmap_len = 0;
7265 cmap_len = QT_UINT32 (cmap->data);
7266 if (cmap_len >= 8) {
7267 /* normal box, subtract off header */
7269 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
7270 if (cmap_len % 4 == 0) {
7271 ncomp_map = (cmap_len / 4);
7272 comp_map = g_new0 (gint32, ncomp_map);
7273 for (i = 0; i < ncomp_map; i++) {
7276 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
7277 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
7278 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
7279 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
7284 /* extract channel definitions */
7285 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
7287 guint32 cdef_len = 0;
7289 cdef_len = QT_UINT32 (cdef->data);
7290 if (cdef_len >= 10) {
7291 /* normal box, subtract off header and len */
7293 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
7294 if (cdef_len % 6 == 0) {
7295 nchan_def = (cdef_len / 6);
7296 chan_def = g_new0 (gint32, nchan_def);
7297 for (i = 0; i < nchan_def; i++)
7299 for (i = 0; i < nchan_def; i++) {
7300 guint16 cn, typ, asoc;
7301 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
7302 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
7303 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
7304 if (cn < nchan_def) {
7307 chan_def[cn] = asoc;
7310 chan_def[cn] = 0; /* alpha */
7313 chan_def[cn] = -typ;
7321 gst_caps_set_simple (stream->caps,
7322 "num-components", G_TYPE_INT, ncomp, NULL);
7323 gst_caps_set_simple (stream->caps,
7324 "fourcc", GST_TYPE_FOURCC, fourcc, NULL);
7327 GValue arr = { 0, };
7328 GValue elt = { 0, };
7330 g_value_init (&arr, GST_TYPE_ARRAY);
7331 g_value_init (&elt, G_TYPE_INT);
7332 for (i = 0; i < ncomp_map; i++) {
7333 g_value_set_int (&elt, comp_map[i]);
7334 gst_value_array_append_value (&arr, &elt);
7336 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7337 "component-map", &arr);
7338 g_value_unset (&elt);
7339 g_value_unset (&arr);
7344 GValue arr = { 0, };
7345 GValue elt = { 0, };
7347 g_value_init (&arr, GST_TYPE_ARRAY);
7348 g_value_init (&elt, G_TYPE_INT);
7349 for (i = 0; i < nchan_def; i++) {
7350 g_value_set_int (&elt, chan_def[i]);
7351 gst_value_array_append_value (&arr, &elt);
7353 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7354 "channel-definitions", &arr);
7355 g_value_unset (&elt);
7356 g_value_unset (&arr);
7360 /* some optional atoms */
7361 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
7362 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
7364 /* indicate possible fields in caps */
7366 data = (guint8 *) field->data + 8;
7368 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
7369 (gint) * data, NULL);
7371 /* add codec_data if provided */
7376 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
7377 data = prefix->data;
7378 len = QT_UINT32 (data);
7381 buf = gst_buffer_new_and_alloc (len);
7382 memcpy (GST_BUFFER_DATA (buf), data + 8, len);
7383 gst_caps_set_simple (stream->caps,
7384 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7385 gst_buffer_unref (buf);
7394 GstBuffer *seqh = NULL;
7395 guint8 *gamma_data = NULL;
7396 gint len = QT_UINT32 (stsd_data);
7398 qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
7400 gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
7401 QT_FP32 (gamma_data), NULL);
7404 /* sorry for the bad name, but we don't know what this is, other
7405 * than its own fourcc */
7406 gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
7410 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
7411 buf = gst_buffer_new_and_alloc (len);
7412 memcpy (GST_BUFFER_DATA (buf), stsd_data, len);
7413 gst_caps_set_simple (stream->caps,
7414 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7415 gst_buffer_unref (buf);
7420 gst_caps_set_simple (stream->caps,
7421 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
7428 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
7429 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
7433 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
7437 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
7438 /* collect the headers and store them in a stream list so that we can
7439 * send them out first */
7440 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
7450 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
7451 ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
7454 ovc1_data = ovc1->data;
7455 ovc1_len = QT_UINT32 (ovc1_data);
7456 if (ovc1_len <= 198) {
7457 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
7460 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
7461 memcpy (GST_BUFFER_DATA (buf), ovc1_data + 198, ovc1_len - 198);
7462 gst_caps_set_simple (stream->caps,
7463 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7464 gst_buffer_unref (buf);
7472 GST_INFO_OBJECT (qtdemux,
7473 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7474 GST_FOURCC_ARGS (fourcc), stream->caps);
7476 } else if (stream->subtype == FOURCC_soun) {
7477 int version, samplesize;
7478 guint16 compression_id;
7484 version = QT_UINT32 (stsd_data + offset);
7485 stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
7486 samplesize = QT_UINT16 (stsd_data + offset + 10);
7487 compression_id = QT_UINT16 (stsd_data + offset + 12);
7488 stream->rate = QT_FP32 (stsd_data + offset + 16);
7490 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
7491 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
7492 QT_UINT32 (stsd_data + offset + 4));
7493 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7494 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
7495 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
7496 GST_LOG_OBJECT (qtdemux, "packet size: %d",
7497 QT_UINT16 (stsd_data + offset + 14));
7498 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7500 if (compression_id == 0xfffe)
7501 stream->sampled = TRUE;
7503 /* first assume uncompressed audio */
7504 stream->bytes_per_sample = samplesize / 8;
7505 stream->samples_per_frame = stream->n_channels;
7506 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
7507 stream->samples_per_packet = stream->samples_per_frame;
7508 stream->bytes_per_packet = stream->bytes_per_sample;
7512 /* Yes, these have to be hard-coded */
7515 stream->samples_per_packet = 6;
7516 stream->bytes_per_packet = 1;
7517 stream->bytes_per_frame = 1 * stream->n_channels;
7518 stream->bytes_per_sample = 1;
7519 stream->samples_per_frame = 6 * stream->n_channels;
7524 stream->samples_per_packet = 3;
7525 stream->bytes_per_packet = 1;
7526 stream->bytes_per_frame = 1 * stream->n_channels;
7527 stream->bytes_per_sample = 1;
7528 stream->samples_per_frame = 3 * stream->n_channels;
7533 stream->samples_per_packet = 64;
7534 stream->bytes_per_packet = 34;
7535 stream->bytes_per_frame = 34 * stream->n_channels;
7536 stream->bytes_per_sample = 2;
7537 stream->samples_per_frame = 64 * stream->n_channels;
7543 stream->samples_per_packet = 1;
7544 stream->bytes_per_packet = 1;
7545 stream->bytes_per_frame = 1 * stream->n_channels;
7546 stream->bytes_per_sample = 1;
7547 stream->samples_per_frame = 1 * stream->n_channels;
7552 stream->samples_per_packet = 160;
7553 stream->bytes_per_packet = 33;
7554 stream->bytes_per_frame = 33 * stream->n_channels;
7555 stream->bytes_per_sample = 2;
7556 stream->samples_per_frame = 160 * stream->n_channels;
7563 if (version == 0x00010000) {
7571 /* only parse extra decoding config for non-pcm audio */
7572 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7573 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
7574 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
7575 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);
7577 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7578 stream->samples_per_packet);
7579 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
7580 stream->bytes_per_packet);
7581 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
7582 stream->bytes_per_frame);
7583 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
7584 stream->bytes_per_sample);
7586 if (!stream->sampled && stream->bytes_per_packet) {
7587 stream->samples_per_frame = (stream->bytes_per_frame /
7588 stream->bytes_per_packet) * stream->samples_per_packet;
7589 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
7590 stream->samples_per_frame);
7595 } else if (version == 0x00020000) {
7602 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7603 qtfp.val = QT_UINT64 (stsd_data + offset + 4);
7604 stream->rate = qtfp.fp;
7605 stream->n_channels = QT_UINT32 (stsd_data + offset + 12);
7607 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7608 stream->samples_per_packet);
7609 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7610 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7613 GST_WARNING_OBJECT (qtdemux, "unknown version %08x", version);
7616 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc, NULL, 0,
7625 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
7627 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
7629 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
7631 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
7634 gst_caps_set_simple (stream->caps,
7635 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
7642 const gchar *owma_data, *codec_name = NULL;
7646 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
7647 /* FIXME this should also be gst_riff_strf_auds,
7648 * but the latter one is actually missing bits-per-sample :( */
7653 gint32 nSamplesPerSec;
7654 gint32 nAvgBytesPerSec;
7656 gint16 wBitsPerSample;
7661 GST_DEBUG_OBJECT (qtdemux, "parse owma");
7662 owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
7665 owma_data = owma->data;
7666 owma_len = QT_UINT32 (owma_data);
7667 if (owma_len <= 54) {
7668 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
7671 wfex = (WAVEFORMATEX *) (owma_data + 36);
7672 buf = gst_buffer_new_and_alloc (owma_len - 54);
7673 memcpy (GST_BUFFER_DATA (buf), owma_data + 54, owma_len - 54);
7674 if (wfex->wFormatTag == 0x0161) {
7675 codec_name = "Windows Media Audio";
7677 } else if (wfex->wFormatTag == 0x0162) {
7678 codec_name = "Windows Media Audio 9 Pro";
7680 } else if (wfex->wFormatTag == 0x0163) {
7681 codec_name = "Windows Media Audio 9 Lossless";
7682 /* is that correct? gstffmpegcodecmap.c is missing it, but
7683 * fluendo codec seems to support it */
7687 gst_caps_set_simple (stream->caps,
7688 "codec_data", GST_TYPE_BUFFER, buf,
7689 "wmaversion", G_TYPE_INT, version,
7690 "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
7691 "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
7692 "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7693 "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7695 gst_buffer_unref (buf);
7699 codec = g_strdup (codec_name);
7708 list = gst_tag_list_new ();
7709 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7710 GST_TAG_AUDIO_CODEC, codec, NULL);
7715 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
7719 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
7721 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
7723 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
7727 /* If the fourcc's bottom 16 bits gives 'sm', then the top
7728 16 bits is a byte-swapped wave-style codec identifier,
7729 and we can find a WAVE header internally to a 'wave' atom here.
7730 This can more clearly be thought of as 'ms' as the top 16 bits, and a
7731 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
7734 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
7735 if (len < offset + 20) {
7736 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
7738 guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
7739 const guint8 *data = stsd_data + offset + 16;
7741 GNode *waveheadernode;
7743 wavenode = g_node_new ((guint8 *) data);
7744 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
7745 const guint8 *waveheader;
7748 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
7749 if (waveheadernode) {
7750 waveheader = (const guint8 *) waveheadernode->data;
7751 headerlen = QT_UINT32 (waveheader);
7753 if (headerlen > 8) {
7754 gst_riff_strf_auds *header = NULL;
7755 GstBuffer *headerbuf;
7761 headerbuf = gst_buffer_new ();
7762 GST_BUFFER_DATA (headerbuf) = (guint8 *) waveheader;
7763 GST_BUFFER_SIZE (headerbuf) = headerlen;
7765 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
7766 headerbuf, &header, &extra)) {
7767 gst_caps_unref (stream->caps);
7768 stream->caps = gst_riff_create_audio_caps (header->format, NULL,
7769 header, extra, NULL, NULL);
7772 gst_buffer_unref (extra);
7776 GST_DEBUG ("Didn't find waveheadernode for this codec");
7778 g_node_destroy (wavenode);
7781 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
7785 /* FIXME: what is in the chunk? */
7788 gint len = QT_UINT32 (stsd_data);
7790 /* seems to be always = 116 = 0x74 */
7796 gint len = QT_UINT32 (stsd_data);
7799 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
7801 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x4C, len - 0x4C);
7802 gst_caps_set_simple (stream->caps,
7803 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7804 gst_buffer_unref (buf);
7806 gst_caps_set_simple (stream->caps,
7807 "samplesize", G_TYPE_INT, samplesize, NULL);
7812 GNode *alac, *wave = NULL;
7814 /* apparently, m4a has this atom appended directly in the stsd entry,
7815 * while mov has it in a wave atom */
7816 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
7818 /* alac now refers to stsd entry atom */
7819 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
7821 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
7823 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
7826 gint len = QT_UINT32 (alac->data);
7830 GST_DEBUG_OBJECT (qtdemux,
7831 "discarding alac atom with unexpected len %d", len);
7833 /* codec-data contains alac atom size and prefix,
7834 * ffmpeg likes it that way, not quite gst-ish though ...*/
7835 buf = gst_buffer_new_and_alloc (len);
7836 memcpy (GST_BUFFER_DATA (buf), alac->data, len);
7837 gst_caps_set_simple (stream->caps,
7838 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7839 gst_buffer_unref (buf);
7842 gst_caps_set_simple (stream->caps,
7843 "samplesize", G_TYPE_INT, samplesize, NULL);
7848 gint len = QT_UINT32 (stsd_data);
7851 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
7853 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x34, len - 0x34);
7855 gst_caps_set_simple (stream->caps,
7856 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7857 gst_buffer_unref (buf);
7865 GST_INFO_OBJECT (qtdemux,
7866 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7867 GST_FOURCC_ARGS (fourcc), stream->caps);
7869 } else if (stream->subtype == FOURCC_strm) {
7870 if (fourcc == FOURCC_rtsp) {
7871 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
7873 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
7874 GST_FOURCC_ARGS (fourcc));
7875 goto unknown_stream;
7877 stream->sampled = TRUE;
7878 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
7880 stream->sampled = TRUE;
7885 qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
7887 list = gst_tag_list_new ();
7888 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7889 GST_TAG_SUBTITLE_CODEC, codec, NULL);
7894 /* hunt for sort-of codec data */
7901 /* look for palette */
7902 /* target mp4s atom */
7903 len = QT_UINT32 (stsd_data + offset);
7904 data = stsd_data + offset;
7905 /* verify sufficient length,
7906 * and esds present with decConfigDescr of expected size and position */
7907 if ((len >= 106 + 8)
7908 && (QT_FOURCC (data + 8 + 8 + 4) == FOURCC_esds)
7909 && (QT_UINT16 (data + 8 + 40) == 0x0540)) {
7914 /* move to decConfigDescr data */
7915 data = data + 8 + 42;
7916 for (i = 0; i < 16; i++) {
7917 clut[i] = QT_UINT32 (data);
7921 s = gst_structure_new ("application/x-gst-dvd", "event",
7922 G_TYPE_STRING, "dvd-spu-clut-change",
7923 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
7924 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
7925 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
7926 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
7927 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
7928 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
7929 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
7930 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
7933 /* store event and trigger custom processing */
7934 stream->pending_event =
7935 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
7936 stream->need_process = TRUE;
7944 goto unknown_stream;
7947 /* promote to sampled format */
7948 if (stream->fourcc == FOURCC_samr) {
7949 /* force mono 8000 Hz for AMR */
7950 stream->sampled = TRUE;
7951 stream->n_channels = 1;
7952 stream->rate = 8000;
7953 } else if (stream->fourcc == FOURCC_sawb) {
7954 /* force mono 16000 Hz for AMR-WB */
7955 stream->sampled = TRUE;
7956 stream->n_channels = 1;
7957 stream->rate = 16000;
7958 } else if (stream->fourcc == FOURCC_mp4a) {
7959 stream->sampled = TRUE;
7962 /* collect sample information */
7963 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
7964 goto samples_failed;
7966 if (qtdemux->fragmented) {
7970 /* need all moov samples as basis; probably not many if any at all */
7971 /* prevent moof parsing taking of at this time */
7972 offset = qtdemux->moof_offset;
7973 qtdemux->moof_offset = 0;
7974 if (stream->n_samples &&
7975 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
7976 qtdemux->moof_offset = offset;
7977 goto samples_failed;
7979 qtdemux->moof_offset = 0;
7980 /* movie duration more reliable in this case (e.g. mehd) */
7981 if (qtdemux->segment.duration &&
7982 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
7983 stream->duration = gst_util_uint64_scale (qtdemux->segment.duration,
7984 stream->timescale, GST_SECOND);
7985 /* need defaults for fragments */
7986 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
7989 /* configure segments */
7990 if (!qtdemux_parse_segments (qtdemux, stream, trak))
7991 goto segments_failed;
7993 /* add some language tag, if useful */
7994 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
7995 strcmp (stream->lang_id, "und")) {
7996 const gchar *lang_code;
7999 list = gst_tag_list_new ();
8001 /* convert ISO 639-2 code to ISO 639-1 */
8002 lang_code = gst_tag_get_language_code (stream->lang_id);
8003 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
8004 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
8007 /* now we are ready to add the stream */
8008 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
8009 goto too_many_streams;
8011 stream->pending_tags = list;
8012 qtdemux->streams[qtdemux->n_streams] = stream;
8013 qtdemux->n_streams++;
8014 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
8021 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8022 (_("This file is corrupt and cannot be played.")), (NULL));
8028 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
8035 /* we posted an error already */
8036 /* free stbl sub-atoms */
8037 gst_qtdemux_stbl_free (stream);
8043 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
8044 GST_FOURCC_ARGS (stream->subtype));
8050 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
8051 (_("This file contains too many streams. Only playing first %d"),
8052 GST_QTDEMUX_MAX_STREAMS), (NULL));
8057 static GstFlowReturn
8058 qtdemux_expose_streams (GstQTDemux * qtdemux)
8061 GstFlowReturn ret = GST_FLOW_OK;
8063 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
8065 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
8066 QtDemuxStream *stream = qtdemux->streams[i];
8067 guint32 sample_num = 0;
8072 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
8073 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
8075 if (qtdemux->fragmented) {
8076 /* need all moov samples first */
8077 GST_OBJECT_LOCK (qtdemux);
8078 while (stream->n_samples == 0)
8079 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
8081 GST_OBJECT_UNLOCK (qtdemux);
8083 /* discard any stray moof */
8084 qtdemux->moof_offset = 0;
8087 /* prepare braking */
8088 if (ret != GST_FLOW_ERROR)
8091 /* in pull mode, we should have parsed some sample info by now;
8092 * and quite some code will not handle no samples.
8093 * in push mode, we'll just have to deal with it */
8094 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
8095 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
8096 gst_qtdemux_stream_free (qtdemux, stream);
8097 memmove (&(qtdemux->streams[i]), &(qtdemux->streams[i + 1]),
8098 sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1));
8099 qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL;
8100 qtdemux->n_streams--;
8105 /* parse number of initial sample to set frame rate cap */
8106 while (sample_num < stream->n_samples && sample_num < samples) {
8107 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
8111 /* collect and sort durations */
8112 samples = MIN (stream->stbl_index + 1, samples);
8113 GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples);
8115 durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples);
8117 while (sample_num < samples) {
8118 g_array_append_val (durations, stream->samples[sample_num].duration);
8121 g_array_sort (durations, less_than);
8122 stream->min_duration = g_array_index (durations, guint32, samples / 2);
8123 g_array_free (durations, TRUE);
8126 /* now we have all info and can expose */
8127 list = stream->pending_tags;
8128 stream->pending_tags = NULL;
8129 gst_qtdemux_add_stream (qtdemux, stream, list);
8132 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
8134 /* check if we should post a redirect in case there is a single trak
8135 * and it is a redirecting trak */
8136 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
8139 qtdemux_post_global_tags (qtdemux);
8141 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
8142 "an external content");
8143 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
8144 gst_structure_new ("redirect",
8145 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
8147 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
8148 qtdemux->posted_redirect = TRUE;
8154 /* check if major or compatible brand is 3GP */
8155 static inline gboolean
8156 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
8159 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8160 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8161 } else if (qtdemux->comp_brands != NULL) {
8162 guint8 *data = GST_BUFFER_DATA (qtdemux->comp_brands);
8163 guint size = GST_BUFFER_SIZE (qtdemux->comp_brands);
8164 gboolean res = FALSE;
8167 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8168 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8178 /* check if tag is a spec'ed 3GP tag keyword storing a string */
8179 static inline gboolean
8180 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
8182 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
8183 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
8184 || fourcc == FOURCC_albm;
8188 qtdemux_tag_add_location (GstQTDemux * qtdemux, const char *tag,
8189 const char *dummy, GNode * node)
8191 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8195 gdouble longitude, latitude, altitude;
8198 len = QT_UINT32 (node->data);
8205 /* TODO: language code skipped */
8207 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
8210 /* do not alarm in trivial case, but bail out otherwise */
8211 if (*(data + offset) != 0) {
8212 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
8216 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8217 GST_TAG_GEO_LOCATION_NAME, name, NULL);
8218 offset += strlen (name);
8222 if (len < offset + 2 + 4 + 4 + 4)
8225 /* +1 +1 = skip null-terminator and location role byte */
8227 /* table in spec says unsigned, semantics say negative has meaning ... */
8228 longitude = QT_SFP32 (data + offset);
8231 latitude = QT_SFP32 (data + offset);
8234 altitude = QT_SFP32 (data + offset);
8236 /* one invalid means all are invalid */
8237 if (longitude >= -180.0 && longitude <= 180.0 &&
8238 latitude >= -90.0 && latitude <= 90.0) {
8239 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8240 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
8241 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
8242 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
8245 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
8252 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
8259 qtdemux_tag_add_year (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8266 len = QT_UINT32 (node->data);
8270 y = QT_UINT16 ((guint8 *) node->data + 12);
8272 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
8275 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
8277 date = g_date_new_dmy (1, 1, y);
8278 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, date, NULL);
8283 qtdemux_tag_add_classification (GstQTDemux * qtdemux, const char *tag,
8284 const char *dummy, GNode * node)
8287 char *tag_str = NULL;
8292 len = QT_UINT32 (node->data);
8297 entity = (guint8 *) node->data + offset;
8298 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
8299 GST_DEBUG_OBJECT (qtdemux,
8300 "classification info: %c%c%c%c invalid classification entity",
8301 entity[0], entity[1], entity[2], entity[3]);
8306 table = QT_UINT16 ((guint8 *) node->data + offset);
8308 /* Language code skipped */
8312 /* Tag format: "XXXX://Y[YYYY]/classification info string"
8313 * XXXX: classification entity, fixed length 4 chars.
8314 * Y[YYYY]: classification table, max 5 chars.
8316 tag_str = g_strdup_printf ("----://%u/%s",
8317 table, (char *) node->data + offset);
8319 /* memcpy To be sure we're preserving byte order */
8320 memcpy (tag_str, entity, 4);
8321 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
8323 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_APPEND, tag,
8333 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
8339 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, const char *tag,
8340 const char *dummy, GNode * node)
8342 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8348 gboolean ret = TRUE;
8350 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8352 len = QT_UINT32 (data->data);
8353 type = QT_UINT32 ((guint8 *) data->data + 8);
8354 if (type == 0x00000001 && len > 16) {
8355 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
8358 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8359 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s,
8363 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8367 len = QT_UINT32 (node->data);
8368 type = QT_UINT32 ((guint8 *) node->data + 4);
8369 if ((type >> 24) == 0xa9) {
8370 /* Type starts with the (C) symbol, so the next 32 bits are
8371 * the language code, which we ignore */
8373 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
8374 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
8375 QT_FOURCC ((guint8 *) node->data + 4))) {
8376 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
8378 /* we go for 3GP style encoding if major brands claims so,
8379 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
8380 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8381 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
8382 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
8384 /* 16-bit Language code is ignored here as well */
8385 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
8392 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
8393 ret = FALSE; /* may have to fallback */
8395 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8396 len - offset, env_vars);
8398 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8399 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, NULL);
8403 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8410 qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
8411 const char *dummy, GNode * node)
8413 qtdemux_tag_add_str_full (qtdemux, tag, dummy, node);
8417 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, const char *tag,
8418 const char *dummy, GNode * node)
8420 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8422 char *s, *t, *k = NULL;
8427 /* first try normal string tag if major brand not 3GP */
8428 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
8429 if (!qtdemux_tag_add_str_full (qtdemux, tag, dummy, node)) {
8430 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
8431 * let's try it 3gpp way after minor safety check */
8433 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
8439 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
8443 len = QT_UINT32 (data);
8447 count = QT_UINT8 (data + 14);
8449 for (; count; count--) {
8452 if (offset + 1 > len)
8454 slen = QT_UINT8 (data + offset);
8456 if (offset + slen > len)
8458 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8461 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
8463 t = g_strjoin (",", k, s, NULL);
8471 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
8478 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
8479 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, k, NULL);
8488 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
8494 qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1,
8495 const char *tag2, GNode * node)
8502 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8504 len = QT_UINT32 (data->data);
8505 type = QT_UINT32 ((guint8 *) data->data + 8);
8506 if (type == 0x00000000 && len >= 22) {
8507 n1 = QT_UINT16 ((guint8 *) data->data + 18);
8508 n2 = QT_UINT16 ((guint8 *) data->data + 20);
8510 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
8511 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8515 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
8516 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8524 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8532 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8534 len = QT_UINT32 (data->data);
8535 type = QT_UINT32 ((guint8 *) data->data + 8);
8536 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
8537 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8538 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
8539 n1 = QT_UINT16 ((guint8 *) data->data + 16);
8541 /* do not add bpm=0 */
8542 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
8543 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8544 tag1, (gdouble) n1, NULL);
8551 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, const char *tag1,
8552 const char *dummy, GNode * node)
8559 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8561 len = QT_UINT32 (data->data);
8562 type = QT_UINT32 ((guint8 *) data->data + 8);
8563 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
8564 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8565 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
8566 num = QT_UINT32 ((guint8 *) data->data + 16);
8568 /* do not add num=0 */
8569 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
8570 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8578 qtdemux_tag_add_covr (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8586 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8588 len = QT_UINT32 (data->data);
8589 type = QT_UINT32 ((guint8 *) data->data + 8);
8590 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
8591 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
8592 if ((buf = gst_tag_image_data_to_image_buffer ((guint8 *) data->data + 16,
8593 len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
8594 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
8595 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8597 gst_buffer_unref (buf);
8604 qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8612 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8614 len = QT_UINT32 (data->data);
8615 type = QT_UINT32 ((guint8 *) data->data + 8);
8616 if (type == 0x00000001 && len > 16) {
8617 guint y, m = 1, d = 1;
8620 s = g_strndup ((char *) data->data + 16, len - 16);
8621 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
8622 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
8623 if (ret >= 1 && y > 1500 && y < 3000) {
8626 date = g_date_new_dmy (d, m, y);
8627 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag,
8631 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
8639 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8644 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8646 /* re-route to normal string tag if major brand says so
8647 * or no data atom and compatible brand suggests so */
8648 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8649 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8650 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8657 len = QT_UINT32 (data->data);
8658 type = QT_UINT32 ((guint8 *) data->data + 8);
8659 if (type == 0x00000000 && len >= 18) {
8660 n = QT_UINT16 ((guint8 *) data->data + 16);
8664 genre = gst_tag_id3_genre_get (n - 1);
8665 if (genre != NULL) {
8666 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
8667 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8676 qtdemux_add_double_tag_from_str (GstQTDemux * demux, const gchar * tag,
8677 guint8 * data, guint32 datasize)
8682 /* make a copy to have \0 at the end */
8683 datacopy = g_strndup ((gchar *) data, datasize);
8685 /* convert the str to double */
8686 if (sscanf (datacopy, "%lf", &value) == 1) {
8687 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
8688 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_REPLACE, tag, value, NULL);
8690 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
8698 qtdemux_tag_add_revdns (GstQTDemux * demux, const char *tag,
8699 const char *tag_bis, GNode * node)
8708 const gchar *meanstr;
8709 const gchar *namestr;
8711 /* checking the whole ---- atom size for consistency */
8712 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
8713 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
8717 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
8719 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
8723 meansize = QT_UINT32 (mean->data);
8724 if (meansize <= 12) {
8725 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
8728 meanstr = ((gchar *) mean->data) + 12;
8730 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
8732 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
8736 namesize = QT_UINT32 (name->data);
8737 if (namesize <= 12) {
8738 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
8741 namestr = ((gchar *) name->data) + 12;
8748 * uint24 - data type
8752 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8754 GST_WARNING_OBJECT (demux, "No data atom in this tag");
8757 datasize = QT_UINT32 (data->data);
8758 if (datasize <= 16) {
8759 GST_WARNING_OBJECT (demux, "Data atom too small");
8762 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
8764 if (strncmp (meanstr, "com.apple.iTunes", meansize - 12) == 0) {
8767 const gchar name[28];
8768 const gchar tag[28];
8771 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
8772 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
8773 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
8774 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
8775 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
8776 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
8777 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
8778 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
8782 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
8783 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize - 12)) {
8784 switch (gst_tag_get_type (tags[i].tag)) {
8786 qtdemux_add_double_tag_from_str (demux, tags[i].tag,
8787 ((guint8 *) data->data) + 16, datasize - 16);
8790 qtdemux_tag_add_str (demux, tags[i].tag, NULL, node);
8799 if (i == G_N_ELEMENTS (tags))
8813 meanstr_dbg = g_strndup (meanstr, meansize - 12);
8814 namestr_dbg = g_strndup (namestr, namesize - 12);
8816 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
8817 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
8819 g_free (namestr_dbg);
8820 g_free (meanstr_dbg);
8825 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
8826 const char *tag, const char *tag_bis, GNode * node);
8829 FOURCC_pcst -> if media is a podcast -> bool
8830 FOURCC_cpil -> if media is part of a compilation -> bool
8831 FOURCC_pgap -> if media is part of a gapless context -> bool
8832 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
8838 const gchar *gst_tag;
8839 const gchar *gst_tag_bis;
8840 const GstQTDemuxAddTagFunc func;
8843 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8844 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8845 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
8846 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8847 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8848 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
8849 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8850 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8851 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8852 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8853 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8854 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8855 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8856 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8857 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8858 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8859 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
8860 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
8861 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
8862 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8863 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8864 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
8865 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8866 qtdemux_tag_add_num}, {
8867 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8868 qtdemux_tag_add_num}, {
8869 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
8870 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
8871 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
8872 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
8873 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
8874 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
8875 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8876 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8877 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
8878 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
8879 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
8880 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8881 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8882 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
8883 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
8884 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8885 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
8886 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
8887 qtdemux_tag_add_classification}, {
8889 /* This is a special case, some tags are stored in this
8890 * 'reverse dns naming', according to:
8891 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
8894 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}
8898 qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
8910 len = QT_UINT32 (data);
8911 buf = gst_buffer_new_and_alloc (len);
8912 memcpy (GST_BUFFER_DATA (buf), data, len);
8914 /* heuristic to determine style of tag */
8915 if (QT_FOURCC (data + 4) == FOURCC_____ ||
8916 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
8918 else if (demux->major_brand == FOURCC_qt__)
8919 style = "quicktime";
8920 /* fall back to assuming iso/3gp tag style */
8924 /* santize the name for the caps. */
8925 for (i = 0; i < 4; i++) {
8926 guint8 d = data[4 + i];
8927 if (g_ascii_isalnum (d))
8928 ndata[i] = g_ascii_tolower (d);
8933 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
8934 ndata[0], ndata[1], ndata[2], ndata[3]);
8935 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
8937 caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL);
8938 gst_buffer_set_caps (buf, caps);
8939 gst_caps_unref (caps);
8940 g_free (media_type);
8942 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT,
8943 GST_BUFFER_SIZE (buf), caps);
8945 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND,
8946 GST_QT_DEMUX_PRIVATE_TAG, buf, NULL);
8947 gst_buffer_unref (buf);
8951 qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta)
8959 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
8961 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
8963 GST_LOG_OBJECT (qtdemux, "no ilst");
8968 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
8971 GST_DEBUG_OBJECT (qtdemux, "new tag list");
8972 if (!qtdemux->tag_list)
8973 qtdemux->tag_list = gst_tag_list_new ();
8976 while (i < G_N_ELEMENTS (add_funcs)) {
8977 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
8981 len = QT_UINT32 (node->data);
8983 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
8984 GST_FOURCC_ARGS (add_funcs[i].fourcc));
8986 add_funcs[i].func (qtdemux, add_funcs[i].gst_tag,
8987 add_funcs[i].gst_tag_bis, node);
8989 g_node_destroy (node);
8995 /* parsed nodes have been removed, pass along remainder as blob */
8996 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
8997 (GNodeForeachFunc) qtdemux_tag_add_blob, qtdemux);
8999 /* parse up XMP_ node if existing */
9000 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
9003 GstTagList *taglist;
9005 buf = gst_buffer_new ();
9006 GST_BUFFER_DATA (buf) = ((guint8 *) xmp_->data) + 8;
9007 GST_BUFFER_SIZE (buf) = QT_UINT32 ((guint8 *) xmp_->data) - 8;
9009 taglist = gst_tag_list_from_xmp_buffer (buf);
9010 gst_buffer_unref (buf);
9012 qtdemux_handle_xmp_taglist (qtdemux, taglist);
9014 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
9021 GstStructure *structure; /* helper for sort function */
9023 guint min_req_bitrate;
9024 guint min_req_qt_version;
9028 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
9030 GstQtReference *ref_a = (GstQtReference *) a;
9031 GstQtReference *ref_b = (GstQtReference *) b;
9033 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
9034 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
9036 /* known bitrates go before unknown; higher bitrates go first */
9037 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
9040 /* sort the redirects and post a message for the application.
9043 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
9045 GstQtReference *best;
9048 GValue list_val = { 0, };
9051 g_assert (references != NULL);
9053 references = g_list_sort (references, qtdemux_redirects_sort_func);
9055 best = (GstQtReference *) references->data;
9057 g_value_init (&list_val, GST_TYPE_LIST);
9059 for (l = references; l != NULL; l = l->next) {
9060 GstQtReference *ref = (GstQtReference *) l->data;
9061 GValue struct_val = { 0, };
9063 ref->structure = gst_structure_new ("redirect",
9064 "new-location", G_TYPE_STRING, ref->location, NULL);
9066 if (ref->min_req_bitrate > 0) {
9067 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
9068 ref->min_req_bitrate, NULL);
9071 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
9072 g_value_set_boxed (&struct_val, ref->structure);
9073 gst_value_list_append_value (&list_val, &struct_val);
9074 g_value_unset (&struct_val);
9075 /* don't free anything here yet, since we need best->structure below */
9078 g_assert (best != NULL);
9079 s = gst_structure_copy (best->structure);
9081 if (g_list_length (references) > 1) {
9082 gst_structure_set_value (s, "locations", &list_val);
9085 g_value_unset (&list_val);
9087 for (l = references; l != NULL; l = l->next) {
9088 GstQtReference *ref = (GstQtReference *) l->data;
9090 gst_structure_free (ref->structure);
9091 g_free (ref->location);
9094 g_list_free (references);
9096 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
9097 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
9098 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
9099 qtdemux->posted_redirect = TRUE;
9102 /* look for redirect nodes, collect all redirect information and
9106 qtdemux_parse_redirects (GstQTDemux * qtdemux)
9108 GNode *rmra, *rmda, *rdrf;
9110 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
9112 GList *redirects = NULL;
9114 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
9116 GstQtReference ref = { NULL, NULL, 0, 0 };
9119 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
9120 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
9121 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
9122 ref.min_req_bitrate);
9125 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
9126 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
9127 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
9129 #ifndef GST_DISABLE_GST_DEBUG
9130 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
9132 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
9134 GST_LOG_OBJECT (qtdemux,
9135 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
9136 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
9137 bitmask, check_type);
9138 if (package == FOURCC_qtim && check_type == 0) {
9139 ref.min_req_qt_version = version;
9143 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
9148 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
9149 ref_data = (guint8 *) rdrf->data + 20;
9150 if (ref_type == FOURCC_alis) {
9151 guint record_len, record_version, fn_len;
9153 /* MacOSX alias record, google for alias-layout.txt */
9154 record_len = QT_UINT16 (ref_data + 4);
9155 record_version = QT_UINT16 (ref_data + 4 + 2);
9156 fn_len = QT_UINT8 (ref_data + 50);
9157 if (record_len > 50 && record_version == 2 && fn_len > 0) {
9158 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
9160 } else if (ref_type == FOURCC_url_) {
9161 ref.location = g_strdup ((gchar *) ref_data);
9163 GST_DEBUG_OBJECT (qtdemux,
9164 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
9165 GST_FOURCC_ARGS (ref_type));
9167 if (ref.location != NULL) {
9168 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
9169 redirects = g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
9171 GST_WARNING_OBJECT (qtdemux,
9172 "Failed to extract redirect location from rdrf atom");
9176 /* look for others */
9177 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
9180 if (redirects != NULL) {
9181 qtdemux_process_redirects (qtdemux, redirects);
9188 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
9193 tags = gst_tag_list_new ();
9195 if (qtdemux->major_brand == FOURCC_mjp2)
9196 fmt = "Motion JPEG 2000";
9197 else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0))
9199 else if (qtdemux->major_brand == FOURCC_qt__)
9201 else if (qtdemux->fragmented)
9204 fmt = "ISO MP4/M4A";
9206 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
9207 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
9209 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
9215 /* we have read th complete moov node now.
9216 * This function parses all of the relevant info, creates the traks and
9217 * prepares all data structures for playback
9220 qtdemux_parse_tree (GstQTDemux * qtdemux)
9227 guint64 creation_time;
9228 GstDateTime *datetime = NULL;
9231 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
9233 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
9234 return qtdemux_parse_redirects (qtdemux);
9237 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
9239 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
9240 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
9241 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
9242 } else if (version == 0) {
9243 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
9244 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
9245 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
9247 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
9251 /* Moving qt creation time (secs since 1904) to unix time */
9252 if (creation_time != 0) {
9253 if (creation_time > QTDEMUX_SECONDS_FROM_1904_TO_1970) {
9254 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
9255 datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time);
9257 GST_WARNING_OBJECT (qtdemux, "Can't handle datetimes before 1970 yet, "
9258 "please file a bug at http://bugzilla.gnome.org");
9262 if (!qtdemux->tag_list)
9263 qtdemux->tag_list = gst_tag_list_new ();
9265 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
9266 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
9268 gst_date_time_unref (datetime);
9271 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
9272 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
9274 /* check for fragmented file and get some (default) data */
9275 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
9278 GstByteReader mehd_data;
9280 /* let track parsing or anyone know weird stuff might happen ... */
9281 qtdemux->fragmented = TRUE;
9283 /* compensate for total duration */
9284 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
9286 qtdemux_parse_mehd (qtdemux, &mehd_data);
9289 /* set duration in the segment info */
9290 gst_qtdemux_get_duration (qtdemux, &duration);
9292 gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
9294 /* parse all traks */
9295 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
9297 qtdemux_parse_trak (qtdemux, trak);
9298 /* iterate all siblings */
9299 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
9303 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
9305 qtdemux_parse_udta (qtdemux, udta);
9307 GST_LOG_OBJECT (qtdemux, "No udta node found.");
9310 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
9315 /* taken from ffmpeg */
9317 get_size (guint8 * ptr, guint8 ** end)
9326 len = (len << 7) | (c & 0x7f);
9335 /* this can change the codec originally present in @list */
9337 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
9338 GNode * esds, GstTagList * list)
9340 int len = QT_UINT32 (esds->data);
9341 guint8 *ptr = esds->data;
9342 guint8 *end = ptr + len;
9344 guint8 *data_ptr = NULL;
9346 guint8 object_type_id = 0;
9347 const char *codec_name = NULL;
9348 GstCaps *caps = NULL;
9350 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
9352 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
9355 tag = QT_UINT8 (ptr);
9356 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
9358 len = get_size (ptr, &ptr);
9359 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
9363 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
9364 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
9368 guint max_bitrate, avg_bitrate;
9370 object_type_id = QT_UINT8 (ptr);
9371 max_bitrate = QT_UINT32 (ptr + 5);
9372 avg_bitrate = QT_UINT32 (ptr + 9);
9373 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
9374 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
9375 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
9376 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
9377 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
9378 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
9379 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9380 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
9382 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
9383 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
9390 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
9396 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
9400 GST_ERROR_OBJECT (qtdemux, "parse error");
9405 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
9406 * in use, and should also be used to override some other parameters for some
9408 switch (object_type_id) {
9409 case 0x20: /* MPEG-4 */
9410 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
9411 * profile_and_level_indication */
9412 if (data_ptr != NULL && data_len >= 5 &&
9413 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
9414 gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
9415 data_ptr + 4, data_len - 4);
9417 break; /* Nothing special needed here */
9418 case 0x21: /* H.264 */
9419 codec_name = "H.264 / AVC";
9420 caps = gst_caps_new_simple ("video/x-h264",
9421 "stream-format", G_TYPE_STRING, "avc",
9422 "alignment", G_TYPE_STRING, "au", NULL);
9424 case 0x40: /* AAC (any) */
9425 case 0x66: /* AAC Main */
9426 case 0x67: /* AAC LC */
9427 case 0x68: /* AAC SSR */
9428 /* Override channels and rate based on the codec_data, as it's often
9430 /* Only do so for basic setup without HE-AAC extension */
9431 if (data_ptr && data_len == 2) {
9432 guint channels, rateindex, rate;
9434 /* FIXME: add gst_codec_utils_aac_get_{channels|sample_rate}()? */
9435 channels = (data_ptr[1] & 0x7f) >> 3;
9436 if (channels > 0 && channels < 7) {
9437 stream->n_channels = channels;
9438 } else if (channels == 7) {
9439 stream->n_channels = 8;
9442 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7);
9443 rate = gst_codec_utils_aac_get_sample_rate_from_index (rateindex);
9445 stream->rate = rate;
9448 /* Set level and profile if possible */
9449 if (data_ptr != NULL && data_len >= 2) {
9450 gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
9451 data_ptr, data_len);
9454 case 0x60: /* MPEG-2, various profiles */
9460 codec_name = "MPEG-2 video";
9462 gst_caps_unref (stream->caps);
9463 stream->caps = gst_caps_new_simple ("video/mpeg",
9464 "mpegversion", G_TYPE_INT, 2,
9465 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9467 case 0x69: /* MP3 has two different values, accept either */
9469 /* change to mpeg1 layer 3 audio */
9470 gst_caps_set_simple (stream->caps, "layer", G_TYPE_INT, 3,
9471 "mpegversion", G_TYPE_INT, 1, NULL);
9472 codec_name = "MPEG-1 layer 3";
9474 case 0x6A: /* MPEG-1 */
9475 codec_name = "MPEG-1 video";
9477 gst_caps_unref (stream->caps);
9478 stream->caps = gst_caps_new_simple ("video/mpeg",
9479 "mpegversion", G_TYPE_INT, 1,
9480 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9482 case 0x6C: /* MJPEG */
9483 caps = gst_caps_new_simple ("image/jpeg", NULL);
9484 codec_name = "Motion-JPEG";
9486 case 0x6D: /* PNG */
9487 caps = gst_caps_new_simple ("image/png", NULL);
9488 codec_name = "PNG still images";
9490 case 0x6E: /* JPEG2000 */
9491 codec_name = "JPEG-2000";
9492 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9494 case 0xA4: /* Dirac */
9495 codec_name = "Dirac";
9496 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9498 case 0xA5: /* AC3 */
9499 codec_name = "AC-3 audio";
9500 caps = gst_caps_new_simple ("audio/x-ac3",
9501 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
9503 case 0xE1: /* QCELP */
9504 /* QCELP, the codec_data is a riff tag (little endian) with
9505 * 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). */
9506 caps = gst_caps_new_simple ("audio/qcelp", NULL);
9507 codec_name = "QCELP";
9513 /* If we have a replacement caps, then change our caps for this stream */
9515 gst_caps_unref (stream->caps);
9516 stream->caps = caps;
9519 if (codec_name && list)
9520 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9521 GST_TAG_AUDIO_CODEC, codec_name, NULL);
9523 /* Add the codec_data attribute to caps, if we have it */
9527 buffer = gst_buffer_new_and_alloc (data_len);
9528 memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len);
9530 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
9531 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
9533 #ifdef QTDEMUX_MODIFICATION
9534 /* Check the possiblity of H263 in case of mp4v fourCC */
9535 if(stream->fourcc == FOURCC_mp4v)
9537 gboolean err = TRUE;
9540 GST_ERROR_OBJECT (qtdemux, "Checking for the Possibility of H263");
9542 for(i=0; i<data_len-4; i++)
9544 if(QT_UINT32(data_ptr+i) == 0x00000120)
9546 GST_ERROR_OBJECT (qtdemux, "Found the VOL Marker in DCI Info, It is MPEG-4 Content");
9553 GST_ERROR_OBJECT (qtdemux, "Not Found the VOL Marker in DCI Info, Modifying the CAPS to H263");
9554 stream->fourcc = GST_MAKE_FOURCC ('h', '2', '6', '3');
9555 stream->caps = gst_caps_from_string ("video/x-h263");
9556 gst_buffer_unref (buffer);
9562 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
9564 gst_buffer_unref (buffer);
9569 #define _codec(name) \
9572 *codec_name = g_strdup (name); \
9577 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9578 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
9581 const GstStructure *s;
9585 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
9586 _codec ("PNG still images");
9587 caps = gst_caps_new_simple ("image/png", NULL);
9589 case GST_MAKE_FOURCC ('j', 'p', 'e', 'g'):
9590 _codec ("JPEG still images");
9591 caps = gst_caps_new_simple ("image/jpeg", NULL);
9593 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
9594 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
9595 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
9596 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
9597 _codec ("Motion-JPEG");
9598 caps = gst_caps_new_simple ("image/jpeg", NULL);
9600 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
9601 _codec ("Motion-JPEG format B");
9602 caps = gst_caps_new_simple ("video/x-mjpeg-b", NULL);
9604 case GST_MAKE_FOURCC ('m', 'j', 'p', '2'):
9605 _codec ("JPEG-2000");
9606 /* override to what it should be according to spec, avoid palette_data */
9607 stream->bits_per_sample = 24;
9608 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9610 case GST_MAKE_FOURCC ('S', 'V', 'Q', '3'):
9611 _codec ("Sorensen video v.3");
9612 caps = gst_caps_new_simple ("video/x-svq",
9613 "svqversion", G_TYPE_INT, 3, NULL);
9615 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
9616 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
9617 _codec ("Sorensen video v.1");
9618 caps = gst_caps_new_simple ("video/x-svq",
9619 "svqversion", G_TYPE_INT, 1, NULL);
9621 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9625 _codec ("Raw RGB video");
9626 bps = QT_UINT16 (stsd_data + 98);
9627 /* set common stuff */
9628 caps = gst_caps_new_simple ("video/x-raw-rgb",
9629 "endianness", G_TYPE_INT, G_BYTE_ORDER, "depth", G_TYPE_INT, bps,
9634 gst_caps_set_simple (caps,
9635 "bpp", G_TYPE_INT, 16,
9636 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9637 "red_mask", G_TYPE_INT, 0x7c00,
9638 "green_mask", G_TYPE_INT, 0x03e0,
9639 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9642 gst_caps_set_simple (caps,
9643 "bpp", G_TYPE_INT, 16,
9644 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9645 "red_mask", G_TYPE_INT, 0xf800,
9646 "green_mask", G_TYPE_INT, 0x07e0,
9647 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9650 gst_caps_set_simple (caps,
9651 "bpp", G_TYPE_INT, 24,
9652 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9653 "red_mask", G_TYPE_INT, 0xff0000,
9654 "green_mask", G_TYPE_INT, 0x00ff00,
9655 "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
9658 gst_caps_set_simple (caps,
9659 "bpp", G_TYPE_INT, 32,
9660 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9661 "alpha_mask", G_TYPE_INT, 0xff000000,
9662 "red_mask", G_TYPE_INT, 0x00ff0000,
9663 "green_mask", G_TYPE_INT, 0x0000ff00,
9664 "blue_mask", G_TYPE_INT, 0x000000ff, NULL);
9672 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
9673 _codec ("Raw planar YUV 4:2:0");
9674 caps = gst_caps_new_simple ("video/x-raw-yuv",
9675 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
9678 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
9679 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
9680 _codec ("Raw packed YUV 4:2:2");
9681 caps = gst_caps_new_simple ("video/x-raw-yuv",
9682 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
9685 case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'):
9686 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
9687 _codec ("Raw packed YUV 4:2:2");
9688 caps = gst_caps_new_simple ("video/x-raw-yuv",
9689 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'),
9692 case GST_MAKE_FOURCC ('v', '2', '1', '0'):
9693 _codec ("Raw packed YUV 10-bit 4:2:2");
9694 caps = gst_caps_new_simple ("video/x-raw-yuv",
9695 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('v', '2', '1', '0'),
9698 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
9699 _codec ("Raw packed RGB 10-bit 4:4:4");
9700 caps = gst_caps_new_simple ("video/x-raw-rgb",
9701 "endianness", G_TYPE_INT, G_BIG_ENDIAN, "depth", G_TYPE_INT, 30,
9702 "bpp", G_TYPE_INT, 32,
9703 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9704 "red_mask", G_TYPE_INT, 0x3ff00000,
9705 "green_mask", G_TYPE_INT, 0x000ffc00,
9706 "blue_mask", G_TYPE_INT, 0x000003ff, NULL);
9708 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
9709 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
9710 _codec ("MPEG-1 video");
9711 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
9712 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9714 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
9715 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
9716 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
9717 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
9718 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080i60 */
9719 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
9720 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
9721 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
9722 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
9723 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
9724 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
9725 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 */
9726 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
9727 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
9728 _codec ("MPEG-2 video");
9729 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
9730 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9732 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
9733 _codec ("GIF still images");
9734 caps = gst_caps_new_simple ("image/gif", NULL);
9736 case GST_MAKE_FOURCC ('h', '2', '6', '3'):
9737 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
9738 case GST_MAKE_FOURCC ('s', '2', '6', '3'):
9739 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
9741 /* ffmpeg uses the height/width props, don't know why */
9742 caps = gst_caps_new_simple ("video/x-h263", NULL);
9744 case GST_MAKE_FOURCC ('m', 'p', '4', 'v'):
9745 case GST_MAKE_FOURCC ('M', 'P', '4', 'V'):
9746 _codec ("MPEG-4 video");
9747 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
9748 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9750 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
9751 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
9752 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
9753 caps = gst_caps_new_simple ("video/x-msmpeg",
9754 "msmpegversion", G_TYPE_INT, 43, NULL);
9756 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
9757 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
9758 _codec ("3ivX video");
9759 caps = gst_caps_new_simple ("video/x-3ivx", NULL);
9761 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
9763 caps = gst_caps_new_simple ("video/x-divx",
9764 "divxversion", G_TYPE_INT, 3, NULL);
9766 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
9767 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
9769 caps = gst_caps_new_simple ("video/x-divx",
9770 "divxversion", G_TYPE_INT, 4, NULL);
9772 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
9774 caps = gst_caps_new_simple ("video/x-divx",
9775 "divxversion", G_TYPE_INT, 5, NULL);
9777 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
9778 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
9779 _codec ("XVID MPEG-4");
9780 caps = gst_caps_new_simple ("video/x-xvid", NULL);
9783 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
9784 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
9785 caps = gst_caps_new_simple ("video/mpeg",
9786 "mpegversion", G_TYPE_INT, 4, NULL);
9788 *codec_name = g_strdup ("FFmpeg MPEG-4");
9791 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
9793 caps = gst_caps_new_simple ("video/x-cinepak", NULL);
9795 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
9796 _codec ("Apple QuickDraw");
9797 caps = gst_caps_new_simple ("video/x-qdrw", NULL);
9799 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
9800 _codec ("Apple video");
9801 caps = gst_caps_new_simple ("video/x-apple-video", NULL);
9803 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
9804 _codec ("H.264 / AVC");
9805 caps = gst_caps_new_simple ("video/x-h264",
9806 "stream-format", G_TYPE_STRING, "avc",
9807 "alignment", G_TYPE_STRING, "au", NULL);
9809 case GST_MAKE_FOURCC ('r', 'l', 'e', ' '):
9810 _codec ("Run-length encoding");
9811 caps = gst_caps_new_simple ("video/x-rle",
9812 "layout", G_TYPE_STRING, "quicktime", NULL);
9814 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
9815 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
9816 _codec ("Indeo Video 3");
9817 caps = gst_caps_new_simple ("video/x-indeo",
9818 "indeoversion", G_TYPE_INT, 3, NULL);
9820 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
9821 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
9822 _codec ("Intel Video 4");
9823 caps = gst_caps_new_simple ("video/x-indeo",
9824 "indeoversion", G_TYPE_INT, 4, NULL);
9826 case GST_MAKE_FOURCC ('d', 'v', 'c', 'p'):
9827 case GST_MAKE_FOURCC ('d', 'v', 'c', ' '):
9828 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
9829 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
9830 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
9831 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
9832 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
9833 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
9834 _codec ("DV Video");
9835 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
9836 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9838 case GST_MAKE_FOURCC ('d', 'v', '5', 'n'): /* DVCPRO50 NTSC */
9839 case GST_MAKE_FOURCC ('d', 'v', '5', 'p'): /* DVCPRO50 PAL */
9840 _codec ("DVCPro50 Video");
9841 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
9842 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9844 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
9845 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
9846 _codec ("DVCProHD Video");
9847 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
9848 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9850 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
9851 _codec ("Apple Graphics (SMC)");
9852 caps = gst_caps_new_simple ("video/x-smc", NULL);
9854 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
9856 caps = gst_caps_new_simple ("video/x-vp3", NULL);
9858 case GST_MAKE_FOURCC ('X', 'i', 'T', 'h'):
9860 caps = gst_caps_new_simple ("video/x-theora", NULL);
9861 /* theora uses one byte of padding in the data stream because it does not
9862 * allow 0 sized packets while theora does */
9863 stream->padding = 1;
9865 case GST_MAKE_FOURCC ('d', 'r', 'a', 'c'):
9867 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9869 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
9870 _codec ("TIFF still images");
9871 caps = gst_caps_new_simple ("image/tiff", NULL);
9873 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
9874 _codec ("Apple Intermediate Codec");
9875 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
9877 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
9878 _codec ("AVID DNxHD");
9879 caps = gst_caps_from_string ("video/x-dnxhd");
9881 case GST_MAKE_FOURCC ('V', 'P', '8', '0'):
9883 caps = gst_caps_from_string ("video/x-vp8");
9886 caps = gst_caps_new_simple ("video/x-wmv",
9887 "wmvversion", G_TYPE_INT, 3,
9888 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
9891 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
9896 s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
9897 GST_FOURCC_ARGS (fourcc));
9898 caps = gst_caps_new_simple (s, NULL);
9903 /* enable clipping for raw video streams */
9904 s = gst_caps_get_structure (caps, 0);
9905 name = gst_structure_get_name (s);
9906 if (g_str_has_prefix (name, "video/x-raw-")) {
9907 stream->need_clip = TRUE;
9913 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9914 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
9917 const GstStructure *s;
9921 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
9924 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
9925 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9926 _codec ("Raw 8-bit PCM audio");
9927 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 8,
9928 "depth", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
9930 case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
9931 endian = G_BIG_ENDIAN;
9933 case GST_MAKE_FOURCC ('s', 'o', 'w', 't'):
9939 endian = G_LITTLE_ENDIAN;
9941 depth = stream->bytes_per_packet * 8;
9942 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
9945 caps = gst_caps_new_simple ("audio/x-raw-int",
9946 "width", G_TYPE_INT, depth, "depth", G_TYPE_INT, depth,
9947 "endianness", G_TYPE_INT, endian,
9948 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9951 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
9952 _codec ("Raw 64-bit floating-point audio");
9953 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 64,
9954 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9956 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
9957 _codec ("Raw 32-bit floating-point audio");
9958 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 32,
9959 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9962 _codec ("Raw 24-bit PCM audio");
9963 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
9965 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 24,
9966 "depth", G_TYPE_INT, 24,
9967 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9968 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9970 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
9971 _codec ("Raw 32-bit PCM audio");
9972 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 32,
9973 "depth", G_TYPE_INT, 32,
9974 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9975 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9977 case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
9978 _codec ("Mu-law audio");
9979 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
9981 case GST_MAKE_FOURCC ('a', 'l', 'a', 'w'):
9982 _codec ("A-law audio");
9983 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
9987 _codec ("Microsoft ADPCM");
9988 /* Microsoft ADPCM-ACM code 2 */
9989 caps = gst_caps_new_simple ("audio/x-adpcm",
9990 "layout", G_TYPE_STRING, "microsoft", NULL);
9994 _codec ("DVI/IMA ADPCM");
9995 caps = gst_caps_new_simple ("audio/x-adpcm",
9996 "layout", G_TYPE_STRING, "dvi", NULL);
10000 _codec ("DVI/Intel IMA ADPCM");
10001 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
10002 caps = gst_caps_new_simple ("audio/x-adpcm",
10003 "layout", G_TYPE_STRING, "quicktime", NULL);
10007 /* MPEG layer 3, CBR only (pre QT4.1) */
10008 case GST_MAKE_FOURCC ('.', 'm', 'p', '3'):
10009 _codec ("MPEG-1 layer 3");
10010 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
10011 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
10012 "mpegversion", G_TYPE_INT, 1, NULL);
10015 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
10016 _codec ("EAC-3 audio");
10017 caps = gst_caps_new_simple ("audio/x-eac3",
10018 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
10019 stream->sampled = TRUE;
10021 case GST_MAKE_FOURCC ('a', 'c', '-', '3'):
10022 _codec ("AC-3 audio");
10023 caps = gst_caps_new_simple ("audio/x-ac3",
10024 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
10025 stream->sampled = TRUE;
10027 case GST_MAKE_FOURCC ('M', 'A', 'C', '3'):
10029 caps = gst_caps_new_simple ("audio/x-mace",
10030 "maceversion", G_TYPE_INT, 3, NULL);
10032 case GST_MAKE_FOURCC ('M', 'A', 'C', '6'):
10034 caps = gst_caps_new_simple ("audio/x-mace",
10035 "maceversion", G_TYPE_INT, 6, NULL);
10037 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
10039 caps = gst_caps_new_simple ("application/ogg", NULL);
10041 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
10042 _codec ("DV audio");
10043 caps = gst_caps_new_simple ("audio/x-dv", NULL);
10045 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
10046 _codec ("MPEG-4 AAC audio");
10047 caps = gst_caps_new_simple ("audio/mpeg",
10048 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
10049 "stream-format", G_TYPE_STRING, "raw", NULL);
10051 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
10052 _codec ("QDesign Music");
10053 caps = gst_caps_new_simple ("audio/x-qdm", NULL);
10055 case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'):
10056 _codec ("QDesign Music v.2");
10057 /* FIXME: QDesign music version 2 (no constant) */
10059 caps = gst_caps_new_simple ("audio/x-qdm2",
10060 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
10061 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
10062 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
10064 caps = gst_caps_new_simple ("audio/x-qdm2", NULL);
10067 case GST_MAKE_FOURCC ('a', 'g', 's', 'm'):
10068 _codec ("GSM audio");
10069 caps = gst_caps_new_simple ("audio/x-gsm", NULL);
10071 case GST_MAKE_FOURCC ('s', 'a', 'm', 'r'):
10072 _codec ("AMR audio");
10073 caps = gst_caps_new_simple ("audio/AMR", NULL);
10075 case GST_MAKE_FOURCC ('s', 'a', 'w', 'b'):
10076 _codec ("AMR-WB audio");
10077 caps = gst_caps_new_simple ("audio/AMR-WB", NULL);
10079 case GST_MAKE_FOURCC ('i', 'm', 'a', '4'):
10080 _codec ("Quicktime IMA ADPCM");
10081 caps = gst_caps_new_simple ("audio/x-adpcm",
10082 "layout", G_TYPE_STRING, "quicktime", NULL);
10084 case GST_MAKE_FOURCC ('a', 'l', 'a', 'c'):
10085 _codec ("Apple lossless audio");
10086 caps = gst_caps_new_simple ("audio/x-alac", NULL);
10088 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
10089 _codec ("QualComm PureVoice");
10090 caps = gst_caps_from_string ("audio/qcelp");
10094 caps = gst_caps_new_simple ("audio/x-wma", NULL);
10096 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
10102 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10103 GST_FOURCC_ARGS (fourcc));
10104 caps = gst_caps_new_simple (s, NULL);
10109 /* enable clipping for raw audio streams */
10110 s = gst_caps_get_structure (caps, 0);
10111 name = gst_structure_get_name (s);
10112 if (g_str_has_prefix (name, "audio/x-raw-")) {
10113 stream->need_clip = TRUE;
10119 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
10120 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
10124 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
10127 case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
10128 _codec ("DVD subtitle");
10129 caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
10131 case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
10132 _codec ("Quicktime timed text");
10134 case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
10135 _codec ("3GPP timed text");
10137 caps = gst_caps_new_simple ("text/plain", NULL);
10138 /* actual text piece needs to be extracted */
10139 stream->need_process = TRUE;
10145 s = g_strdup_printf ("text/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10146 GST_FOURCC_ARGS (fourcc));
10147 caps = gst_caps_new_simple (s, NULL);