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 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
51 * with newer GLib versions (>= 2.31.0) */
52 #define GLIB_DISABLE_DEPRECATION_WARNINGS
54 #include "gst/gst-i18n-plugin.h"
56 #include <glib/gprintf.h>
57 #include <gst/tag/tag.h>
59 #include "qtatomparser.h"
60 #include "qtdemux_types.h"
61 #include "qtdemux_dump.h"
62 #include "qtdemux_fourcc.h"
63 #include "qtdemux_lang.h"
65 #include "qtpalette.h"
67 #include "gst/riff/riff-media.h"
68 #include "gst/riff/riff-read.h"
70 #include <gst/pbutils/pbutils.h>
80 /* max. size considered 'sane' for non-mdat atoms */
81 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
83 /* if the sample index is larger than this, something is likely wrong */
84 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
86 /* For converting qt creation times to unix epoch times */
87 #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
88 #define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
89 #define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
90 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
92 #ifdef QTDEMUX_MODIFICATION
93 /* default value of buffer-size property */
94 #define QTDEMUX_MAX_BUFFER_SIZE (100*1024*1024)
96 #define QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE 0 /* 0: sending P-frames also, 1: only key frames */
97 /* properties considering different cases of file formats */
101 PROP_MAX_BUFFER_SIZE,
107 GST_DEBUG_CATEGORY (qtdemux_debug);
109 /*typedef struct _QtNode QtNode; */
110 typedef struct _QtDemuxSegment QtDemuxSegment;
111 typedef struct _QtDemuxSample QtDemuxSample;
113 #ifdef QTDEMUX_MODIFICATION
114 typedef struct _TrickPlayInfo TrickPlayInfo;
116 struct _TrickPlayInfo
120 guint64 kidxs_dur_diff; /* duration between two consecutive key frames */
121 gint32 show_samples; /* samples to show between two consecutive key frames */
122 guint64 start_pos; /* trickplay start position */
134 struct _QtDemuxSample
137 gint32 pts_offset; /* Add this value to timestamp to get the pts */
139 guint64 timestamp; /* DTS In mov time */
140 guint32 duration; /* In mov time */
141 gboolean keyframe; /* TRUE when this packet is a keyframe */
144 /* timestamp is the DTS */
145 #define QTSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\
146 GST_SECOND, (stream)->timescale)
147 /* timestamp + offset is the PTS */
148 #define QTSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \
149 (sample)->pts_offset, GST_SECOND, (stream)->timescale)
150 /* timestamp + duration - dts is the duration */
151 #define QTSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
152 (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
153 /* timestamp + offset + duration - pts is the duration */
154 #define QTSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
155 (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
157 #define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
160 * Quicktime has tracks and segments. A track is a continuous piece of
161 * multimedia content. The track is not always played from start to finish but
162 * instead, pieces of the track are 'cut out' and played in sequence. This is
163 * what the segments do.
165 * Inside the track we have keyframes (K) and delta frames. The track has its
166 * own timing, which starts from 0 and extends to end. The position in the track
167 * is called the media_time.
169 * The segments now describe the pieces that should be played from this track
170 * and are basically tupples of media_time/duration/rate entries. We can have
171 * multiple segments and they are all played after one another. An example:
173 * segment 1: media_time: 1 second, duration: 1 second, rate 1
174 * segment 2: media_time: 3 second, duration: 2 second, rate 2
176 * To correctly play back this track, one must play: 1 second of media starting
177 * from media_time 1 followed by 2 seconds of media starting from media_time 3
180 * Each of the segments will be played at a specific time, the first segment at
181 * time 0, the second one after the duration of the first one, etc.. Note that
182 * the time in resulting playback is not identical to the media_time of the
185 * Visually, assuming the track has 4 second of media_time:
188 * .-----------------------------------------------------------.
189 * track: | K.....K.........K........K.......K.......K...........K... |
190 * '-----------------------------------------------------------'
192 * .------------^ ^ .----------^ ^
193 * / .-------------' / .------------------'
195 * .--------------. .--------------.
196 * | segment 1 | | segment 2 |
197 * '--------------' '--------------'
199 * The challenge here is to cut out the right pieces of the track for each of
200 * the playback segments. This fortunatly can easily be done with the SEGMENT
201 * events of gstreamer.
203 * For playback of segment 1, we need to provide the decoder with the keyframe
204 * (a), in the above figure, but we must instruct it only to output the decoded
205 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
206 * position set to the time of the segment: 0.
208 * We then proceed to push data from keyframe (a) to frame (b). The decoder
209 * decodes but clips all before media_time 1.
211 * After finishing a segment, we push out a new SEGMENT event with the clipping
212 * boundaries of the new data.
214 * This is a good usecase for the GStreamer accumulated SEGMENT events.
217 struct _QtDemuxSegment
219 /* global time and duration, all gst time */
223 /* media time of trak, all gst time */
229 struct _QtDemuxStream
238 /* if the stream has a redirect URI in its headers, we store it here */
245 guint64 duration; /* in timescale */
249 gchar lang_id[4]; /* ISO 639-2T language code */
253 QtDemuxSample *samples;
254 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
255 guint32 min_duration; /* duration in timescale of first sample, used for figuring out
256 the framerate, in timescale units */
258 /* if we use chunks or samples */
270 /* Numerator/denominator framerate */
273 guint16 bits_per_sample;
274 guint16 color_table_id;
279 guint samples_per_packet;
280 guint samples_per_frame;
281 guint bytes_per_packet;
282 guint bytes_per_sample;
283 guint bytes_per_frame;
286 /* when a discontinuity is pending */
289 /* list of buffers to push first */
292 /* if we need to clip this buffer. This is only needed for uncompressed
296 /* buffer needs some custom processing, e.g. subtitles */
297 gboolean need_process;
299 /* current position */
300 guint32 segment_index;
301 guint32 sample_index;
302 guint64 time_position; /* in gst time */
304 /* the Gst segment we are processing out, used for clipping */
307 /* last GstFlowReturn */
308 GstFlowReturn last_ret;
310 /* quicktime segments */
312 QtDemuxSegment *segments;
317 GstTagList *pending_tags;
318 gboolean send_global_tags;
320 GstEvent *pending_event;
330 gboolean chunks_are_chunks;
334 GstByteReader co_chunk;
336 guint32 current_chunk;
338 guint32 samples_per_chunk;
339 guint32 stco_sample_index;
341 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
344 guint32 n_samples_per_chunk;
345 guint32 stsc_chunk_index;
346 guint32 stsc_sample_index;
347 guint64 chunk_offset;
350 guint32 stts_samples;
351 guint32 n_sample_times;
352 guint32 stts_sample_index;
354 guint32 stts_duration;
356 gboolean stss_present;
357 guint32 n_sample_syncs;
360 gboolean stps_present;
361 guint32 n_sample_partial_syncs;
364 gboolean ctts_present;
365 guint32 n_composition_times;
367 guint32 ctts_sample_index;
372 gboolean parsed_trex;
373 guint32 def_sample_duration;
374 guint32 def_sample_size;
375 guint32 def_sample_flags;
376 #ifdef QTDEMUX_MODIFICATION
377 TrickPlayInfo *trickplay_info; /* trickplay specific handle */
383 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
384 QTDEMUX_STATE_HEADER, /* Parsing the header */
385 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
386 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
389 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
390 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
391 guint32 fourcc, GstByteReader * parser);
392 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
393 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
394 guint32 fourcc, GstByteReader * parser);
396 static GstStaticPadTemplate gst_qtdemux_sink_template =
397 GST_STATIC_PAD_TEMPLATE ("sink",
400 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
404 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
405 GST_STATIC_PAD_TEMPLATE ("video_%02d",
408 GST_STATIC_CAPS_ANY);
410 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
411 GST_STATIC_PAD_TEMPLATE ("audio_%02d",
414 GST_STATIC_CAPS_ANY);
416 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
417 GST_STATIC_PAD_TEMPLATE ("subtitle_%02d",
420 GST_STATIC_CAPS_ANY);
422 GST_BOILERPLATE (GstQTDemux, gst_qtdemux, GstQTDemux, GST_TYPE_ELEMENT);
424 static void gst_qtdemux_dispose (GObject * object);
427 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
430 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
431 QtDemuxStream * str, gint64 media_offset);
433 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
434 static GstIndex *gst_qtdemux_get_index (GstElement * element);
435 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
436 GstStateChange transition);
437 static gboolean qtdemux_sink_activate (GstPad * sinkpad);
438 static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
439 static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
441 static void gst_qtdemux_loop (GstPad * pad);
442 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
443 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
445 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
446 const guint8 * buffer, guint length);
447 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
448 const guint8 * buffer, guint length);
449 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
451 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
452 QtDemuxStream * stream, GNode * esds, GstTagList * list);
453 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
454 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
455 gchar ** codec_name);
456 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
457 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
458 gchar ** codec_name);
459 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
460 QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
461 gchar ** codec_name);
462 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
463 QtDemuxStream * stream, guint32 n);
464 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
466 #ifdef QTDEMUX_MODIFICATION
467 static void gst_qtdemux_set_property (GObject * object, guint prop_id,
468 const GValue * value, GParamSpec * pspec);
469 static void gst_qtdemux_get_property (GObject * object, guint prop_id,
470 GValue * value, GParamSpec * pspec);
471 static gint32 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index);
472 static void gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
473 static void gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
477 gst_qtdemux_base_init (gpointer klass)
479 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
481 gst_element_class_add_static_pad_template (element_class,
482 &gst_qtdemux_sink_template);
483 gst_element_class_add_static_pad_template (element_class,
484 &gst_qtdemux_videosrc_template);
485 gst_element_class_add_static_pad_template (element_class,
486 &gst_qtdemux_audiosrc_template);
487 gst_element_class_add_static_pad_template (element_class,
488 &gst_qtdemux_subsrc_template);
489 gst_element_class_set_details_simple (element_class, "QuickTime demuxer",
491 "Demultiplex a QuickTime file into audio and video streams",
492 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
494 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
498 gst_qtdemux_class_init (GstQTDemuxClass * klass)
500 GObjectClass *gobject_class;
501 GstElementClass *gstelement_class;
503 gobject_class = (GObjectClass *) klass;
504 gstelement_class = (GstElementClass *) klass;
506 parent_class = g_type_class_peek_parent (klass);
508 gobject_class->dispose = gst_qtdemux_dispose;
510 #ifdef QTDEMUX_MODIFICATION
511 gobject_class->set_property = gst_qtdemux_set_property;
512 gobject_class->get_property = gst_qtdemux_get_property;
515 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
517 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
518 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
520 #ifdef QTDEMUX_MODIFICATION
521 g_object_class_install_property (gobject_class, PROP_MAX_BUFFER_SIZE,
522 g_param_spec_uint ("buffer-size", "buffer-size",
523 "Maximum buffer size for mdat atom buffering in case it comes before the moov atom",
524 1, G_MAXUINT, QTDEMUX_MAX_BUFFER_SIZE,
525 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
527 g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
528 g_param_spec_string ("temp-location", "temp-location",
529 "Location for the mdat atom buffering incase it comes before the moov atom",
531 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
533 g_object_class_install_property (gobject_class, PROP_FWDTRICK_MODE,
534 g_param_spec_boolean ("fwd-trick-mode",
535 "Forward trickplay mode to use",
536 "(0) Sending Non-Keyframes also in forward trickplay (1) Sending only keyframes in forward trickplay",
537 QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE,
542 gst_tag_register_musicbrainz_tags ();
546 gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
549 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
550 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
551 gst_pad_set_activatepull_function (qtdemux->sinkpad,
552 qtdemux_sink_activate_pull);
553 gst_pad_set_activatepush_function (qtdemux->sinkpad,
554 qtdemux_sink_activate_push);
555 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
556 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
557 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
559 qtdemux->state = QTDEMUX_STATE_INITIAL;
560 qtdemux->pullbased = FALSE;
561 qtdemux->posted_redirect = FALSE;
562 qtdemux->neededbytes = 16;
564 qtdemux->adapter = gst_adapter_new ();
566 qtdemux->first_mdat = -1;
567 qtdemux->got_moov = FALSE;
568 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
569 qtdemux->mdatbuffer = NULL;
570 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
571 #ifdef QTDEMUX_MODIFICATION
572 /* Initialization of properties with default values*/
573 qtdemux->filename = g_strdup("/tmp/qtdemux");
574 qtdemux->file = NULL;
575 qtdemux->ofile = NULL;
576 qtdemux->filesize = 0;
577 qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
578 qtdemux->fwdtrick_mode = QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE;
583 gst_qtdemux_dispose (GObject * object)
585 GstQTDemux *qtdemux = GST_QTDEMUX (object);
587 if (qtdemux->adapter) {
588 g_object_unref (G_OBJECT (qtdemux->adapter));
589 qtdemux->adapter = NULL;
592 #ifdef QTDEMUX_MODIFICATION
593 if (qtdemux->filename)
594 g_free (qtdemux->filename);
597 G_OBJECT_CLASS (parent_class)->dispose (object);
600 #ifdef QTDEMUX_MODIFICATION
602 gst_qtdemux_set_property (GObject * object, guint prop_id,
603 const GValue * value, GParamSpec * pspec)
606 demux = GST_QTDEMUX (object);
609 case PROP_MAX_BUFFER_SIZE:
610 demux->maxbuffersize = g_value_get_uint (value);
612 case PROP_TEMP_LOCATION:
614 g_free (demux->filename);
615 demux->filename = g_strdup (g_value_get_string (value));
617 case PROP_FWDTRICK_MODE:
618 demux->fwdtrick_mode = g_value_get_boolean(value);
621 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
627 gst_qtdemux_get_property (GObject * object, guint prop_id,
628 GValue * value, GParamSpec * pspec)
631 demux = GST_QTDEMUX (object);
634 case PROP_MAX_BUFFER_SIZE:
635 g_value_set_uint (value, demux->maxbuffersize);
637 case PROP_TEMP_LOCATION:
638 g_value_set_string (value, demux->filename);
640 case PROP_FWDTRICK_MODE:
641 g_value_set_boolean(value, demux->fwdtrick_mode);
644 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
654 if (qtdemux->posted_redirect) {
655 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
656 (_("This file contains no playable streams.")),
657 ("no known streams found, a redirect message has been posted"));
659 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
660 (_("This file contains no playable streams.")),
661 ("no known streams found"));
666 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
671 if (G_UNLIKELY (size == 0)) {
673 GstBuffer *tmp = NULL;
675 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
676 if (ret != GST_FLOW_OK)
679 size = QT_UINT32 (GST_BUFFER_DATA (tmp));
680 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
682 gst_buffer_unref (tmp);
685 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
686 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
687 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
688 /* we're pulling header but already got most interesting bits,
689 * so never mind the rest (e.g. tags) (that much) */
690 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
692 return GST_FLOW_UNEXPECTED;
694 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
695 (_("This file is invalid and cannot be played.")),
696 ("atom has bogus size %" G_GUINT64_FORMAT, size));
697 return GST_FLOW_ERROR;
701 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
703 if (G_UNLIKELY (flow != GST_FLOW_OK))
706 /* Catch short reads - we don't want any partial atoms */
707 if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) {
708 GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT,
709 GST_BUFFER_SIZE (*buf), size);
710 gst_buffer_unref (*buf);
712 return GST_FLOW_UNEXPECTED;
720 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
721 GstFormat dest_format, gint64 * dest_value)
724 QtDemuxStream *stream = gst_pad_get_element_private (pad);
725 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
728 if (stream->subtype != FOURCC_vide) {
733 switch (src_format) {
734 case GST_FORMAT_TIME:
735 switch (dest_format) {
736 case GST_FORMAT_BYTES:{
737 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
741 *dest_value = stream->samples[index].offset;
743 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
744 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
745 GST_TIME_ARGS (src_value), *dest_value);
753 case GST_FORMAT_BYTES:
754 switch (dest_format) {
755 case GST_FORMAT_TIME:{
757 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
764 gst_util_uint64_scale (stream->samples[index].timestamp,
765 GST_SECOND, stream->timescale);
766 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Offset->Time :%"
767 G_GUINT64_FORMAT "->%" GST_TIME_FORMAT,
768 src_value, GST_TIME_ARGS (*dest_value));
781 gst_object_unref (qtdemux);
787 static const GstQueryType *
788 gst_qtdemux_get_src_query_types (GstPad * pad)
790 static const GstQueryType src_types[] = {
803 gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration)
807 *duration = GST_CLOCK_TIME_NONE;
809 if (qtdemux->duration != 0) {
810 if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
811 *duration = gst_util_uint64_scale (qtdemux->duration,
812 GST_SECOND, qtdemux->timescale);
819 gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
821 gboolean res = FALSE;
822 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
824 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
826 switch (GST_QUERY_TYPE (query)) {
827 case GST_QUERY_POSITION:
828 if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) {
829 gst_query_set_position (query, GST_FORMAT_TIME,
830 qtdemux->segment.last_stop);
834 case GST_QUERY_DURATION:{
837 gst_query_parse_duration (query, &fmt, NULL);
838 if (fmt == GST_FORMAT_TIME) {
839 gint64 duration = -1;
841 gst_qtdemux_get_duration (qtdemux, &duration);
843 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
849 case GST_QUERY_CONVERT:{
850 GstFormat src_fmt, dest_fmt;
851 gint64 src_value, dest_value = 0;
853 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
855 res = gst_qtdemux_src_convert (pad,
856 src_fmt, src_value, dest_fmt, &dest_value);
858 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
863 case GST_QUERY_FORMATS:
864 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
867 case GST_QUERY_SEEKING:{
871 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
872 if (fmt == GST_FORMAT_TIME) {
873 gint64 duration = -1;
875 gst_qtdemux_get_duration (qtdemux, &duration);
877 if (!qtdemux->pullbased) {
880 /* we might be able with help from upstream */
882 q = gst_query_new_seeking (GST_FORMAT_BYTES);
883 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
884 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
885 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
889 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
895 res = gst_pad_query_default (pad, query);
899 gst_object_unref (qtdemux);
905 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
907 if (G_LIKELY (stream->pad)) {
908 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
909 GST_DEBUG_PAD_NAME (stream->pad));
911 if (G_UNLIKELY (stream->pending_tags)) {
912 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
913 stream->pending_tags);
914 gst_pad_push_event (stream->pad,
915 gst_event_new_tag (stream->pending_tags));
916 stream->pending_tags = NULL;
919 if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
920 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
922 gst_pad_push_event (stream->pad,
923 gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
924 stream->send_global_tags = FALSE;
929 /* push event on all source pads; takes ownership of the event */
931 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
934 gboolean has_valid_stream = FALSE;
935 GstEventType etype = GST_EVENT_TYPE (event);
937 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
938 GST_EVENT_TYPE_NAME (event));
940 for (n = 0; n < qtdemux->n_streams; n++) {
942 QtDemuxStream *stream = qtdemux->streams[n];
944 if ((pad = stream->pad)) {
945 has_valid_stream = TRUE;
947 if (etype == GST_EVENT_EOS) {
948 /* let's not send twice */
949 if (stream->sent_eos)
951 stream->sent_eos = TRUE;
954 gst_pad_push_event (pad, gst_event_ref (event));
958 gst_event_unref (event);
960 /* if it is EOS and there are no pads, post an error */
961 if (!has_valid_stream && etype == GST_EVENT_EOS) {
962 gst_qtdemux_post_no_playable_stream_error (qtdemux);
966 /* push a pending newsegment event, if any from the streaming thread */
968 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
970 if (qtdemux->pending_newsegment) {
971 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
972 qtdemux->pending_newsegment = NULL;
982 find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data)
984 if (s1->timestamp > *media_time)
990 /* find the index of the sample that includes the data for @media_time using a
991 * binary search. Only to be called in optimized cases of linear search below.
993 * Returns the index of the sample.
996 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
999 QtDemuxSample *result;
1002 /* convert media_time to mov format */
1004 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1006 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1007 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1008 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1010 if (G_LIKELY (result))
1011 index = result - str->samples;
1020 /* find the index of the sample that includes the data for @media_offset using a
1023 * Returns the index of the sample.
1026 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1027 QtDemuxStream * str, gint64 media_offset)
1029 QtDemuxSample *result = str->samples;
1032 if (result == NULL || str->n_samples == 0)
1035 if (media_offset == result->offset)
1039 while (index < str->n_samples - 1) {
1040 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1043 if (media_offset < result->offset)
1054 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1059 /* find the index of the sample that includes the data for @media_time using a
1060 * linear search, and keeping in mind that not all samples may have been parsed
1061 * yet. If possible, it will delegate to binary search.
1063 * Returns the index of the sample.
1066 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1072 /* convert media_time to mov format */
1074 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1076 if (mov_time == str->samples[0].timestamp)
1079 /* use faster search if requested time in already parsed range */
1080 if (str->stbl_index >= 0 &&
1081 mov_time <= str->samples[str->stbl_index].timestamp)
1082 return gst_qtdemux_find_index (qtdemux, str, media_time);
1084 while (index < str->n_samples - 1) {
1085 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1088 if (mov_time < str->samples[index + 1].timestamp)
1098 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1103 /* find the index of the keyframe needed to decode the sample at @index
1106 * Returns the index of the keyframe.
1109 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1112 guint32 new_index = index;
1114 if (index >= str->n_samples) {
1115 new_index = str->n_samples;
1119 /* all keyframes, return index */
1120 if (str->all_keyframe) {
1122 #ifdef QTDEMUX_MODIFICATION
1123 if(qtdemux->segment.rate < 0.0 && new_index && (FOURCC_vide == str->subtype))
1129 #ifdef QTDEMUX_MODIFICATION
1130 if(qtdemux->segment.rate < 0.0 && new_index && (FOURCC_vide == str->subtype)) {
1131 /* If index is keyframe, reduce index by 1, so that we could fetch prev keyframe for video
1132 * This change is done to fix the out of segment issue when seek position is a keyframe position */
1137 /* else go back until we have a keyframe */
1139 if (str->samples[new_index].keyframe)
1149 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1150 "gave %u", index, new_index);
1155 /* find the segment for @time_position for @stream
1157 * Returns -1 if the segment cannot be found.
1160 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1161 guint64 time_position)
1166 GST_LOG_OBJECT (qtdemux, "finding segment for %" GST_TIME_FORMAT,
1167 GST_TIME_ARGS (time_position));
1169 /* find segment corresponding to time_position if we are looking
1172 for (i = 0; i < stream->n_segments; i++) {
1173 QtDemuxSegment *segment = &stream->segments[i];
1175 GST_LOG_OBJECT (qtdemux,
1176 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1177 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1179 /* For the last segment we include stop_time in the last segment */
1180 if (i < stream->n_segments - 1) {
1181 if (segment->time <= time_position && time_position < segment->stop_time) {
1182 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1187 if (segment->time <= time_position && time_position <= segment->stop_time) {
1188 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1197 /* move the stream @str to the sample position @index.
1199 * Updates @str->sample_index and marks discontinuity if needed.
1202 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1205 /* no change needed */
1206 if (index == str->sample_index)
1209 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1212 /* position changed, we have a discont */
1213 str->sample_index = index;
1214 /* Each time we move in the stream we store the position where we are
1216 str->from_sample = index;
1217 str->discont = TRUE;
1221 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1222 gint64 * key_time, gint64 * key_offset)
1225 gint64 min_byte_offset = -1;
1228 min_offset = desired_time;
1230 /* for each stream, find the index of the sample in the segment
1231 * and move back to the previous keyframe. */
1232 for (n = 0; n < qtdemux->n_streams; n++) {
1234 guint32 index, kindex;
1236 guint64 media_start;
1239 QtDemuxSegment *seg;
1241 str = qtdemux->streams[n];
1243 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1244 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1246 /* segment not found, continue with normal flow */
1250 /* get segment and time in the segment */
1251 seg = &str->segments[seg_idx];
1252 seg_time = desired_time - seg->time;
1254 /* get the media time in the segment */
1255 media_start = seg->media_start + seg_time;
1257 /* get the index of the sample with media time */
1258 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1259 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1260 " at offset %" G_GUINT64_FORMAT,
1261 GST_TIME_ARGS (media_start), index, str->samples[index].offset);
1263 /* find previous keyframe */
1264 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);
1266 /* if the keyframe is at a different position, we need to update the
1267 * requested seek time */
1268 if (index != kindex) {
1271 /* get timestamp of keyframe */
1273 gst_util_uint64_scale (str->samples[kindex].timestamp, GST_SECOND,
1275 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT
1276 " at offset %" G_GUINT64_FORMAT,
1277 kindex, GST_TIME_ARGS (media_time), str->samples[kindex].offset);
1279 /* keyframes in the segment get a chance to change the
1280 * desired_offset. keyframes out of the segment are
1282 if (media_time >= seg->media_start) {
1285 /* this keyframe is inside the segment, convert back to
1287 seg_time = (media_time - seg->media_start) + seg->time;
1288 if (seg_time < min_offset)
1289 min_offset = seg_time;
1293 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1294 min_byte_offset = str->samples[index].offset;
1298 *key_time = min_offset;
1300 *key_offset = min_byte_offset;
1304 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1305 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1310 g_return_val_if_fail (format != NULL, FALSE);
1311 g_return_val_if_fail (cur != NULL, FALSE);
1312 g_return_val_if_fail (stop != NULL, FALSE);
1314 if (*format == GST_FORMAT_TIME)
1317 fmt = GST_FORMAT_TIME;
1319 if (cur_type != GST_SEEK_TYPE_NONE)
1320 res = gst_pad_query_convert (pad, *format, *cur, &fmt, cur);
1321 if (res && stop_type != GST_SEEK_TYPE_NONE)
1322 res = gst_pad_query_convert (pad, *format, *stop, &fmt, stop);
1325 *format = GST_FORMAT_TIME;
1330 /* perform seek in push based mode:
1331 find BYTE position to move to based on time and delegate to upstream
1334 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1339 GstSeekType cur_type, stop_type;
1344 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1346 gst_event_parse_seek (event, &rate, &format, &flags,
1347 &cur_type, &cur, &stop_type, &stop);
1349 /* FIXME, always play to the end */
1352 /* only forward streaming and seeking is possible */
1354 goto unsupported_seek;
1356 /* convert to TIME if needed and possible */
1357 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1361 /* find reasonable corresponding BYTE position,
1362 * also try to mind about keyframes, since we can not go back a bit for them
1364 gst_qtdemux_adjust_seek (qtdemux, cur, NULL, &byte_cur);
1369 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1370 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1373 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1374 GST_DEBUG_OBJECT (qtdemux,
1375 "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %"
1376 G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
1377 GST_OBJECT_LOCK (qtdemux);
1378 qtdemux->requested_seek_time = cur;
1379 qtdemux->seek_offset = byte_cur;
1380 GST_OBJECT_UNLOCK (qtdemux);
1383 /* BYTE seek event */
1384 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1386 res = gst_pad_push_event (qtdemux->sinkpad, event);
1393 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1399 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1404 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1409 /* perform the seek.
1411 * We set all segment_indexes in the streams to unknown and
1412 * adjust the time_position to the desired position. this is enough
1413 * to trigger a segment switch in the streaming thread to start
1414 * streaming from the desired position.
1416 * Keyframe seeking is a little more complicated when dealing with
1417 * segments. Ideally we want to move to the previous keyframe in
1418 * the segment but there might not be a keyframe in the segment. In
1419 * fact, none of the segments could contain a keyframe. We take a
1420 * practical approach: seek to the previous keyframe in the segment,
1421 * if there is none, seek to the beginning of the segment.
1423 * Called with STREAM_LOCK
1426 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
1428 gint64 desired_offset;
1431 desired_offset = segment->last_stop;
1433 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1434 GST_TIME_ARGS (desired_offset));
1436 /* may not have enough fragmented info to do this adjustment,
1437 * and we can't scan (and probably should not) at this time with
1438 * possibly flushing upstream */
1439 if ((segment->flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1442 gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL);
1443 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1444 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1445 desired_offset = min_offset;
1448 /* and set all streams to the final position */
1449 for (n = 0; n < qtdemux->n_streams; n++) {
1450 QtDemuxStream *stream = qtdemux->streams[n];
1452 stream->time_position = desired_offset;
1453 stream->sample_index = -1;
1454 stream->segment_index = -1;
1455 stream->last_ret = GST_FLOW_OK;
1456 stream->sent_eos = FALSE;
1457 #ifdef QTDEMUX_MODIFICATION
1458 stream->trickplay_info->prev_kidx = 0;
1459 stream->trickplay_info->next_kidx = 0;
1460 stream->trickplay_info->kidxs_dur_diff = 0;
1461 stream->trickplay_info->start_pos = segment->last_stop;
1464 segment->last_stop = desired_offset;
1465 segment->time = desired_offset;
1467 /* we stop at the end */
1468 if (segment->stop == -1)
1469 segment->stop = segment->duration;
1474 /* do a seek in pull based mode */
1476 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1481 GstSeekType cur_type, stop_type;
1485 GstSegment seeksegment;
1489 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1491 gst_event_parse_seek (event, &rate, &format, &flags,
1492 &cur_type, &cur, &stop_type, &stop);
1493 #ifdef QTDEMUX_MODIFICATION
1494 GST_INFO_OBJECT (qtdemux, "Going to seek to %"GST_TIME_FORMAT", with rate = %f", GST_TIME_ARGS(cur), rate);
1497 /* we have to have a format as the segment format. Try to convert
1499 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1503 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1505 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1509 flush = flags & GST_SEEK_FLAG_FLUSH;
1511 /* stop streaming, either by flushing or by pausing the task */
1513 /* unlock upstream pull_range */
1514 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
1515 /* make sure out loop function exits */
1516 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
1518 /* non flushing seek, pause the task */
1519 gst_pad_pause_task (qtdemux->sinkpad);
1522 /* wait for streaming to finish */
1523 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1525 /* copy segment, we need this because we still need the old
1526 * segment when we close the current segment. */
1527 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1530 /* configure the segment with the seek variables */
1531 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1532 gst_segment_set_seek (&seeksegment, rate, format, flags,
1533 cur_type, cur, stop_type, stop, &update);
1536 #ifdef QTDEMUX_MODIFICATION
1537 if (cur != GST_CLOCK_TIME_NONE)
1538 gst_segment_set_last_stop (&seeksegment, GST_FORMAT_TIME, cur);
1541 /* now do the seek, this actually never returns FALSE */
1542 gst_qtdemux_perform_seek (qtdemux, &seeksegment);
1544 /* prepare for streaming again */
1546 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
1547 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ());
1548 } else if (qtdemux->segment_running) {
1549 /* we are running the current segment and doing a non-flushing seek,
1550 * close the segment first based on the last_stop. */
1551 GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT
1552 " to %" G_GINT64_FORMAT, qtdemux->segment.start,
1553 qtdemux->segment.last_stop);
1555 if (qtdemux->segment.rate >= 0) {
1556 /* FIXME, rate is the product of the global rate and the (quicktime)
1558 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1559 qtdemux->segment.rate, qtdemux->segment.format,
1560 qtdemux->segment.start, qtdemux->segment.last_stop,
1561 qtdemux->segment.time);
1562 } else { /* For Reverse Playback */
1565 if ((stop = qtdemux->segment.stop) == -1)
1566 stop = qtdemux->segment.duration;
1567 /* for reverse playback, we played from stop to last_stop. */
1568 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1569 qtdemux->segment.rate, qtdemux->segment.format,
1570 qtdemux->segment.last_stop, stop, qtdemux->segment.last_stop);
1574 #ifdef QTDEMUX_MODIFICATION
1575 if (seeksegment.rate < 0.0) {
1576 seeksegment.start = 0.0;
1580 /* commit the new segment */
1581 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1583 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1584 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1585 gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1586 qtdemux->segment.format, qtdemux->segment.last_stop));
1589 /* restart streaming, NEWSEGMENT will be sent from the streaming
1591 qtdemux->segment_running = TRUE;
1592 for (i = 0; i < qtdemux->n_streams; i++)
1593 qtdemux->streams[i]->last_ret = GST_FLOW_OK;
1595 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1598 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1605 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1611 qtdemux_ensure_index (GstQTDemux * qtdemux)
1615 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1617 /* Build complete index */
1618 for (i = 0; i < qtdemux->n_streams; i++) {
1619 QtDemuxStream *stream = qtdemux->streams[i];
1621 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1629 GST_LOG_OBJECT (qtdemux,
1630 "Building complete index of stream %u for seeking failed!", i);
1636 gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
1638 gboolean res = TRUE;
1639 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
1641 switch (GST_EVENT_TYPE (event)) {
1642 case GST_EVENT_SEEK:
1644 #ifndef GST_DISABLE_GST_DEBUG
1645 GstClockTime ts = gst_util_get_timestamp ();
1647 /* Build complete index for seeking;
1648 * if not a fragmented file at least */
1649 if (!qtdemux->fragmented)
1650 if (!qtdemux_ensure_index (qtdemux))
1652 #ifndef GST_DISABLE_GST_DEBUG
1653 ts = gst_util_get_timestamp () - ts;
1654 GST_INFO_OBJECT (qtdemux,
1655 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1658 if (qtdemux->pullbased) {
1659 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1660 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
1661 !qtdemux->fragmented) {
1662 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1664 GST_DEBUG_OBJECT (qtdemux,
1665 "ignoring seek in push mode in current state");
1668 gst_event_unref (event);
1671 case GST_EVENT_NAVIGATION:
1673 gst_event_unref (event);
1676 res = gst_pad_event_default (pad, event);
1680 gst_object_unref (qtdemux);
1688 GST_ERROR_OBJECT (qtdemux, "Index failed");
1689 gst_event_unref (event);
1695 /* stream/index return sample that is min/max w.r.t. byte position,
1696 * time is min/max w.r.t. time of samples,
1697 * the latter need not be time of the former sample */
1699 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1700 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1703 gint64 time, min_time;
1704 QtDemuxStream *stream;
1710 for (n = 0; n < qtdemux->n_streams; ++n) {
1713 gboolean set_sample;
1715 str = qtdemux->streams[n];
1722 i = str->n_samples - 1;
1725 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1726 if (str->samples[i].size &&
1727 ((fw && (str->samples[i].offset >= byte_pos)) ||
1729 (str->samples[i].offset + str->samples[i].size <=
1731 /* move stream to first available sample */
1733 gst_qtdemux_move_stream (qtdemux, str, i);
1736 /* determine min/max time */
1737 time = str->samples[i].timestamp + str->samples[i].pts_offset;
1738 time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);
1739 if (min_time == -1 || (!fw && time > min_time) ||
1740 (fw && time < min_time)) {
1743 /* determine stream with leading sample, to get its position */
1745 && (str->samples[i].offset < stream->samples[index].offset))
1747 && (str->samples[i].offset > stream->samples[index].offset))) {
1754 /* no sample for this stream, mark eos */
1756 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1768 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
1770 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
1773 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
1775 switch (GST_EVENT_TYPE (event)) {
1776 case GST_EVENT_NEWSEGMENT:
1779 gdouble rate, arate;
1780 gint64 start, stop, time, offset = 0;
1781 QtDemuxStream *stream;
1786 /* some debug output */
1787 gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
1788 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
1789 &start, &stop, &time);
1790 gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
1792 GST_DEBUG_OBJECT (demux,
1793 "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
1796 /* chain will send initial newsegment after pads have been added */
1797 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
1798 GST_DEBUG_OBJECT (demux, "still starting, eating event");
1802 /* we only expect a BYTE segment, e.g. following a seek */
1803 if (format == GST_FORMAT_BYTES) {
1805 gint64 requested_seek_time;
1806 guint64 seek_offset;
1810 GST_OBJECT_LOCK (demux);
1811 requested_seek_time = demux->requested_seek_time;
1812 seek_offset = demux->seek_offset;
1813 demux->requested_seek_time = -1;
1814 demux->seek_offset = -1;
1815 GST_OBJECT_UNLOCK (demux);
1817 if (offset == seek_offset) {
1818 start = requested_seek_time;
1820 gst_qtdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,
1822 start = MAX (start, 0);
1826 gst_qtdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,
1828 /* keyframe seeking should already arrange for start >= stop,
1829 * but make sure in other rare cases */
1830 stop = MAX (stop, start);
1833 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
1837 /* accept upstream's notion of segment and distribute along */
1838 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
1839 GST_FORMAT_TIME, start, stop, start);
1840 GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, "
1841 "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
1842 "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
1843 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
1845 gst_qtdemux_push_event (demux,
1846 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
1847 start, stop, start));
1849 /* clear leftover in current segment, if any */
1850 gst_adapter_clear (demux->adapter);
1851 /* set up streaming thread */
1852 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);
1853 demux->offset = offset;
1855 demux->todrop = stream->samples[idx].offset - offset;
1856 demux->neededbytes = demux->todrop + stream->samples[idx].size;
1858 /* set up for EOS */
1859 demux->neededbytes = -1;
1863 gst_event_unref (event);
1868 case GST_EVENT_FLUSH_STOP:
1872 /* clean up, force EOS if no more info follows */
1873 gst_adapter_clear (demux->adapter);
1875 demux->neededbytes = -1;
1876 /* reset flow return, e.g. following seek */
1877 for (i = 0; i < demux->n_streams; i++) {
1878 demux->streams[i]->last_ret = GST_FLOW_OK;
1879 demux->streams[i]->sent_eos = FALSE;
1884 /* If we are in push mode, and get an EOS before we've seen any streams,
1885 * then error out - we have nowhere to send the EOS */
1886 if (!demux->pullbased) {
1888 gboolean has_valid_stream = FALSE;
1889 for (i = 0; i < demux->n_streams; i++) {
1890 if (demux->streams[i]->pad != NULL) {
1891 has_valid_stream = TRUE;
1895 if (!has_valid_stream)
1896 gst_qtdemux_post_no_playable_stream_error (demux);
1903 res = gst_pad_event_default (demux->sinkpad, event);
1910 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
1912 GstQTDemux *demux = GST_QTDEMUX (element);
1914 GST_OBJECT_LOCK (demux);
1915 if (demux->element_index)
1916 gst_object_unref (demux->element_index);
1918 demux->element_index = gst_object_ref (index);
1920 demux->element_index = NULL;
1922 GST_OBJECT_UNLOCK (demux);
1923 /* object lock might be taken again */
1925 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
1926 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
1927 demux->element_index, demux->index_id);
1931 gst_qtdemux_get_index (GstElement * element)
1933 GstIndex *result = NULL;
1934 GstQTDemux *demux = GST_QTDEMUX (element);
1936 GST_OBJECT_LOCK (demux);
1937 if (demux->element_index)
1938 result = gst_object_ref (demux->element_index);
1939 GST_OBJECT_UNLOCK (demux);
1941 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
1947 gst_qtdemux_stbl_free (QtDemuxStream * stream)
1949 g_free ((gpointer) stream->stco.data);
1950 stream->stco.data = NULL;
1951 g_free ((gpointer) stream->stsz.data);
1952 stream->stsz.data = NULL;
1953 g_free ((gpointer) stream->stsc.data);
1954 stream->stsc.data = NULL;
1955 g_free ((gpointer) stream->stts.data);
1956 stream->stts.data = NULL;
1957 g_free ((gpointer) stream->stss.data);
1958 stream->stss.data = NULL;
1959 g_free ((gpointer) stream->stps.data);
1960 stream->stps.data = NULL;
1961 g_free ((gpointer) stream->ctts.data);
1962 stream->ctts.data = NULL;
1966 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
1968 while (stream->buffers) {
1969 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
1970 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
1973 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
1974 g_free (stream->samples);
1976 gst_caps_unref (stream->caps);
1977 g_free (stream->segments);
1978 if (stream->pending_tags)
1979 gst_tag_list_free (stream->pending_tags);
1980 g_free (stream->redirect_uri);
1981 /* free stbl sub-atoms */
1982 gst_qtdemux_stbl_free (stream);
1983 #ifdef QTDEMUX_MODIFICATION
1984 if (stream->trickplay_info)
1985 g_free (stream->trickplay_info);
1990 static GstStateChangeReturn
1991 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
1993 GstQTDemux *qtdemux = GST_QTDEMUX (element);
1994 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1996 switch (transition) {
1997 case GST_STATE_CHANGE_PAUSED_TO_READY:
2003 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2005 switch (transition) {
2006 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2009 qtdemux->state = QTDEMUX_STATE_INITIAL;
2010 qtdemux->neededbytes = 16;
2011 qtdemux->todrop = 0;
2012 qtdemux->pullbased = FALSE;
2013 qtdemux->posted_redirect = FALSE;
2014 qtdemux->offset = 0;
2015 qtdemux->first_mdat = -1;
2016 qtdemux->header_size = 0;
2017 qtdemux->got_moov = FALSE;
2018 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
2019 if (qtdemux->mdatbuffer)
2020 gst_buffer_unref (qtdemux->mdatbuffer);
2021 qtdemux->mdatbuffer = NULL;
2022 if (qtdemux->comp_brands)
2023 gst_buffer_unref (qtdemux->comp_brands);
2024 qtdemux->comp_brands = NULL;
2025 if (qtdemux->tag_list)
2026 gst_tag_list_free (qtdemux->tag_list);
2027 qtdemux->tag_list = NULL;
2028 if (qtdemux->element_index)
2029 gst_object_unref (qtdemux->element_index);
2030 qtdemux->element_index = NULL;
2031 gst_adapter_clear (qtdemux->adapter);
2032 #ifdef QTDEMUX_MODIFICATION
2033 // TODO: Check new modifications
2034 if (qtdemux->file) {
2035 fclose (qtdemux->file);
2037 fclose (qtdemux->ofile);
2038 remove (qtdemux->filename);
2040 qtdemux->file = NULL;
2041 qtdemux->ofile = NULL;
2042 qtdemux->filesize = 0;
2043 qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
2045 for (n = 0; n < qtdemux->n_streams; n++) {
2046 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2047 qtdemux->streams[n] = NULL;
2049 qtdemux->major_brand = 0;
2050 qtdemux->n_streams = 0;
2051 qtdemux->n_video_streams = 0;
2052 qtdemux->n_audio_streams = 0;
2053 qtdemux->n_sub_streams = 0;
2054 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2055 qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE;
2056 qtdemux->seek_offset = 0;
2057 qtdemux->upstream_seekable = FALSE;
2058 qtdemux->upstream_size = 0;
2069 qtdemux_post_global_tags (GstQTDemux * qtdemux)
2071 if (qtdemux->tag_list) {
2072 /* all header tags ready and parsed, push them */
2073 GST_INFO_OBJECT (qtdemux, "posting global tags: %" GST_PTR_FORMAT,
2075 /* post now, send event on pads later */
2076 gst_element_post_message (GST_ELEMENT (qtdemux),
2077 gst_message_new_tag (GST_OBJECT (qtdemux),
2078 gst_tag_list_copy (qtdemux->tag_list)));
2083 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2085 /* counts as header data */
2086 qtdemux->header_size += length;
2088 /* only consider at least a sufficiently complete ftyp atom */
2092 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2093 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2094 GST_FOURCC_ARGS (qtdemux->major_brand));
2095 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2096 memcpy (GST_BUFFER_DATA (buf), buffer + 16, GST_BUFFER_SIZE (buf));
2101 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist)
2103 /* Strip out bogus fields */
2105 gst_tag_list_remove_tag (taglist, GST_TAG_VIDEO_CODEC);
2107 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, taglist);
2109 if (qtdemux->tag_list) {
2110 /* prioritize native tags using _KEEP mode */
2111 gst_tag_list_insert (qtdemux->tag_list, taglist, GST_TAG_MERGE_KEEP);
2112 gst_tag_list_free (taglist);
2114 qtdemux->tag_list = taglist;
2119 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2121 static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2122 0x97, 0xA9, 0x42, 0xE8,
2123 0x9C, 0x71, 0x99, 0x94,
2124 0x91, 0xE3, 0xAF, 0xAC
2128 /* counts as header data */
2129 qtdemux->header_size += length;
2131 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2133 if (length <= offset + 16) {
2134 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2138 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2140 GstTagList *taglist;
2142 buf = gst_buffer_new ();
2143 GST_BUFFER_DATA (buf) = (guint8 *) buffer + offset + 16;
2144 GST_BUFFER_SIZE (buf) = length - offset - 16;
2146 taglist = gst_tag_list_from_xmp_buffer (buf);
2147 gst_buffer_unref (buf);
2149 qtdemux_handle_xmp_taglist (qtdemux, taglist);
2152 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid");
2156 /* caller verifies at least 8 bytes in buf */
2158 extract_initial_length_and_fourcc (const guint8 * data, guint size,
2159 guint64 * plength, guint32 * pfourcc)
2164 length = QT_UINT32 (data);
2165 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2166 fourcc = QT_FOURCC (data + 4);
2167 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
2170 length = G_MAXUINT32;
2171 } else if (length == 1 && size >= 16) {
2172 /* this means we have an extended size, which is the 64 bit value of
2173 * the next 8 bytes */
2174 length = QT_UINT64 (data + 8);
2175 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2185 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
2187 guint32 version = 0;
2188 guint64 duration = 0;
2190 if (!gst_byte_reader_get_uint32_be (br, &version))
2195 if (!gst_byte_reader_get_uint64_be (br, &duration))
2200 if (!gst_byte_reader_get_uint32_be (br, &dur))
2205 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
2206 qtdemux->duration = duration;
2212 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
2218 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
2219 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
2221 if (!stream->parsed_trex && qtdemux->moov_node) {
2223 GstByteReader trex_data;
2225 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
2227 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
2230 guint32 id = 0, dur = 0, size = 0, flags = 0;
2232 /* skip version/flags */
2233 if (!gst_byte_reader_skip (&trex_data, 4))
2235 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
2237 if (id != stream->track_id)
2239 /* sample description index; ignore */
2240 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2242 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2244 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
2246 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
2249 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
2250 "duration %d, size %d, flags 0x%x", stream->track_id,
2253 stream->parsed_trex = TRUE;
2254 stream->def_sample_duration = dur;
2255 stream->def_sample_size = size;
2256 stream->def_sample_flags = flags;
2259 /* iterate all siblings */
2260 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
2266 *ds_duration = stream->def_sample_duration;
2267 *ds_size = stream->def_sample_size;
2268 *ds_size = stream->def_sample_size;
2270 /* even then, above values are better than random ... */
2271 if (G_UNLIKELY (!stream->parsed_trex)) {
2272 GST_WARNING_OBJECT (qtdemux,
2273 "failed to find fragment defaults for stream %d", stream->track_id);
2281 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
2282 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
2283 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
2284 gint64 * base_offset, gint64 * running_offset)
2287 gint32 data_offset = 0;
2288 guint32 flags = 0, first_flags = 0, samples_count = 0;
2291 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
2292 QtDemuxSample *sample;
2293 gboolean ismv = FALSE;
2295 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
2296 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,
2297 stream->track_id, d_sample_duration, d_sample_size, d_sample_flags,
2300 /* presence of stss or not can't really tell us much,
2301 * and flags and so on tend to be marginally reliable in these files */
2302 if (stream->subtype == FOURCC_soun) {
2303 GST_DEBUG_OBJECT (qtdemux,
2304 "sound track in fragmented file; marking all keyframes");
2305 stream->all_keyframe = TRUE;
2308 if (!gst_byte_reader_skip (trun, 1) ||
2309 !gst_byte_reader_get_uint24_be (trun, &flags))
2312 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
2315 if (flags & TR_DATA_OFFSET) {
2316 /* note this is really signed */
2317 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
2319 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
2320 /* default base offset = first byte of moof */
2321 if (*base_offset == -1) {
2322 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
2323 *base_offset = moof_offset;
2325 *running_offset = *base_offset + data_offset;
2327 /* if no offset at all, that would mean data starts at moof start,
2328 * which is a bit wrong and is ismv crappy way, so compensate
2329 * assuming data is in mdat following moof */
2330 if (*base_offset == -1) {
2331 *base_offset = moof_offset + moof_length + 8;
2332 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
2335 if (*running_offset == -1)
2336 *running_offset = *base_offset;
2339 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
2341 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
2342 data_offset, flags, samples_count);
2344 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2345 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
2346 GST_DEBUG_OBJECT (qtdemux,
2347 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
2348 flags ^= TR_FIRST_SAMPLE_FLAGS;
2350 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
2352 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
2356 /* FIXME ? spec says other bits should also be checked to determine
2357 * entry size (and prefix size for that matter) */
2359 dur_offset = size_offset = 0;
2360 if (flags & TR_SAMPLE_DURATION) {
2361 GST_LOG_OBJECT (qtdemux, "entry duration present");
2362 dur_offset = entry_size;
2365 if (flags & TR_SAMPLE_SIZE) {
2366 GST_LOG_OBJECT (qtdemux, "entry size present");
2367 size_offset = entry_size;
2370 if (flags & TR_SAMPLE_FLAGS) {
2371 GST_LOG_OBJECT (qtdemux, "entry flags present");
2372 flags_offset = entry_size;
2375 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2376 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
2377 ct_offset = entry_size;
2381 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
2383 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
2385 if (stream->n_samples >=
2386 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
2389 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
2390 stream->n_samples, (guint) sizeof (QtDemuxSample),
2391 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
2393 /* create a new array of samples if it's the first sample parsed */
2394 if (stream->n_samples == 0)
2395 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
2396 /* or try to reallocate it with space enough to insert the new samples */
2398 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
2399 stream->n_samples + samples_count);
2400 if (stream->samples == NULL)
2403 if (G_UNLIKELY (stream->n_samples == 0)) {
2404 /* the timestamp of the first sample is also provided by the tfra entry
2405 * but we shouldn't rely on it as it is at the end of files */
2408 /* subsequent fragments extend stream */
2410 stream->samples[stream->n_samples - 1].timestamp +
2411 stream->samples[stream->n_samples - 1].duration;
2413 sample = stream->samples + stream->n_samples;
2414 for (i = 0; i < samples_count; i++) {
2415 guint32 dur, size, sflags, ct;
2417 /* first read sample data */
2418 if (flags & TR_SAMPLE_DURATION) {
2419 dur = QT_UINT32 (data + dur_offset);
2421 dur = d_sample_duration;
2423 if (flags & TR_SAMPLE_SIZE) {
2424 size = QT_UINT32 (data + size_offset);
2426 size = d_sample_size;
2428 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2430 sflags = first_flags;
2432 sflags = d_sample_flags;
2434 } else if (flags & TR_SAMPLE_FLAGS) {
2435 sflags = QT_UINT32 (data + flags_offset);
2437 sflags = d_sample_flags;
2439 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2440 ct = QT_UINT32 (data + ct_offset);
2446 /* fill the sample information */
2447 sample->offset = *running_offset;
2448 sample->pts_offset = ct;
2449 sample->size = size;
2450 sample->timestamp = timestamp;
2451 sample->duration = dur;
2452 /* sample-is-difference-sample */
2453 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
2454 * now idea how it relates to bitfield other than massive LE/BE confusion */
2455 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
2456 *running_offset += size;
2461 stream->n_samples += samples_count;
2467 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
2472 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
2478 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
2479 "be larger than %uMB (broken file?)", stream->n_samples,
2480 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
2485 /* find stream with @id */
2486 static inline QtDemuxStream *
2487 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
2489 QtDemuxStream *stream;
2493 if (G_UNLIKELY (!id)) {
2494 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
2498 /* try to get it fast and simple */
2499 if (G_LIKELY (id <= qtdemux->n_streams)) {
2500 stream = qtdemux->streams[id - 1];
2501 if (G_LIKELY (stream->track_id == id))
2505 /* linear search otherwise */
2506 for (i = 0; i < qtdemux->n_streams; i++) {
2507 stream = qtdemux->streams[i];
2508 if (stream->track_id == id)
2516 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
2517 QtDemuxStream ** stream, guint32 * default_sample_duration,
2518 guint32 * default_sample_size, guint32 * default_sample_flags,
2519 gint64 * base_offset)
2522 guint32 track_id = 0;
2524 if (!gst_byte_reader_skip (tfhd, 1) ||
2525 !gst_byte_reader_get_uint24_be (tfhd, &flags))
2528 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
2531 *stream = qtdemux_find_stream (qtdemux, track_id);
2532 if (G_UNLIKELY (!*stream))
2533 goto unknown_stream;
2535 if (flags & TF_BASE_DATA_OFFSET)
2536 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
2539 /* obtain stream defaults */
2540 qtdemux_parse_trex (qtdemux, *stream,
2541 default_sample_duration, default_sample_size, default_sample_flags);
2543 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
2544 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
2545 if (!gst_byte_reader_skip (tfhd, 4))
2548 if (flags & TF_DEFAULT_SAMPLE_DURATION)
2549 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
2552 if (flags & TF_DEFAULT_SAMPLE_SIZE)
2553 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
2556 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
2557 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
2564 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
2569 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
2575 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
2576 guint64 moof_offset, QtDemuxStream * stream)
2578 GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
2579 GstByteReader trun_data, tfhd_data;
2580 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
2581 gint64 base_offset, running_offset;
2583 /* NOTE @stream ignored */
2585 moof_node = g_node_new ((guint8 *) buffer);
2586 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
2587 qtdemux_node_dump (qtdemux, moof_node);
2589 /* unknown base_offset to start with */
2590 base_offset = running_offset = -1;
2591 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
2593 /* Fragment Header node */
2595 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
2599 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
2600 &ds_size, &ds_flags, &base_offset))
2602 if (G_UNLIKELY (!stream)) {
2603 /* we lost track of offset, we'll need to regain it,
2604 * but can delay complaining until later or avoid doing so altogether */
2608 if (G_UNLIKELY (base_offset < -1))
2610 /* Track Run node */
2612 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
2615 qtdemux_parse_trun (qtdemux, &trun_data, stream,
2616 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
2618 /* iterate all siblings */
2619 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
2622 /* if no new base_offset provided for next traf,
2623 * base is end of current traf */
2624 base_offset = running_offset;
2625 running_offset = -1;
2627 /* iterate all siblings */
2628 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
2630 g_node_destroy (moof_node);
2635 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
2640 GST_DEBUG_OBJECT (qtdemux, "lost offset");
2645 g_node_destroy (moof_node);
2646 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2647 (_("This file is corrupt and cannot be played.")), (NULL));
2652 /* might be used if some day we actually use mfra & co
2653 * for random access to fragments,
2654 * but that will require quite some modifications and much less relying
2655 * on a sample array */
2658 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node,
2659 QtDemuxStream * stream)
2661 guint64 time = 0, moof_offset = 0;
2662 guint32 ver_flags, track_id, len, num_entries, i;
2663 guint value_size, traf_size, trun_size, sample_size;
2664 GstBuffer *buf = NULL;
2668 gst_byte_reader_init (&tfra, (guint8 *) tfra_node->data + (4 + 4),
2669 QT_UINT32 ((guint8 *) tfra_node->data) - (4 + 4));
2671 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
2674 if (!(gst_byte_reader_get_uint32_be (&tfra, &track_id) &&
2675 gst_byte_reader_get_uint32_be (&tfra, &len) &&
2676 gst_byte_reader_get_uint32_be (&tfra, &num_entries)))
2679 GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?",
2680 track_id, stream->track_id);
2681 if (track_id != stream->track_id) {
2685 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
2686 sample_size = (len & 3) + 1;
2687 trun_size = ((len & 12) >> 2) + 1;
2688 traf_size = ((len & 48) >> 4) + 1;
2690 if (num_entries == 0)
2693 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
2694 value_size + value_size + traf_size + trun_size + sample_size))
2697 for (i = 0; i < num_entries; i++) {
2698 qt_atom_parser_get_offset (&tfra, value_size, &time);
2699 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
2700 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
2701 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
2702 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
2704 GST_LOG_OBJECT (qtdemux,
2705 "fragment time: %" GST_TIME_FORMAT " moof_offset: %u",
2706 GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND,
2707 stream->timescale)), moof_offset);
2709 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
2710 if (ret != GST_FLOW_OK)
2712 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
2713 moof_offset, stream);
2714 gst_buffer_unref (buf);
2722 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2723 (_("This file is corrupt and cannot be played.")), (NULL));
2728 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
2734 qtdemux_parse_mfra (GstQTDemux * qtdemux, QtDemuxStream * stream)
2737 GNode *mfra_node, *tfra_node;
2740 if (!qtdemux->mfra_offset)
2743 ret = gst_qtdemux_pull_atom (qtdemux, qtdemux->mfra_offset, 0, &buffer);
2744 if (ret != GST_FLOW_OK)
2747 mfra_node = g_node_new ((guint8 *) GST_BUFFER_DATA (buffer));
2748 qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer),
2749 GST_BUFFER_SIZE (buffer));
2751 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
2754 qtdemux_parse_tfra (qtdemux, tfra_node, stream);
2755 /* iterate all siblings */
2756 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
2758 g_node_destroy (mfra_node);
2759 gst_buffer_unref (buffer);
2765 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2766 (_("This file is corrupt and cannot be played.")), (NULL));
2771 static GstFlowReturn
2772 qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset,
2773 guint32 * mfro_size)
2775 GstFlowReturn ret = GST_FLOW_ERROR;
2776 GstBuffer *mfro = NULL;
2779 GstFormat fmt = GST_FORMAT_BYTES;
2781 if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &fmt, &len)) {
2782 GST_DEBUG_OBJECT (qtdemux, "upstream size not available; "
2783 "can not locate mfro");
2787 ret = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
2788 if (ret != GST_FLOW_OK)
2791 fourcc = QT_FOURCC (GST_BUFFER_DATA (mfro) + 4);
2792 if (fourcc != FOURCC_mfro)
2795 GST_INFO_OBJECT (qtdemux, "Found mfro atom: fragmented mp4 container");
2796 if (GST_BUFFER_SIZE (mfro) >= 16) {
2797 GST_DEBUG_OBJECT (qtdemux, "parsing 'mfro' atom");
2798 *mfro_size = QT_UINT32 (GST_BUFFER_DATA (mfro) + 12);
2799 if (*mfro_size >= len) {
2800 GST_WARNING_OBJECT (qtdemux, "mfro.size is invalid");
2801 ret = GST_FLOW_ERROR;
2804 *mfra_offset = len - *mfro_size;
2809 gst_buffer_unref (mfro);
2815 qtdemux_parse_fragmented (GstQTDemux * qtdemux)
2818 guint32 mfra_size = 0;
2819 guint64 mfra_offset = 0;
2822 qtdemux->fragmented = FALSE;
2824 /* We check here if it is a fragmented mp4 container */
2825 ret = qtdemux_parse_mfro (qtdemux, &mfra_offset, &mfra_size);
2826 if (ret == GST_FLOW_OK && mfra_size != 0 && mfra_offset != 0) {
2827 qtdemux->fragmented = TRUE;
2828 GST_DEBUG_OBJECT (qtdemux,
2829 "mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset);
2830 qtdemux->mfra_offset = mfra_offset;
2835 static GstFlowReturn
2836 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
2840 GstBuffer *buf = NULL;
2841 GstFlowReturn ret = GST_FLOW_OK;
2842 guint64 cur_offset = qtdemux->offset;
2844 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
2845 if (G_UNLIKELY (ret != GST_FLOW_OK))
2847 if (G_LIKELY (GST_BUFFER_SIZE (buf) >= 8))
2848 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf),
2849 GST_BUFFER_SIZE (buf), &length, &fourcc);
2850 gst_buffer_unref (buf);
2852 /* maybe we already got most we needed, so only consider this eof */
2853 if (G_UNLIKELY (length == 0)) {
2854 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
2855 (_("Invalid atom size.")),
2856 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
2857 GST_FOURCC_ARGS (fourcc)));
2858 ret = GST_FLOW_UNEXPECTED;
2864 /* record for later parsing when needed */
2865 if (!qtdemux->moof_offset) {
2866 qtdemux->moof_offset = qtdemux->offset;
2875 GST_LOG_OBJECT (qtdemux,
2876 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
2877 GST_FOURCC_ARGS (fourcc), cur_offset);
2878 qtdemux->offset += length;
2885 if (qtdemux->got_moov) {
2886 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
2887 qtdemux->offset += length;
2891 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
2892 if (ret != GST_FLOW_OK)
2894 if (length != GST_BUFFER_SIZE (moov)) {
2895 /* Some files have a 'moov' atom at the end of the file which contains
2896 * a terminal 'free' atom where the body of the atom is missing.
2897 * Check for, and permit, this special case.
2899 if (GST_BUFFER_SIZE (moov) >= 8) {
2900 guint8 *final_data = GST_BUFFER_DATA (moov) +
2901 (GST_BUFFER_SIZE (moov) - 8);
2902 guint32 final_length = QT_UINT32 (final_data);
2903 guint32 final_fourcc = QT_FOURCC (final_data + 4);
2904 if (final_fourcc == FOURCC_free &&
2905 GST_BUFFER_SIZE (moov) + final_length - 8 == length) {
2906 /* Ok, we've found that special case. Allocate a new buffer with
2907 * that free atom actually present. */
2908 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
2909 gst_buffer_copy_metadata (newmoov, moov,
2910 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2911 GST_BUFFER_COPY_CAPS);
2912 memcpy (GST_BUFFER_DATA (newmoov), GST_BUFFER_DATA (moov),
2913 GST_BUFFER_SIZE (moov));
2914 memset (GST_BUFFER_DATA (newmoov) + GST_BUFFER_SIZE (moov), 0,
2916 gst_buffer_unref (moov);
2922 if (length != GST_BUFFER_SIZE (moov)) {
2923 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2924 (_("This file is incomplete and cannot be played.")),
2925 ("We got less than expected (received %u, wanted %u, offset %"
2926 G_GUINT64_FORMAT ")",
2927 GST_BUFFER_SIZE (moov), (guint) length, cur_offset));
2928 gst_buffer_unref (moov);
2929 ret = GST_FLOW_ERROR;
2932 qtdemux->offset += length;
2934 qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
2935 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
2937 qtdemux_parse_tree (qtdemux);
2938 g_node_destroy (qtdemux->moov_node);
2939 gst_buffer_unref (moov);
2940 qtdemux->moov_node = NULL;
2941 qtdemux->got_moov = TRUE;
2949 /* extract major brand; might come in handy for ISO vs QT issues */
2950 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
2951 if (ret != GST_FLOW_OK)
2953 qtdemux->offset += length;
2954 qtdemux_parse_ftyp (qtdemux, GST_BUFFER_DATA (ftyp),
2955 GST_BUFFER_SIZE (ftyp));
2956 gst_buffer_unref (ftyp);
2963 /* uuid are extension atoms */
2964 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
2965 if (ret != GST_FLOW_OK)
2967 qtdemux->offset += length;
2968 qtdemux_parse_uuid (qtdemux, GST_BUFFER_DATA (uuid),
2969 GST_BUFFER_SIZE (uuid));
2970 gst_buffer_unref (uuid);
2977 GST_LOG_OBJECT (qtdemux,
2978 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
2979 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
2981 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
2982 if (ret != GST_FLOW_OK)
2984 GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown),
2985 GST_BUFFER_SIZE (unknown));
2986 gst_buffer_unref (unknown);
2987 qtdemux->offset += length;
2993 if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) {
2994 /* digested all data, show what we have */
2995 ret = qtdemux_expose_streams (qtdemux);
2997 /* Only post, event on pads is done after newsegment */
2998 qtdemux_post_global_tags (qtdemux);
3000 qtdemux->state = QTDEMUX_STATE_MOVIE;
3001 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
3008 /* Seeks to the previous keyframe of the indexed stream and
3009 * aligns other streams with respect to the keyframe timestamp
3010 * of indexed stream. Only called in case of Reverse Playback
3012 static GstFlowReturn
3013 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
3016 guint32 seg_idx = 0, k_index = 0;
3017 guint32 ref_seg_idx, ref_k_index;
3018 guint64 k_pos = 0, last_stop = 0;
3019 QtDemuxSegment *seg = NULL;
3020 QtDemuxStream *ref_str = NULL;
3021 guint64 seg_media_start_mov; /* segment media start time in mov format */
3022 #ifdef QTDEMUX_MODIFICATION
3023 QtDemuxSample *sample;
3024 gdouble minusone = -1;
3025 guint64 time_position;
3028 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
3029 * and finally align all the other streams on that timestamp with their
3030 * respective keyframes */
3031 for (n = 0; n < qtdemux->n_streams; n++) {
3032 QtDemuxStream *str = qtdemux->streams[n];
3034 #ifdef QTDEMUX_MODIFICATION
3035 sample = &str->samples[str->sample_index];
3037 if((time_position - (minusone *qtdemux->segment.rate)*sample->duration)>0)
3038 time_position -= (minusone *qtdemux->segment.rate)*sample->duration;
3042 seg_idx = gst_qtdemux_find_segment (qtdemux, str,
3045 seg_idx = gst_qtdemux_find_segment (qtdemux, str,
3046 qtdemux->segment.last_stop);
3049 /* segment not found, continue with normal flow */
3053 /* No candidate yet, take that one */
3059 /* So that stream has a segment, we prefer video streams */
3060 if (str->subtype == FOURCC_vide) {
3066 if (G_UNLIKELY (!ref_str)) {
3067 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
3071 if (G_UNLIKELY (!ref_str->from_sample)) {
3072 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
3076 /* So that stream has been playing from from_sample to to_sample. We will
3077 * get the timestamp of the previous sample and search for a keyframe before
3078 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
3079 if (ref_str->subtype == FOURCC_vide) {
3080 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
3081 ref_str->from_sample - 1);
3083 if (ref_str->from_sample >= 10)
3084 k_index = ref_str->from_sample - 10;
3089 /* get current segment for that stream */
3090 seg = &ref_str->segments[ref_str->segment_index];
3091 /* convert seg->media_start to mov format time for timestamp comparison */
3092 seg_media_start_mov =
3093 gst_util_uint64_scale (seg->media_start, ref_str->timescale, GST_SECOND);
3094 /* Crawl back through segments to find the one containing this I frame */
3095 while (ref_str->samples[k_index].timestamp < seg_media_start_mov) {
3096 GST_DEBUG_OBJECT (qtdemux, "keyframe position is out of segment %u",
3097 ref_str->segment_index);
3098 if (G_UNLIKELY (!ref_str->segment_index)) {
3099 /* Reached first segment, let's consider it's EOS */
3102 ref_str->segment_index--;
3103 seg = &ref_str->segments[ref_str->segment_index];
3104 /* convert seg->media_start to mov format time for timestamp comparison */
3105 seg_media_start_mov =
3106 gst_util_uint64_scale (seg->media_start, ref_str->timescale,
3109 /* Calculate time position of the keyframe and where we should stop */
3111 (gst_util_uint64_scale (ref_str->samples[k_index].timestamp, GST_SECOND,
3112 ref_str->timescale) - seg->media_start) + seg->time;
3114 gst_util_uint64_scale (ref_str->samples[ref_str->from_sample].timestamp,
3115 GST_SECOND, ref_str->timescale);
3116 last_stop = (last_stop - seg->media_start) + seg->time;
3118 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
3119 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
3120 k_index, GST_TIME_ARGS (k_pos));
3122 /* Set last_stop with the keyframe timestamp we pushed of that stream */
3123 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, last_stop);
3124 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
3125 GST_TIME_ARGS (last_stop));
3127 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
3128 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
3132 ref_seg_idx = ref_str->segment_index;
3133 ref_k_index = k_index;
3135 /* Align them all on this */
3136 for (n = 0; n < qtdemux->n_streams; n++) {
3138 guint64 media_start = 0, seg_time = 0;
3139 QtDemuxStream *str = qtdemux->streams[n];
3141 /* aligning reference stream again might lead to backing up to yet another
3142 * keyframe (due to timestamp rounding issues),
3143 * potentially putting more load on downstream; so let's try to avoid */
3144 if (str == ref_str) {
3145 seg_idx = ref_seg_idx;
3146 seg = &str->segments[seg_idx];
3147 k_index = ref_k_index;
3148 GST_DEBUG_OBJECT (qtdemux, "reference stream segment %d, "
3149 "sample at index %d", ref_str->segment_index, k_index);
3151 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
3152 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
3154 /* segment not found, continue with normal flow */
3158 /* get segment and time in the segment */
3159 seg = &str->segments[seg_idx];
3160 seg_time = k_pos - seg->time;
3162 /* get the media time in the segment */
3163 media_start = seg->media_start + seg_time;
3165 /* get the index of the sample with media time */
3166 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
3167 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
3168 GST_TIME_ARGS (media_start), index);
3170 /* find previous keyframe */
3171 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index);
3174 /* Remember until where we want to go */
3175 str->to_sample = str->from_sample - 1;
3176 /* Define our time position */
3177 str->time_position =
3178 (gst_util_uint64_scale (str->samples[k_index].timestamp, GST_SECOND,
3179 str->timescale) - seg->media_start) + seg->time;
3180 /* Now seek back in time */
3181 gst_qtdemux_move_stream (qtdemux, str, k_index);
3182 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u, time position %"
3183 GST_TIME_FORMAT " playing from sample %u to %u", k_index,
3184 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
3190 return GST_FLOW_UNEXPECTED;
3193 /* activate the given segment number @seg_idx of @stream at time @offset.
3194 * @offset is an absolute global position over all the segments.
3196 * This will push out a NEWSEGMENT event with the right values and
3197 * position the stream index to the first decodable sample before
3201 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
3202 guint32 seg_idx, guint64 offset)
3205 QtDemuxSegment *segment;
3206 guint32 index, kf_index;
3208 guint64 start, stop, time;
3211 GST_LOG_OBJECT (qtdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,
3214 /* update the current segment */
3215 stream->segment_index = seg_idx;
3217 /* get the segment */
3218 segment = &stream->segments[seg_idx];
3220 if (G_UNLIKELY (offset < segment->time)) {
3221 GST_WARNING_OBJECT (qtdemux, "offset < segment->time %" G_GUINT64_FORMAT,
3226 /* segment lies beyond total indicated duration */
3227 if (G_UNLIKELY (qtdemux->segment.duration != -1 &&
3228 segment->time > qtdemux->segment.duration)) {
3229 GST_WARNING_OBJECT (qtdemux, "file duration %" G_GINT64_FORMAT
3230 " < segment->time %" G_GUINT64_FORMAT, qtdemux->segment.duration,
3235 /* get time in this segment */
3236 seg_time = offset - segment->time;
3238 GST_LOG_OBJECT (qtdemux, "seg_time %" GST_TIME_FORMAT,
3239 GST_TIME_ARGS (seg_time));
3241 if (G_UNLIKELY (seg_time > segment->duration)) {
3242 GST_LOG_OBJECT (qtdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,
3243 GST_TIME_ARGS (segment->duration));
3247 /* qtdemux->segment.stop is in outside-time-realm, whereas
3248 * segment->media_stop is in track-time-realm.
3250 * In order to compare the two, we need to bring segment.stop
3251 * into the track-time-realm */
3253 stop = qtdemux->segment.stop;
3255 stop = qtdemux->segment.duration;
3257 stop = segment->media_stop;
3260 MIN (segment->media_stop, stop - segment->time + segment->media_start);
3262 if (qtdemux->segment.rate >= 0) {
3263 start = MIN (segment->media_start + seg_time, stop);
3266 if (segment->media_start >= qtdemux->segment.start) {
3267 start = segment->media_start;
3268 time = segment->time;
3270 start = qtdemux->segment.start;
3271 time = segment->time + (qtdemux->segment.start - segment->media_start);
3274 start = MAX (segment->media_start, qtdemux->segment.start);
3275 stop = MIN (segment->media_start + seg_time, stop);
3278 GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT
3279 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
3280 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
3282 /* combine global rate with that of the segment */
3283 rate = segment->rate * qtdemux->segment.rate;
3285 /* update the segment values used for clipping */
3286 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
3287 gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
3290 /* now prepare and send the segment */
3292 event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
3294 gst_pad_push_event (stream->pad, event);
3295 /* assume we can send more data now */
3296 stream->last_ret = GST_FLOW_OK;
3297 /* clear to send tags on this pad now */
3298 gst_qtdemux_push_tags (qtdemux, stream);
3301 /* and move to the keyframe before the indicated media time of the
3303 if (qtdemux->segment.rate >= 0) {
3304 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
3305 stream->to_sample = G_MAXUINT32;
3306 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3307 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
3308 GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
3309 GST_SECOND, stream->timescale)));
3311 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
3312 stream->to_sample = index;
3313 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3314 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
3315 GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
3316 GST_SECOND, stream->timescale)));
3319 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
3320 * encountered an error and printed a message so we return appropriately */
3324 /* we're at the right spot */
3325 if (index == stream->sample_index) {
3326 GST_DEBUG_OBJECT (qtdemux, "we are at the right index");
3330 /* find keyframe of the target index */
3331 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
3334 /* indent does stupid stuff with stream->samples[].timestamp */
3336 /* if we move forwards, we don't have to go back to the previous
3337 * keyframe since we already sent that. We can also just jump to
3338 * the keyframe right before the target index if there is one. */
3339 if (index > stream->sample_index) {
3340 /* moving forwards check if we move past a keyframe */
3341 if (kf_index > stream->sample_index) {
3342 GST_DEBUG_OBJECT (qtdemux,
3343 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3344 GST_TIME_ARGS (gst_util_uint64_scale (
3345 stream->samples[kf_index].timestamp,
3346 GST_SECOND, stream->timescale)));
3347 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3349 GST_DEBUG_OBJECT (qtdemux,
3350 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT
3351 " already sent", kf_index,
3352 GST_TIME_ARGS (gst_util_uint64_scale (
3353 stream->samples[kf_index].timestamp,
3354 GST_SECOND, stream->timescale)));
3357 GST_DEBUG_OBJECT (qtdemux,
3358 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3359 GST_TIME_ARGS (gst_util_uint64_scale (
3360 stream->samples[kf_index].timestamp,
3361 GST_SECOND, stream->timescale)));
3362 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3370 /* prepare to get the current sample of @stream, getting essential values.
3372 * This function will also prepare and send the segment when needed.
3374 * Return FALSE if the stream is EOS.
3377 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
3378 QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
3379 guint64 * duration, gboolean * keyframe)
3381 QtDemuxSample *sample;
3382 guint64 time_position;
3385 g_return_val_if_fail (stream != NULL, FALSE);
3387 time_position = stream->time_position;
3388 if (G_UNLIKELY (time_position == -1))
3391 seg_idx = stream->segment_index;
3392 if (G_UNLIKELY (seg_idx == -1)) {
3393 /* find segment corresponding to time_position if we are looking
3395 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
3397 /* nothing found, we're really eos */
3402 /* different segment, activate it, sample_index will be set. */
3403 if (G_UNLIKELY (stream->segment_index != seg_idx))
3404 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
3406 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
3407 stream->sample_index, stream->n_samples);
3409 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3412 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3413 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3414 stream->sample_index);
3418 /* now get the info for the sample we're at */
3419 sample = &stream->samples[stream->sample_index];
3421 *timestamp = QTSAMPLE_PTS (stream, sample);
3422 *offset = sample->offset;
3423 *size = sample->size;
3424 *duration = QTSAMPLE_DUR_PTS (stream, sample, *timestamp);
3425 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
3427 #ifdef QTDEMUX_MODIFICATION
3428 if (qtdemux->segment.rate > 1.0 && (FOURCC_vide == stream->subtype)) {
3429 /* forward trick play */
3430 gst_qtdemux_forward_trickplay (qtdemux, stream, timestamp);
3432 else if (qtdemux->segment.rate < 0.0 && (FOURCC_vide == stream->subtype)) {
3433 /* backward trick play */
3434 gst_qtdemux_backward_trickplay (qtdemux, stream, timestamp);
3443 stream->time_position = -1;
3448 /* move to the next sample in @stream.
3450 * Moves to the next segment when needed.
3453 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
3455 QtDemuxSample *sample;
3456 QtDemuxSegment *segment;
3458 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
3459 /* Mark the stream as EOS */
3460 GST_DEBUG_OBJECT (qtdemux,
3461 "reached max allowed sample %u, mark EOS", stream->to_sample);
3462 stream->time_position = -1;
3466 /* move to next sample */
3467 stream->sample_index++;
3469 /* get current segment */
3470 segment = &stream->segments[stream->segment_index];
3472 /* reached the last sample, we need the next segment */
3473 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3476 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3477 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3478 stream->sample_index);
3482 /* get next sample */
3483 sample = &stream->samples[stream->sample_index];
3485 /* see if we are past the segment */
3486 if (G_UNLIKELY (gst_util_uint64_scale (sample->timestamp,
3487 GST_SECOND, stream->timescale) >= segment->media_stop))
3490 if (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3491 stream->timescale) >= segment->media_start) {
3492 /* inside the segment, update time_position, looks very familiar to
3493 * GStreamer segments, doesn't it? */
3494 stream->time_position =
3495 (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3496 stream->timescale) - segment->media_start) + segment->time;
3498 /* not yet in segment, time does not yet increment. This means
3499 * that we are still prerolling keyframes to the decoder so it can
3500 * decode the first sample of the segment. */
3501 stream->time_position = segment->time;
3505 /* move to the next segment */
3508 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
3510 if (stream->segment_index == stream->n_segments - 1) {
3511 /* are we at the end of the last segment, we're EOS */
3512 stream->time_position = -1;
3514 /* else we're only at the end of the current segment */
3515 stream->time_position = segment->stop_time;
3517 /* make sure we select a new segment */
3518 stream->segment_index = -1;
3523 gst_qtdemux_sync_streams (GstQTDemux * demux)
3527 if (demux->n_streams <= 1)
3530 for (i = 0; i < demux->n_streams; i++) {
3531 QtDemuxStream *stream;
3532 GstClockTime end_time;
3534 stream = demux->streams[i];
3539 /* TODO advance time on subtitle streams here, if any some day */
3541 /* some clips/trailers may have unbalanced streams at the end,
3542 * so send EOS on shorter stream to prevent stalling others */
3544 /* do not mess with EOS if SEGMENT seeking */
3545 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
3548 if (demux->pullbased) {
3549 /* loop mode is sample time based */
3550 if (stream->time_position != -1)
3553 /* push mode is byte position based */
3554 if (stream->n_samples &&
3555 stream->samples[stream->n_samples - 1].offset >= demux->offset)
3559 if (stream->sent_eos)
3562 /* only act if some gap */
3563 end_time = stream->segments[stream->n_segments - 1].stop_time;
3564 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
3565 ", stream end: %" GST_TIME_FORMAT,
3566 GST_TIME_ARGS (demux->segment.last_stop), GST_TIME_ARGS (end_time));
3567 if (end_time + 2 * GST_SECOND < demux->segment.last_stop) {
3568 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
3569 GST_PAD_NAME (stream->pad));
3570 stream->sent_eos = TRUE;
3571 gst_pad_push_event (stream->pad, gst_event_new_eos ());
3576 /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return:
3578 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
3579 * GST_FLOW_UNEXPECTED: when all pads UNEXPECTED or NOT_LINKED.
3581 static GstFlowReturn
3582 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
3586 gboolean unexpected = FALSE, not_linked = TRUE;
3588 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
3590 /* store the value */
3591 stream->last_ret = ret;
3593 /* any other error that is not-linked or eos can be returned right away */
3594 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3597 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
3598 for (i = 0; i < demux->n_streams; i++) {
3599 QtDemuxStream *ostream = demux->streams[i];
3601 ret = ostream->last_ret;
3603 /* no unexpected or unlinked, return */
3604 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3607 /* we check to see if we have at least 1 unexpected or all unlinked */
3608 unexpected |= (ret == GST_FLOW_UNEXPECTED);
3609 not_linked &= (ret == GST_FLOW_NOT_LINKED);
3612 /* when we get here, we all have unlinked or unexpected */
3614 ret = GST_FLOW_NOT_LINKED;
3615 else if (unexpected)
3616 ret = GST_FLOW_UNEXPECTED;
3618 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
3622 /* the input buffer metadata must be writable. Returns NULL when the buffer is
3623 * completely cliped */
3625 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3628 gint64 start, stop, cstart, cstop, diff;
3629 GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
3632 gint num_rate, denom_rate;
3636 data = GST_BUFFER_DATA (buf);
3637 size = GST_BUFFER_SIZE (buf);
3639 /* depending on the type, setup the clip parameters */
3640 if (stream->subtype == FOURCC_soun) {
3641 frame_size = stream->bytes_per_frame;
3642 num_rate = GST_SECOND;
3643 denom_rate = (gint) stream->rate;
3645 } else if (stream->subtype == FOURCC_vide) {
3647 num_rate = stream->fps_n;
3648 denom_rate = stream->fps_d;
3653 /* we can only clip if we have a valid timestamp */
3654 timestamp = GST_BUFFER_TIMESTAMP (buf);
3655 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
3658 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) {
3659 duration = GST_BUFFER_DURATION (buf);
3662 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
3666 stop = start + duration;
3668 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
3669 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
3672 /* see if some clipping happened */
3673 diff = cstart - start;
3679 /* bring clipped time to samples and to bytes */
3680 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3683 GST_DEBUG_OBJECT (qtdemux,
3684 "clipping start to %" GST_TIME_FORMAT " %"
3685 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
3691 diff = stop - cstop;
3696 /* bring clipped time to samples and then to bytes */
3697 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3699 GST_DEBUG_OBJECT (qtdemux,
3700 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
3701 " bytes", GST_TIME_ARGS (cstop), diff);
3706 GST_BUFFER_TIMESTAMP (buf) = timestamp;
3707 GST_BUFFER_DURATION (buf) = duration;
3708 GST_BUFFER_SIZE (buf) = size;
3709 GST_BUFFER_DATA (buf) = data;
3713 /* dropped buffer */
3716 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
3721 GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer");
3726 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
3727 gst_buffer_unref (buf);
3732 /* the input buffer metadata must be writable,
3733 * but time/duration etc not yet set and need not be preserved */
3735 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3739 guint size, nsize = 0;
3742 data = GST_BUFFER_DATA (buf);
3743 size = GST_BUFFER_SIZE (buf);
3745 /* not many cases for now */
3746 if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
3747 /* send a one time dvd clut event */
3748 if (stream->pending_event && stream->pad)
3749 gst_pad_push_event (stream->pad, stream->pending_event);
3750 stream->pending_event = NULL;
3751 /* no further processing needed */
3752 stream->need_process = FALSE;
3755 if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
3759 if (G_LIKELY (size >= 2)) {
3760 nsize = GST_READ_UINT16_BE (data);
3761 nsize = MIN (nsize, size - 2);
3764 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%d", nsize, size);
3766 /* takes care of UTF-8 validation or UTF-16 recognition,
3767 * no other encoding expected */
3768 str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
3770 gst_buffer_unref (buf);
3771 buf = gst_buffer_new ();
3772 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;
3773 GST_BUFFER_SIZE (buf) = strlen (str);
3775 /* may be 0-size subtitle, which is also sent to keep pipeline going */
3776 GST_BUFFER_DATA (buf) = data + 2;
3777 GST_BUFFER_SIZE (buf) = nsize;
3780 /* FIXME ? convert optional subsequent style info to markup */
3785 /* Sets a buffer's attributes properly and pushes it downstream.
3786 * Also checks for additional actions and custom processing that may
3787 * need to be done first.
3790 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
3791 QtDemuxStream * stream, GstBuffer * buf,
3792 guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
3793 guint64 byte_position)
3795 GstFlowReturn ret = GST_FLOW_OK;
3797 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
3800 url = g_strndup ((gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
3801 if (url != NULL && strlen (url) != 0) {
3802 /* we have RTSP redirect now */
3803 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3804 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
3805 gst_structure_new ("redirect",
3806 "new-location", G_TYPE_STRING, url, NULL)));
3807 qtdemux->posted_redirect = TRUE;
3809 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
3815 /* position reporting */
3816 if (qtdemux->segment.rate >= 0) {
3817 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, position);
3818 gst_qtdemux_sync_streams (qtdemux);
3821 if (G_UNLIKELY (!stream->pad)) {
3822 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
3823 gst_buffer_unref (buf);
3827 /* send out pending buffers */
3828 while (stream->buffers) {
3829 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
3831 if (G_UNLIKELY (stream->discont)) {
3832 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
3833 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
3834 stream->discont = FALSE;
3836 gst_buffer_set_caps (buffer, stream->caps);
3838 gst_pad_push (stream->pad, buffer);
3840 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
3843 /* we're going to modify the metadata */
3844 buf = gst_buffer_make_metadata_writable (buf);
3846 if (G_UNLIKELY (stream->need_process))
3847 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
3849 GST_BUFFER_TIMESTAMP (buf) = timestamp;
3850 GST_BUFFER_DURATION (buf) = duration;
3851 GST_BUFFER_OFFSET (buf) = -1;
3852 GST_BUFFER_OFFSET_END (buf) = -1;
3854 if (G_UNLIKELY (stream->padding)) {
3855 GST_BUFFER_DATA (buf) += stream->padding;
3856 GST_BUFFER_SIZE (buf) -= stream->padding;
3859 if (G_UNLIKELY (qtdemux->element_index)) {
3860 GstClockTime stream_time;
3863 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
3865 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
3866 GST_LOG_OBJECT (qtdemux,
3867 "adding association %" GST_TIME_FORMAT "-> %"
3868 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
3869 gst_index_add_association (qtdemux->element_index,
3871 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
3872 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
3873 GST_FORMAT_BYTES, byte_position, NULL);
3877 if (stream->need_clip)
3878 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
3880 if (G_UNLIKELY (buf == NULL))
3883 if (G_UNLIKELY (stream->discont)) {
3884 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
3885 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
3886 stream->discont = FALSE;
3890 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
3892 gst_buffer_set_caps (buf, stream->caps);
3894 GST_LOG_OBJECT (qtdemux,
3895 "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
3896 GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3897 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
3899 ret = gst_pad_push (stream->pad, buf);
3905 static GstFlowReturn
3906 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
3908 GstFlowReturn ret = GST_FLOW_OK;
3909 GstBuffer *buf = NULL;
3910 QtDemuxStream *stream;
3913 guint64 timestamp = GST_CLOCK_TIME_NONE;
3914 guint64 duration = 0;
3915 gboolean keyframe = FALSE;
3920 gst_qtdemux_push_pending_newsegment (qtdemux);
3922 /* Figure out the next stream sample to output, min_time is expressed in
3923 * global time and runs over the edit list segments. */
3924 min_time = G_MAXUINT64;
3926 for (i = 0; i < qtdemux->n_streams; i++) {
3929 stream = qtdemux->streams[i];
3930 position = stream->time_position;
3932 /* position of -1 is EOS */
3933 if (position != -1 && position < min_time) {
3934 min_time = position;
3939 if (G_UNLIKELY (index == -1)) {
3940 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
3944 /* check for segment end */
3945 if (G_UNLIKELY (qtdemux->segment.stop != -1
3946 && qtdemux->segment.stop < min_time)) {
3947 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
3951 stream = qtdemux->streams[index];
3953 /* fetch info for the current sample of this stream */
3954 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
3955 &size, ×tamp, &duration, &keyframe)))
3958 GST_LOG_OBJECT (qtdemux,
3959 "pushing from stream %d, offset %" G_GUINT64_FORMAT
3960 ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
3961 index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
3963 /* hmm, empty sample, skip and move to next sample */
3964 if (G_UNLIKELY (size <= 0))
3967 /* last pushed sample was out of boundary, goto next sample */
3968 if (G_UNLIKELY (stream->last_ret == GST_FLOW_UNEXPECTED))
3971 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
3974 ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
3975 if (G_UNLIKELY (ret != GST_FLOW_OK))
3978 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
3979 timestamp, duration, keyframe, min_time, offset);
3982 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
3983 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
3984 * we have no more data for the pad to push */
3985 if (ret == GST_FLOW_UNEXPECTED)
3989 gst_qtdemux_advance_sample (qtdemux, stream);
3997 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
3998 ret = GST_FLOW_UNEXPECTED;
4003 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
4004 /* EOS will be raised if all are EOS */
4011 gst_qtdemux_loop (GstPad * pad)
4013 GstQTDemux *qtdemux;
4017 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
4019 cur_offset = qtdemux->offset;
4020 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
4021 cur_offset, qtdemux->state);
4023 switch (qtdemux->state) {
4024 case QTDEMUX_STATE_INITIAL:
4025 case QTDEMUX_STATE_HEADER:
4026 ret = gst_qtdemux_loop_state_header (qtdemux);
4028 case QTDEMUX_STATE_MOVIE:
4029 ret = gst_qtdemux_loop_state_movie (qtdemux);
4030 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_UNEXPECTED) {
4031 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
4039 /* if something went wrong, pause */
4040 if (ret != GST_FLOW_OK)
4044 gst_object_unref (qtdemux);
4050 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4051 (NULL), ("streaming stopped, invalid state"));
4052 qtdemux->segment_running = FALSE;
4053 gst_pad_pause_task (pad);
4054 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4059 const gchar *reason = gst_flow_get_name (ret);
4061 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
4063 qtdemux->segment_running = FALSE;
4064 gst_pad_pause_task (pad);
4066 /* fatal errors need special actions */
4068 if (ret == GST_FLOW_UNEXPECTED) {
4069 if (qtdemux->n_streams == 0) {
4070 /* we have no streams, post an error */
4071 gst_qtdemux_post_no_playable_stream_error (qtdemux);
4073 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4076 /* FIXME: I am not sure this is the right fix. If the sinks are
4077 * supposed to detect the segment is complete and accumulate
4078 * automatically, it does not seem to work here. Need more work */
4079 qtdemux->segment_running = TRUE;
4081 if ((stop = qtdemux->segment.stop) == -1)
4082 stop = qtdemux->segment.duration;
4084 if (qtdemux->segment.rate >= 0) {
4085 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
4086 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4087 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4088 GST_FORMAT_TIME, stop));
4090 /* For Reverse Playback */
4091 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
4092 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4093 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4094 GST_FORMAT_TIME, qtdemux->segment.start));
4097 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
4098 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4100 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
4101 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4102 (NULL), ("streaming stopped, reason %s", reason));
4103 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4112 * Returns the size of the first entry at the current offset.
4113 * If -1, there are none (which means EOS or empty file).
4116 next_entry_size (GstQTDemux * demux)
4118 QtDemuxStream *stream;
4121 guint64 smalloffs = (guint64) - 1;
4122 QtDemuxSample *sample;
4124 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
4127 for (i = 0; i < demux->n_streams; i++) {
4128 stream = demux->streams[i];
4130 if (stream->sample_index == -1)
4131 stream->sample_index = 0;
4133 if (stream->sample_index >= stream->n_samples) {
4134 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
4138 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
4139 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
4140 stream->sample_index);
4144 sample = &stream->samples[stream->sample_index];
4146 GST_LOG_OBJECT (demux,
4147 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4148 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
4149 sample->offset, sample->size);
4151 if (((smalloffs == -1)
4152 || (sample->offset < smalloffs)) && (sample->size)) {
4154 smalloffs = sample->offset;
4158 GST_LOG_OBJECT (demux,
4159 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
4160 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
4165 stream = demux->streams[smallidx];
4166 sample = &stream->samples[stream->sample_index];
4168 if (sample->offset >= demux->offset) {
4169 demux->todrop = sample->offset - demux->offset;
4170 return sample->size + demux->todrop;
4173 GST_DEBUG_OBJECT (demux,
4174 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
4179 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
4181 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
4183 gst_element_post_message (GST_ELEMENT_CAST (demux),
4184 gst_message_new_element (GST_OBJECT_CAST (demux),
4185 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
4189 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
4194 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
4197 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
4198 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
4199 GST_SEEK_TYPE_NONE, -1);
4201 res = gst_pad_push_event (demux->sinkpad, event);
4206 /* check for seekable upstream, above and beyond a mere query */
4208 gst_qtdemux_check_seekability (GstQTDemux * demux)
4211 gboolean seekable = FALSE;
4212 gint64 start = -1, stop = -1;
4214 if (demux->upstream_size)
4217 query = gst_query_new_seeking (GST_FORMAT_BYTES);
4218 if (!gst_pad_peer_query (demux->sinkpad, query)) {
4219 GST_DEBUG_OBJECT (demux, "seeking query failed");
4223 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
4225 /* try harder to query upstream size if we didn't get it the first time */
4226 if (seekable && stop == -1) {
4227 GstFormat fmt = GST_FORMAT_BYTES;
4229 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
4230 gst_pad_query_peer_duration (demux->sinkpad, &fmt, &stop);
4233 /* if upstream doesn't know the size, it's likely that it's not seekable in
4234 * practice even if it technically may be seekable */
4235 if (seekable && (start != 0 || stop <= start)) {
4236 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
4241 gst_query_unref (query);
4243 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
4244 G_GUINT64_FORMAT ")", seekable, start, stop);
4245 demux->upstream_seekable = seekable;
4246 demux->upstream_size = seekable ? stop : -1;
4249 /* FIXME, unverified after edit list updates */
4250 static GstFlowReturn
4251 gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
4254 GstFlowReturn ret = GST_FLOW_OK;
4256 demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad));
4258 #ifdef QTDEMUX_MODIFICATION
4259 /* Added consideration of demuxer state */
4260 if (demux->file && demux->state == QTDEMUX_STATE_MOVIE) {
4261 if (fwrite (GST_BUFFER_DATA (inbuf), GST_BUFFER_SIZE (inbuf), 1, demux->file) != 1) {
4262 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4263 (_("Could not write to temporary buffer file.")),
4264 ("File Write Failure: '%s'",
4266 return GST_FLOW_ERROR;
4268 demux->filesize += GST_BUFFER_SIZE (inbuf);
4270 gst_buffer_unref (inbuf);
4272 gst_adapter_push (demux->adapter, inbuf);
4275 gst_adapter_push (demux->adapter, inbuf);
4278 /* we never really mean to buffer that much */
4279 if (demux->neededbytes == -1)
4282 GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
4283 inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
4285 #ifdef QTDEMUX_MODIFICATION
4286 /* Added consideration of demuxer state and file size*/
4287 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes
4288 || (demux->file && demux->state == QTDEMUX_STATE_MOVIE && demux->filesize >= demux->neededbytes))
4289 && ret == GST_FLOW_OK) {
4291 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
4292 (ret == GST_FLOW_OK)) {
4295 GST_DEBUG_OBJECT (demux,
4296 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
4297 demux->state, demux->neededbytes, demux->offset);
4299 switch (demux->state) {
4300 case QTDEMUX_STATE_INITIAL:{
4305 gst_qtdemux_check_seekability (demux);
4307 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4309 /* get fourcc/length, set neededbytes */
4310 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
4312 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
4313 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
4315 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4316 (_("This file is invalid and cannot be played.")),
4317 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
4318 GST_FOURCC_ARGS (fourcc)));
4319 ret = GST_FLOW_ERROR;
4322 if (fourcc == FOURCC_mdat) {
4323 if (demux->n_streams > 0) {
4324 /* we have the headers, start playback */
4325 demux->state = QTDEMUX_STATE_MOVIE;
4326 demux->neededbytes = next_entry_size (demux);
4327 demux->mdatleft = size;
4329 /* Only post, event on pads is done after newsegment */
4330 qtdemux_post_global_tags (demux);
4333 /* no headers yet, try to get them */
4336 guint64 old, target;
4339 old = demux->offset;
4340 target = old + size;
4342 /* try to jump over the atom with a seek */
4343 /* only bother if it seems worth doing so,
4344 * and avoids possible upstream/server problems */
4345 if (demux->upstream_seekable &&
4346 demux->upstream_size > 4 * (1 << 20)) {
4347 res = qtdemux_seek_offset (demux, target);
4349 GST_DEBUG_OBJECT (demux, "skipping seek");
4354 GST_DEBUG_OBJECT (demux, "seek success");
4355 /* remember the offset fo the first mdat so we can seek back to it
4356 * after we have the headers */
4357 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
4358 demux->first_mdat = old;
4359 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
4362 /* seek worked, continue reading */
4363 demux->offset = target;
4364 demux->neededbytes = 16;
4365 demux->state = QTDEMUX_STATE_INITIAL;
4367 /* seek failed, need to buffer */
4368 demux->offset = old;
4369 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
4370 /* there may be multiple mdat (or alike) buffers */
4372 if (demux->mdatbuffer)
4373 bs = GST_BUFFER_SIZE (demux->mdatbuffer);
4374 #ifdef QTDEMUX_MODIFICATION
4375 else if (demux->file)
4376 bs = demux->filesize;
4380 #ifdef QTDEMUX_MODIFICATION
4381 if (size + bs > demux->maxbuffersize)
4383 if (size + bs > 10 * (1 << 20))
4386 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
4387 demux->neededbytes = size;
4388 #ifdef QTDEMUX_MODIFICATION
4389 if ((demux->filename && demux->file == NULL) || !demux->mdatbuffer)
4391 if (!demux->mdatbuffer)
4393 demux->mdatoffset = demux->offset;
4396 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
4397 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4398 (_("This file is invalid and cannot be played.")),
4399 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
4400 GST_FOURCC_ARGS (fourcc), size));
4401 ret = GST_FLOW_ERROR;
4404 /* this means we already started buffering and still no moov header,
4405 * let's continue buffering everything till we get moov */
4406 #ifdef QTDEMUX_MODIFICATION
4407 if ((demux->mdatbuffer || demux->file) && (fourcc != FOURCC_moov))
4409 if (demux->mdatbuffer && (fourcc != FOURCC_moov))
4412 demux->neededbytes = size;
4413 demux->state = QTDEMUX_STATE_HEADER;
4417 case QTDEMUX_STATE_HEADER:{
4421 GST_DEBUG_OBJECT (demux, "In header");
4423 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4425 /* parse the header */
4426 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
4428 if (fourcc == FOURCC_moov) {
4429 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
4431 demux->got_moov = TRUE;
4433 /* prepare newsegment to send when streaming actually starts */
4434 if (!demux->pending_newsegment) {
4435 demux->pending_newsegment =
4436 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
4437 0, GST_CLOCK_TIME_NONE, 0);
4440 qtdemux_parse_moov (demux, data, demux->neededbytes);
4441 qtdemux_node_dump (demux, demux->moov_node);
4442 qtdemux_parse_tree (demux);
4443 qtdemux_expose_streams (demux);
4445 g_node_destroy (demux->moov_node);
4446 demux->moov_node = NULL;
4447 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
4448 } else if (fourcc == FOURCC_moof) {
4449 if (demux->got_moov && demux->fragmented) {
4450 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
4451 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
4452 demux->offset, NULL)) {
4453 ret = GST_FLOW_ERROR;
4457 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
4459 } else if (fourcc == FOURCC_ftyp) {
4460 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
4461 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
4462 } else if (fourcc == FOURCC_uuid) {
4463 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
4464 qtdemux_parse_uuid (demux, data, demux->neededbytes);
4466 GST_WARNING_OBJECT (demux,
4467 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
4468 GST_FOURCC_ARGS (fourcc));
4469 /* Let's jump that one and go back to initial state */
4472 #ifdef QTDEMUX_MODIFICATION
4473 if ((demux->mdatbuffer || demux->file) && demux->n_streams) {
4476 if (demux->mdatbuffer && demux->n_streams) {
4479 /* the mdat was before the header */
4480 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
4481 demux->n_streams, demux->mdatbuffer);
4482 /* restore our adapter/offset view of things with upstream;
4483 * put preceding buffered data ahead of current moov data.
4484 * This should also handle evil mdat, moov, mdat cases and alike */
4485 #ifdef QTDEMUX_MODIFICATION
4486 if (demux->file == NULL) {
4487 // TODO: need to know what we need to do with buf, it is not freed
4488 //buf = gst_adapter_take_buffer (demux->adapter, gst_adapter_available (demux->adapter));
4490 gst_adapter_clear (demux->adapter);
4491 gst_adapter_push (demux->adapter, demux->mdatbuffer); /* need to check */
4492 demux->mdatbuffer = NULL;
4493 #ifdef QTDEMUX_MODIFICATION
4495 buf = gst_adapter_take_buffer (demux->adapter, gst_adapter_available (demux->adapter));
4496 gst_adapter_clear (demux->adapter);
4497 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, demux->file) != 1) {
4498 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4499 (_("Could not write to temporary buffer file.")),
4500 ("File Write Failure: '%s'", demux->filename));
4501 ret = GST_FLOW_ERROR;
4504 demux->filesize += GST_BUFFER_SIZE (buf);
4506 gst_buffer_unref (buf);
4509 demux->offset = demux->mdatoffset;
4510 demux->neededbytes = next_entry_size (demux);
4511 demux->state = QTDEMUX_STATE_MOVIE;
4512 demux->mdatleft = gst_adapter_available (demux->adapter);
4514 /* Only post, event on pads is done after newsegment */
4515 qtdemux_post_global_tags (demux);
4517 #ifdef QTDEMUX_MODIFICATION
4519 demux->ofile = fopen (demux->filename, "rb");
4520 if (demux->ofile == NULL) {
4521 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4522 (_("Could not open temporary buffer file.")),
4523 ("File Open Failure %d: '%s'", __LINE__, demux->filename));
4524 ret = GST_FLOW_ERROR;
4530 GST_DEBUG_OBJECT (demux, "Carrying on normally");
4531 gst_adapter_flush (demux->adapter, demux->neededbytes);
4533 if (demux->got_moov && demux->first_mdat != -1) {
4536 /* we need to seek back */
4537 res = qtdemux_seek_offset (demux, demux->first_mdat);
4539 demux->offset = demux->first_mdat;
4541 GST_DEBUG_OBJECT (demux, "Seek back failed");
4544 demux->offset += demux->neededbytes;
4546 demux->neededbytes = 16;
4547 demux->state = QTDEMUX_STATE_INITIAL;
4552 case QTDEMUX_STATE_BUFFER_MDAT:{
4555 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
4557 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4558 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
4559 GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4)));
4561 #ifdef QTDEMUX_MODIFICATION
4562 if (demux->filename == NULL) {
4564 if (demux->mdatbuffer)
4565 demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
4567 demux->mdatbuffer = buf;
4568 #ifdef QTDEMUX_MODIFICATION
4572 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, demux->file) != 1) {
4573 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4574 (_("Could not write to temporary buffer file.")),
4575 ("File Write Failure: '%s'", demux->filename));
4576 ret = GST_FLOW_ERROR;
4579 demux->filesize += GST_BUFFER_SIZE (buf);
4581 gst_buffer_unref (buf);
4583 demux->file = fopen (demux->filename, "wb");
4584 if (demux->file == NULL) {
4585 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4586 (_("Could not create temporary buffer file.")),
4587 ("File Open Failure: '%s'", demux->filename));
4588 ret = GST_FLOW_ERROR;
4595 demux->offset += demux->neededbytes;
4596 demux->neededbytes = 16;
4597 demux->state = QTDEMUX_STATE_INITIAL;
4598 gst_qtdemux_post_progress (demux, 1, 1);
4602 case QTDEMUX_STATE_MOVIE:{
4604 QtDemuxStream *stream = NULL;
4605 QtDemuxSample *sample;
4607 guint64 timestamp, duration, position;
4610 GST_DEBUG_OBJECT (demux,
4611 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
4613 if (demux->fragmented) {
4614 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
4616 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
4617 /* if needed data starts within this atom,
4618 * then it should not exceed this atom */
4619 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
4620 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4621 (_("This file is invalid and cannot be played.")),
4622 ("sample data crosses atom boundary"));
4623 ret = GST_FLOW_ERROR;
4626 demux->mdatleft -= demux->neededbytes;
4628 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
4629 /* so we are dropping more than left in this atom */
4630 demux->todrop -= demux->mdatleft;
4631 demux->neededbytes -= demux->mdatleft;
4632 demux->mdatleft = 0;
4633 /* need to resume atom parsing so we do not miss any other pieces */
4634 demux->state = QTDEMUX_STATE_INITIAL;
4635 demux->neededbytes = 16;
4640 if (demux->todrop) {
4641 GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);
4642 #ifdef QTDEMUX_MODIFICATION
4643 if (demux->file == NULL)
4645 gst_adapter_flush (demux->adapter, demux->todrop);
4646 #ifdef QTDEMUX_MODIFICATION
4648 fseek (demux->ofile, (long) demux->todrop, SEEK_CUR);
4649 demux->filesize -= demux->todrop;
4652 demux->neededbytes -= demux->todrop;
4653 demux->offset += demux->todrop;
4657 /* initial newsegment sent here after having added pads,
4658 * possible others in sink_event */
4659 if (G_UNLIKELY (demux->pending_newsegment)) {
4660 gst_qtdemux_push_event (demux, demux->pending_newsegment);
4661 demux->pending_newsegment = NULL;
4662 /* clear to send tags on all streams */
4663 for (i = 0; i < demux->n_streams; i++) {
4664 gst_qtdemux_push_tags (demux, demux->streams[i]);
4668 /* Figure out which stream this is packet belongs to */
4669 for (i = 0; i < demux->n_streams; i++) {
4670 stream = demux->streams[i];
4671 if (stream->sample_index >= stream->n_samples)
4673 GST_LOG_OBJECT (demux,
4674 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4675 " / size:%d)", i, stream->sample_index,
4676 stream->samples[stream->sample_index].offset,
4677 stream->samples[stream->sample_index].size);
4679 if (stream->samples[stream->sample_index].offset == demux->offset)
4683 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
4684 goto unknown_stream;
4686 /* Put data in a buffer, set timestamps, caps, ... */
4687 #ifdef QTDEMUX_MODIFICATION
4688 if (demux->file == NULL) {
4690 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4691 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
4692 GST_FOURCC_ARGS (stream->fourcc));
4693 #ifdef QTDEMUX_MODIFICATION
4695 outbuf = gst_buffer_new_and_alloc (demux->neededbytes);
4696 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4697 if (fread (GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf), 1, demux->ofile) != 1) {
4698 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4699 (_("Could not read from temporary buffer file.")),
4700 ("File Read Failure: '%s'", demux->filename));
4701 ret = GST_FLOW_ERROR;
4704 demux->filesize -= demux->neededbytes;
4707 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4709 sample = &stream->samples[stream->sample_index];
4711 position = QTSAMPLE_DTS (stream, sample);
4712 timestamp = QTSAMPLE_PTS (stream, sample);
4713 duration = QTSAMPLE_DUR_DTS (stream, sample, position);
4714 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
4716 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
4717 timestamp, duration, keyframe, position, demux->offset);
4720 ret = gst_qtdemux_combine_flows (demux, stream, ret);
4722 stream->sample_index++;
4724 /* update current offset and figure out size of next buffer */
4725 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
4726 demux->offset, demux->neededbytes);
4727 demux->offset += demux->neededbytes;
4728 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
4731 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
4732 if (demux->fragmented) {
4733 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
4734 /* there may be more to follow, only finish this atom */
4735 demux->todrop = demux->mdatleft;
4736 demux->neededbytes = demux->todrop;
4748 /* when buffering movie data, at least show user something is happening */
4749 #ifdef QTDEMUX_MODIFICATION
4750 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT) {
4753 available = demux->filesize;
4755 available = gst_adapter_available (demux->adapter);
4757 if (available <= demux->neededbytes) {
4758 gst_qtdemux_post_progress (demux, available, demux->neededbytes);
4762 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
4763 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
4764 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
4765 demux->neededbytes);
4769 gst_object_unref (demux);
4776 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
4777 ret = GST_FLOW_ERROR;
4782 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
4783 ret = GST_FLOW_UNEXPECTED;
4788 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
4789 (NULL), ("qtdemuxer invalid state %d", demux->state));
4790 ret = GST_FLOW_ERROR;
4795 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
4796 (NULL), ("no 'moov' atom within the first 10 MB"));
4797 ret = GST_FLOW_ERROR;
4803 qtdemux_sink_activate (GstPad * sinkpad)
4805 if (gst_pad_check_pull_range (sinkpad))
4806 return gst_pad_activate_pull (sinkpad, TRUE);
4808 return gst_pad_activate_push (sinkpad, TRUE);
4812 qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4814 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
4817 demux->pullbased = TRUE;
4818 demux->segment_running = TRUE;
4819 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
4822 demux->segment_running = FALSE;
4823 return gst_pad_stop_task (sinkpad);
4828 qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
4830 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
4832 demux->pullbased = FALSE;
4839 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size)
4841 return g_malloc (items * size);
4845 qtdemux_zfree (void *opaque, void *addr)
4851 qtdemux_inflate (void *z_buffer, guint z_length, guint length)
4857 z = g_new0 (z_stream, 1);
4858 z->zalloc = qtdemux_zalloc;
4859 z->zfree = qtdemux_zfree;
4862 z->next_in = z_buffer;
4863 z->avail_in = z_length;
4865 buffer = (guint8 *) g_malloc (length);
4866 ret = inflateInit (z);
4867 while (z->avail_in > 0) {
4868 if (z->avail_out == 0) {
4870 buffer = (guint8 *) g_realloc (buffer, length);
4871 z->next_out = buffer + z->total_out;
4872 z->avail_out = 1024;
4874 ret = inflate (z, Z_SYNC_FLUSH);
4878 if (ret != Z_STREAM_END) {
4879 g_warning ("inflate() returned %d", ret);
4885 #endif /* HAVE_ZLIB */
4888 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
4892 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
4894 /* counts as header data */
4895 qtdemux->header_size += length;
4897 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
4898 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
4900 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
4906 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
4907 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
4908 if (dcom == NULL || cmvd == NULL)
4909 goto invalid_compression;
4911 method = QT_FOURCC ((guint8 *) dcom->data + 8);
4914 case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{
4915 guint uncompressed_length;
4916 guint compressed_length;
4919 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
4920 compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12;
4921 GST_LOG ("length = %u", uncompressed_length);
4924 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
4925 compressed_length, uncompressed_length);
4927 qtdemux->moov_node_compressed = qtdemux->moov_node;
4928 qtdemux->moov_node = g_node_new (buf);
4930 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
4931 uncompressed_length);
4934 #endif /* HAVE_ZLIB */
4936 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
4937 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
4944 invalid_compression:
4946 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
4952 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
4955 while (G_UNLIKELY (buf < end)) {
4959 if (G_UNLIKELY (buf + 4 > end)) {
4960 GST_LOG_OBJECT (qtdemux, "buffer overrun");
4963 len = QT_UINT32 (buf);
4964 if (G_UNLIKELY (len == 0)) {
4965 GST_LOG_OBJECT (qtdemux, "empty container");
4968 if (G_UNLIKELY (len < 8)) {
4969 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
4972 if (G_UNLIKELY (len > (end - buf))) {
4973 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
4974 (gint) (end - buf));
4978 child = g_node_new ((guint8 *) buf);
4979 g_node_append (node, child);
4980 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
4981 qtdemux_parse_node (qtdemux, child, buf, len);
4989 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
4992 int len = QT_UINT32 (xdxt->data);
4993 guint8 *buf = xdxt->data;
4994 guint8 *end = buf + len;
4997 /* skip size and type */
5005 size = QT_UINT32 (buf);
5006 type = QT_FOURCC (buf + 4);
5008 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
5010 if (buf + size > end || size <= 0)
5016 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
5017 GST_FOURCC_ARGS (type));
5021 buffer = gst_buffer_new_and_alloc (size);
5022 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5023 stream->buffers = g_slist_append (stream->buffers, buffer);
5024 GST_LOG_OBJECT (qtdemux, "parsing theora header");
5027 buffer = gst_buffer_new_and_alloc (size);
5028 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5029 stream->buffers = g_slist_append (stream->buffers, buffer);
5030 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
5033 buffer = gst_buffer_new_and_alloc (size);
5034 memcpy (GST_BUFFER_DATA (buffer), buf, size);
5035 stream->buffers = g_slist_append (stream->buffers, buffer);
5036 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
5039 GST_WARNING_OBJECT (qtdemux,
5040 "unknown theora cookie %" GST_FOURCC_FORMAT,
5041 GST_FOURCC_ARGS (type));
5050 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
5054 guint32 node_length = 0;
5055 const QtNodeType *type;
5058 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
5060 if (G_UNLIKELY (length < 8))
5061 goto not_enough_data;
5063 node_length = QT_UINT32 (buffer);
5064 fourcc = QT_FOURCC (buffer + 4);
5066 /* ignore empty nodes */
5067 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
5070 type = qtdemux_type_get (fourcc);
5072 end = buffer + length;
5074 GST_LOG_OBJECT (qtdemux,
5075 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
5076 GST_FOURCC_ARGS (fourcc), node_length, type->name);
5078 if (node_length > length)
5079 goto broken_atom_size;
5081 if (type->flags & QT_FLAG_CONTAINER) {
5082 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
5087 if (node_length < 20) {
5088 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
5091 GST_DEBUG_OBJECT (qtdemux,
5092 "parsing stsd (sample table, sample description) atom");
5093 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
5103 /* also read alac (or whatever) in stead of mp4a in the following,
5104 * since a similar layout is used in other cases as well */
5105 if (fourcc == FOURCC_mp4a)
5110 /* There are two things we might encounter here: a true mp4a atom, and
5111 an mp4a entry in an stsd atom. The latter is what we're interested
5112 in, and it looks like an atom, but isn't really one. The true mp4a
5113 atom is short, so we detect it based on length here. */
5114 if (length < min_size) {
5115 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
5116 GST_FOURCC_ARGS (fourcc));
5120 /* 'version' here is the sound sample description version. Types 0 and
5121 1 are documented in the QTFF reference, but type 2 is not: it's
5122 described in Apple header files instead (struct SoundDescriptionV2
5124 version = QT_UINT16 (buffer + 16);
5126 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
5127 GST_FOURCC_ARGS (fourcc), version);
5129 /* parse any esds descriptors */
5141 GST_WARNING_OBJECT (qtdemux,
5142 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
5143 GST_FOURCC_ARGS (fourcc), version);
5148 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5160 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
5161 GST_FOURCC_ARGS (fourcc));
5162 version = QT_UINT32 (buffer + 16);
5163 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
5164 if (1 || version == 0x00000000) {
5165 buf = buffer + 0x32;
5167 /* FIXME Quicktime uses PASCAL string while
5168 * the iso format uses C strings. Check the file
5169 * type before attempting to parse the string here. */
5170 tlen = QT_UINT8 (buf);
5171 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen);
5173 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf);
5174 /* the string has a reserved space of 32 bytes so skip
5175 * the remaining 31 */
5177 buf += 4; /* and 4 bytes reserved */
5179 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf);
5181 qtdemux_parse_container (qtdemux, node, buf, end);
5187 GST_MEMDUMP_OBJECT (qtdemux, "avc1", buffer, end - buffer);
5188 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
5193 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
5198 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
5199 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
5207 version = QT_UINT32 (buffer + 12);
5208 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
5215 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
5220 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5225 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
5229 if (!strcmp (type->name, "unknown"))
5230 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
5234 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
5235 GST_FOURCC_ARGS (fourcc));
5241 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5242 (_("This file is corrupt and cannot be played.")),
5243 ("Not enough data for an atom header, got only %u bytes", length));
5248 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5249 (_("This file is corrupt and cannot be played.")),
5250 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
5251 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
5258 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
5262 guint32 child_fourcc;
5264 for (child = g_node_first_child (node); child;
5265 child = g_node_next_sibling (child)) {
5266 buffer = (guint8 *) child->data;
5268 child_fourcc = QT_FOURCC (buffer + 4);
5270 if (G_UNLIKELY (child_fourcc == fourcc)) {
5278 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
5279 GstByteReader * parser)
5283 guint32 child_fourcc, child_len;
5285 for (child = g_node_first_child (node); child;
5286 child = g_node_next_sibling (child)) {
5287 buffer = (guint8 *) child->data;
5289 child_len = QT_UINT32 (buffer);
5290 child_fourcc = QT_FOURCC (buffer + 4);
5292 if (G_UNLIKELY (child_fourcc == fourcc)) {
5293 if (G_UNLIKELY (child_len < (4 + 4)))
5295 /* FIXME: must verify if atom length < parent atom length */
5296 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5304 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
5305 GstByteReader * parser)
5309 guint32 child_fourcc, child_len;
5311 for (child = g_node_next_sibling (node); child;
5312 child = g_node_next_sibling (child)) {
5313 buffer = (guint8 *) child->data;
5315 child_fourcc = QT_FOURCC (buffer + 4);
5317 if (child_fourcc == fourcc) {
5319 child_len = QT_UINT32 (buffer);
5320 if (G_UNLIKELY (child_len < (4 + 4)))
5322 /* FIXME: must verify if atom length < parent atom length */
5323 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5332 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
5334 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
5338 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
5339 QtDemuxStream * stream, GstTagList * list)
5341 /* consistent default for push based mode */
5342 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
5343 gst_segment_set_newsegment (&stream->segment, FALSE, 1.0, GST_FORMAT_TIME,
5344 0, GST_CLOCK_TIME_NONE, 0);
5346 if (stream->subtype == FOURCC_vide) {
5347 gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
5350 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
5353 /* fps is calculated base on the duration of the first frames since
5354 * qt does not have a fixed framerate. */
5355 if ((stream->n_samples == 1) && (stream->min_duration == 0)) {
5360 stream->fps_n = stream->timescale;
5361 if (stream->min_duration == 0)
5364 stream->fps_d = stream->min_duration;
5369 gint depth, palette_count;
5370 const guint32 *palette_data = NULL;
5372 gst_caps_set_simple (stream->caps,
5373 "width", G_TYPE_INT, stream->width,
5374 "height", G_TYPE_INT, stream->height,
5375 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
5377 /* calculate pixel-aspect-ratio using display width and height */
5378 GST_DEBUG_OBJECT (qtdemux,
5379 "video size %dx%d, target display size %dx%d", stream->width,
5380 stream->height, stream->display_width, stream->display_height);
5382 if (stream->display_width > 0 && stream->display_height > 0 &&
5383 stream->width > 0 && stream->height > 0) {
5386 /* calculate the pixel aspect ratio using the display and pixel w/h */
5387 n = stream->display_width * stream->height;
5388 d = stream->display_height * stream->width;
5391 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
5392 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5393 GST_TYPE_FRACTION, n, d, NULL);
5396 /* qt file might have pasp atom */
5397 if (stream->par_w > 0 && stream->par_h > 0) {
5398 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
5399 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5400 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
5403 depth = stream->bits_per_sample;
5405 /* more than 32 bits means grayscale */
5406 gray = (depth > 32);
5407 /* low 32 bits specify the depth */
5410 /* different number of palette entries is determined by depth. */
5412 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
5413 palette_count = (1 << depth);
5415 switch (palette_count) {
5419 palette_data = ff_qt_default_palette_2;
5422 palette_data = ff_qt_default_palette_4;
5426 palette_data = ff_qt_grayscale_palette_16;
5428 palette_data = ff_qt_default_palette_16;
5432 palette_data = ff_qt_grayscale_palette_256;
5434 palette_data = ff_qt_default_palette_256;
5437 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5438 (_("The video in this file might not play correctly.")),
5439 ("unsupported palette depth %d", depth));
5445 /* make sure it's not writable. We leave MALLOCDATA to NULL so that we
5446 * don't free any of the buffer data. */
5447 palette = gst_buffer_new ();
5448 GST_BUFFER_FLAG_SET (palette, GST_BUFFER_FLAG_READONLY);
5449 GST_BUFFER_DATA (palette) = (guint8 *) palette_data;
5450 GST_BUFFER_SIZE (palette) = sizeof (guint32) * palette_count;
5452 gst_caps_set_simple (stream->caps, "palette_data",
5453 GST_TYPE_BUFFER, palette, NULL);
5454 gst_buffer_unref (palette);
5455 } else if (palette_count != 0) {
5456 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
5457 (NULL), ("Unsupported palette depth %d. Ignoring stream.", depth));
5459 gst_object_unref (stream->pad);
5463 qtdemux->n_video_streams++;
5464 } else if (stream->subtype == FOURCC_soun) {
5465 gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
5468 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
5471 gst_caps_set_simple (stream->caps,
5472 "rate", G_TYPE_INT, (int) stream->rate,
5473 "channels", G_TYPE_INT, stream->n_channels, NULL);
5475 qtdemux->n_audio_streams++;
5476 } else if (stream->subtype == FOURCC_strm) {
5477 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
5478 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
5479 gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams);
5482 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
5484 qtdemux->n_sub_streams++;
5486 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5491 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
5493 gst_pad_use_fixed_caps (stream->pad);
5494 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
5495 gst_pad_set_query_type_function (stream->pad,
5496 gst_qtdemux_get_src_query_types);
5497 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
5499 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
5500 gst_pad_set_caps (stream->pad, stream->caps);
5502 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
5503 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
5504 gst_pad_set_active (stream->pad, TRUE);
5505 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
5506 if (stream->pending_tags)
5507 gst_tag_list_free (stream->pending_tags);
5508 stream->pending_tags = list;
5510 /* post now, send event on pad later */
5511 GST_DEBUG_OBJECT (qtdemux, "Posting tags %" GST_PTR_FORMAT, list);
5512 gst_element_post_message (GST_ELEMENT (qtdemux),
5513 gst_message_new_tag_full (GST_OBJECT (qtdemux), stream->pad,
5514 gst_tag_list_copy (list)));
5516 /* global tags go on each pad anyway */
5517 stream->send_global_tags = TRUE;
5523 /* find next atom with @fourcc starting at @offset */
5524 static GstFlowReturn
5525 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
5526 guint64 * length, guint32 fourcc)
5532 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
5533 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
5536 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
5537 if (G_UNLIKELY (ret != GST_FLOW_OK))
5539 if (G_LIKELY (GST_BUFFER_SIZE (buf) != 16)) {
5541 ret = GST_FLOW_UNEXPECTED;
5542 gst_buffer_unref (buf);
5545 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), 16, length,
5547 gst_buffer_unref (buf);
5549 if (G_UNLIKELY (*length == 0)) {
5550 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
5551 ret = GST_FLOW_ERROR;
5555 if (lfourcc == fourcc) {
5556 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
5560 GST_LOG_OBJECT (qtdemux,
5561 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5562 GST_FOURCC_ARGS (fourcc), *offset);
5571 /* might simply have had last one */
5572 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
5577 /* should only do something in pull mode */
5578 /* call with OBJECT lock */
5579 static GstFlowReturn
5580 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
5582 guint64 length, offset;
5583 GstBuffer *buf = NULL;
5584 GstFlowReturn ret = GST_FLOW_OK;
5585 GstFlowReturn res = GST_FLOW_OK;
5587 offset = qtdemux->moof_offset;
5588 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
5591 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5592 return GST_FLOW_UNEXPECTED;
5595 /* best not do pull etc with lock held */
5596 GST_OBJECT_UNLOCK (qtdemux);
5598 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5599 if (ret != GST_FLOW_OK)
5602 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
5603 if (G_UNLIKELY (ret != GST_FLOW_OK))
5605 if (!qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf),
5606 GST_BUFFER_SIZE (buf), offset, NULL)) {
5607 gst_buffer_unref (buf);
5612 gst_buffer_unref (buf);
5616 /* look for next moof */
5617 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5618 if (G_UNLIKELY (ret != GST_FLOW_OK))
5622 GST_OBJECT_LOCK (qtdemux);
5624 qtdemux->moof_offset = offset;
5630 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
5632 res = GST_FLOW_ERROR;
5637 /* maybe upstream temporarily flushing */
5638 if (ret != GST_FLOW_WRONG_STATE) {
5639 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5642 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
5643 /* resume at current position next time */
5650 /* initialise bytereaders for stbl sub-atoms */
5652 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
5654 stream->stbl_index = -1; /* no samples have yet been parsed */
5656 /* time-to-sample atom */
5657 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
5660 /* copy atom data into a new buffer for later use */
5661 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
5663 /* skip version + flags */
5664 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
5665 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
5667 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
5669 /* make sure there's enough data */
5670 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
5671 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
5672 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
5673 stream->n_sample_times);
5674 if (!stream->n_sample_times)
5678 /* sync sample atom */
5679 stream->stps_present = FALSE;
5680 if ((stream->stss_present =
5681 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
5682 &stream->stss) ? TRUE : FALSE) == TRUE) {
5683 /* copy atom data into a new buffer for later use */
5684 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
5686 /* skip version + flags */
5687 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
5688 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
5691 if (stream->n_sample_syncs) {
5692 /* make sure there's enough data */
5693 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
5697 /* partial sync sample atom */
5698 if ((stream->stps_present =
5699 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
5700 &stream->stps) ? TRUE : FALSE) == TRUE) {
5701 /* copy atom data into a new buffer for later use */
5702 stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
5704 /* skip version + flags */
5705 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
5706 !gst_byte_reader_get_uint32_be (&stream->stps,
5707 &stream->n_sample_partial_syncs))
5710 /* if there are no entries, the stss table contains the real
5712 if (stream->n_sample_partial_syncs) {
5713 /* make sure there's enough data */
5714 if (!qt_atom_parser_has_chunks (&stream->stps,
5715 stream->n_sample_partial_syncs, 4))
5722 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
5725 /* copy atom data into a new buffer for later use */
5726 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
5728 /* skip version + flags */
5729 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
5730 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
5733 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
5736 if (!stream->n_samples)
5739 /* sample-to-chunk atom */
5740 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
5743 /* copy atom data into a new buffer for later use */
5744 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
5746 /* skip version + flags */
5747 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
5748 !gst_byte_reader_get_uint32_be (&stream->stsc,
5749 &stream->n_samples_per_chunk))
5752 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
5753 stream->n_samples_per_chunk);
5755 /* make sure there's enough data */
5756 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
5762 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
5763 stream->co_size = sizeof (guint32);
5764 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
5766 stream->co_size = sizeof (guint64);
5770 /* copy atom data into a new buffer for later use */
5771 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
5773 /* skip version + flags */
5774 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
5777 /* chunks_are_chunks == 0 means treat chunks as samples */
5778 stream->chunks_are_chunks = !stream->sample_size || stream->sampled;
5779 if (stream->chunks_are_chunks) {
5780 /* skip number of entries */
5781 if (!gst_byte_reader_skip (&stream->stco, 4))
5784 /* make sure there are enough data in the stsz atom */
5785 if (!stream->sample_size) {
5786 /* different sizes for each sample */
5787 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
5791 /* treat chunks as samples */
5792 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
5796 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
5797 stream->n_samples, (guint) sizeof (QtDemuxSample),
5798 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
5800 if (stream->n_samples >=
5801 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
5802 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
5803 "be larger than %uMB (broken file?)", stream->n_samples,
5804 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
5808 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
5809 if (!stream->samples) {
5810 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
5816 /* composition time-to-sample */
5817 if ((stream->ctts_present =
5818 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
5819 &stream->ctts) ? TRUE : FALSE) == TRUE) {
5820 /* copy atom data into a new buffer for later use */
5821 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
5823 /* skip version + flags */
5824 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
5825 || !gst_byte_reader_get_uint32_be (&stream->ctts,
5826 &stream->n_composition_times))
5829 /* make sure there's enough data */
5830 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
5839 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5840 (_("This file is corrupt and cannot be played.")), (NULL));
5845 gst_qtdemux_stbl_free (stream);
5846 if (!qtdemux->fragmented) {
5847 /* not quite good */
5848 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
5851 /* may pick up samples elsewhere */
5857 /* collect samples from the next sample to be parsed up to sample @n for @stream
5858 * by reading the info from @stbl
5860 * This code can be executed from both the streaming thread and the seeking
5861 * thread so it takes the object lock to protect itself
5864 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
5867 QtDemuxSample *samples, *first, *cur, *last;
5868 guint32 n_samples_per_chunk;
5871 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
5872 GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc),
5873 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
5875 n_samples = stream->n_samples;
5878 goto out_of_samples;
5880 GST_OBJECT_LOCK (qtdemux);
5881 if (n <= stream->stbl_index)
5882 goto already_parsed;
5884 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
5886 if (!stream->stsz.data) {
5887 /* so we already parsed and passed all the moov samples;
5888 * onto fragmented ones */
5889 g_assert (qtdemux->fragmented);
5893 /* pointer to the sample table */
5894 samples = stream->samples;
5896 /* starts from -1, moves to the next sample index to parse */
5897 stream->stbl_index++;
5899 /* keep track of the first and last sample to fill */
5900 first = &samples[stream->stbl_index];
5903 if (stream->chunks_are_chunks) {
5904 /* set the sample sizes */
5905 if (stream->sample_size == 0) {
5906 /* different sizes for each sample */
5907 for (cur = first; cur <= last; cur++) {
5908 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
5909 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
5910 (guint) (cur - samples), cur->size);
5913 /* samples have the same size */
5914 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
5915 for (cur = first; cur <= last; cur++)
5916 cur->size = stream->sample_size;
5920 n_samples_per_chunk = stream->n_samples_per_chunk;
5923 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
5926 if (stream->stsc_chunk_index >= stream->last_chunk
5927 || stream->stsc_chunk_index < stream->first_chunk) {
5928 stream->first_chunk =
5929 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
5930 stream->samples_per_chunk =
5931 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
5932 gst_byte_reader_skip_unchecked (&stream->stsc, 4);
5934 /* chunk numbers are counted from 1 it seems */
5935 if (G_UNLIKELY (stream->first_chunk == 0))
5938 --stream->first_chunk;
5940 /* the last chunk of each entry is calculated by taking the first chunk
5941 * of the next entry; except if there is no next, where we fake it with
5943 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
5944 stream->last_chunk = G_MAXUINT32;
5946 stream->last_chunk =
5947 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
5948 if (G_UNLIKELY (stream->last_chunk == 0))
5951 --stream->last_chunk;
5954 GST_LOG_OBJECT (qtdemux,
5955 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i,
5956 stream->first_chunk, stream->last_chunk, stream->samples_per_chunk);
5958 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
5961 if (stream->last_chunk != G_MAXUINT32) {
5962 if (!qt_atom_parser_peek_sub (&stream->stco,
5963 stream->first_chunk * stream->co_size,
5964 (stream->last_chunk - stream->first_chunk) * stream->co_size,
5969 stream->co_chunk = stream->stco;
5970 if (!gst_byte_reader_skip (&stream->co_chunk,
5971 stream->first_chunk * stream->co_size))
5975 stream->stsc_chunk_index = stream->first_chunk;
5978 last_chunk = stream->last_chunk;
5980 if (stream->chunks_are_chunks) {
5981 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
5982 guint32 samples_per_chunk;
5983 guint64 chunk_offset;
5985 if (!stream->stsc_sample_index
5986 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
5987 &stream->chunk_offset))
5990 samples_per_chunk = stream->samples_per_chunk;
5991 chunk_offset = stream->chunk_offset;
5993 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
5994 GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %"
5995 G_GUINT64_FORMAT, (guint) (cur - samples), stream->chunk_offset);
5997 cur->offset = chunk_offset;
5998 chunk_offset += cur->size;
6001 if (G_UNLIKELY (cur > last)) {
6003 stream->stsc_sample_index = k + 1;
6004 stream->chunk_offset = chunk_offset;
6005 stream->stsc_chunk_index = j;
6009 stream->stsc_sample_index = 0;
6011 stream->stsc_chunk_index = j;
6013 cur = &samples[stream->stsc_chunk_index];
6015 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
6018 stream->stsc_chunk_index = j;
6023 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
6026 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
6027 "%" G_GUINT64_FORMAT, j, cur->offset);
6029 if (stream->samples_per_frame * stream->bytes_per_frame) {
6031 (stream->samples_per_chunk * stream->n_channels) /
6032 stream->samples_per_frame * stream->bytes_per_frame;
6034 cur->size = stream->samples_per_chunk;
6037 GST_DEBUG_OBJECT (qtdemux,
6038 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
6039 j, GST_TIME_ARGS (gst_util_uint64_scale (stream->stco_sample_index,
6040 GST_SECOND, stream->timescale)), cur->size);
6042 cur->timestamp = stream->stco_sample_index;
6043 cur->duration = stream->samples_per_chunk;
6044 cur->keyframe = TRUE;
6047 stream->stco_sample_index += stream->samples_per_chunk;
6049 stream->stsc_chunk_index = j;
6051 stream->stsc_index++;
6054 if (!stream->chunks_are_chunks)
6058 guint32 n_sample_times;
6060 n_sample_times = stream->n_sample_times;
6063 for (i = stream->stts_index; i < n_sample_times; i++) {
6064 guint32 stts_samples;
6065 gint32 stts_duration;
6068 if (stream->stts_sample_index >= stream->stts_samples
6069 || !stream->stts_sample_index) {
6071 stream->stts_samples =
6072 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
6073 stream->stts_duration =
6074 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
6076 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
6077 i, stream->stts_samples, stream->stts_duration);
6079 stream->stts_sample_index = 0;
6082 stts_samples = stream->stts_samples;
6083 stts_duration = stream->stts_duration;
6084 stts_time = stream->stts_time;
6086 for (j = stream->stts_sample_index; j < stts_samples; j++) {
6087 GST_DEBUG_OBJECT (qtdemux,
6088 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
6089 (guint) (cur - samples), j,
6090 GST_TIME_ARGS (gst_util_uint64_scale (stts_time, GST_SECOND,
6091 stream->timescale)));
6093 cur->timestamp = stts_time;
6094 cur->duration = stts_duration;
6096 /* avoid 32-bit wrap-around,
6097 * but still mind possible 'negative' duration */
6098 stts_time += (gint64) stts_duration;
6101 if (G_UNLIKELY (cur > last)) {
6103 stream->stts_time = stts_time;
6104 stream->stts_sample_index = j + 1;
6108 stream->stts_sample_index = 0;
6109 stream->stts_time = stts_time;
6110 stream->stts_index++;
6112 /* fill up empty timestamps with the last timestamp, this can happen when
6113 * the last samples do not decode and so we don't have timestamps for them.
6114 * We however look at the last timestamp to estimate the track length so we
6115 * need something in here. */
6116 for (; cur < last; cur++) {
6117 GST_DEBUG_OBJECT (qtdemux,
6118 "fill sample %d: timestamp %" GST_TIME_FORMAT,
6119 (guint) (cur - samples),
6120 GST_TIME_ARGS (gst_util_uint64_scale (stream->stts_time, GST_SECOND,
6121 stream->timescale)));
6122 cur->timestamp = stream->stts_time;
6128 /* sample sync, can be NULL */
6129 if (stream->stss_present == TRUE) {
6130 guint32 n_sample_syncs;
6132 n_sample_syncs = stream->n_sample_syncs;
6134 if (!n_sample_syncs) {
6135 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
6136 stream->all_keyframe = TRUE;
6138 for (i = stream->stss_index; i < n_sample_syncs; i++) {
6139 /* note that the first sample is index 1, not 0 */
6142 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
6144 if (G_LIKELY (index > 0 && index <= n_samples)) {
6146 samples[index].keyframe = TRUE;
6147 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
6148 /* and exit if we have enough samples */
6149 if (G_UNLIKELY (index >= n)) {
6156 stream->stss_index = i;
6159 /* stps marks partial sync frames like open GOP I-Frames */
6160 if (stream->stps_present == TRUE) {
6161 guint32 n_sample_partial_syncs;
6163 n_sample_partial_syncs = stream->n_sample_partial_syncs;
6165 /* if there are no entries, the stss table contains the real
6167 if (n_sample_partial_syncs) {
6168 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
6169 /* note that the first sample is index 1, not 0 */
6172 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
6174 if (G_LIKELY (index > 0 && index <= n_samples)) {
6176 samples[index].keyframe = TRUE;
6177 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
6178 /* and exit if we have enough samples */
6179 if (G_UNLIKELY (index >= n)) {
6186 stream->stps_index = i;
6190 /* no stss, all samples are keyframes */
6191 stream->all_keyframe = TRUE;
6192 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
6197 /* composition time to sample */
6198 if (stream->ctts_present == TRUE) {
6199 guint32 n_composition_times;
6201 gint32 ctts_soffset;
6203 /* Fill in the pts_offsets */
6205 n_composition_times = stream->n_composition_times;
6207 for (i = stream->ctts_index; i < n_composition_times; i++) {
6208 if (stream->ctts_sample_index >= stream->ctts_count
6209 || !stream->ctts_sample_index) {
6210 stream->ctts_count =
6211 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
6212 stream->ctts_soffset =
6213 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
6214 stream->ctts_sample_index = 0;
6217 ctts_count = stream->ctts_count;
6218 ctts_soffset = stream->ctts_soffset;
6220 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
6221 cur->pts_offset = ctts_soffset;
6224 if (G_UNLIKELY (cur > last)) {
6226 stream->ctts_sample_index = j + 1;
6230 stream->ctts_sample_index = 0;
6231 stream->ctts_index++;
6235 stream->stbl_index = n;
6236 /* if index has been completely parsed, free data that is no-longer needed */
6237 if (n + 1 == stream->n_samples) {
6238 gst_qtdemux_stbl_free (stream);
6239 GST_DEBUG_OBJECT (qtdemux,
6240 "parsed all available samples; checking for more");
6241 while (n + 1 == stream->n_samples)
6242 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
6245 GST_OBJECT_UNLOCK (qtdemux);
6252 GST_LOG_OBJECT (qtdemux,
6253 "Tried to parse up to sample %u but this sample has already been parsed",
6255 /* if fragmented, there may be more */
6256 if (qtdemux->fragmented && n == stream->stbl_index)
6258 GST_OBJECT_UNLOCK (qtdemux);
6264 GST_LOG_OBJECT (qtdemux,
6265 "Tried to parse up to sample %u but there are only %u samples", n + 1,
6267 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6268 (_("This file is corrupt and cannot be played.")), (NULL));
6273 GST_OBJECT_UNLOCK (qtdemux);
6274 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6275 (_("This file is corrupt and cannot be played.")), (NULL));
6280 /* collect all segment info for @stream.
6283 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
6288 /* parse and prepare segment info from the edit list */
6289 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
6290 stream->n_segments = 0;
6291 stream->segments = NULL;
6292 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
6296 guint64 time, stime;
6299 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
6300 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
6303 buffer = elst->data;
6305 n_segments = QT_UINT32 (buffer + 12);
6307 /* we might allocate a bit too much, at least allocate 1 segment */
6308 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
6310 /* segments always start from 0 */
6314 for (i = 0; i < n_segments; i++) {
6317 QtDemuxSegment *segment;
6320 media_time = QT_UINT32 (buffer + 20 + i * 12);
6322 /* -1 media time is an empty segment, just ignore it */
6323 if (media_time == G_MAXUINT32)
6326 duration = QT_UINT32 (buffer + 16 + i * 12);
6328 segment = &stream->segments[count++];
6330 /* time and duration expressed in global timescale */
6331 segment->time = stime;
6332 /* add non scaled values so we don't cause roundoff errors */
6334 stime = gst_util_uint64_scale (time, GST_SECOND, qtdemux->timescale);
6335 segment->stop_time = stime;
6336 segment->duration = stime - segment->time;
6337 /* media_time expressed in stream timescale */
6338 segment->media_start =
6339 gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale);
6340 segment->media_stop = segment->media_start + segment->duration;
6341 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
6343 if (rate_int <= 1) {
6344 /* 0 is not allowed, some programs write 1 instead of the floating point
6346 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
6350 segment->rate = rate_int / 65536.0;
6353 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
6354 ", duration %" GST_TIME_FORMAT ", media_time %" GST_TIME_FORMAT
6355 ", rate %g, (%d)", i, GST_TIME_ARGS (segment->time),
6356 GST_TIME_ARGS (segment->duration),
6357 GST_TIME_ARGS (segment->media_start), segment->rate, rate_int);
6359 GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count);
6360 stream->n_segments = count;
6364 /* push based does not handle segments, so act accordingly here,
6365 * and warn if applicable */
6366 if (!qtdemux->pullbased) {
6367 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
6368 /* remove and use default one below, we stream like it anyway */
6369 g_free (stream->segments);
6370 stream->segments = NULL;
6371 stream->n_segments = 0;
6374 /* no segments, create one to play the complete trak */
6375 if (stream->n_segments == 0) {
6376 GstClockTime stream_duration =
6377 gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale);
6379 if (stream->segments == NULL)
6380 stream->segments = g_new (QtDemuxSegment, 1);
6382 /* represent unknown our way */
6383 if (stream_duration == 0)
6384 stream_duration = -1;
6386 stream->segments[0].time = 0;
6387 stream->segments[0].stop_time = stream_duration;
6388 stream->segments[0].duration = stream_duration;
6389 stream->segments[0].media_start = 0;
6390 stream->segments[0].media_stop = stream_duration;
6391 stream->segments[0].rate = 1.0;
6393 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
6394 GST_TIME_ARGS (stream_duration));
6395 stream->n_segments = 1;
6397 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
6403 * Parses the stsd atom of a svq3 trak looking for
6404 * the SMI and gama atoms.
6407 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
6408 guint8 ** gamma, GstBuffer ** seqh)
6410 guint8 *_gamma = NULL;
6411 GstBuffer *_seqh = NULL;
6412 guint8 *stsd_data = stsd->data;
6413 guint32 length = QT_UINT32 (stsd_data);
6417 GST_WARNING_OBJECT (qtdemux, "stsd too short");
6423 version = QT_UINT16 (stsd_data);
6428 while (length > 8) {
6429 guint32 fourcc, size;
6431 size = QT_UINT32 (stsd_data);
6432 fourcc = QT_FOURCC (stsd_data + 4);
6433 data = stsd_data + 8;
6440 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
6441 " for gama atom, expected 12", size);
6446 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
6448 if (_seqh != NULL) {
6449 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
6450 " found, ignoring");
6452 seqh_size = QT_UINT32 (data + 4);
6453 if (seqh_size > 0) {
6454 _seqh = gst_buffer_new_and_alloc (seqh_size);
6455 memcpy (GST_BUFFER_DATA (_seqh), data + 8, seqh_size);
6462 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
6463 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
6467 if (size <= length) {
6473 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
6476 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
6477 G_GUINT16_FORMAT, version);
6488 gst_buffer_unref (_seqh);
6493 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
6500 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
6501 * atom that might contain a 'data' atom with the rtsp uri.
6502 * This case was reported in bug #597497, some info about
6503 * the hndl atom can be found in TN1195
6505 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
6506 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
6509 guint32 dref_num_entries = 0;
6510 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
6511 gst_byte_reader_skip (&dref, 4) &&
6512 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
6515 /* search dref entries for hndl atom */
6516 for (i = 0; i < dref_num_entries; i++) {
6517 guint32 size = 0, type;
6518 guint8 string_len = 0;
6519 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
6520 qt_atom_parser_get_fourcc (&dref, &type)) {
6521 if (type == FOURCC_hndl) {
6522 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
6524 /* skip data reference handle bytes and the
6525 * following pascal string and some extra 4
6526 * bytes I have no idea what are */
6527 if (!gst_byte_reader_skip (&dref, 4) ||
6528 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
6529 !gst_byte_reader_skip (&dref, string_len + 4)) {
6530 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
6534 /* iterate over the atoms to find the data atom */
6535 while (gst_byte_reader_get_remaining (&dref) >= 8) {
6539 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
6540 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
6541 if (atom_type == FOURCC_data) {
6542 const guint8 *uri_aux = NULL;
6544 /* found the data atom that might contain the rtsp uri */
6545 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
6546 "hndl atom, interpreting it as an URI");
6547 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
6549 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
6550 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
6552 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
6553 "didn't contain a rtsp address");
6555 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
6560 /* skipping to the next entry */
6561 gst_byte_reader_skip (&dref, atom_size - 8);
6563 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
6570 /* skip to the next entry */
6571 gst_byte_reader_skip (&dref, size - 8);
6573 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
6576 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
6583 less_than (gconstpointer a, gconstpointer b)
6585 const guint32 *av = a, *bv = b;
6590 #define AMR_NB_ALL_MODES 0x81ff
6591 #define AMR_WB_ALL_MODES 0x83ff
6593 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
6595 /* The 'damr' atom is of the form:
6597 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
6598 * 32 b 8 b 16 b 8 b 8 b
6600 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
6601 * represents the highest mode used in the stream (and thus the maximum
6602 * bitrate), with a couple of special cases as seen below.
6605 /* Map of frame type ID -> bitrate */
6606 static const guint nb_bitrates[] = {
6607 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
6609 static const guint wb_bitrates[] = {
6610 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
6612 const guint8 *data = GST_BUFFER_DATA (buf);
6613 guint size = QT_UINT32 (data), max_mode;
6616 if (GST_BUFFER_SIZE (buf) != 0x11) {
6617 GST_DEBUG ("Atom should have size 0x11, not %u", size);
6621 if (QT_FOURCC (data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
6622 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
6623 GST_FOURCC_ARGS (QT_UINT32 (data + 4)));
6627 mode_set = QT_UINT16 (data + 13);
6629 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
6630 max_mode = 7 + (wb ? 1 : 0);
6632 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
6633 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
6635 if (max_mode == -1) {
6636 GST_DEBUG ("No mode indication was found (mode set) = %x",
6641 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
6648 * With each track we associate a new QtDemuxStream that contains all the info
6650 * traks that do not decode to something (like strm traks) will not have a pad.
6653 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
6668 QtDemuxStream *stream;
6669 GstTagList *list = NULL;
6670 gchar *codec = NULL;
6671 const guint8 *stsd_data;
6672 guint16 lang_code; /* quicktime lang code or packed iso code */
6674 guint32 tkhd_flags = 0;
6675 guint8 tkhd_version = 0;
6677 guint value_size, len;
6679 stream = g_new0 (QtDemuxStream, 1);
6680 /* new streams always need a discont */
6681 stream->discont = TRUE;
6682 /* we enable clipping for raw audio/video streams */
6683 stream->need_clip = FALSE;
6684 stream->need_process = FALSE;
6685 stream->segment_index = -1;
6686 stream->time_position = 0;
6687 stream->sample_index = -1;
6688 stream->last_ret = GST_FLOW_OK;
6689 #ifdef QTDEMUX_MODIFICATION
6690 stream->trickplay_info = g_new0 (TrickPlayInfo, 1);
6691 stream->trickplay_info->prev_kidx = 0;
6692 stream->trickplay_info->next_kidx = 0;
6693 stream->trickplay_info->kidxs_dur_diff = 0;
6695 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
6696 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
6697 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
6700 /* pick between 64 or 32 bits */
6701 value_size = tkhd_version == 1 ? 8 : 4;
6702 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
6703 !gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id))
6706 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
6707 tkhd_version, tkhd_flags, stream->track_id);
6709 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
6712 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
6713 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
6714 if (qtdemux->major_brand != FOURCC_mjp2 ||
6715 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
6719 len = QT_UINT32 ((guint8 *) mdhd->data);
6720 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
6721 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
6722 if (version == 0x01000000) {
6725 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
6726 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
6727 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
6731 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
6732 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
6733 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
6736 if (lang_code < 0x800) {
6737 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
6739 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
6740 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
6741 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
6742 stream->lang_id[3] = 0;
6745 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
6747 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
6749 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
6750 lang_code, stream->lang_id);
6752 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
6755 /* fragmented files may have bogus duration in moov */
6756 if (!qtdemux->fragmented &&
6757 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
6758 guint64 tdur1, tdur2;
6760 /* don't overflow */
6761 tdur1 = stream->timescale * (guint64) qtdemux->duration;
6762 tdur2 = qtdemux->timescale * (guint64) stream->duration;
6765 * some of those trailers, nowadays, have prologue images that are
6766 * themselves vide tracks as well. I haven't really found a way to
6767 * identify those yet, except for just looking at their duration. */
6768 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
6769 GST_WARNING_OBJECT (qtdemux,
6770 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
6771 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
6772 "found, assuming preview image or something; skipping track",
6773 stream->duration, stream->timescale, qtdemux->duration,
6774 qtdemux->timescale);
6775 #ifdef QTDEMUX_MODIFICATION
6776 g_free (stream->trickplay_info);
6783 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
6786 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
6787 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
6789 len = QT_UINT32 ((guint8 *) hdlr->data);
6791 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
6792 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
6793 GST_FOURCC_ARGS (stream->subtype));
6795 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
6798 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
6802 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
6804 stsd_data = (const guint8 *) stsd->data;
6806 /* stsd should at least have one entry */
6807 len = QT_UINT32 (stsd_data);
6811 /* and that entry should fit within stsd */
6812 len = QT_UINT32 (stsd_data + 16);
6813 if (len > QT_UINT32 (stsd_data) + 16)
6815 GST_LOG_OBJECT (qtdemux, "stsd len: %d", len);
6817 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
6818 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
6819 GST_FOURCC_ARGS (stream->fourcc));
6821 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) ||
6822 ((fourcc & 0xFFFFFF00) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)))
6823 goto error_encrypted;
6825 if (stream->subtype == FOURCC_vide) {
6826 guint32 w = 0, h = 0;
6828 stream->sampled = TRUE;
6830 /* version 1 uses some 64-bit ints */
6831 if (!gst_byte_reader_skip (&tkhd, 56 + value_size)
6832 || !gst_byte_reader_get_uint32_be (&tkhd, &w)
6833 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
6836 stream->display_width = w >> 16;
6837 stream->display_height = h >> 16;
6843 stream->width = QT_UINT16 (stsd_data + offset + 32);
6844 stream->height = QT_UINT16 (stsd_data + offset + 34);
6845 stream->fps_n = 0; /* this is filled in later */
6846 stream->fps_d = 0; /* this is filled in later */
6847 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
6848 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);
6850 GST_LOG_OBJECT (qtdemux, "frame count: %u",
6851 QT_UINT16 (stsd_data + offset + 48));
6854 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
6856 list = gst_tag_list_new ();
6857 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6858 GST_TAG_VIDEO_CODEC, codec, NULL);
6865 #ifdef QTDEMUX_MODIFICATION
6866 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4v);
6868 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_MP4V);
6869 /* H264 is MPEG-4 after all,
6870 * and qt seems to put MPEG-4 stuff in there as well */
6872 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_avc1);
6874 /* pick 'the' stsd child */
6875 mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc);
6878 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
6879 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
6883 const guint8 *pasp_data = (const guint8 *) pasp->data;
6885 stream->par_w = QT_UINT32 (pasp_data + 8);
6886 stream->par_h = QT_UINT32 (pasp_data + 12);
6893 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
6894 #ifdef QTDEMUX_MODIFICATION
6895 /* If the caps modified to H263 from MPEG-4, update the fourcc variable */
6896 fourcc = stream->fourcc;
6902 gint len = QT_UINT32 (stsd_data) - 0x66;
6903 const guint8 *avc_data = stsd_data + 0x66;
6906 while (len >= 0x8) {
6909 if (QT_UINT32 (avc_data) <= len)
6910 size = QT_UINT32 (avc_data) - 0x8;
6915 /* No real data, so break out */
6918 switch (QT_FOURCC (avc_data + 0x4)) {
6921 /* parse, if found */
6924 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
6926 /* First 4 bytes are the length of the atom, the next 4 bytes
6927 * are the fourcc, the next 1 byte is the version, and the
6928 * subsequent bytes are sequence parameter set like data. */
6929 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
6930 avc_data + 8 + 1, size - 1);
6932 buf = gst_buffer_new_and_alloc (size);
6933 memcpy (GST_BUFFER_DATA (buf), avc_data + 0x8, size);
6934 gst_caps_set_simple (stream->caps,
6935 "codec_data", GST_TYPE_BUFFER, buf, NULL);
6936 gst_buffer_unref (buf);
6942 guint avg_bitrate, max_bitrate;
6944 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
6948 max_bitrate = QT_UINT32 (avc_data + 0xc);
6949 avg_bitrate = QT_UINT32 (avc_data + 0x10);
6951 if (!max_bitrate && !avg_bitrate)
6954 /* Some muxers seem to swap the average and maximum bitrates
6955 * (I'm looking at you, YouTube), so we swap for sanity. */
6956 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
6957 guint temp = avg_bitrate;
6959 avg_bitrate = max_bitrate;
6964 list = gst_tag_list_new ();
6966 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
6967 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6968 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
6970 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
6971 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6972 GST_TAG_BITRATE, avg_bitrate, NULL);
6983 avc_data += size + 8;
6995 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
6996 GST_FOURCC_ARGS (fourcc));
6998 /* codec data might be in glbl extension atom */
6999 #ifdef QTDEMUX_MODIFICATION
7000 glbl = qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl);
7003 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
7010 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
7012 len = QT_UINT32 (data);
7015 buf = gst_buffer_new_and_alloc (len);
7016 memcpy (GST_BUFFER_DATA (buf), data + 8, len);
7017 gst_caps_set_simple (stream->caps,
7018 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7019 gst_buffer_unref (buf);
7026 /* see annex I of the jpeg2000 spec */
7027 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
7031 guint32 ncomp_map = 0;
7032 gint32 *comp_map = NULL;
7033 guint32 nchan_def = 0;
7034 gint32 *chan_def = NULL;
7036 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
7037 /* some required atoms */
7038 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
7041 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
7045 /* number of components; redundant with info in codestream, but useful
7047 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
7048 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
7050 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
7052 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
7055 GST_DEBUG_OBJECT (qtdemux, "found colr");
7056 /* extract colour space info */
7057 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
7058 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
7060 fourcc = GST_MAKE_FOURCC ('s', 'R', 'G', 'B');
7063 fourcc = GST_MAKE_FOURCC ('G', 'R', 'A', 'Y');
7066 fourcc = GST_MAKE_FOURCC ('s', 'Y', 'U', 'V');
7073 /* colr is required, and only values 16, 17, and 18 are specified,
7074 so error if we have no fourcc */
7077 /* extract component mapping */
7078 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
7080 guint32 cmap_len = 0;
7082 cmap_len = QT_UINT32 (cmap->data);
7083 if (cmap_len >= 8) {
7084 /* normal box, subtract off header */
7086 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
7087 if (cmap_len % 4 == 0) {
7088 ncomp_map = (cmap_len / 4);
7089 comp_map = g_new0 (gint32, ncomp_map);
7090 for (i = 0; i < ncomp_map; i++) {
7093 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
7094 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
7095 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
7096 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
7101 /* extract channel definitions */
7102 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
7104 guint32 cdef_len = 0;
7106 cdef_len = QT_UINT32 (cdef->data);
7107 if (cdef_len >= 10) {
7108 /* normal box, subtract off header and len */
7110 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
7111 if (cdef_len % 6 == 0) {
7112 nchan_def = (cdef_len / 6);
7113 chan_def = g_new0 (gint32, nchan_def);
7114 for (i = 0; i < nchan_def; i++)
7116 for (i = 0; i < nchan_def; i++) {
7117 guint16 cn, typ, asoc;
7118 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
7119 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
7120 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
7121 if (cn < nchan_def) {
7124 chan_def[cn] = asoc;
7127 chan_def[cn] = 0; /* alpha */
7130 chan_def[cn] = -typ;
7138 gst_caps_set_simple (stream->caps,
7139 "num-components", G_TYPE_INT, ncomp, NULL);
7140 gst_caps_set_simple (stream->caps,
7141 "fourcc", GST_TYPE_FOURCC, fourcc, NULL);
7144 GValue arr = { 0, };
7145 GValue elt = { 0, };
7147 g_value_init (&arr, GST_TYPE_ARRAY);
7148 g_value_init (&elt, G_TYPE_INT);
7149 for (i = 0; i < ncomp_map; i++) {
7150 g_value_set_int (&elt, comp_map[i]);
7151 gst_value_array_append_value (&arr, &elt);
7153 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7154 "component-map", &arr);
7155 g_value_unset (&elt);
7156 g_value_unset (&arr);
7161 GValue arr = { 0, };
7162 GValue elt = { 0, };
7164 g_value_init (&arr, GST_TYPE_ARRAY);
7165 g_value_init (&elt, G_TYPE_INT);
7166 for (i = 0; i < nchan_def; i++) {
7167 g_value_set_int (&elt, chan_def[i]);
7168 gst_value_array_append_value (&arr, &elt);
7170 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7171 "channel-definitions", &arr);
7172 g_value_unset (&elt);
7173 g_value_unset (&arr);
7177 /* some optional atoms */
7178 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
7179 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
7181 /* indicate possible fields in caps */
7183 data = (guint8 *) field->data + 8;
7185 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
7186 (gint) * data, NULL);
7188 /* add codec_data if provided */
7193 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
7194 data = prefix->data;
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);
7211 GstBuffer *seqh = NULL;
7212 guint8 *gamma_data = NULL;
7213 gint len = QT_UINT32 (stsd_data);
7215 qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
7217 gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
7218 QT_FP32 (gamma_data), NULL);
7221 /* sorry for the bad name, but we don't know what this is, other
7222 * than its own fourcc */
7223 gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
7227 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
7228 buf = gst_buffer_new_and_alloc (len);
7229 memcpy (GST_BUFFER_DATA (buf), stsd_data, len);
7230 gst_caps_set_simple (stream->caps,
7231 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7232 gst_buffer_unref (buf);
7237 gst_caps_set_simple (stream->caps,
7238 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
7245 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
7246 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
7250 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
7254 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
7255 /* collect the headers and store them in a stream list so that we can
7256 * send them out first */
7257 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
7267 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
7268 ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
7271 ovc1_data = ovc1->data;
7272 ovc1_len = QT_UINT32 (ovc1_data);
7273 if (ovc1_len <= 198) {
7274 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
7277 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
7278 memcpy (GST_BUFFER_DATA (buf), ovc1_data + 198, ovc1_len - 198);
7279 gst_caps_set_simple (stream->caps,
7280 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7281 gst_buffer_unref (buf);
7289 GST_INFO_OBJECT (qtdemux,
7290 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7291 GST_FOURCC_ARGS (fourcc), stream->caps);
7293 } else if (stream->subtype == FOURCC_soun) {
7294 int version, samplesize;
7295 guint16 compression_id;
7296 gboolean amrwb = FALSE;
7302 version = QT_UINT32 (stsd_data + offset);
7303 stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
7304 samplesize = QT_UINT16 (stsd_data + offset + 10);
7305 compression_id = QT_UINT16 (stsd_data + offset + 12);
7306 stream->rate = QT_FP32 (stsd_data + offset + 16);
7308 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
7309 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
7310 QT_UINT32 (stsd_data + offset + 4));
7311 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7312 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
7313 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
7314 GST_LOG_OBJECT (qtdemux, "packet size: %d",
7315 QT_UINT16 (stsd_data + offset + 14));
7316 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7318 if (compression_id == 0xfffe)
7319 stream->sampled = TRUE;
7321 /* first assume uncompressed audio */
7322 stream->bytes_per_sample = samplesize / 8;
7323 stream->samples_per_frame = stream->n_channels;
7324 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
7325 stream->samples_per_packet = stream->samples_per_frame;
7326 stream->bytes_per_packet = stream->bytes_per_sample;
7330 /* Yes, these have to be hard-coded */
7333 stream->samples_per_packet = 6;
7334 stream->bytes_per_packet = 1;
7335 stream->bytes_per_frame = 1 * stream->n_channels;
7336 stream->bytes_per_sample = 1;
7337 stream->samples_per_frame = 6 * stream->n_channels;
7342 stream->samples_per_packet = 3;
7343 stream->bytes_per_packet = 1;
7344 stream->bytes_per_frame = 1 * stream->n_channels;
7345 stream->bytes_per_sample = 1;
7346 stream->samples_per_frame = 3 * stream->n_channels;
7351 stream->samples_per_packet = 64;
7352 stream->bytes_per_packet = 34;
7353 stream->bytes_per_frame = 34 * stream->n_channels;
7354 stream->bytes_per_sample = 2;
7355 stream->samples_per_frame = 64 * stream->n_channels;
7361 stream->samples_per_packet = 1;
7362 stream->bytes_per_packet = 1;
7363 stream->bytes_per_frame = 1 * stream->n_channels;
7364 stream->bytes_per_sample = 1;
7365 stream->samples_per_frame = 1 * stream->n_channels;
7370 stream->samples_per_packet = 160;
7371 stream->bytes_per_packet = 33;
7372 stream->bytes_per_frame = 33 * stream->n_channels;
7373 stream->bytes_per_sample = 2;
7374 stream->samples_per_frame = 160 * stream->n_channels;
7381 if (version == 0x00010000) {
7389 /* only parse extra decoding config for non-pcm audio */
7390 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7391 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
7392 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
7393 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);
7395 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7396 stream->samples_per_packet);
7397 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
7398 stream->bytes_per_packet);
7399 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
7400 stream->bytes_per_frame);
7401 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
7402 stream->bytes_per_sample);
7404 if (!stream->sampled && stream->bytes_per_packet) {
7405 stream->samples_per_frame = (stream->bytes_per_frame /
7406 stream->bytes_per_packet) * stream->samples_per_packet;
7407 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
7408 stream->samples_per_frame);
7413 } else if (version == 0x00020000) {
7420 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7421 qtfp.val = QT_UINT64 (stsd_data + offset + 4);
7422 stream->rate = qtfp.fp;
7423 stream->n_channels = QT_UINT32 (stsd_data + offset + 12);
7425 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7426 stream->samples_per_packet);
7427 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7428 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7431 GST_WARNING_OBJECT (qtdemux, "unknown version %08x", version);
7434 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc, NULL, 0,
7443 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
7445 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
7447 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
7449 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
7452 gst_caps_set_simple (stream->caps,
7453 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
7460 const gchar *owma_data, *codec_name = NULL;
7464 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
7465 /* FIXME this should also be gst_riff_strf_auds,
7466 * but the latter one is actually missing bits-per-sample :( */
7471 gint32 nSamplesPerSec;
7472 gint32 nAvgBytesPerSec;
7474 gint16 wBitsPerSample;
7479 GST_DEBUG_OBJECT (qtdemux, "parse owma");
7480 owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
7483 owma_data = owma->data;
7484 owma_len = QT_UINT32 (owma_data);
7485 if (owma_len <= 54) {
7486 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
7489 wfex = (WAVEFORMATEX *) (owma_data + 36);
7490 buf = gst_buffer_new_and_alloc (owma_len - 54);
7491 memcpy (GST_BUFFER_DATA (buf), owma_data + 54, owma_len - 54);
7492 if (wfex->wFormatTag == 0x0161) {
7493 codec_name = "Windows Media Audio";
7495 } else if (wfex->wFormatTag == 0x0162) {
7496 codec_name = "Windows Media Audio 9 Pro";
7498 } else if (wfex->wFormatTag == 0x0163) {
7499 codec_name = "Windows Media Audio 9 Lossless";
7500 /* is that correct? gstffmpegcodecmap.c is missing it, but
7501 * fluendo codec seems to support it */
7505 gst_caps_set_simple (stream->caps,
7506 "codec_data", GST_TYPE_BUFFER, buf,
7507 "wmaversion", G_TYPE_INT, version,
7508 "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
7509 "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
7510 "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7511 "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7513 gst_buffer_unref (buf);
7517 codec = g_strdup (codec_name);
7529 list = gst_tag_list_new ();
7530 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7531 GST_TAG_AUDIO_CODEC, codec, NULL);
7535 /* some bitrate info may have ended up in caps */
7536 s = gst_caps_get_structure (stream->caps, 0);
7537 gst_structure_get_int (s, "bitrate", &bitrate);
7539 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
7543 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
7547 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
7549 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
7551 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
7555 /* If the fourcc's bottom 16 bits gives 'sm', then the top
7556 16 bits is a byte-swapped wave-style codec identifier,
7557 and we can find a WAVE header internally to a 'wave' atom here.
7558 This can more clearly be thought of as 'ms' as the top 16 bits, and a
7559 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
7562 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
7563 if (len < offset + 20) {
7564 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
7566 guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
7567 const guint8 *data = stsd_data + offset + 16;
7569 GNode *waveheadernode;
7571 wavenode = g_node_new ((guint8 *) data);
7572 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
7573 const guint8 *waveheader;
7576 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
7577 if (waveheadernode) {
7578 waveheader = (const guint8 *) waveheadernode->data;
7579 headerlen = QT_UINT32 (waveheader);
7581 if (headerlen > 8) {
7582 gst_riff_strf_auds *header = NULL;
7583 GstBuffer *headerbuf;
7589 headerbuf = gst_buffer_new ();
7590 GST_BUFFER_DATA (headerbuf) = (guint8 *) waveheader;
7591 GST_BUFFER_SIZE (headerbuf) = headerlen;
7593 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
7594 headerbuf, &header, &extra)) {
7595 gst_caps_unref (stream->caps);
7596 stream->caps = gst_riff_create_audio_caps (header->format, NULL,
7597 header, extra, NULL, NULL);
7600 gst_buffer_unref (extra);
7604 GST_DEBUG ("Didn't find waveheadernode for this codec");
7606 g_node_destroy (wavenode);
7609 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
7613 /* FIXME: what is in the chunk? */
7616 gint len = QT_UINT32 (stsd_data);
7618 /* seems to be always = 116 = 0x74 */
7624 gint len = QT_UINT32 (stsd_data);
7627 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
7629 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x4C, len - 0x4C);
7630 gst_caps_set_simple (stream->caps,
7631 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7632 gst_buffer_unref (buf);
7634 gst_caps_set_simple (stream->caps,
7635 "samplesize", G_TYPE_INT, samplesize, NULL);
7640 GNode *alac, *wave = NULL;
7642 /* apparently, m4a has this atom appended directly in the stsd entry,
7643 * while mov has it in a wave atom */
7644 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
7646 /* alac now refers to stsd entry atom */
7647 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
7649 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
7651 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
7654 gint len = QT_UINT32 (alac->data);
7658 GST_DEBUG_OBJECT (qtdemux,
7659 "discarding alac atom with unexpected len %d", len);
7661 /* codec-data contains alac atom size and prefix,
7662 * ffmpeg likes it that way, not quite gst-ish though ...*/
7663 buf = gst_buffer_new_and_alloc (len);
7664 memcpy (GST_BUFFER_DATA (buf), alac->data, len);
7665 gst_caps_set_simple (stream->caps,
7666 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7667 gst_buffer_unref (buf);
7670 gst_caps_set_simple (stream->caps,
7671 "samplesize", G_TYPE_INT, samplesize, NULL);
7679 gint len = QT_UINT32 (stsd_data);
7682 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
7685 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x34, len - 0x34);
7687 /* If we have enough data, let's try to get the 'damr' atom. See
7688 * the 3GPP container spec (26.244) for more details. */
7689 if ((len - 0x34) > 8 &&
7690 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
7692 list = gst_tag_list_new ();
7693 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7694 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
7697 gst_caps_set_simple (stream->caps,
7698 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7699 gst_buffer_unref (buf);
7707 GST_INFO_OBJECT (qtdemux,
7708 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7709 GST_FOURCC_ARGS (fourcc), stream->caps);
7711 } else if (stream->subtype == FOURCC_strm) {
7712 if (fourcc == FOURCC_rtsp) {
7713 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
7715 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
7716 GST_FOURCC_ARGS (fourcc));
7717 goto unknown_stream;
7719 stream->sampled = TRUE;
7720 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
7722 stream->sampled = TRUE;
7727 qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
7729 list = gst_tag_list_new ();
7730 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7731 GST_TAG_SUBTITLE_CODEC, codec, NULL);
7736 /* hunt for sort-of codec data */
7743 /* look for palette */
7744 /* target mp4s atom */
7745 len = QT_UINT32 (stsd_data + offset);
7746 data = stsd_data + offset;
7747 /* verify sufficient length,
7748 * and esds present with decConfigDescr of expected size and position */
7749 if ((len >= 106 + 8)
7750 && (QT_FOURCC (data + 8 + 8 + 4) == FOURCC_esds)
7751 && (QT_UINT16 (data + 8 + 40) == 0x0540)) {
7756 /* move to decConfigDescr data */
7757 data = data + 8 + 42;
7758 for (i = 0; i < 16; i++) {
7759 clut[i] = QT_UINT32 (data);
7763 s = gst_structure_new ("application/x-gst-dvd", "event",
7764 G_TYPE_STRING, "dvd-spu-clut-change",
7765 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
7766 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
7767 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
7768 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
7769 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
7770 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
7771 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
7772 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
7775 /* store event and trigger custom processing */
7776 stream->pending_event =
7777 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
7778 stream->need_process = TRUE;
7786 goto unknown_stream;
7789 /* promote to sampled format */
7790 if (stream->fourcc == FOURCC_samr) {
7791 /* force mono 8000 Hz for AMR */
7792 stream->sampled = TRUE;
7793 stream->n_channels = 1;
7794 stream->rate = 8000;
7795 } else if (stream->fourcc == FOURCC_sawb) {
7796 /* force mono 16000 Hz for AMR-WB */
7797 stream->sampled = TRUE;
7798 stream->n_channels = 1;
7799 stream->rate = 16000;
7800 } else if (stream->fourcc == FOURCC_mp4a) {
7801 stream->sampled = TRUE;
7804 /* collect sample information */
7805 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
7806 goto samples_failed;
7808 if (qtdemux->fragmented) {
7812 /* need all moov samples as basis; probably not many if any at all */
7813 /* prevent moof parsing taking of at this time */
7814 offset = qtdemux->moof_offset;
7815 qtdemux->moof_offset = 0;
7816 if (stream->n_samples &&
7817 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
7818 qtdemux->moof_offset = offset;
7819 goto samples_failed;
7821 qtdemux->moof_offset = 0;
7822 /* movie duration more reliable in this case (e.g. mehd) */
7823 if (qtdemux->segment.duration &&
7824 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
7825 stream->duration = gst_util_uint64_scale (qtdemux->segment.duration,
7826 stream->timescale, GST_SECOND);
7827 /* need defaults for fragments */
7828 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
7831 /* configure segments */
7832 if (!qtdemux_parse_segments (qtdemux, stream, trak))
7833 goto segments_failed;
7835 /* add some language tag, if useful */
7836 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
7837 strcmp (stream->lang_id, "und")) {
7838 const gchar *lang_code;
7841 list = gst_tag_list_new ();
7843 /* convert ISO 639-2 code to ISO 639-1 */
7844 lang_code = gst_tag_get_language_code (stream->lang_id);
7845 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7846 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
7849 /* now we are ready to add the stream */
7850 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
7851 goto too_many_streams;
7853 stream->pending_tags = list;
7854 qtdemux->streams[qtdemux->n_streams] = stream;
7855 qtdemux->n_streams++;
7856 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
7863 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7864 (_("This file is corrupt and cannot be played.")), (NULL));
7865 #ifdef QTDEMUX_MODIFICATION
7866 g_free (stream->trickplay_info);
7873 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
7874 #ifdef QTDEMUX_MODIFICATION
7875 g_free (stream->trickplay_info);
7883 /* we posted an error already */
7884 /* free stbl sub-atoms */
7885 gst_qtdemux_stbl_free (stream);
7886 #ifdef QTDEMUX_MODIFICATION
7887 g_free (stream->trickplay_info);
7894 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
7895 GST_FOURCC_ARGS (stream->subtype));
7896 #ifdef QTDEMUX_MODIFICATION
7897 g_free (stream->trickplay_info);
7904 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
7905 (_("This file contains too many streams. Only playing first %d"),
7906 GST_QTDEMUX_MAX_STREAMS), (NULL));
7911 /* If we can estimate the overall bitrate, and don't have information about the
7912 * stream bitrate for exactly one stream, this guesses the stream bitrate as
7913 * the overall bitrate minus the sum of the bitrates of all other streams. This
7914 * should be useful for the common case where we have one audio and one video
7915 * stream and can estimate the bitrate of one, but not the other. */
7917 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
7919 GstFormat format = GST_FORMAT_BYTES;
7920 QtDemuxStream *stream = NULL;
7921 gint64 size, duration, sys_bitrate, sum_bitrate = 0;
7925 if (qtdemux->fragmented)
7928 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
7930 if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &format, &size) ||
7931 format != GST_FORMAT_BYTES) {
7932 GST_DEBUG_OBJECT (qtdemux,
7933 "Size in bytes of the stream not known - bailing");
7937 /* Subtract the header size */
7938 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
7939 size, qtdemux->header_size);
7940 g_assert (size >= qtdemux->header_size);
7941 size = size - qtdemux->header_size;
7943 if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
7944 duration == GST_CLOCK_TIME_NONE) {
7945 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
7949 for (i = 0; i < qtdemux->n_streams; i++) {
7950 switch (qtdemux->streams[i]->subtype) {
7953 /* retrieve bitrate, prefer avg then max */
7955 if (qtdemux->streams[i]->pending_tags) {
7956 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
7957 GST_TAG_MAXIMUM_BITRATE, &bitrate);
7958 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
7959 GST_TAG_BITRATE, &bitrate);
7962 sum_bitrate += bitrate;
7965 GST_DEBUG_OBJECT (qtdemux,
7966 ">1 stream with unknown bitrate - bailing");
7969 stream = qtdemux->streams[i];
7973 /* For other subtypes, we assume no significant impact on bitrate */
7979 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
7983 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
7985 if (sys_bitrate < sum_bitrate) {
7986 /* This can happen, since sum_bitrate might be derived from maximum
7987 * bitrates and not average bitrates */
7988 GST_DEBUG_OBJECT (qtdemux,
7989 "System bitrate less than sum bitrate - bailing");
7993 bitrate = sys_bitrate - sum_bitrate;
7994 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
7995 ", Stream bitrate = %u", sys_bitrate, bitrate);
7997 if (!stream->pending_tags)
7998 stream->pending_tags = gst_tag_list_new ();
8000 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
8001 GST_TAG_BITRATE, bitrate, NULL);
8004 static GstFlowReturn
8005 qtdemux_expose_streams (GstQTDemux * qtdemux)
8008 GstFlowReturn ret = GST_FLOW_OK;
8010 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
8012 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
8013 QtDemuxStream *stream = qtdemux->streams[i];
8014 guint32 sample_num = 0;
8019 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
8020 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
8022 if (qtdemux->fragmented) {
8023 /* need all moov samples first */
8024 GST_OBJECT_LOCK (qtdemux);
8025 while (stream->n_samples == 0)
8026 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
8028 GST_OBJECT_UNLOCK (qtdemux);
8030 /* discard any stray moof */
8031 qtdemux->moof_offset = 0;
8034 /* prepare braking */
8035 if (ret != GST_FLOW_ERROR)
8038 /* in pull mode, we should have parsed some sample info by now;
8039 * and quite some code will not handle no samples.
8040 * in push mode, we'll just have to deal with it */
8041 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
8042 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
8043 gst_qtdemux_stream_free (qtdemux, stream);
8044 memmove (&(qtdemux->streams[i]), &(qtdemux->streams[i + 1]),
8045 sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1));
8046 qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL;
8047 qtdemux->n_streams--;
8052 /* parse number of initial sample to set frame rate cap */
8053 while (sample_num < stream->n_samples && sample_num < samples) {
8054 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
8058 /* collect and sort durations */
8059 samples = MIN (stream->stbl_index + 1, samples);
8060 GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples);
8062 durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples);
8064 while (sample_num < samples) {
8065 g_array_append_val (durations, stream->samples[sample_num].duration);
8068 g_array_sort (durations, less_than);
8069 stream->min_duration = g_array_index (durations, guint32, samples / 2);
8070 g_array_free (durations, TRUE);
8073 /* now we have all info and can expose */
8074 list = stream->pending_tags;
8075 stream->pending_tags = NULL;
8076 gst_qtdemux_add_stream (qtdemux, stream, list);
8079 gst_qtdemux_guess_bitrate (qtdemux);
8081 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
8083 /* check if we should post a redirect in case there is a single trak
8084 * and it is a redirecting trak */
8085 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
8088 qtdemux_post_global_tags (qtdemux);
8090 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
8091 "an external content");
8092 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
8093 gst_structure_new ("redirect",
8094 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
8096 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
8097 qtdemux->posted_redirect = TRUE;
8103 /* check if major or compatible brand is 3GP */
8104 static inline gboolean
8105 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
8108 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8109 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8110 } else if (qtdemux->comp_brands != NULL) {
8111 guint8 *data = GST_BUFFER_DATA (qtdemux->comp_brands);
8112 guint size = GST_BUFFER_SIZE (qtdemux->comp_brands);
8113 gboolean res = FALSE;
8116 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8117 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8127 /* check if tag is a spec'ed 3GP tag keyword storing a string */
8128 static inline gboolean
8129 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
8131 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
8132 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
8133 || fourcc == FOURCC_albm;
8137 qtdemux_tag_add_location (GstQTDemux * qtdemux, const char *tag,
8138 const char *dummy, GNode * node)
8140 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8144 gdouble longitude, latitude, altitude;
8147 len = QT_UINT32 (node->data);
8154 /* TODO: language code skipped */
8156 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
8159 /* do not alarm in trivial case, but bail out otherwise */
8160 if (*(data + offset) != 0) {
8161 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
8165 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8166 GST_TAG_GEO_LOCATION_NAME, name, NULL);
8167 offset += strlen (name);
8171 if (len < offset + 2 + 4 + 4 + 4)
8174 /* +1 +1 = skip null-terminator and location role byte */
8176 /* table in spec says unsigned, semantics say negative has meaning ... */
8177 longitude = QT_SFP32 (data + offset);
8180 latitude = QT_SFP32 (data + offset);
8183 altitude = QT_SFP32 (data + offset);
8185 /* one invalid means all are invalid */
8186 if (longitude >= -180.0 && longitude <= 180.0 &&
8187 latitude >= -90.0 && latitude <= 90.0) {
8188 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8189 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
8190 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
8191 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
8194 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
8201 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
8208 qtdemux_tag_add_year (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8215 len = QT_UINT32 (node->data);
8219 y = QT_UINT16 ((guint8 *) node->data + 12);
8221 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
8224 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
8226 date = g_date_new_dmy (1, 1, y);
8227 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, date, NULL);
8232 qtdemux_tag_add_classification (GstQTDemux * qtdemux, const char *tag,
8233 const char *dummy, GNode * node)
8236 char *tag_str = NULL;
8241 len = QT_UINT32 (node->data);
8246 entity = (guint8 *) node->data + offset;
8247 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
8248 GST_DEBUG_OBJECT (qtdemux,
8249 "classification info: %c%c%c%c invalid classification entity",
8250 entity[0], entity[1], entity[2], entity[3]);
8255 table = QT_UINT16 ((guint8 *) node->data + offset);
8257 /* Language code skipped */
8261 /* Tag format: "XXXX://Y[YYYY]/classification info string"
8262 * XXXX: classification entity, fixed length 4 chars.
8263 * Y[YYYY]: classification table, max 5 chars.
8265 tag_str = g_strdup_printf ("----://%u/%s",
8266 table, (char *) node->data + offset);
8268 /* memcpy To be sure we're preserving byte order */
8269 memcpy (tag_str, entity, 4);
8270 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
8272 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_APPEND, tag,
8282 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
8288 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, const char *tag,
8289 const char *dummy, GNode * node)
8291 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8297 gboolean ret = TRUE;
8299 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8301 len = QT_UINT32 (data->data);
8302 type = QT_UINT32 ((guint8 *) data->data + 8);
8303 if (type == 0x00000001 && len > 16) {
8304 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
8307 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8308 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s,
8312 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8316 len = QT_UINT32 (node->data);
8317 type = QT_UINT32 ((guint8 *) node->data + 4);
8318 if ((type >> 24) == 0xa9) {
8319 /* Type starts with the (C) symbol, so the next 32 bits are
8320 * the language code, which we ignore */
8322 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
8323 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
8324 QT_FOURCC ((guint8 *) node->data + 4))) {
8325 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
8327 /* we go for 3GP style encoding if major brands claims so,
8328 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
8329 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8330 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
8331 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
8333 /* 16-bit Language code is ignored here as well */
8334 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
8341 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
8342 ret = FALSE; /* may have to fallback */
8344 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8345 len - offset, env_vars);
8347 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8348 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, NULL);
8352 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8359 qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
8360 const char *dummy, GNode * node)
8362 qtdemux_tag_add_str_full (qtdemux, tag, dummy, node);
8366 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, const char *tag,
8367 const char *dummy, GNode * node)
8369 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8371 char *s, *t, *k = NULL;
8376 /* first try normal string tag if major brand not 3GP */
8377 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
8378 if (!qtdemux_tag_add_str_full (qtdemux, tag, dummy, node)) {
8379 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
8380 * let's try it 3gpp way after minor safety check */
8382 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
8388 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
8392 len = QT_UINT32 (data);
8396 count = QT_UINT8 (data + 14);
8398 for (; count; count--) {
8401 if (offset + 1 > len)
8403 slen = QT_UINT8 (data + offset);
8405 if (offset + slen > len)
8407 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8410 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
8412 t = g_strjoin (",", k, s, NULL);
8420 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
8427 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
8428 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, k, NULL);
8437 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
8443 qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1,
8444 const char *tag2, GNode * node)
8451 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8453 len = QT_UINT32 (data->data);
8454 type = QT_UINT32 ((guint8 *) data->data + 8);
8455 if (type == 0x00000000 && len >= 22) {
8456 n1 = QT_UINT16 ((guint8 *) data->data + 18);
8457 n2 = QT_UINT16 ((guint8 *) data->data + 20);
8459 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
8460 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8464 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
8465 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8473 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8481 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8483 len = QT_UINT32 (data->data);
8484 type = QT_UINT32 ((guint8 *) data->data + 8);
8485 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
8486 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8487 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
8488 n1 = QT_UINT16 ((guint8 *) data->data + 16);
8490 /* do not add bpm=0 */
8491 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
8492 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8493 tag1, (gdouble) n1, NULL);
8500 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, const char *tag1,
8501 const char *dummy, GNode * node)
8508 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8510 len = QT_UINT32 (data->data);
8511 type = QT_UINT32 ((guint8 *) data->data + 8);
8512 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
8513 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8514 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
8515 num = QT_UINT32 ((guint8 *) data->data + 16);
8517 /* do not add num=0 */
8518 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
8519 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8527 qtdemux_tag_add_covr (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8535 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8537 len = QT_UINT32 (data->data);
8538 type = QT_UINT32 ((guint8 *) data->data + 8);
8539 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
8540 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
8541 if ((buf = gst_tag_image_data_to_image_buffer ((guint8 *) data->data + 16,
8542 len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
8543 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
8544 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8546 gst_buffer_unref (buf);
8553 qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8561 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8563 len = QT_UINT32 (data->data);
8564 type = QT_UINT32 ((guint8 *) data->data + 8);
8565 if (type == 0x00000001 && len > 16) {
8566 guint y, m = 1, d = 1;
8569 s = g_strndup ((char *) data->data + 16, len - 16);
8570 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
8571 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
8572 if (ret >= 1 && y > 1500 && y < 3000) {
8575 date = g_date_new_dmy (d, m, y);
8576 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag,
8580 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
8588 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8593 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8595 #ifndef QTDEMUX_MODIFICATION
8596 /* re-route to normal string tag if major brand says so
8597 * or no data atom and compatible brand suggests so */
8598 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8599 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8600 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8608 len = QT_UINT32 (data->data);
8609 type = QT_UINT32 ((guint8 *) data->data + 8);
8610 if (type == 0x00000000 && len >= 18) {
8611 n = QT_UINT16 ((guint8 *) data->data + 16);
8615 genre = gst_tag_id3_genre_get (n - 1);
8616 if (genre != NULL) {
8617 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
8618 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8623 #ifdef QTDEMUX_MODIFICATION
8625 /* re-route to normal string tag if major brand says so
8626 * or no data atom and compatible brand suggests so */
8627 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8628 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8629 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8635 #ifdef QTDEMUX_MODIFICATION
8637 /* re-route to normal string tag if major brand says so
8638 * or no data atom and compatible brand suggests so */
8639 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8640 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8641 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8649 qtdemux_add_double_tag_from_str (GstQTDemux * demux, const gchar * tag,
8650 guint8 * data, guint32 datasize)
8655 /* make a copy to have \0 at the end */
8656 datacopy = g_strndup ((gchar *) data, datasize);
8658 /* convert the str to double */
8659 if (sscanf (datacopy, "%lf", &value) == 1) {
8660 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
8661 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_REPLACE, tag, value, NULL);
8663 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
8671 qtdemux_tag_add_revdns (GstQTDemux * demux, const char *tag,
8672 const char *tag_bis, GNode * node)
8681 const gchar *meanstr;
8682 const gchar *namestr;
8684 /* checking the whole ---- atom size for consistency */
8685 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
8686 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
8690 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
8692 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
8696 meansize = QT_UINT32 (mean->data);
8697 if (meansize <= 12) {
8698 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
8701 meanstr = ((gchar *) mean->data) + 12;
8703 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
8705 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
8709 namesize = QT_UINT32 (name->data);
8710 if (namesize <= 12) {
8711 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
8714 namestr = ((gchar *) name->data) + 12;
8721 * uint24 - data type
8725 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8727 GST_WARNING_OBJECT (demux, "No data atom in this tag");
8730 datasize = QT_UINT32 (data->data);
8731 if (datasize <= 16) {
8732 GST_WARNING_OBJECT (demux, "Data atom too small");
8735 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
8737 if (strncmp (meanstr, "com.apple.iTunes", meansize - 12) == 0) {
8740 const gchar name[28];
8741 const gchar tag[28];
8744 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
8745 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
8746 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
8747 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
8748 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
8749 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
8750 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
8751 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
8755 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
8756 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize - 12)) {
8757 switch (gst_tag_get_type (tags[i].tag)) {
8759 qtdemux_add_double_tag_from_str (demux, tags[i].tag,
8760 ((guint8 *) data->data) + 16, datasize - 16);
8763 qtdemux_tag_add_str (demux, tags[i].tag, NULL, node);
8772 if (i == G_N_ELEMENTS (tags))
8786 meanstr_dbg = g_strndup (meanstr, meansize - 12);
8787 namestr_dbg = g_strndup (namestr, namesize - 12);
8789 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
8790 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
8792 g_free (namestr_dbg);
8793 g_free (meanstr_dbg);
8799 qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
8800 const char *tag_bis, GNode * node)
8805 GstTagList *taglist = NULL;
8807 GST_LOG_OBJECT (demux, "parsing ID32");
8810 len = GST_READ_UINT32_BE (data);
8812 /* need at least full box and language tag */
8816 buf = gst_buffer_new ();
8817 GST_BUFFER_DATA (buf) = data + 14;
8818 GST_BUFFER_SIZE (buf) = len - 14;
8820 taglist = gst_tag_list_from_id3v2_tag (buf);
8822 GST_LOG_OBJECT (demux, "parsing ok");
8823 gst_tag_list_insert (demux->tag_list, taglist, GST_TAG_MERGE_KEEP);
8825 GST_LOG_OBJECT (demux, "parsing failed");
8829 gst_tag_list_free (taglist);
8831 gst_buffer_unref (buf);
8834 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
8835 const char *tag, const char *tag_bis, GNode * node);
8838 FOURCC_pcst -> if media is a podcast -> bool
8839 FOURCC_cpil -> if media is part of a compilation -> bool
8840 FOURCC_pgap -> if media is part of a gapless context -> bool
8841 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
8847 const gchar *gst_tag;
8848 const gchar *gst_tag_bis;
8849 const GstQTDemuxAddTagFunc func;
8852 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8853 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8854 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
8855 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8856 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8857 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
8858 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8859 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8860 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8861 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8862 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8863 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8864 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8865 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8866 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8867 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8868 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
8869 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
8870 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
8871 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8872 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8873 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
8874 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8875 qtdemux_tag_add_num}, {
8876 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8877 qtdemux_tag_add_num}, {
8878 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
8879 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
8880 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
8881 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
8882 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
8883 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
8884 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8885 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8886 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
8887 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
8888 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
8889 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8890 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8891 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
8892 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
8893 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8894 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
8895 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
8896 qtdemux_tag_add_classification}, {
8898 /* This is a special case, some tags are stored in this
8899 * 'reverse dns naming', according to:
8900 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
8903 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
8904 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
8905 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
8909 qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
8921 len = QT_UINT32 (data);
8922 buf = gst_buffer_new_and_alloc (len);
8923 memcpy (GST_BUFFER_DATA (buf), data, len);
8925 /* heuristic to determine style of tag */
8926 if (QT_FOURCC (data + 4) == FOURCC_____ ||
8927 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
8929 else if (demux->major_brand == FOURCC_qt__)
8930 style = "quicktime";
8931 /* fall back to assuming iso/3gp tag style */
8935 /* santize the name for the caps. */
8936 for (i = 0; i < 4; i++) {
8937 guint8 d = data[4 + i];
8938 if (g_ascii_isalnum (d))
8939 ndata[i] = g_ascii_tolower (d);
8944 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
8945 ndata[0], ndata[1], ndata[2], ndata[3]);
8946 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
8948 caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL);
8949 gst_buffer_set_caps (buf, caps);
8950 gst_caps_unref (caps);
8951 g_free (media_type);
8953 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT,
8954 GST_BUFFER_SIZE (buf), caps);
8956 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND,
8957 GST_QT_DEMUX_PRIVATE_TAG, buf, NULL);
8958 gst_buffer_unref (buf);
8962 qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta)
8970 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
8972 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
8974 GST_LOG_OBJECT (qtdemux, "no ilst");
8979 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
8982 GST_DEBUG_OBJECT (qtdemux, "new tag list");
8983 if (!qtdemux->tag_list)
8984 qtdemux->tag_list = gst_tag_list_new ();
8987 while (i < G_N_ELEMENTS (add_funcs)) {
8988 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
8992 len = QT_UINT32 (node->data);
8994 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
8995 GST_FOURCC_ARGS (add_funcs[i].fourcc));
8997 add_funcs[i].func (qtdemux, add_funcs[i].gst_tag,
8998 add_funcs[i].gst_tag_bis, node);
9000 g_node_destroy (node);
9006 /* parsed nodes have been removed, pass along remainder as blob */
9007 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
9008 (GNodeForeachFunc) qtdemux_tag_add_blob, qtdemux);
9010 /* parse up XMP_ node if existing */
9011 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
9014 GstTagList *taglist;
9016 buf = gst_buffer_new ();
9017 GST_BUFFER_DATA (buf) = ((guint8 *) xmp_->data) + 8;
9018 GST_BUFFER_SIZE (buf) = QT_UINT32 ((guint8 *) xmp_->data) - 8;
9020 taglist = gst_tag_list_from_xmp_buffer (buf);
9021 gst_buffer_unref (buf);
9023 qtdemux_handle_xmp_taglist (qtdemux, taglist);
9025 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
9032 GstStructure *structure; /* helper for sort function */
9034 guint min_req_bitrate;
9035 guint min_req_qt_version;
9039 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
9041 GstQtReference *ref_a = (GstQtReference *) a;
9042 GstQtReference *ref_b = (GstQtReference *) b;
9044 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
9045 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
9047 /* known bitrates go before unknown; higher bitrates go first */
9048 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
9051 /* sort the redirects and post a message for the application.
9054 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
9056 GstQtReference *best;
9059 GValue list_val = { 0, };
9062 g_assert (references != NULL);
9064 references = g_list_sort (references, qtdemux_redirects_sort_func);
9066 best = (GstQtReference *) references->data;
9068 g_value_init (&list_val, GST_TYPE_LIST);
9070 for (l = references; l != NULL; l = l->next) {
9071 GstQtReference *ref = (GstQtReference *) l->data;
9072 GValue struct_val = { 0, };
9074 ref->structure = gst_structure_new ("redirect",
9075 "new-location", G_TYPE_STRING, ref->location, NULL);
9077 if (ref->min_req_bitrate > 0) {
9078 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
9079 ref->min_req_bitrate, NULL);
9082 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
9083 g_value_set_boxed (&struct_val, ref->structure);
9084 gst_value_list_append_value (&list_val, &struct_val);
9085 g_value_unset (&struct_val);
9086 /* don't free anything here yet, since we need best->structure below */
9089 g_assert (best != NULL);
9090 s = gst_structure_copy (best->structure);
9092 if (g_list_length (references) > 1) {
9093 gst_structure_set_value (s, "locations", &list_val);
9096 g_value_unset (&list_val);
9098 for (l = references; l != NULL; l = l->next) {
9099 GstQtReference *ref = (GstQtReference *) l->data;
9101 gst_structure_free (ref->structure);
9102 g_free (ref->location);
9105 g_list_free (references);
9107 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
9108 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
9109 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
9110 qtdemux->posted_redirect = TRUE;
9113 /* look for redirect nodes, collect all redirect information and
9117 qtdemux_parse_redirects (GstQTDemux * qtdemux)
9119 GNode *rmra, *rmda, *rdrf;
9121 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
9123 GList *redirects = NULL;
9125 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
9127 GstQtReference ref = { NULL, NULL, 0, 0 };
9130 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
9131 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
9132 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
9133 ref.min_req_bitrate);
9136 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
9137 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
9138 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
9140 #ifndef GST_DISABLE_GST_DEBUG
9141 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
9143 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
9145 GST_LOG_OBJECT (qtdemux,
9146 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
9147 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
9148 bitmask, check_type);
9149 if (package == FOURCC_qtim && check_type == 0) {
9150 ref.min_req_qt_version = version;
9154 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
9159 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
9160 ref_data = (guint8 *) rdrf->data + 20;
9161 if (ref_type == FOURCC_alis) {
9162 guint record_len, record_version, fn_len;
9164 /* MacOSX alias record, google for alias-layout.txt */
9165 record_len = QT_UINT16 (ref_data + 4);
9166 record_version = QT_UINT16 (ref_data + 4 + 2);
9167 fn_len = QT_UINT8 (ref_data + 50);
9168 if (record_len > 50 && record_version == 2 && fn_len > 0) {
9169 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
9171 } else if (ref_type == FOURCC_url_) {
9172 ref.location = g_strdup ((gchar *) ref_data);
9174 GST_DEBUG_OBJECT (qtdemux,
9175 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
9176 GST_FOURCC_ARGS (ref_type));
9178 if (ref.location != NULL) {
9179 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
9180 redirects = g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
9182 GST_WARNING_OBJECT (qtdemux,
9183 "Failed to extract redirect location from rdrf atom");
9187 /* look for others */
9188 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
9191 if (redirects != NULL) {
9192 qtdemux_process_redirects (qtdemux, redirects);
9199 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
9204 tags = gst_tag_list_new ();
9206 if (qtdemux->major_brand == FOURCC_mjp2)
9207 fmt = "Motion JPEG 2000";
9208 else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0))
9210 else if (qtdemux->major_brand == FOURCC_qt__)
9212 else if (qtdemux->fragmented)
9215 fmt = "ISO MP4/M4A";
9217 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
9218 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
9220 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
9226 /* we have read th complete moov node now.
9227 * This function parses all of the relevant info, creates the traks and
9228 * prepares all data structures for playback
9231 qtdemux_parse_tree (GstQTDemux * qtdemux)
9238 guint64 creation_time;
9239 GstDateTime *datetime = NULL;
9242 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
9244 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
9245 return qtdemux_parse_redirects (qtdemux);
9248 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
9250 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
9251 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
9252 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
9253 } else if (version == 0) {
9254 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
9255 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
9256 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
9258 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
9262 /* Moving qt creation time (secs since 1904) to unix time */
9263 if (creation_time != 0) {
9264 if (creation_time > QTDEMUX_SECONDS_FROM_1904_TO_1970) {
9267 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
9268 /* some data cleansing sanity */
9269 g_get_current_time (&now);
9270 if (now.tv_sec + 24 * 3600 < creation_time) {
9271 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
9273 datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time);
9276 GST_WARNING_OBJECT (qtdemux, "Can't handle datetimes before 1970 yet, "
9277 "please file a bug at http://bugzilla.gnome.org");
9281 if (!qtdemux->tag_list)
9282 qtdemux->tag_list = gst_tag_list_new ();
9284 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
9285 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
9287 gst_date_time_unref (datetime);
9290 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
9291 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
9293 /* check for fragmented file and get some (default) data */
9294 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
9297 GstByteReader mehd_data;
9299 /* let track parsing or anyone know weird stuff might happen ... */
9300 qtdemux->fragmented = TRUE;
9302 /* compensate for total duration */
9303 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
9305 qtdemux_parse_mehd (qtdemux, &mehd_data);
9308 /* set duration in the segment info */
9309 gst_qtdemux_get_duration (qtdemux, &duration);
9311 gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
9312 /* also do not exceed duration; stop is set that way post seek anyway,
9313 * and segment activation falls back to duration,
9314 * whereas loop only checks stop, so let's align this here as well */
9315 qtdemux->segment.stop = duration;
9318 /* parse all traks */
9319 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
9321 qtdemux_parse_trak (qtdemux, trak);
9322 /* iterate all siblings */
9323 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
9327 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
9329 qtdemux_parse_udta (qtdemux, udta);
9331 GST_LOG_OBJECT (qtdemux, "No udta node found.");
9334 /* maybe also some tags in meta box */
9335 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
9337 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
9338 qtdemux_parse_udta (qtdemux, udta);
9340 GST_LOG_OBJECT (qtdemux, "No meta node found.");
9343 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
9348 /* taken from ffmpeg */
9350 get_size (guint8 * ptr, guint8 ** end)
9359 len = (len << 7) | (c & 0x7f);
9368 /* this can change the codec originally present in @list */
9370 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
9371 GNode * esds, GstTagList * list)
9373 int len = QT_UINT32 (esds->data);
9374 guint8 *ptr = esds->data;
9375 guint8 *end = ptr + len;
9377 guint8 *data_ptr = NULL;
9379 guint8 object_type_id = 0;
9380 const char *codec_name = NULL;
9381 GstCaps *caps = NULL;
9383 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
9385 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
9388 tag = QT_UINT8 (ptr);
9389 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
9391 len = get_size (ptr, &ptr);
9392 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
9396 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
9397 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
9401 guint max_bitrate, avg_bitrate;
9403 object_type_id = QT_UINT8 (ptr);
9404 max_bitrate = QT_UINT32 (ptr + 5);
9405 avg_bitrate = QT_UINT32 (ptr + 9);
9406 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
9407 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
9408 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
9409 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
9410 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
9411 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
9412 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9413 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
9415 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
9416 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
9423 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
9429 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
9433 GST_ERROR_OBJECT (qtdemux, "parse error");
9438 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
9439 * in use, and should also be used to override some other parameters for some
9441 switch (object_type_id) {
9442 case 0x20: /* MPEG-4 */
9443 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
9444 * profile_and_level_indication */
9445 if (data_ptr != NULL && data_len >= 5 &&
9446 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
9447 gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
9448 data_ptr + 4, data_len - 4);
9450 break; /* Nothing special needed here */
9451 case 0x21: /* H.264 */
9452 codec_name = "H.264 / AVC";
9453 caps = gst_caps_new_simple ("video/x-h264",
9454 "stream-format", G_TYPE_STRING, "avc",
9455 "alignment", G_TYPE_STRING, "au", NULL);
9457 case 0x40: /* AAC (any) */
9458 case 0x66: /* AAC Main */
9459 case 0x67: /* AAC LC */
9460 case 0x68: /* AAC SSR */
9461 /* Override channels and rate based on the codec_data, as it's often
9463 /* Only do so for basic setup without HE-AAC extension */
9464 if (data_ptr && data_len == 2) {
9465 guint channels, rateindex, rate;
9467 /* FIXME: add gst_codec_utils_aac_get_{channels|sample_rate}()? */
9468 channels = (data_ptr[1] & 0x7f) >> 3;
9469 if (channels > 0 && channels < 7) {
9470 stream->n_channels = channels;
9471 } else if (channels == 7) {
9472 stream->n_channels = 8;
9475 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7);
9476 rate = gst_codec_utils_aac_get_sample_rate_from_index (rateindex);
9478 stream->rate = rate;
9481 /* Set level and profile if possible */
9482 if (data_ptr != NULL && data_len >= 2) {
9483 gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
9484 data_ptr, data_len);
9487 case 0x60: /* MPEG-2, various profiles */
9493 codec_name = "MPEG-2 video";
9495 gst_caps_unref (stream->caps);
9496 stream->caps = gst_caps_new_simple ("video/mpeg",
9497 "mpegversion", G_TYPE_INT, 2,
9498 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9500 case 0x69: /* MP3 has two different values, accept either */
9502 /* change to mpeg1 layer 3 audio */
9503 gst_caps_set_simple (stream->caps, "layer", G_TYPE_INT, 3,
9504 "mpegversion", G_TYPE_INT, 1, NULL);
9505 codec_name = "MPEG-1 layer 3";
9507 case 0x6A: /* MPEG-1 */
9508 codec_name = "MPEG-1 video";
9510 gst_caps_unref (stream->caps);
9511 stream->caps = gst_caps_new_simple ("video/mpeg",
9512 "mpegversion", G_TYPE_INT, 1,
9513 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9515 case 0x6C: /* MJPEG */
9516 caps = gst_caps_new_simple ("image/jpeg", NULL);
9517 codec_name = "Motion-JPEG";
9519 case 0x6D: /* PNG */
9520 caps = gst_caps_new_simple ("image/png", NULL);
9521 codec_name = "PNG still images";
9523 case 0x6E: /* JPEG2000 */
9524 codec_name = "JPEG-2000";
9525 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9527 case 0xA4: /* Dirac */
9528 codec_name = "Dirac";
9529 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9531 case 0xA5: /* AC3 */
9532 codec_name = "AC-3 audio";
9533 caps = gst_caps_new_simple ("audio/x-ac3",
9534 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
9536 case 0xE1: /* QCELP */
9537 /* QCELP, the codec_data is a riff tag (little endian) with
9538 * 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). */
9539 caps = gst_caps_new_simple ("audio/qcelp", NULL);
9540 codec_name = "QCELP";
9546 /* If we have a replacement caps, then change our caps for this stream */
9548 gst_caps_unref (stream->caps);
9549 stream->caps = caps;
9552 if (codec_name && list)
9553 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9554 GST_TAG_AUDIO_CODEC, codec_name, NULL);
9556 /* Add the codec_data attribute to caps, if we have it */
9560 buffer = gst_buffer_new_and_alloc (data_len);
9561 memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len);
9563 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
9564 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
9566 #ifdef QTDEMUX_MODIFICATION
9567 /* Check the possiblity of H263 in case of mp4v fourCC */
9568 if(stream->fourcc == FOURCC_mp4v) {
9569 gboolean err = TRUE;
9572 GST_ERROR_OBJECT (qtdemux, "Checking for the Possibility of H263");
9573 for(i=0; i<data_len-4; i++) {
9574 if(QT_UINT32(data_ptr+i) == 0x00000120) {
9575 GST_ERROR_OBJECT (qtdemux, "Found the VOL Marker in DCI Info, It is MPEG-4 Content");
9581 GST_ERROR_OBJECT (qtdemux, "Not Found the VOL Marker in DCI Info, Modifying the CAPS to H263");
9582 stream->fourcc = GST_MAKE_FOURCC ('h', '2', '6', '3');
9583 stream->caps = gst_caps_from_string ("video/x-h263");
9584 gst_buffer_unref (buffer);
9590 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
9592 gst_buffer_unref (buffer);
9597 #define _codec(name) \
9600 *codec_name = g_strdup (name); \
9605 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9606 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
9609 const GstStructure *s;
9613 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
9614 _codec ("PNG still images");
9615 caps = gst_caps_new_simple ("image/png", NULL);
9617 case GST_MAKE_FOURCC ('j', 'p', 'e', 'g'):
9618 _codec ("JPEG still images");
9619 caps = gst_caps_new_simple ("image/jpeg", NULL);
9621 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
9622 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
9623 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
9624 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
9625 _codec ("Motion-JPEG");
9626 caps = gst_caps_new_simple ("image/jpeg", NULL);
9628 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
9629 _codec ("Motion-JPEG format B");
9630 caps = gst_caps_new_simple ("video/x-mjpeg-b", NULL);
9632 case GST_MAKE_FOURCC ('m', 'j', 'p', '2'):
9633 _codec ("JPEG-2000");
9634 /* override to what it should be according to spec, avoid palette_data */
9635 stream->bits_per_sample = 24;
9636 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9638 case GST_MAKE_FOURCC ('S', 'V', 'Q', '3'):
9639 _codec ("Sorensen video v.3");
9640 caps = gst_caps_new_simple ("video/x-svq",
9641 "svqversion", G_TYPE_INT, 3, NULL);
9643 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
9644 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
9645 _codec ("Sorensen video v.1");
9646 caps = gst_caps_new_simple ("video/x-svq",
9647 "svqversion", G_TYPE_INT, 1, NULL);
9649 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9653 _codec ("Raw RGB video");
9654 bps = QT_UINT16 (stsd_data + 98);
9655 /* set common stuff */
9656 caps = gst_caps_new_simple ("video/x-raw-rgb",
9657 "endianness", G_TYPE_INT, G_BYTE_ORDER, "depth", G_TYPE_INT, bps,
9662 gst_caps_set_simple (caps,
9663 "bpp", G_TYPE_INT, 16,
9664 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9665 "red_mask", G_TYPE_INT, 0x7c00,
9666 "green_mask", G_TYPE_INT, 0x03e0,
9667 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9670 gst_caps_set_simple (caps,
9671 "bpp", G_TYPE_INT, 16,
9672 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9673 "red_mask", G_TYPE_INT, 0xf800,
9674 "green_mask", G_TYPE_INT, 0x07e0,
9675 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9678 gst_caps_set_simple (caps,
9679 "bpp", G_TYPE_INT, 24,
9680 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9681 "red_mask", G_TYPE_INT, 0xff0000,
9682 "green_mask", G_TYPE_INT, 0x00ff00,
9683 "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
9686 gst_caps_set_simple (caps,
9687 "bpp", G_TYPE_INT, 32,
9688 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9689 "alpha_mask", G_TYPE_INT, 0xff000000,
9690 "red_mask", G_TYPE_INT, 0x00ff0000,
9691 "green_mask", G_TYPE_INT, 0x0000ff00,
9692 "blue_mask", G_TYPE_INT, 0x000000ff, NULL);
9700 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
9701 _codec ("Raw planar YUV 4:2:0");
9702 caps = gst_caps_new_simple ("video/x-raw-yuv",
9703 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
9706 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
9707 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
9708 _codec ("Raw packed YUV 4:2:2");
9709 caps = gst_caps_new_simple ("video/x-raw-yuv",
9710 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
9713 case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'):
9714 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
9715 _codec ("Raw packed YUV 4:2:2");
9716 caps = gst_caps_new_simple ("video/x-raw-yuv",
9717 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'),
9720 case GST_MAKE_FOURCC ('v', '2', '1', '0'):
9721 _codec ("Raw packed YUV 10-bit 4:2:2");
9722 caps = gst_caps_new_simple ("video/x-raw-yuv",
9723 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('v', '2', '1', '0'),
9726 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
9727 _codec ("Raw packed RGB 10-bit 4:4:4");
9728 caps = gst_caps_new_simple ("video/x-raw-rgb",
9729 "endianness", G_TYPE_INT, G_BIG_ENDIAN, "depth", G_TYPE_INT, 30,
9730 "bpp", G_TYPE_INT, 32,
9731 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9732 "red_mask", G_TYPE_INT, 0x3ff00000,
9733 "green_mask", G_TYPE_INT, 0x000ffc00,
9734 "blue_mask", G_TYPE_INT, 0x000003ff, NULL);
9736 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
9737 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
9738 _codec ("MPEG-1 video");
9739 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
9740 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9742 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
9743 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
9744 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
9745 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
9746 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080i60 */
9747 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
9748 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
9749 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
9750 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
9751 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
9752 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
9753 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 */
9754 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
9755 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
9756 _codec ("MPEG-2 video");
9757 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
9758 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9760 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
9761 _codec ("GIF still images");
9762 caps = gst_caps_new_simple ("image/gif", NULL);
9764 case GST_MAKE_FOURCC ('h', '2', '6', '3'):
9765 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
9766 case GST_MAKE_FOURCC ('s', '2', '6', '3'):
9767 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
9769 /* ffmpeg uses the height/width props, don't know why */
9770 caps = gst_caps_new_simple ("video/x-h263", NULL);
9772 case GST_MAKE_FOURCC ('m', 'p', '4', 'v'):
9773 case GST_MAKE_FOURCC ('M', 'P', '4', 'V'):
9774 _codec ("MPEG-4 video");
9775 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
9776 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9778 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
9779 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
9780 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
9781 caps = gst_caps_new_simple ("video/x-msmpeg",
9782 "msmpegversion", G_TYPE_INT, 43, NULL);
9784 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
9785 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
9786 _codec ("3ivX video");
9787 caps = gst_caps_new_simple ("video/x-3ivx", NULL);
9789 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
9791 caps = gst_caps_new_simple ("video/x-divx",
9792 "divxversion", G_TYPE_INT, 3, NULL);
9794 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
9795 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
9797 caps = gst_caps_new_simple ("video/x-divx",
9798 "divxversion", G_TYPE_INT, 4, NULL);
9800 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
9802 caps = gst_caps_new_simple ("video/x-divx",
9803 "divxversion", G_TYPE_INT, 5, NULL);
9805 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
9806 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
9807 _codec ("XVID MPEG-4");
9808 caps = gst_caps_new_simple ("video/x-xvid", NULL);
9811 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
9812 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
9813 caps = gst_caps_new_simple ("video/mpeg",
9814 "mpegversion", G_TYPE_INT, 4, NULL);
9816 *codec_name = g_strdup ("FFmpeg MPEG-4");
9819 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
9821 caps = gst_caps_new_simple ("video/x-cinepak", NULL);
9823 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
9824 _codec ("Apple QuickDraw");
9825 caps = gst_caps_new_simple ("video/x-qdrw", NULL);
9827 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
9828 _codec ("Apple video");
9829 caps = gst_caps_new_simple ("video/x-apple-video", NULL);
9831 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
9832 _codec ("H.264 / AVC");
9833 caps = gst_caps_new_simple ("video/x-h264",
9834 "stream-format", G_TYPE_STRING, "avc",
9835 "alignment", G_TYPE_STRING, "au", NULL);
9837 case GST_MAKE_FOURCC ('r', 'l', 'e', ' '):
9838 _codec ("Run-length encoding");
9839 caps = gst_caps_new_simple ("video/x-rle",
9840 "layout", G_TYPE_STRING, "quicktime", NULL);
9842 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
9843 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
9844 _codec ("Indeo Video 3");
9845 caps = gst_caps_new_simple ("video/x-indeo",
9846 "indeoversion", G_TYPE_INT, 3, NULL);
9848 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
9849 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
9850 _codec ("Intel Video 4");
9851 caps = gst_caps_new_simple ("video/x-indeo",
9852 "indeoversion", G_TYPE_INT, 4, NULL);
9854 case GST_MAKE_FOURCC ('d', 'v', 'c', 'p'):
9855 case GST_MAKE_FOURCC ('d', 'v', 'c', ' '):
9856 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
9857 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
9858 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
9859 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
9860 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
9861 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
9862 _codec ("DV Video");
9863 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
9864 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9866 case GST_MAKE_FOURCC ('d', 'v', '5', 'n'): /* DVCPRO50 NTSC */
9867 case GST_MAKE_FOURCC ('d', 'v', '5', 'p'): /* DVCPRO50 PAL */
9868 _codec ("DVCPro50 Video");
9869 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
9870 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9872 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
9873 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
9874 _codec ("DVCProHD Video");
9875 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
9876 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9878 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
9879 _codec ("Apple Graphics (SMC)");
9880 caps = gst_caps_new_simple ("video/x-smc", NULL);
9882 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
9884 caps = gst_caps_new_simple ("video/x-vp3", NULL);
9886 case GST_MAKE_FOURCC ('X', 'i', 'T', 'h'):
9888 caps = gst_caps_new_simple ("video/x-theora", NULL);
9889 /* theora uses one byte of padding in the data stream because it does not
9890 * allow 0 sized packets while theora does */
9891 stream->padding = 1;
9893 case GST_MAKE_FOURCC ('d', 'r', 'a', 'c'):
9895 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9897 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
9898 _codec ("TIFF still images");
9899 caps = gst_caps_new_simple ("image/tiff", NULL);
9901 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
9902 _codec ("Apple Intermediate Codec");
9903 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
9905 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
9906 _codec ("AVID DNxHD");
9907 caps = gst_caps_from_string ("video/x-dnxhd");
9909 case GST_MAKE_FOURCC ('V', 'P', '8', '0'):
9911 caps = gst_caps_from_string ("video/x-vp8");
9915 caps = gst_caps_new_simple ("video/x-wmv",
9916 "wmvversion", G_TYPE_INT, 3,
9917 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
9920 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
9925 s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
9926 GST_FOURCC_ARGS (fourcc));
9927 caps = gst_caps_new_simple (s, NULL);
9932 /* enable clipping for raw video streams */
9933 s = gst_caps_get_structure (caps, 0);
9934 name = gst_structure_get_name (s);
9935 if (g_str_has_prefix (name, "video/x-raw-")) {
9936 stream->need_clip = TRUE;
9942 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9943 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
9946 const GstStructure *s;
9950 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
9953 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
9954 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9955 _codec ("Raw 8-bit PCM audio");
9956 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 8,
9957 "depth", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
9959 case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
9960 endian = G_BIG_ENDIAN;
9962 case GST_MAKE_FOURCC ('s', 'o', 'w', 't'):
9968 endian = G_LITTLE_ENDIAN;
9970 depth = stream->bytes_per_packet * 8;
9971 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
9974 caps = gst_caps_new_simple ("audio/x-raw-int",
9975 "width", G_TYPE_INT, depth, "depth", G_TYPE_INT, depth,
9976 "endianness", G_TYPE_INT, endian,
9977 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9980 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
9981 _codec ("Raw 64-bit floating-point audio");
9982 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 64,
9983 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9985 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
9986 _codec ("Raw 32-bit floating-point audio");
9987 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 32,
9988 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9991 _codec ("Raw 24-bit PCM audio");
9992 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
9994 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 24,
9995 "depth", G_TYPE_INT, 24,
9996 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9997 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9999 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
10000 _codec ("Raw 32-bit PCM audio");
10001 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 32,
10002 "depth", G_TYPE_INT, 32,
10003 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
10004 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
10006 case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
10007 _codec ("Mu-law audio");
10008 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
10010 case GST_MAKE_FOURCC ('a', 'l', 'a', 'w'):
10011 _codec ("A-law audio");
10012 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
10016 _codec ("Microsoft ADPCM");
10017 /* Microsoft ADPCM-ACM code 2 */
10018 caps = gst_caps_new_simple ("audio/x-adpcm",
10019 "layout", G_TYPE_STRING, "microsoft", NULL);
10023 _codec ("DVI/IMA ADPCM");
10024 caps = gst_caps_new_simple ("audio/x-adpcm",
10025 "layout", G_TYPE_STRING, "dvi", NULL);
10029 _codec ("DVI/Intel IMA ADPCM");
10030 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
10031 caps = gst_caps_new_simple ("audio/x-adpcm",
10032 "layout", G_TYPE_STRING, "quicktime", NULL);
10036 /* MPEG layer 3, CBR only (pre QT4.1) */
10037 case GST_MAKE_FOURCC ('.', 'm', 'p', '3'):
10038 _codec ("MPEG-1 layer 3");
10039 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
10040 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
10041 "mpegversion", G_TYPE_INT, 1, NULL);
10044 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
10045 _codec ("EAC-3 audio");
10046 caps = gst_caps_new_simple ("audio/x-eac3",
10047 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
10048 stream->sampled = TRUE;
10050 case GST_MAKE_FOURCC ('a', 'c', '-', '3'):
10051 _codec ("AC-3 audio");
10052 caps = gst_caps_new_simple ("audio/x-ac3",
10053 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
10054 stream->sampled = TRUE;
10056 case GST_MAKE_FOURCC ('M', 'A', 'C', '3'):
10058 caps = gst_caps_new_simple ("audio/x-mace",
10059 "maceversion", G_TYPE_INT, 3, NULL);
10061 case GST_MAKE_FOURCC ('M', 'A', 'C', '6'):
10063 caps = gst_caps_new_simple ("audio/x-mace",
10064 "maceversion", G_TYPE_INT, 6, NULL);
10066 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
10068 caps = gst_caps_new_simple ("application/ogg", NULL);
10070 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
10071 _codec ("DV audio");
10072 caps = gst_caps_new_simple ("audio/x-dv", NULL);
10074 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
10075 _codec ("MPEG-4 AAC audio");
10076 caps = gst_caps_new_simple ("audio/mpeg",
10077 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
10078 "stream-format", G_TYPE_STRING, "raw", NULL);
10080 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
10081 _codec ("QDesign Music");
10082 caps = gst_caps_new_simple ("audio/x-qdm", NULL);
10084 case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'):
10085 _codec ("QDesign Music v.2");
10086 /* FIXME: QDesign music version 2 (no constant) */
10088 caps = gst_caps_new_simple ("audio/x-qdm2",
10089 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
10090 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
10091 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
10093 caps = gst_caps_new_simple ("audio/x-qdm2", NULL);
10096 case GST_MAKE_FOURCC ('a', 'g', 's', 'm'):
10097 _codec ("GSM audio");
10098 caps = gst_caps_new_simple ("audio/x-gsm", NULL);
10100 case GST_MAKE_FOURCC ('s', 'a', 'm', 'r'):
10101 _codec ("AMR audio");
10102 caps = gst_caps_new_simple ("audio/AMR", NULL);
10104 case GST_MAKE_FOURCC ('s', 'a', 'w', 'b'):
10105 _codec ("AMR-WB audio");
10106 caps = gst_caps_new_simple ("audio/AMR-WB", NULL);
10108 case GST_MAKE_FOURCC ('i', 'm', 'a', '4'):
10109 _codec ("Quicktime IMA ADPCM");
10110 caps = gst_caps_new_simple ("audio/x-adpcm",
10111 "layout", G_TYPE_STRING, "quicktime", NULL);
10113 case GST_MAKE_FOURCC ('a', 'l', 'a', 'c'):
10114 _codec ("Apple lossless audio");
10115 caps = gst_caps_new_simple ("audio/x-alac", NULL);
10117 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
10118 _codec ("QualComm PureVoice");
10119 caps = gst_caps_from_string ("audio/qcelp");
10123 caps = gst_caps_new_simple ("audio/x-wma", NULL);
10125 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
10131 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10132 GST_FOURCC_ARGS (fourcc));
10133 caps = gst_caps_new_simple (s, NULL);
10138 /* enable clipping for raw audio streams */
10139 s = gst_caps_get_structure (caps, 0);
10140 name = gst_structure_get_name (s);
10141 if (g_str_has_prefix (name, "audio/x-raw-")) {
10142 stream->need_clip = TRUE;
10148 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
10149 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
10153 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
10156 case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
10157 _codec ("DVD subtitle");
10158 caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
10160 case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
10161 _codec ("Quicktime timed text");
10163 case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
10164 _codec ("3GPP timed text");
10166 caps = gst_caps_new_simple ("text/plain", NULL);
10167 /* actual text piece needs to be extracted */
10168 stream->need_process = TRUE;
10174 s = g_strdup_printf ("text/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10175 GST_FOURCC_ARGS (fourcc));
10176 caps = gst_caps_new_simple (s, NULL);
10183 #ifdef QTDEMUX_MODIFICATION
10185 gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp)
10187 guint32 nsamples = 0; /* Number of samples between two consecutive keyframes */
10188 QtDemuxSample *sample;
10190 if ((*timestamp < stream->trickplay_info->start_pos) && !qtdemux->fwdtrick_mode) {
10191 GST_LOG_OBJECT (qtdemux, "Received shown sample... not applying trickplay algo");
10195 if ((stream->trickplay_info->next_kidx == 0 ) && (stream->sample_index < stream->to_sample)) {
10196 stream->trickplay_info->next_kidx = stream->trickplay_info->prev_kidx = stream->sample_index;
10198 GST_DEBUG_OBJECT (qtdemux, " Previous index : %d and ts = %"GST_TIME_FORMAT,
10199 stream->trickplay_info->prev_kidx, GST_TIME_ARGS(QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])));
10201 while(1) { /* while loop to handle multiple consecutive key frames */
10202 /* find previous key frame */
10203 stream->trickplay_info->next_kidx = gst_qtdemux_find_next_keyframe (qtdemux, stream, stream->trickplay_info->next_kidx + 1);
10205 /* based no.of sample between key frame and rate, drop frames */
10206 GST_DEBUG_OBJECT (qtdemux, "current index : %d, next key index : %d", stream->sample_index, stream->trickplay_info->next_kidx);
10208 if (qtdemux->fwdtrick_mode) {
10209 /* sending only key frames */
10210 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10211 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10212 stream->trickplay_info->next_kidx = 0;
10213 stream->discont = TRUE;
10217 /* find no.of samples between present and previous key frames */
10218 nsamples = stream->trickplay_info->next_kidx - stream->trickplay_info->prev_kidx;
10220 /* find average duration between key frames */
10221 stream->trickplay_info->kidxs_dur_diff = (QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->next_kidx]) -
10222 QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])) /
10225 stream->trickplay_info->show_samples = nsamples / qtdemux->segment.rate;
10227 GST_DEBUG_OBJECT (qtdemux, "duration 2 between consecutive frames : %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->trickplay_info->kidxs_dur_diff));
10229 if(stream->trickplay_info->show_samples) {
10230 GST_DEBUG_OBJECT (qtdemux, "samples to display between two key frames = %d", stream->trickplay_info->show_samples);
10231 /* found no. of samples to show between key frames */
10232 *timestamp = QTSAMPLE_PTS (stream, &stream->samples[stream->sample_index]);
10234 } else if (!stream->trickplay_info->show_samples &&
10235 (stream->trickplay_info->next_kidx >= (stream->n_samples-1))) {
10236 /* shown samples required to show between 2 key frames */
10237 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10238 if (stream->trickplay_info->next_kidx == stream->n_samples - 1)
10239 stream->sample_index = stream->trickplay_info->next_kidx;
10241 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10242 stream->trickplay_info->next_kidx = 0;
10243 stream->discont = TRUE;
10248 stream->discont = TRUE;
10249 } else if (stream->trickplay_info->next_kidx != 0) {
10250 GST_LOG_OBJECT (qtdemux, "next_kidx = %d and show_samples = %d", stream->trickplay_info->next_kidx, stream->trickplay_info->show_samples);
10252 stream->trickplay_info->show_samples--;
10253 sample = &stream->samples[stream->trickplay_info->prev_kidx];
10254 *timestamp = QTSAMPLE_PTS (stream, sample) +
10255 ((stream->sample_index -stream->trickplay_info->prev_kidx) * (abs (qtdemux->segment.rate)) * (stream->trickplay_info->kidxs_dur_diff));
10257 if (stream->trickplay_info->show_samples == 0) {
10258 /* shown samples required to show between 2 key frames */
10259 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10260 if (stream->trickplay_info->next_kidx == stream->n_samples - 1)
10261 stream->sample_index = stream->trickplay_info->next_kidx;
10263 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10264 stream->trickplay_info->next_kidx = 0;
10265 stream->discont = TRUE;
10271 gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp)
10273 if (stream->samples[stream->sample_index].keyframe) {
10274 stream->sample_index = stream->to_sample;
10275 GST_LOG_OBJECT (qtdemux, "Received key frame...Jump to end of the segment (idx = %d)...", stream->sample_index);
10279 /* searches next immediate key frame after index */
10281 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index)
10283 gint32 new_index = index;
10285 if (index > str->n_samples) {
10286 GST_WARNING_OBJECT (qtdemux, "index is greater than n_samples...");
10287 new_index = str->n_samples - 1;
10291 /* all keyframes, return index */
10292 if (str->all_keyframe) {
10297 /* else go back until we have a keyframe */
10299 if (new_index == str->n_samples) {
10300 GST_INFO_OBJECT (qtdemux, "Reached n_samples, taking last frame as reference...");
10301 new_index = str->n_samples - 1;
10305 if (str->samples[new_index].keyframe) {
10306 GST_LOG ("Found keyframe...");
10313 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
10314 "gave %d", index, new_index);