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;
329 gboolean chunks_are_chunks;
333 GstByteReader co_chunk;
335 guint32 current_chunk;
337 guint32 samples_per_chunk;
338 guint32 stco_sample_index;
340 guint32 sample_size; /* 0 means variable sizes are stored in stsz */
343 guint32 n_samples_per_chunk;
344 guint32 stsc_chunk_index;
345 guint32 stsc_sample_index;
346 guint64 chunk_offset;
349 guint32 stts_samples;
350 guint32 n_sample_times;
351 guint32 stts_sample_index;
353 guint32 stts_duration;
355 gboolean stss_present;
356 guint32 n_sample_syncs;
358 guint32 n_sample_partial_syncs;
360 gboolean ctts_present;
361 guint32 n_composition_times;
363 guint32 ctts_sample_index;
368 gboolean parsed_trex;
369 guint32 def_sample_duration;
370 guint32 def_sample_size;
371 guint32 def_sample_flags;
372 #ifdef QTDEMUX_MODIFICATION
374 TrickPlayInfo *trickplay_info; /* trickplay specific handle */
380 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
381 QTDEMUX_STATE_HEADER, /* Parsing the header */
382 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
383 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
386 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
387 static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
388 guint32 fourcc, GstByteReader * parser);
389 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
390 static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
391 guint32 fourcc, GstByteReader * parser);
393 static GstStaticPadTemplate gst_qtdemux_sink_template =
394 GST_STATIC_PAD_TEMPLATE ("sink",
397 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
401 static GstStaticPadTemplate gst_qtdemux_videosrc_template =
402 GST_STATIC_PAD_TEMPLATE ("video_%02d",
405 GST_STATIC_CAPS_ANY);
407 static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
408 GST_STATIC_PAD_TEMPLATE ("audio_%02d",
411 GST_STATIC_CAPS_ANY);
413 static GstStaticPadTemplate gst_qtdemux_subsrc_template =
414 GST_STATIC_PAD_TEMPLATE ("subtitle_%02d",
417 GST_STATIC_CAPS_ANY);
419 GST_BOILERPLATE (GstQTDemux, gst_qtdemux, GstQTDemux, GST_TYPE_ELEMENT);
421 static void gst_qtdemux_dispose (GObject * object);
424 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
427 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
428 QtDemuxStream * str, gint64 media_offset);
430 static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
431 static GstIndex *gst_qtdemux_get_index (GstElement * element);
432 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
433 GstStateChange transition);
434 static gboolean qtdemux_sink_activate (GstPad * sinkpad);
435 static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
436 static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
438 static void gst_qtdemux_loop (GstPad * pad);
439 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
440 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
442 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
443 const guint8 * buffer, guint length);
444 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
445 const guint8 * buffer, guint length);
446 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
448 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
449 QtDemuxStream * stream, GNode * esds, GstTagList * list);
450 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
451 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
452 gchar ** codec_name);
453 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
454 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
455 gchar ** codec_name);
456 static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
457 QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
458 gchar ** codec_name);
459 static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
460 QtDemuxStream * stream, guint32 n);
461 static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
463 #ifdef QTDEMUX_MODIFICATION
464 static void gst_qtdemux_set_property (GObject * object, guint prop_id,
465 const GValue * value, GParamSpec * pspec);
466 static void gst_qtdemux_get_property (GObject * object, guint prop_id,
467 GValue * value, GParamSpec * pspec);
468 static gint32 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index);
469 static void gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
470 static void gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
474 gst_qtdemux_base_init (gpointer klass)
476 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
478 gst_element_class_add_static_pad_template (element_class,
479 &gst_qtdemux_sink_template);
480 gst_element_class_add_static_pad_template (element_class,
481 &gst_qtdemux_videosrc_template);
482 gst_element_class_add_static_pad_template (element_class,
483 &gst_qtdemux_audiosrc_template);
484 gst_element_class_add_static_pad_template (element_class,
485 &gst_qtdemux_subsrc_template);
486 gst_element_class_set_details_simple (element_class, "QuickTime demuxer",
488 "Demultiplex a QuickTime file into audio and video streams",
489 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
491 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
495 gst_qtdemux_class_init (GstQTDemuxClass * klass)
497 GObjectClass *gobject_class;
498 GstElementClass *gstelement_class;
500 gobject_class = (GObjectClass *) klass;
501 gstelement_class = (GstElementClass *) klass;
503 parent_class = g_type_class_peek_parent (klass);
505 gobject_class->dispose = gst_qtdemux_dispose;
507 #ifdef QTDEMUX_MODIFICATION
508 gobject_class->set_property = gst_qtdemux_set_property;
509 gobject_class->get_property = gst_qtdemux_get_property;
512 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
514 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
515 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
517 #ifdef QTDEMUX_MODIFICATION
518 g_object_class_install_property (gobject_class, PROP_MAX_BUFFER_SIZE,
519 g_param_spec_uint ("buffer-size", "buffer-size",
520 "Maximum buffer size for mdat atom buffering in case it comes before the moov atom",
521 1, G_MAXUINT, QTDEMUX_MAX_BUFFER_SIZE,
522 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
524 g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
525 g_param_spec_string ("temp-location", "temp-location",
526 "Location for the mdat atom buffering incase it comes before the moov atom",
528 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
530 g_object_class_install_property (gobject_class, PROP_FWDTRICK_MODE,
531 g_param_spec_boolean ("fwd-trick-mode",
532 "Forward trickplay mode to use",
533 "(0) Sending Non-Keyframes also in forward trickplay (1) Sending only keyframes in forward trickplay",
534 QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE,
539 gst_tag_register_musicbrainz_tags ();
543 gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
546 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
547 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
548 gst_pad_set_activatepull_function (qtdemux->sinkpad,
549 qtdemux_sink_activate_pull);
550 gst_pad_set_activatepush_function (qtdemux->sinkpad,
551 qtdemux_sink_activate_push);
552 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
553 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
554 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
556 qtdemux->state = QTDEMUX_STATE_INITIAL;
557 qtdemux->pullbased = FALSE;
558 qtdemux->posted_redirect = FALSE;
559 qtdemux->neededbytes = 16;
561 qtdemux->adapter = gst_adapter_new ();
563 qtdemux->first_mdat = -1;
564 qtdemux->got_moov = FALSE;
565 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
566 qtdemux->mdatbuffer = NULL;
567 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
568 #ifdef QTDEMUX_MODIFICATION
569 /* Initialization of properties with default values*/
570 qtdemux->filename = g_strdup("/tmp/qtdemux");
571 qtdemux->file = NULL;
572 qtdemux->ofile = NULL;
573 qtdemux->filesize = 0;
574 qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
575 qtdemux->fwdtrick_mode = QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE;
580 gst_qtdemux_dispose (GObject * object)
582 GstQTDemux *qtdemux = GST_QTDEMUX (object);
584 if (qtdemux->adapter) {
585 g_object_unref (G_OBJECT (qtdemux->adapter));
586 qtdemux->adapter = NULL;
589 #ifdef QTDEMUX_MODIFICATION
590 if (qtdemux->filename)
591 g_free (qtdemux->filename);
594 G_OBJECT_CLASS (parent_class)->dispose (object);
597 #ifdef QTDEMUX_MODIFICATION
599 gst_qtdemux_set_property (GObject * object, guint prop_id,
600 const GValue * value, GParamSpec * pspec)
603 demux = GST_QTDEMUX (object);
606 case PROP_MAX_BUFFER_SIZE:
607 demux->maxbuffersize = g_value_get_uint (value);
609 case PROP_TEMP_LOCATION:
611 g_free (demux->filename);
612 demux->filename = g_strdup (g_value_get_string (value));
614 case PROP_FWDTRICK_MODE:
615 demux->fwdtrick_mode = g_value_get_boolean(value);
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
624 gst_qtdemux_get_property (GObject * object, guint prop_id,
625 GValue * value, GParamSpec * pspec)
628 demux = GST_QTDEMUX (object);
631 case PROP_MAX_BUFFER_SIZE:
632 g_value_set_uint (value, demux->maxbuffersize);
634 case PROP_TEMP_LOCATION:
635 g_value_set_string (value, demux->filename);
637 case PROP_FWDTRICK_MODE:
638 g_value_set_boolean(value, demux->fwdtrick_mode);
641 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
649 gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
651 if (qtdemux->posted_redirect) {
652 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
653 (_("This file contains no playable streams.")),
654 ("no known streams found, a redirect message has been posted"));
656 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
657 (_("This file contains no playable streams.")),
658 ("no known streams found"));
663 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
668 if (G_UNLIKELY (size == 0)) {
670 GstBuffer *tmp = NULL;
672 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
673 if (ret != GST_FLOW_OK)
676 size = QT_UINT32 (GST_BUFFER_DATA (tmp));
677 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
679 gst_buffer_unref (tmp);
682 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
683 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
684 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
685 /* we're pulling header but already got most interesting bits,
686 * so never mind the rest (e.g. tags) (that much) */
687 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
689 return GST_FLOW_UNEXPECTED;
691 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
692 (_("This file is invalid and cannot be played.")),
693 ("atom has bogus size %" G_GUINT64_FORMAT, size));
694 return GST_FLOW_ERROR;
698 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
700 if (G_UNLIKELY (flow != GST_FLOW_OK))
703 /* Catch short reads - we don't want any partial atoms */
704 if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) {
705 GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT,
706 GST_BUFFER_SIZE (*buf), size);
707 gst_buffer_unref (*buf);
709 return GST_FLOW_UNEXPECTED;
717 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
718 GstFormat dest_format, gint64 * dest_value)
721 QtDemuxStream *stream = gst_pad_get_element_private (pad);
722 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
725 if (stream->subtype != FOURCC_vide) {
730 switch (src_format) {
731 case GST_FORMAT_TIME:
732 switch (dest_format) {
733 case GST_FORMAT_BYTES:{
734 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
738 *dest_value = stream->samples[index].offset;
740 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
741 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
742 GST_TIME_ARGS (src_value), *dest_value);
750 case GST_FORMAT_BYTES:
751 switch (dest_format) {
752 case GST_FORMAT_TIME:{
754 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
761 gst_util_uint64_scale (stream->samples[index].timestamp,
762 GST_SECOND, stream->timescale);
763 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Offset->Time :%"
764 G_GUINT64_FORMAT "->%" GST_TIME_FORMAT,
765 src_value, GST_TIME_ARGS (*dest_value));
778 gst_object_unref (qtdemux);
784 static const GstQueryType *
785 gst_qtdemux_get_src_query_types (GstPad * pad)
787 static const GstQueryType src_types[] = {
800 gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration)
804 *duration = GST_CLOCK_TIME_NONE;
806 if (qtdemux->duration != 0) {
807 if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
808 *duration = gst_util_uint64_scale (qtdemux->duration,
809 GST_SECOND, qtdemux->timescale);
816 gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
818 gboolean res = FALSE;
819 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
821 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
823 switch (GST_QUERY_TYPE (query)) {
824 case GST_QUERY_POSITION:
825 if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) {
826 gst_query_set_position (query, GST_FORMAT_TIME,
827 qtdemux->segment.last_stop);
831 case GST_QUERY_DURATION:{
834 gst_query_parse_duration (query, &fmt, NULL);
835 if (fmt == GST_FORMAT_TIME) {
836 gint64 duration = -1;
838 gst_qtdemux_get_duration (qtdemux, &duration);
840 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
846 case GST_QUERY_CONVERT:{
847 GstFormat src_fmt, dest_fmt;
848 gint64 src_value, dest_value = 0;
850 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
852 res = gst_qtdemux_src_convert (pad,
853 src_fmt, src_value, dest_fmt, &dest_value);
855 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
860 case GST_QUERY_FORMATS:
861 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
864 case GST_QUERY_SEEKING:{
868 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
869 if (fmt == GST_FORMAT_TIME) {
870 gint64 duration = -1;
872 gst_qtdemux_get_duration (qtdemux, &duration);
874 if (!qtdemux->pullbased) {
877 /* we might be able with help from upstream */
879 q = gst_query_new_seeking (GST_FORMAT_BYTES);
880 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
881 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
882 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
886 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
892 res = gst_pad_query_default (pad, query);
896 gst_object_unref (qtdemux);
902 gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
904 if (G_LIKELY (stream->pad)) {
905 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
906 GST_DEBUG_PAD_NAME (stream->pad));
908 if (G_UNLIKELY (stream->pending_tags)) {
909 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
910 stream->pending_tags);
911 gst_pad_push_event (stream->pad,
912 gst_event_new_tag (stream->pending_tags));
913 stream->pending_tags = NULL;
916 if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
917 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
919 gst_pad_push_event (stream->pad,
920 gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
921 stream->send_global_tags = FALSE;
926 /* push event on all source pads; takes ownership of the event */
928 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
931 gboolean has_valid_stream = FALSE;
932 GstEventType etype = GST_EVENT_TYPE (event);
934 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
935 GST_EVENT_TYPE_NAME (event));
937 for (n = 0; n < qtdemux->n_streams; n++) {
939 QtDemuxStream *stream = qtdemux->streams[n];
941 if ((pad = stream->pad)) {
942 has_valid_stream = TRUE;
944 if (etype == GST_EVENT_EOS) {
945 /* let's not send twice */
946 if (stream->sent_eos)
948 stream->sent_eos = TRUE;
951 gst_pad_push_event (pad, gst_event_ref (event));
955 gst_event_unref (event);
957 /* if it is EOS and there are no pads, post an error */
958 if (!has_valid_stream && etype == GST_EVENT_EOS) {
959 gst_qtdemux_post_no_playable_stream_error (qtdemux);
963 /* push a pending newsegment event, if any from the streaming thread */
965 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
967 if (qtdemux->pending_newsegment) {
968 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
969 qtdemux->pending_newsegment = NULL;
979 find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data)
981 if (s1->timestamp > *media_time)
987 /* find the index of the sample that includes the data for @media_time using a
988 * binary search. Only to be called in optimized cases of linear search below.
990 * Returns the index of the sample.
993 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
996 QtDemuxSample *result;
999 /* convert media_time to mov format */
1001 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1003 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1004 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
1005 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1007 if (G_LIKELY (result))
1008 index = result - str->samples;
1017 /* find the index of the sample that includes the data for @media_offset using a
1020 * Returns the index of the sample.
1023 gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
1024 QtDemuxStream * str, gint64 media_offset)
1026 QtDemuxSample *result = str->samples;
1029 if (result == NULL || str->n_samples == 0)
1032 if (media_offset == result->offset)
1036 while (index < str->n_samples - 1) {
1037 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1040 if (media_offset < result->offset)
1051 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1056 /* find the index of the sample that includes the data for @media_time using a
1057 * linear search, and keeping in mind that not all samples may have been parsed
1058 * yet. If possible, it will delegate to binary search.
1060 * Returns the index of the sample.
1063 gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
1069 /* convert media_time to mov format */
1071 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1073 if (mov_time == str->samples[0].timestamp)
1076 /* use faster search if requested time in already parsed range */
1077 if (str->stbl_index >= 0 &&
1078 mov_time <= str->samples[str->stbl_index].timestamp)
1079 return gst_qtdemux_find_index (qtdemux, str, media_time);
1081 while (index < str->n_samples - 1) {
1082 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
1085 if (mov_time < str->samples[index + 1].timestamp)
1095 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
1100 /* find the index of the keyframe needed to decode the sample at @index
1103 * Returns the index of the keyframe.
1106 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
1109 guint32 new_index = index;
1111 if (index >= str->n_samples) {
1112 new_index = str->n_samples;
1116 /* all keyframes, return index */
1117 if (str->all_keyframe) {
1119 #ifdef QTDEMUX_MODIFICATION
1120 if(qtdemux->segment.rate < 0.0 && new_index && (FOURCC_vide == str->subtype))
1126 #ifdef QTDEMUX_MODIFICATION
1127 if(qtdemux->segment.rate < 0.0 && new_index && (FOURCC_vide == str->subtype)) {
1128 /* If index is keyframe, reduce index by 1, so that we could fetch prev keyframe for video
1129 * This change is done to fix the out of segment issue when seek position is a keyframe position */
1134 /* else go back until we have a keyframe */
1136 if (str->samples[new_index].keyframe)
1146 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
1147 "gave %u", index, new_index);
1152 /* find the segment for @time_position for @stream
1154 * Returns -1 if the segment cannot be found.
1157 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1158 guint64 time_position)
1163 GST_LOG_OBJECT (qtdemux, "finding segment for %" GST_TIME_FORMAT,
1164 GST_TIME_ARGS (time_position));
1166 /* find segment corresponding to time_position if we are looking
1169 for (i = 0; i < stream->n_segments; i++) {
1170 QtDemuxSegment *segment = &stream->segments[i];
1172 GST_LOG_OBJECT (qtdemux,
1173 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1174 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1176 /* For the last segment we include stop_time in the last segment */
1177 if (i < stream->n_segments - 1) {
1178 if (segment->time <= time_position && time_position < segment->stop_time) {
1179 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1184 if (segment->time <= time_position && time_position <= segment->stop_time) {
1185 GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
1194 /* move the stream @str to the sample position @index.
1196 * Updates @str->sample_index and marks discontinuity if needed.
1199 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1202 /* no change needed */
1203 if (index == str->sample_index)
1206 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1209 /* position changed, we have a discont */
1210 str->sample_index = index;
1211 /* Each time we move in the stream we store the position where we are
1213 str->from_sample = index;
1214 str->discont = TRUE;
1218 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1219 gint64 * key_time, gint64 * key_offset)
1222 gint64 min_byte_offset = -1;
1225 min_offset = desired_time;
1227 /* for each stream, find the index of the sample in the segment
1228 * and move back to the previous keyframe. */
1229 for (n = 0; n < qtdemux->n_streams; n++) {
1231 guint32 index, kindex;
1233 guint64 media_start;
1236 QtDemuxSegment *seg;
1238 str = qtdemux->streams[n];
1240 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1241 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1243 /* segment not found, continue with normal flow */
1247 /* get segment and time in the segment */
1248 seg = &str->segments[seg_idx];
1249 seg_time = desired_time - seg->time;
1251 /* get the media time in the segment */
1252 media_start = seg->media_start + seg_time;
1254 /* get the index of the sample with media time */
1255 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1256 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1257 " at offset %" G_GUINT64_FORMAT,
1258 GST_TIME_ARGS (media_start), index, str->samples[index].offset);
1260 /* find previous keyframe */
1261 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);
1263 /* if the keyframe is at a different position, we need to update the
1264 * requested seek time */
1265 if (index != kindex) {
1268 /* get timestamp of keyframe */
1270 gst_util_uint64_scale (str->samples[kindex].timestamp, GST_SECOND,
1272 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT
1273 " at offset %" G_GUINT64_FORMAT,
1274 kindex, GST_TIME_ARGS (media_time), str->samples[kindex].offset);
1276 /* keyframes in the segment get a chance to change the
1277 * desired_offset. keyframes out of the segment are
1279 if (media_time >= seg->media_start) {
1282 /* this keyframe is inside the segment, convert back to
1284 seg_time = (media_time - seg->media_start) + seg->time;
1285 if (seg_time < min_offset)
1286 min_offset = seg_time;
1290 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1291 min_byte_offset = str->samples[index].offset;
1295 *key_time = min_offset;
1297 *key_offset = min_byte_offset;
1301 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1302 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1307 g_return_val_if_fail (format != NULL, FALSE);
1308 g_return_val_if_fail (cur != NULL, FALSE);
1309 g_return_val_if_fail (stop != NULL, FALSE);
1311 if (*format == GST_FORMAT_TIME)
1314 fmt = GST_FORMAT_TIME;
1316 if (cur_type != GST_SEEK_TYPE_NONE)
1317 res = gst_pad_query_convert (pad, *format, *cur, &fmt, cur);
1318 if (res && stop_type != GST_SEEK_TYPE_NONE)
1319 res = gst_pad_query_convert (pad, *format, *stop, &fmt, stop);
1322 *format = GST_FORMAT_TIME;
1327 /* perform seek in push based mode:
1328 find BYTE position to move to based on time and delegate to upstream
1331 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1336 GstSeekType cur_type, stop_type;
1341 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1343 gst_event_parse_seek (event, &rate, &format, &flags,
1344 &cur_type, &cur, &stop_type, &stop);
1346 /* FIXME, always play to the end */
1349 /* only forward streaming and seeking is possible */
1351 goto unsupported_seek;
1353 /* convert to TIME if needed and possible */
1354 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1358 /* find reasonable corresponding BYTE position,
1359 * also try to mind about keyframes, since we can not go back a bit for them
1361 gst_qtdemux_adjust_seek (qtdemux, cur, NULL, &byte_cur);
1366 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1367 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1370 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1371 GST_DEBUG_OBJECT (qtdemux,
1372 "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %"
1373 G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
1374 GST_OBJECT_LOCK (qtdemux);
1375 qtdemux->requested_seek_time = cur;
1376 qtdemux->seek_offset = byte_cur;
1377 GST_OBJECT_UNLOCK (qtdemux);
1380 /* BYTE seek event */
1381 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1383 res = gst_pad_push_event (qtdemux->sinkpad, event);
1390 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1396 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1401 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1406 /* perform the seek.
1408 * We set all segment_indexes in the streams to unknown and
1409 * adjust the time_position to the desired position. this is enough
1410 * to trigger a segment switch in the streaming thread to start
1411 * streaming from the desired position.
1413 * Keyframe seeking is a little more complicated when dealing with
1414 * segments. Ideally we want to move to the previous keyframe in
1415 * the segment but there might not be a keyframe in the segment. In
1416 * fact, none of the segments could contain a keyframe. We take a
1417 * practical approach: seek to the previous keyframe in the segment,
1418 * if there is none, seek to the beginning of the segment.
1420 * Called with STREAM_LOCK
1423 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
1425 gint64 desired_offset;
1428 desired_offset = segment->last_stop;
1430 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1431 GST_TIME_ARGS (desired_offset));
1433 /* may not have enough fragmented info to do this adjustment,
1434 * and we can't scan (and probably should not) at this time with
1435 * possibly flushing upstream */
1436 if ((segment->flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1439 gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL);
1440 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1441 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1442 desired_offset = min_offset;
1445 /* and set all streams to the final position */
1446 for (n = 0; n < qtdemux->n_streams; n++) {
1447 QtDemuxStream *stream = qtdemux->streams[n];
1449 stream->time_position = desired_offset;
1450 stream->sample_index = -1;
1451 stream->segment_index = -1;
1452 stream->last_ret = GST_FLOW_OK;
1453 stream->sent_eos = FALSE;
1454 #ifdef QTDEMUX_MODIFICATION
1455 stream->trickplay_info->prev_kidx = 0;
1456 stream->trickplay_info->next_kidx = 0;
1457 stream->trickplay_info->kidxs_dur_diff = 0;
1458 stream->trickplay_info->start_pos = segment->last_stop;
1461 segment->last_stop = desired_offset;
1462 segment->time = desired_offset;
1464 /* we stop at the end */
1465 if (segment->stop == -1)
1466 segment->stop = segment->duration;
1471 /* do a seek in pull based mode */
1473 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1478 GstSeekType cur_type, stop_type;
1479 gint64 cur = 0, stop;
1482 GstSegment seeksegment;
1486 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1488 gst_event_parse_seek (event, &rate, &format, &flags,
1489 &cur_type, &cur, &stop_type, &stop);
1490 #ifdef QTDEMUX_MODIFICATION
1491 GST_INFO_OBJECT (qtdemux, "Going to seek to %"GST_TIME_FORMAT", with rate = %f", GST_TIME_ARGS(cur), rate);
1494 /* we have to have a format as the segment format. Try to convert
1496 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1500 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1502 GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
1506 flush = flags & GST_SEEK_FLAG_FLUSH;
1508 /* stop streaming, either by flushing or by pausing the task */
1510 /* unlock upstream pull_range */
1511 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
1512 /* make sure out loop function exits */
1513 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
1515 /* non flushing seek, pause the task */
1516 gst_pad_pause_task (qtdemux->sinkpad);
1519 /* wait for streaming to finish */
1520 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1522 /* copy segment, we need this because we still need the old
1523 * segment when we close the current segment. */
1524 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1527 /* configure the segment with the seek variables */
1528 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1529 gst_segment_set_seek (&seeksegment, rate, format, flags,
1530 cur_type, cur, stop_type, stop, &update);
1533 #ifdef QTDEMUX_MODIFICATION
1534 if (cur != GST_CLOCK_TIME_NONE)
1535 gst_segment_set_last_stop (&seeksegment, GST_FORMAT_TIME, cur);
1538 /* now do the seek, this actually never returns FALSE */
1539 gst_qtdemux_perform_seek (qtdemux, &seeksegment);
1541 /* prepare for streaming again */
1543 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
1544 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ());
1545 } else if (qtdemux->segment_running) {
1546 /* we are running the current segment and doing a non-flushing seek,
1547 * close the segment first based on the last_stop. */
1548 GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT
1549 " to %" G_GINT64_FORMAT, qtdemux->segment.start,
1550 qtdemux->segment.last_stop);
1552 if (qtdemux->segment.rate >= 0) {
1553 /* FIXME, rate is the product of the global rate and the (quicktime)
1555 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1556 qtdemux->segment.rate, qtdemux->segment.format,
1557 qtdemux->segment.start, qtdemux->segment.last_stop,
1558 qtdemux->segment.time);
1559 } else { /* For Reverse Playback */
1562 if ((stop = qtdemux->segment.stop) == -1)
1563 stop = qtdemux->segment.duration;
1564 /* for reverse playback, we played from stop to last_stop. */
1565 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE,
1566 qtdemux->segment.rate, qtdemux->segment.format,
1567 qtdemux->segment.last_stop, stop, qtdemux->segment.last_stop);
1571 #ifdef QTDEMUX_MODIFICATION
1572 if (seeksegment.rate < 0.0) {
1573 seeksegment.start = 0.0;
1577 /* commit the new segment */
1578 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1580 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1581 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
1582 gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1583 qtdemux->segment.format, qtdemux->segment.last_stop));
1586 /* restart streaming, NEWSEGMENT will be sent from the streaming
1588 qtdemux->segment_running = TRUE;
1589 for (i = 0; i < qtdemux->n_streams; i++)
1590 qtdemux->streams[i]->last_ret = GST_FLOW_OK;
1592 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1595 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1602 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1608 qtdemux_ensure_index (GstQTDemux * qtdemux)
1612 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1614 /* Build complete index */
1615 for (i = 0; i < qtdemux->n_streams; i++) {
1616 QtDemuxStream *stream = qtdemux->streams[i];
1618 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
1626 GST_LOG_OBJECT (qtdemux,
1627 "Building complete index of stream %u for seeking failed!", i);
1633 gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
1635 gboolean res = TRUE;
1636 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
1638 switch (GST_EVENT_TYPE (event)) {
1639 case GST_EVENT_SEEK:
1641 #ifndef GST_DISABLE_GST_DEBUG
1642 GstClockTime ts = gst_util_get_timestamp ();
1644 /* Build complete index for seeking;
1645 * if not a fragmented file at least */
1646 if (!qtdemux->fragmented)
1647 if (!qtdemux_ensure_index (qtdemux))
1649 #ifndef GST_DISABLE_GST_DEBUG
1650 ts = gst_util_get_timestamp () - ts;
1651 GST_INFO_OBJECT (qtdemux,
1652 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1655 if (qtdemux->pullbased) {
1656 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1657 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
1658 !qtdemux->fragmented) {
1659 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1661 GST_DEBUG_OBJECT (qtdemux,
1662 "ignoring seek in push mode in current state");
1665 gst_event_unref (event);
1668 case GST_EVENT_NAVIGATION:
1670 gst_event_unref (event);
1673 res = gst_pad_event_default (pad, event);
1677 gst_object_unref (qtdemux);
1685 GST_ERROR_OBJECT (qtdemux, "Index failed");
1686 gst_event_unref (event);
1692 /* stream/index return sample that is min/max w.r.t. byte position,
1693 * time is min/max w.r.t. time of samples,
1694 * the latter need not be time of the former sample */
1696 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1697 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1700 gint64 time, min_time;
1701 QtDemuxStream *stream;
1707 for (n = 0; n < qtdemux->n_streams; ++n) {
1710 gboolean set_sample;
1712 str = qtdemux->streams[n];
1719 i = str->n_samples - 1;
1722 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1723 if (str->samples[i].size &&
1724 ((fw && (str->samples[i].offset >= byte_pos)) ||
1726 (str->samples[i].offset + str->samples[i].size <=
1728 /* move stream to first available sample */
1730 gst_qtdemux_move_stream (qtdemux, str, i);
1733 /* determine min/max time */
1734 time = str->samples[i].timestamp + str->samples[i].pts_offset;
1735 time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);
1736 if (min_time == -1 || (!fw && time > min_time) ||
1737 (fw && time < min_time)) {
1740 /* determine stream with leading sample, to get its position */
1742 && (str->samples[i].offset < stream->samples[index].offset))
1744 && (str->samples[i].offset > stream->samples[index].offset))) {
1751 /* no sample for this stream, mark eos */
1753 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1765 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
1767 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
1770 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
1772 switch (GST_EVENT_TYPE (event)) {
1773 case GST_EVENT_NEWSEGMENT:
1776 gdouble rate, arate;
1777 gint64 start, stop, time, offset = 0;
1778 QtDemuxStream *stream;
1783 /* some debug output */
1784 gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
1785 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
1786 &start, &stop, &time);
1787 gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
1789 GST_DEBUG_OBJECT (demux,
1790 "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
1793 /* chain will send initial newsegment after pads have been added */
1794 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
1795 GST_DEBUG_OBJECT (demux, "still starting, eating event");
1799 /* we only expect a BYTE segment, e.g. following a seek */
1800 if (format == GST_FORMAT_BYTES) {
1802 gint64 requested_seek_time;
1803 guint64 seek_offset;
1807 GST_OBJECT_LOCK (demux);
1808 requested_seek_time = demux->requested_seek_time;
1809 seek_offset = demux->seek_offset;
1810 demux->requested_seek_time = -1;
1811 demux->seek_offset = -1;
1812 GST_OBJECT_UNLOCK (demux);
1814 if (offset == seek_offset) {
1815 start = requested_seek_time;
1817 gst_qtdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,
1819 start = MAX (start, 0);
1823 gst_qtdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,
1825 /* keyframe seeking should already arrange for start >= stop,
1826 * but make sure in other rare cases */
1827 stop = MAX (stop, start);
1830 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
1834 /* accept upstream's notion of segment and distribute along */
1835 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
1836 GST_FORMAT_TIME, start, stop, start);
1837 GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, "
1838 "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
1839 "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
1840 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
1842 gst_qtdemux_push_event (demux,
1843 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
1844 start, stop, start));
1846 /* clear leftover in current segment, if any */
1847 gst_adapter_clear (demux->adapter);
1848 /* set up streaming thread */
1849 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);
1850 demux->offset = offset;
1852 demux->todrop = stream->samples[idx].offset - offset;
1853 demux->neededbytes = demux->todrop + stream->samples[idx].size;
1855 /* set up for EOS */
1856 demux->neededbytes = -1;
1860 gst_event_unref (event);
1865 case GST_EVENT_FLUSH_STOP:
1869 /* clean up, force EOS if no more info follows */
1870 gst_adapter_clear (demux->adapter);
1872 demux->neededbytes = -1;
1873 /* reset flow return, e.g. following seek */
1874 for (i = 0; i < demux->n_streams; i++) {
1875 demux->streams[i]->last_ret = GST_FLOW_OK;
1876 demux->streams[i]->sent_eos = FALSE;
1881 /* If we are in push mode, and get an EOS before we've seen any streams,
1882 * then error out - we have nowhere to send the EOS */
1883 if (!demux->pullbased) {
1885 gboolean has_valid_stream = FALSE;
1886 for (i = 0; i < demux->n_streams; i++) {
1887 if (demux->streams[i]->pad != NULL) {
1888 has_valid_stream = TRUE;
1892 if (!has_valid_stream)
1893 gst_qtdemux_post_no_playable_stream_error (demux);
1900 res = gst_pad_event_default (demux->sinkpad, event);
1907 gst_qtdemux_set_index (GstElement * element, GstIndex * index)
1909 GstQTDemux *demux = GST_QTDEMUX (element);
1911 GST_OBJECT_LOCK (demux);
1912 if (demux->element_index)
1913 gst_object_unref (demux->element_index);
1915 demux->element_index = gst_object_ref (index);
1917 demux->element_index = NULL;
1919 GST_OBJECT_UNLOCK (demux);
1920 /* object lock might be taken again */
1922 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
1923 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
1924 demux->element_index, demux->index_id);
1928 gst_qtdemux_get_index (GstElement * element)
1930 GstIndex *result = NULL;
1931 GstQTDemux *demux = GST_QTDEMUX (element);
1933 GST_OBJECT_LOCK (demux);
1934 if (demux->element_index)
1935 result = gst_object_ref (demux->element_index);
1936 GST_OBJECT_UNLOCK (demux);
1938 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
1944 gst_qtdemux_stbl_free (QtDemuxStream * stream)
1946 g_free ((gpointer) stream->stco.data);
1947 stream->stco.data = NULL;
1948 g_free ((gpointer) stream->stsz.data);
1949 stream->stsz.data = NULL;
1950 g_free ((gpointer) stream->stsc.data);
1951 stream->stsc.data = NULL;
1952 g_free ((gpointer) stream->stts.data);
1953 stream->stts.data = NULL;
1954 g_free ((gpointer) stream->stss.data);
1955 stream->stss.data = NULL;
1956 g_free ((gpointer) stream->ctts.data);
1957 stream->ctts.data = NULL;
1961 gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
1963 while (stream->buffers) {
1964 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
1965 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
1968 gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
1969 g_free (stream->samples);
1971 gst_caps_unref (stream->caps);
1972 g_free (stream->segments);
1973 if (stream->pending_tags)
1974 gst_tag_list_free (stream->pending_tags);
1975 g_free (stream->redirect_uri);
1976 /* free stbl sub-atoms */
1977 gst_qtdemux_stbl_free (stream);
1978 #ifdef QTDEMUX_MODIFICATION
1979 if (stream->trickplay_info)
1980 g_free (stream->trickplay_info);
1985 static GstStateChangeReturn
1986 gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
1988 GstQTDemux *qtdemux = GST_QTDEMUX (element);
1989 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1991 switch (transition) {
1992 case GST_STATE_CHANGE_PAUSED_TO_READY:
1998 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2000 switch (transition) {
2001 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2004 qtdemux->state = QTDEMUX_STATE_INITIAL;
2005 qtdemux->neededbytes = 16;
2006 qtdemux->todrop = 0;
2007 qtdemux->pullbased = FALSE;
2008 qtdemux->posted_redirect = FALSE;
2009 qtdemux->offset = 0;
2010 qtdemux->first_mdat = -1;
2011 qtdemux->header_size = 0;
2012 qtdemux->got_moov = FALSE;
2013 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
2014 if (qtdemux->mdatbuffer)
2015 gst_buffer_unref (qtdemux->mdatbuffer);
2016 qtdemux->mdatbuffer = NULL;
2017 if (qtdemux->comp_brands)
2018 gst_buffer_unref (qtdemux->comp_brands);
2019 qtdemux->comp_brands = NULL;
2020 if (qtdemux->tag_list)
2021 gst_tag_list_free (qtdemux->tag_list);
2022 qtdemux->tag_list = NULL;
2023 if (qtdemux->element_index)
2024 gst_object_unref (qtdemux->element_index);
2025 qtdemux->element_index = NULL;
2026 gst_adapter_clear (qtdemux->adapter);
2027 #ifdef QTDEMUX_MODIFICATION
2028 // TODO: Check new modifications
2029 if (qtdemux->file) {
2030 fclose (qtdemux->file);
2032 fclose (qtdemux->ofile);
2033 if(!remove (qtdemux->filename)) GST_DEBUG_OBJECT (qtdemux, "Error removing temp file");
2035 qtdemux->file = NULL;
2036 qtdemux->ofile = NULL;
2037 qtdemux->filesize = 0;
2038 qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
2040 for (n = 0; n < qtdemux->n_streams; n++) {
2041 gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
2042 qtdemux->streams[n] = NULL;
2044 qtdemux->major_brand = 0;
2045 qtdemux->n_streams = 0;
2046 qtdemux->n_video_streams = 0;
2047 qtdemux->n_audio_streams = 0;
2048 qtdemux->n_sub_streams = 0;
2049 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2050 qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE;
2051 qtdemux->seek_offset = 0;
2052 qtdemux->upstream_seekable = FALSE;
2053 qtdemux->upstream_size = 0;
2064 qtdemux_post_global_tags (GstQTDemux * qtdemux)
2066 if (qtdemux->tag_list) {
2067 /* all header tags ready and parsed, push them */
2068 GST_INFO_OBJECT (qtdemux, "posting global tags: %" GST_PTR_FORMAT,
2070 /* post now, send event on pads later */
2071 gst_element_post_message (GST_ELEMENT (qtdemux),
2072 gst_message_new_tag (GST_OBJECT (qtdemux),
2073 gst_tag_list_copy (qtdemux->tag_list)));
2078 qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2080 /* counts as header data */
2081 qtdemux->header_size += length;
2083 /* only consider at least a sufficiently complete ftyp atom */
2087 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2088 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2089 GST_FOURCC_ARGS (qtdemux->major_brand));
2090 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2091 memcpy (GST_BUFFER_DATA (buf), buffer + 16, GST_BUFFER_SIZE (buf));
2096 qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist)
2098 /* Strip out bogus fields */
2100 gst_tag_list_remove_tag (taglist, GST_TAG_VIDEO_CODEC);
2102 GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, taglist);
2104 if (qtdemux->tag_list) {
2105 /* prioritize native tags using _KEEP mode */
2106 gst_tag_list_insert (qtdemux->tag_list, taglist, GST_TAG_MERGE_KEEP);
2107 gst_tag_list_free (taglist);
2109 qtdemux->tag_list = taglist;
2114 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2116 static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2117 0x97, 0xA9, 0x42, 0xE8,
2118 0x9C, 0x71, 0x99, 0x94,
2119 0x91, 0xE3, 0xAF, 0xAC
2123 /* counts as header data */
2124 qtdemux->header_size += length;
2126 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2128 if (length <= offset + 16) {
2129 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2133 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2135 GstTagList *taglist;
2137 buf = gst_buffer_new ();
2138 GST_BUFFER_DATA (buf) = (guint8 *) buffer + offset + 16;
2139 GST_BUFFER_SIZE (buf) = length - offset - 16;
2141 taglist = gst_tag_list_from_xmp_buffer (buf);
2142 gst_buffer_unref (buf);
2144 qtdemux_handle_xmp_taglist (qtdemux, taglist);
2147 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid");
2151 /* caller verifies at least 8 bytes in buf */
2153 extract_initial_length_and_fourcc (const guint8 * data, guint size,
2154 guint64 * plength, guint32 * pfourcc)
2159 length = QT_UINT32 (data);
2160 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2161 fourcc = QT_FOURCC (data + 4);
2162 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
2165 length = G_MAXUINT32;
2166 } else if (length == 1 && size >= 16) {
2167 /* this means we have an extended size, which is the 64 bit value of
2168 * the next 8 bytes */
2169 length = QT_UINT64 (data + 8);
2170 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
2180 qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
2182 guint32 version = 0;
2183 guint64 duration = 0;
2185 if (!gst_byte_reader_get_uint32_be (br, &version))
2190 if (!gst_byte_reader_get_uint64_be (br, &duration))
2195 if (!gst_byte_reader_get_uint32_be (br, &dur))
2200 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
2201 qtdemux->duration = duration;
2207 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
2213 qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
2214 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
2216 if (!stream->parsed_trex && qtdemux->moov_node) {
2218 GstByteReader trex_data;
2220 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
2222 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
2225 guint32 id = 0, dur = 0, size = 0, flags = 0;
2227 /* skip version/flags */
2228 if (!gst_byte_reader_skip (&trex_data, 4))
2230 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
2232 if (id != stream->track_id)
2234 /* sample description index; ignore */
2235 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2237 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
2239 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
2241 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
2244 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
2245 "duration %d, size %d, flags 0x%x", stream->track_id,
2248 stream->parsed_trex = TRUE;
2249 stream->def_sample_duration = dur;
2250 stream->def_sample_size = size;
2251 stream->def_sample_flags = flags;
2254 /* iterate all siblings */
2255 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
2261 *ds_duration = stream->def_sample_duration;
2262 *ds_size = stream->def_sample_size;
2263 *ds_size = stream->def_sample_size;
2265 /* even then, above values are better than random ... */
2266 if (G_UNLIKELY (!stream->parsed_trex)) {
2267 GST_WARNING_OBJECT (qtdemux,
2268 "failed to find fragment defaults for stream %d", stream->track_id);
2276 qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
2277 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
2278 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
2279 gint64 * base_offset, gint64 * running_offset)
2282 gint32 data_offset = 0;
2283 guint32 flags = 0, first_flags = 0, samples_count = 0;
2286 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
2287 QtDemuxSample *sample;
2288 gboolean ismv = FALSE;
2290 GST_LOG_OBJECT (qtdemux, "parsing trun stream %d; "
2291 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,
2292 stream->track_id, d_sample_duration, d_sample_size, d_sample_flags,
2295 /* presence of stss or not can't really tell us much,
2296 * and flags and so on tend to be marginally reliable in these files */
2297 if (stream->subtype == FOURCC_soun) {
2298 GST_DEBUG_OBJECT (qtdemux,
2299 "sound track in fragmented file; marking all keyframes");
2300 stream->all_keyframe = TRUE;
2303 if (!gst_byte_reader_skip (trun, 1) ||
2304 !gst_byte_reader_get_uint24_be (trun, &flags))
2307 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
2310 if (flags & TR_DATA_OFFSET) {
2311 /* note this is really signed */
2312 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
2314 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
2315 /* default base offset = first byte of moof */
2316 if (*base_offset == -1) {
2317 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
2318 *base_offset = moof_offset;
2320 *running_offset = *base_offset + data_offset;
2322 /* if no offset at all, that would mean data starts at moof start,
2323 * which is a bit wrong and is ismv crappy way, so compensate
2324 * assuming data is in mdat following moof */
2325 if (*base_offset == -1) {
2326 *base_offset = moof_offset + moof_length + 8;
2327 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
2330 if (*running_offset == -1)
2331 *running_offset = *base_offset;
2334 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
2336 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
2337 data_offset, flags, samples_count);
2339 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2340 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
2341 GST_DEBUG_OBJECT (qtdemux,
2342 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
2343 flags ^= TR_FIRST_SAMPLE_FLAGS;
2345 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
2347 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
2351 /* FIXME ? spec says other bits should also be checked to determine
2352 * entry size (and prefix size for that matter) */
2354 dur_offset = size_offset = 0;
2355 if (flags & TR_SAMPLE_DURATION) {
2356 GST_LOG_OBJECT (qtdemux, "entry duration present");
2357 dur_offset = entry_size;
2360 if (flags & TR_SAMPLE_SIZE) {
2361 GST_LOG_OBJECT (qtdemux, "entry size present");
2362 size_offset = entry_size;
2365 if (flags & TR_SAMPLE_FLAGS) {
2366 GST_LOG_OBJECT (qtdemux, "entry flags present");
2367 flags_offset = entry_size;
2370 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2371 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
2372 ct_offset = entry_size;
2376 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
2378 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
2380 if (stream->n_samples >=
2381 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
2384 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
2385 stream->n_samples, (guint) sizeof (QtDemuxSample),
2386 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
2388 /* create a new array of samples if it's the first sample parsed */
2389 if (stream->n_samples == 0)
2390 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
2391 /* or try to reallocate it with space enough to insert the new samples */
2393 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
2394 stream->n_samples + samples_count);
2395 if (stream->samples == NULL)
2398 if (G_UNLIKELY (stream->n_samples == 0)) {
2399 /* the timestamp of the first sample is also provided by the tfra entry
2400 * but we shouldn't rely on it as it is at the end of files */
2403 /* subsequent fragments extend stream */
2405 stream->samples[stream->n_samples - 1].timestamp +
2406 stream->samples[stream->n_samples - 1].duration;
2408 sample = stream->samples + stream->n_samples;
2409 for (i = 0; i < samples_count; i++) {
2410 guint32 dur, size, sflags, ct;
2412 /* first read sample data */
2413 if (flags & TR_SAMPLE_DURATION) {
2414 dur = QT_UINT32 (data + dur_offset);
2416 dur = d_sample_duration;
2418 if (flags & TR_SAMPLE_SIZE) {
2419 size = QT_UINT32 (data + size_offset);
2421 size = d_sample_size;
2423 if (flags & TR_FIRST_SAMPLE_FLAGS) {
2425 sflags = first_flags;
2427 sflags = d_sample_flags;
2429 } else if (flags & TR_SAMPLE_FLAGS) {
2430 sflags = QT_UINT32 (data + flags_offset);
2432 sflags = d_sample_flags;
2434 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
2435 ct = QT_UINT32 (data + ct_offset);
2441 /* fill the sample information */
2442 sample->offset = *running_offset;
2443 sample->pts_offset = ct;
2444 sample->size = size;
2445 sample->timestamp = timestamp;
2446 sample->duration = dur;
2447 /* sample-is-difference-sample */
2448 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
2449 * now idea how it relates to bitfield other than massive LE/BE confusion */
2450 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
2451 *running_offset += size;
2456 stream->n_samples += samples_count;
2462 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
2467 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
2473 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
2474 "be larger than %uMB (broken file?)", stream->n_samples,
2475 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
2480 /* find stream with @id */
2481 static inline QtDemuxStream *
2482 qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
2484 QtDemuxStream *stream;
2488 if (G_UNLIKELY (!id)) {
2489 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
2493 /* try to get it fast and simple */
2494 if (G_LIKELY (id <= qtdemux->n_streams)) {
2495 stream = qtdemux->streams[id - 1];
2496 if (G_LIKELY (stream->track_id == id))
2500 /* linear search otherwise */
2501 for (i = 0; i < qtdemux->n_streams; i++) {
2502 stream = qtdemux->streams[i];
2503 if (stream->track_id == id)
2511 qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
2512 QtDemuxStream ** stream, guint32 * default_sample_duration,
2513 guint32 * default_sample_size, guint32 * default_sample_flags,
2514 gint64 * base_offset)
2517 guint32 track_id = 0;
2519 if (!gst_byte_reader_skip (tfhd, 1) ||
2520 !gst_byte_reader_get_uint24_be (tfhd, &flags))
2523 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
2526 *stream = qtdemux_find_stream (qtdemux, track_id);
2527 if (G_UNLIKELY (!*stream))
2528 goto unknown_stream;
2530 if (flags & TF_BASE_DATA_OFFSET)
2531 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
2534 /* obtain stream defaults */
2535 qtdemux_parse_trex (qtdemux, *stream,
2536 default_sample_duration, default_sample_size, default_sample_flags);
2538 /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
2539 if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
2540 if (!gst_byte_reader_skip (tfhd, 4))
2543 if (flags & TF_DEFAULT_SAMPLE_DURATION)
2544 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
2547 if (flags & TF_DEFAULT_SAMPLE_SIZE)
2548 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
2551 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
2552 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
2559 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
2564 GST_DEBUG_OBJECT (qtdemux, "unknown stream in tfhd");
2570 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
2571 guint64 moof_offset, QtDemuxStream * stream)
2573 GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
2574 GstByteReader trun_data, tfhd_data;
2575 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
2576 gint64 base_offset, running_offset;
2578 /* NOTE @stream ignored */
2580 moof_node = g_node_new ((guint8 *) buffer);
2581 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
2582 qtdemux_node_dump (qtdemux, moof_node);
2584 /* unknown base_offset to start with */
2585 base_offset = running_offset = -1;
2586 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
2588 /* Fragment Header node */
2590 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
2594 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
2595 &ds_size, &ds_flags, &base_offset))
2597 if (G_UNLIKELY (!stream)) {
2598 /* we lost track of offset, we'll need to regain it,
2599 * but can delay complaining until later or avoid doing so altogether */
2603 if (G_UNLIKELY (base_offset < -1))
2605 /* Track Run node */
2607 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
2610 qtdemux_parse_trun (qtdemux, &trun_data, stream,
2611 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
2613 /* iterate all siblings */
2614 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
2617 /* if no new base_offset provided for next traf,
2618 * base is end of current traf */
2619 base_offset = running_offset;
2620 running_offset = -1;
2622 /* iterate all siblings */
2623 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
2625 g_node_destroy (moof_node);
2630 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
2635 GST_DEBUG_OBJECT (qtdemux, "lost offset");
2640 g_node_destroy (moof_node);
2641 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2642 (_("This file is corrupt and cannot be played.")), (NULL));
2647 /* might be used if some day we actually use mfra & co
2648 * for random access to fragments,
2649 * but that will require quite some modifications and much less relying
2650 * on a sample array */
2653 qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node,
2654 QtDemuxStream * stream)
2656 guint64 time = 0, moof_offset = 0;
2657 guint32 ver_flags, track_id, len, num_entries, i;
2658 guint value_size, traf_size, trun_size, sample_size;
2659 GstBuffer *buf = NULL;
2663 gst_byte_reader_init (&tfra, (guint8 *) tfra_node->data + (4 + 4),
2664 QT_UINT32 ((guint8 *) tfra_node->data) - (4 + 4));
2666 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
2669 if (!(gst_byte_reader_get_uint32_be (&tfra, &track_id) &&
2670 gst_byte_reader_get_uint32_be (&tfra, &len) &&
2671 gst_byte_reader_get_uint32_be (&tfra, &num_entries)))
2674 GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?",
2675 track_id, stream->track_id);
2676 if (track_id != stream->track_id) {
2680 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
2681 sample_size = (len & 3) + 1;
2682 trun_size = ((len & 12) >> 2) + 1;
2683 traf_size = ((len & 48) >> 4) + 1;
2685 if (num_entries == 0)
2688 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
2689 value_size + value_size + traf_size + trun_size + sample_size))
2692 for (i = 0; i < num_entries; i++) {
2693 qt_atom_parser_get_offset (&tfra, value_size, &time);
2694 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
2695 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
2696 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
2697 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
2699 GST_LOG_OBJECT (qtdemux,
2700 "fragment time: %" GST_TIME_FORMAT " moof_offset: %u",
2701 GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND,
2702 stream->timescale)), moof_offset);
2704 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
2705 if (ret != GST_FLOW_OK)
2707 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
2708 moof_offset, stream);
2709 gst_buffer_unref (buf);
2717 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2718 (_("This file is corrupt and cannot be played.")), (NULL));
2723 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
2729 qtdemux_parse_mfra (GstQTDemux * qtdemux, QtDemuxStream * stream)
2732 GNode *mfra_node, *tfra_node;
2735 if (!qtdemux->mfra_offset)
2738 ret = gst_qtdemux_pull_atom (qtdemux, qtdemux->mfra_offset, 0, &buffer);
2739 if (ret != GST_FLOW_OK)
2742 mfra_node = g_node_new ((guint8 *) GST_BUFFER_DATA (buffer));
2743 qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer),
2744 GST_BUFFER_SIZE (buffer));
2746 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
2749 qtdemux_parse_tfra (qtdemux, tfra_node, stream);
2750 /* iterate all siblings */
2751 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
2753 g_node_destroy (mfra_node);
2754 gst_buffer_unref (buffer);
2760 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
2761 (_("This file is corrupt and cannot be played.")), (NULL));
2766 static GstFlowReturn
2767 qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset,
2768 guint32 * mfro_size)
2770 GstFlowReturn ret = GST_FLOW_ERROR;
2771 GstBuffer *mfro = NULL;
2774 GstFormat fmt = GST_FORMAT_BYTES;
2776 if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &fmt, &len)) {
2777 GST_DEBUG_OBJECT (qtdemux, "upstream size not available; "
2778 "can not locate mfro");
2782 ret = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
2783 if (ret != GST_FLOW_OK)
2786 fourcc = QT_FOURCC (GST_BUFFER_DATA (mfro) + 4);
2787 if (fourcc != FOURCC_mfro)
2790 GST_INFO_OBJECT (qtdemux, "Found mfro atom: fragmented mp4 container");
2791 if (GST_BUFFER_SIZE (mfro) >= 16) {
2792 GST_DEBUG_OBJECT (qtdemux, "parsing 'mfro' atom");
2793 *mfro_size = QT_UINT32 (GST_BUFFER_DATA (mfro) + 12);
2794 if (*mfro_size >= len) {
2795 GST_WARNING_OBJECT (qtdemux, "mfro.size is invalid");
2796 ret = GST_FLOW_ERROR;
2799 *mfra_offset = len - *mfro_size;
2804 gst_buffer_unref (mfro);
2810 qtdemux_parse_fragmented (GstQTDemux * qtdemux)
2813 guint32 mfra_size = 0;
2814 guint64 mfra_offset = 0;
2817 qtdemux->fragmented = FALSE;
2819 /* We check here if it is a fragmented mp4 container */
2820 ret = qtdemux_parse_mfro (qtdemux, &mfra_offset, &mfra_size);
2821 if (ret == GST_FLOW_OK && mfra_size != 0 && mfra_offset != 0) {
2822 qtdemux->fragmented = TRUE;
2823 GST_DEBUG_OBJECT (qtdemux,
2824 "mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset);
2825 qtdemux->mfra_offset = mfra_offset;
2830 static GstFlowReturn
2831 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
2835 GstBuffer *buf = NULL;
2836 GstFlowReturn ret = GST_FLOW_OK;
2837 guint64 cur_offset = qtdemux->offset;
2839 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
2840 if (G_UNLIKELY (ret != GST_FLOW_OK))
2842 if (G_LIKELY (GST_BUFFER_SIZE (buf) >= 8))
2843 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf),
2844 GST_BUFFER_SIZE (buf), &length, &fourcc);
2845 gst_buffer_unref (buf);
2847 /* maybe we already got most we needed, so only consider this eof */
2848 if (G_UNLIKELY (length == 0)) {
2849 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
2850 (_("Invalid atom size.")),
2851 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
2852 GST_FOURCC_ARGS (fourcc)));
2853 ret = GST_FLOW_UNEXPECTED;
2859 /* record for later parsing when needed */
2860 if (!qtdemux->moof_offset) {
2861 qtdemux->moof_offset = qtdemux->offset;
2868 GST_LOG_OBJECT (qtdemux,
2869 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
2870 GST_FOURCC_ARGS (fourcc), cur_offset);
2871 qtdemux->offset += length;
2878 if (qtdemux->got_moov) {
2879 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
2880 qtdemux->offset += length;
2884 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
2885 if (ret != GST_FLOW_OK)
2887 if (length != GST_BUFFER_SIZE (moov)) {
2888 /* Some files have a 'moov' atom at the end of the file which contains
2889 * a terminal 'free' atom where the body of the atom is missing.
2890 * Check for, and permit, this special case.
2892 if (GST_BUFFER_SIZE (moov) >= 8) {
2893 guint8 *final_data = GST_BUFFER_DATA (moov) +
2894 (GST_BUFFER_SIZE (moov) - 8);
2895 guint32 final_length = QT_UINT32 (final_data);
2896 guint32 final_fourcc = QT_FOURCC (final_data + 4);
2897 if (final_fourcc == FOURCC_free &&
2898 GST_BUFFER_SIZE (moov) + final_length - 8 == length) {
2899 /* Ok, we've found that special case. Allocate a new buffer with
2900 * that free atom actually present. */
2901 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
2902 gst_buffer_copy_metadata (newmoov, moov,
2903 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2904 GST_BUFFER_COPY_CAPS);
2905 memcpy (GST_BUFFER_DATA (newmoov), GST_BUFFER_DATA (moov),
2906 GST_BUFFER_SIZE (moov));
2907 memset (GST_BUFFER_DATA (newmoov) + GST_BUFFER_SIZE (moov), 0,
2909 gst_buffer_unref (moov);
2915 if (length != GST_BUFFER_SIZE (moov)) {
2916 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
2917 (_("This file is incomplete and cannot be played.")),
2918 ("We got less than expected (received %u, wanted %u, offset %"
2919 G_GUINT64_FORMAT ")",
2920 GST_BUFFER_SIZE (moov), (guint) length, cur_offset));
2921 gst_buffer_unref (moov);
2922 ret = GST_FLOW_ERROR;
2925 qtdemux->offset += length;
2927 qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
2928 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
2930 qtdemux_parse_tree (qtdemux);
2931 g_node_destroy (qtdemux->moov_node);
2932 gst_buffer_unref (moov);
2933 qtdemux->moov_node = NULL;
2934 qtdemux->got_moov = TRUE;
2942 /* extract major brand; might come in handy for ISO vs QT issues */
2943 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
2944 if (ret != GST_FLOW_OK)
2946 qtdemux->offset += length;
2947 qtdemux_parse_ftyp (qtdemux, GST_BUFFER_DATA (ftyp),
2948 GST_BUFFER_SIZE (ftyp));
2949 if (qtdemux->major_brand == FOURCC_qt__) {
2950 GST_ERROR_OBJECT (qtdemux, "Unsupported major brand...");
2951 return GST_FLOW_ERROR;
2953 gst_buffer_unref (ftyp);
2960 /* uuid are extension atoms */
2961 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
2962 if (ret != GST_FLOW_OK)
2964 qtdemux->offset += length;
2965 qtdemux_parse_uuid (qtdemux, GST_BUFFER_DATA (uuid),
2966 GST_BUFFER_SIZE (uuid));
2967 gst_buffer_unref (uuid);
2974 GST_LOG_OBJECT (qtdemux,
2975 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
2976 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
2978 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
2979 if (ret != GST_FLOW_OK)
2981 GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown),
2982 GST_BUFFER_SIZE (unknown));
2983 gst_buffer_unref (unknown);
2984 qtdemux->offset += length;
2990 if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) {
2991 /* digested all data, show what we have */
2992 ret = qtdemux_expose_streams (qtdemux);
2994 /* Only post, event on pads is done after newsegment */
2995 qtdemux_post_global_tags (qtdemux);
2997 qtdemux->state = QTDEMUX_STATE_MOVIE;
2998 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
3005 /* Seeks to the previous keyframe of the indexed stream and
3006 * aligns other streams with respect to the keyframe timestamp
3007 * of indexed stream. Only called in case of Reverse Playback
3009 static GstFlowReturn
3010 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
3013 guint32 seg_idx = 0, k_index = 0;
3014 guint32 ref_seg_idx, ref_k_index;
3015 guint64 k_pos = 0, last_stop = 0;
3016 QtDemuxSegment *seg = NULL;
3017 QtDemuxStream *ref_str = NULL;
3018 guint64 seg_media_start_mov; /* segment media start time in mov format */
3019 #ifdef QTDEMUX_MODIFICATION
3020 QtDemuxSample *sample;
3021 gdouble minusone = -1;
3022 guint64 time_position = GST_CLOCK_TIME_NONE;
3025 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
3026 * and finally align all the other streams on that timestamp with their
3027 * respective keyframes */
3028 for (n = 0; n < qtdemux->n_streams; n++) {
3029 QtDemuxStream *str = qtdemux->streams[n];
3031 #ifdef QTDEMUX_MODIFICATION
3032 sample = &str->samples[str->sample_index];
3034 if((time_position - (minusone *qtdemux->segment.rate)*sample->duration)>0)
3035 time_position -= (minusone *qtdemux->segment.rate)*sample->duration;
3039 seg_idx = gst_qtdemux_find_segment (qtdemux, str,
3042 seg_idx = gst_qtdemux_find_segment (qtdemux, str,
3043 qtdemux->segment.last_stop);
3046 /* segment not found, continue with normal flow */
3050 /* No candidate yet, take that one */
3056 /* So that stream has a segment, we prefer video streams */
3057 if (str->subtype == FOURCC_vide) {
3063 if (G_UNLIKELY (!ref_str)) {
3064 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
3068 if (G_UNLIKELY (!ref_str->from_sample)) {
3069 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
3073 /* So that stream has been playing from from_sample to to_sample. We will
3074 * get the timestamp of the previous sample and search for a keyframe before
3075 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
3076 if (ref_str->subtype == FOURCC_vide) {
3077 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
3078 ref_str->from_sample - 1);
3080 if (ref_str->from_sample >= 10)
3081 k_index = ref_str->from_sample - 10;
3086 /* get current segment for that stream */
3087 seg = &ref_str->segments[ref_str->segment_index];
3088 /* convert seg->media_start to mov format time for timestamp comparison */
3089 seg_media_start_mov =
3090 gst_util_uint64_scale (seg->media_start, ref_str->timescale, GST_SECOND);
3091 /* Crawl back through segments to find the one containing this I frame */
3092 while (ref_str->samples[k_index].timestamp < seg_media_start_mov) {
3093 GST_DEBUG_OBJECT (qtdemux, "keyframe position is out of segment %u",
3094 ref_str->segment_index);
3095 if (G_UNLIKELY (!ref_str->segment_index)) {
3096 /* Reached first segment, let's consider it's EOS */
3099 ref_str->segment_index--;
3100 seg = &ref_str->segments[ref_str->segment_index];
3101 /* convert seg->media_start to mov format time for timestamp comparison */
3102 seg_media_start_mov =
3103 gst_util_uint64_scale (seg->media_start, ref_str->timescale,
3106 /* Calculate time position of the keyframe and where we should stop */
3108 (gst_util_uint64_scale (ref_str->samples[k_index].timestamp, GST_SECOND,
3109 ref_str->timescale) - seg->media_start) + seg->time;
3111 gst_util_uint64_scale (ref_str->samples[ref_str->from_sample].timestamp,
3112 GST_SECOND, ref_str->timescale);
3113 last_stop = (last_stop - seg->media_start) + seg->time;
3115 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
3116 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
3117 k_index, GST_TIME_ARGS (k_pos));
3119 /* Set last_stop with the keyframe timestamp we pushed of that stream */
3120 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, last_stop);
3121 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
3122 GST_TIME_ARGS (last_stop));
3124 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
3125 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
3129 ref_seg_idx = ref_str->segment_index;
3130 ref_k_index = k_index;
3132 /* Align them all on this */
3133 for (n = 0; n < qtdemux->n_streams; n++) {
3135 guint64 media_start = 0, seg_time = 0;
3136 QtDemuxStream *str = qtdemux->streams[n];
3138 /* aligning reference stream again might lead to backing up to yet another
3139 * keyframe (due to timestamp rounding issues),
3140 * potentially putting more load on downstream; so let's try to avoid */
3141 if (str == ref_str) {
3142 seg_idx = ref_seg_idx;
3143 seg = &str->segments[seg_idx];
3144 k_index = ref_k_index;
3145 GST_DEBUG_OBJECT (qtdemux, "reference stream segment %d, "
3146 "sample at index %d", ref_str->segment_index, k_index);
3148 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
3149 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
3151 /* segment not found, continue with normal flow */
3155 /* get segment and time in the segment */
3156 seg = &str->segments[seg_idx];
3157 seg_time = k_pos - seg->time;
3159 /* get the media time in the segment */
3160 media_start = seg->media_start + seg_time;
3162 /* get the index of the sample with media time */
3163 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
3164 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
3165 GST_TIME_ARGS (media_start), index);
3167 /* find previous keyframe */
3168 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index);
3171 /* Remember until where we want to go */
3172 str->to_sample = str->from_sample - 1;
3173 /* Define our time position */
3174 str->time_position =
3175 (gst_util_uint64_scale (str->samples[k_index].timestamp, GST_SECOND,
3176 str->timescale) - seg->media_start) + seg->time;
3177 /* Now seek back in time */
3178 gst_qtdemux_move_stream (qtdemux, str, k_index);
3179 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u, time position %"
3180 GST_TIME_FORMAT " playing from sample %u to %u", k_index,
3181 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
3187 return GST_FLOW_UNEXPECTED;
3190 /* activate the given segment number @seg_idx of @stream at time @offset.
3191 * @offset is an absolute global position over all the segments.
3193 * This will push out a NEWSEGMENT event with the right values and
3194 * position the stream index to the first decodable sample before
3198 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
3199 guint32 seg_idx, guint64 offset)
3202 QtDemuxSegment *segment;
3203 guint32 index, kf_index;
3205 guint64 start, stop, time;
3208 GST_LOG_OBJECT (qtdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,
3211 /* update the current segment */
3212 stream->segment_index = seg_idx;
3214 /* get the segment */
3215 segment = &stream->segments[seg_idx];
3217 if (G_UNLIKELY (offset < segment->time)) {
3218 GST_WARNING_OBJECT (qtdemux, "offset < segment->time %" G_GUINT64_FORMAT,
3223 /* segment lies beyond total indicated duration */
3224 if (G_UNLIKELY (qtdemux->segment.duration != -1 &&
3225 segment->time > qtdemux->segment.duration)) {
3226 GST_WARNING_OBJECT (qtdemux, "file duration %" G_GINT64_FORMAT
3227 " < segment->time %" G_GUINT64_FORMAT, qtdemux->segment.duration,
3232 /* get time in this segment */
3233 seg_time = offset - segment->time;
3235 GST_LOG_OBJECT (qtdemux, "seg_time %" GST_TIME_FORMAT,
3236 GST_TIME_ARGS (seg_time));
3238 if (G_UNLIKELY (seg_time > segment->duration)) {
3239 GST_LOG_OBJECT (qtdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,
3240 GST_TIME_ARGS (segment->duration));
3244 /* qtdemux->segment.stop is in outside-time-realm, whereas
3245 * segment->media_stop is in track-time-realm.
3247 * In order to compare the two, we need to bring segment.stop
3248 * into the track-time-realm */
3250 stop = qtdemux->segment.stop;
3252 stop = qtdemux->segment.duration;
3254 stop = segment->media_stop;
3257 MIN (segment->media_stop, stop - segment->time + segment->media_start);
3259 if (qtdemux->segment.rate >= 0) {
3260 start = MIN (segment->media_start + seg_time, stop);
3263 if (segment->media_start >= qtdemux->segment.start) {
3264 start = segment->media_start;
3265 time = segment->time;
3267 start = qtdemux->segment.start;
3268 time = segment->time + (qtdemux->segment.start - segment->media_start);
3271 start = MAX (segment->media_start, qtdemux->segment.start);
3272 stop = MIN (segment->media_start + seg_time, stop);
3275 GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT
3276 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
3277 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
3279 /* combine global rate with that of the segment */
3280 rate = segment->rate * qtdemux->segment.rate;
3282 /* update the segment values used for clipping */
3283 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
3284 gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
3287 /* now prepare and send the segment */
3289 event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
3291 gst_pad_push_event (stream->pad, event);
3292 /* assume we can send more data now */
3293 stream->last_ret = GST_FLOW_OK;
3294 /* clear to send tags on this pad now */
3295 gst_qtdemux_push_tags (qtdemux, stream);
3298 /* and move to the keyframe before the indicated media time of the
3300 if (qtdemux->segment.rate >= 0) {
3301 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
3302 stream->to_sample = G_MAXUINT32;
3303 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3304 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
3305 GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
3306 GST_SECOND, stream->timescale)));
3308 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
3309 stream->to_sample = index;
3310 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
3311 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
3312 GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
3313 GST_SECOND, stream->timescale)));
3316 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
3317 * encountered an error and printed a message so we return appropriately */
3321 /* we're at the right spot */
3322 if (index == stream->sample_index) {
3323 GST_DEBUG_OBJECT (qtdemux, "we are at the right index");
3327 /* find keyframe of the target index */
3328 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
3331 /* indent does stupid stuff with stream->samples[].timestamp */
3333 /* if we move forwards, we don't have to go back to the previous
3334 * keyframe since we already sent that. We can also just jump to
3335 * the keyframe right before the target index if there is one. */
3336 if (index > stream->sample_index) {
3337 /* moving forwards check if we move past a keyframe */
3338 if (kf_index > stream->sample_index) {
3339 GST_DEBUG_OBJECT (qtdemux,
3340 "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3341 GST_TIME_ARGS (gst_util_uint64_scale (
3342 stream->samples[kf_index].timestamp,
3343 GST_SECOND, stream->timescale)));
3344 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3346 GST_DEBUG_OBJECT (qtdemux,
3347 "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT
3348 " already sent", kf_index,
3349 GST_TIME_ARGS (gst_util_uint64_scale (
3350 stream->samples[kf_index].timestamp,
3351 GST_SECOND, stream->timescale)));
3354 GST_DEBUG_OBJECT (qtdemux,
3355 "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT, kf_index,
3356 GST_TIME_ARGS (gst_util_uint64_scale (
3357 stream->samples[kf_index].timestamp,
3358 GST_SECOND, stream->timescale)));
3359 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
3367 /* prepare to get the current sample of @stream, getting essential values.
3369 * This function will also prepare and send the segment when needed.
3371 * Return FALSE if the stream is EOS.
3374 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
3375 QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
3376 guint64 * duration, gboolean * keyframe)
3378 QtDemuxSample *sample;
3379 guint64 time_position;
3382 g_return_val_if_fail (stream != NULL, FALSE);
3384 time_position = stream->time_position;
3385 if (G_UNLIKELY (time_position == -1))
3388 seg_idx = stream->segment_index;
3389 if (G_UNLIKELY (seg_idx == -1)) {
3390 /* find segment corresponding to time_position if we are looking
3392 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
3394 /* nothing found, we're really eos */
3399 /* different segment, activate it, sample_index will be set. */
3400 if (G_UNLIKELY (stream->segment_index != seg_idx))
3401 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
3403 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
3404 stream->sample_index, stream->n_samples);
3406 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3409 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3410 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3411 stream->sample_index);
3415 /* now get the info for the sample we're at */
3416 sample = &stream->samples[stream->sample_index];
3418 *timestamp = QTSAMPLE_PTS (stream, sample);
3419 *offset = sample->offset;
3420 *size = sample->size;
3421 *duration = QTSAMPLE_DUR_PTS (stream, sample, *timestamp);
3422 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
3424 #ifdef QTDEMUX_MODIFICATION
3425 if (qtdemux->segment.rate > 1.0 && (FOURCC_vide == stream->subtype)) {
3426 /* forward trick play */
3427 gst_qtdemux_forward_trickplay (qtdemux, stream, timestamp);
3429 else if (qtdemux->segment.rate < 0.0 && (FOURCC_vide == stream->subtype)) {
3430 /* backward trick play */
3431 gst_qtdemux_backward_trickplay (qtdemux, stream, timestamp);
3440 stream->time_position = -1;
3445 /* move to the next sample in @stream.
3447 * Moves to the next segment when needed.
3450 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
3452 QtDemuxSample *sample;
3453 QtDemuxSegment *segment;
3455 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
3456 /* Mark the stream as EOS */
3457 GST_DEBUG_OBJECT (qtdemux,
3458 "reached max allowed sample %u, mark EOS", stream->to_sample);
3459 stream->time_position = -1;
3463 /* move to next sample */
3464 stream->sample_index++;
3466 /* get current segment */
3467 segment = &stream->segments[stream->segment_index];
3469 /* reached the last sample, we need the next segment */
3470 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
3473 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
3474 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
3475 stream->sample_index);
3479 /* get next sample */
3480 sample = &stream->samples[stream->sample_index];
3482 /* see if we are past the segment */
3483 if (G_UNLIKELY (gst_util_uint64_scale (sample->timestamp,
3484 GST_SECOND, stream->timescale) >= segment->media_stop))
3487 if (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3488 stream->timescale) >= segment->media_start) {
3489 /* inside the segment, update time_position, looks very familiar to
3490 * GStreamer segments, doesn't it? */
3491 stream->time_position =
3492 (gst_util_uint64_scale (sample->timestamp, GST_SECOND,
3493 stream->timescale) - segment->media_start) + segment->time;
3495 /* not yet in segment, time does not yet increment. This means
3496 * that we are still prerolling keyframes to the decoder so it can
3497 * decode the first sample of the segment. */
3498 stream->time_position = segment->time;
3502 /* move to the next segment */
3505 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
3507 if (stream->segment_index == stream->n_segments - 1) {
3508 /* are we at the end of the last segment, we're EOS */
3509 stream->time_position = -1;
3511 /* else we're only at the end of the current segment */
3512 stream->time_position = segment->stop_time;
3514 /* make sure we select a new segment */
3515 stream->segment_index = -1;
3520 gst_qtdemux_sync_streams (GstQTDemux * demux)
3524 if (demux->n_streams <= 1)
3527 for (i = 0; i < demux->n_streams; i++) {
3528 QtDemuxStream *stream;
3529 GstClockTime end_time;
3531 stream = demux->streams[i];
3536 /* TODO advance time on subtitle streams here, if any some day */
3538 /* some clips/trailers may have unbalanced streams at the end,
3539 * so send EOS on shorter stream to prevent stalling others */
3541 /* do not mess with EOS if SEGMENT seeking */
3542 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
3545 if (demux->pullbased) {
3546 /* loop mode is sample time based */
3547 if (stream->time_position != -1)
3550 /* push mode is byte position based */
3551 if (stream->n_samples &&
3552 stream->samples[stream->n_samples - 1].offset >= demux->offset)
3556 if (stream->sent_eos)
3559 /* only act if some gap */
3560 end_time = stream->segments[stream->n_segments - 1].stop_time;
3561 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
3562 ", stream end: %" GST_TIME_FORMAT,
3563 GST_TIME_ARGS (demux->segment.last_stop), GST_TIME_ARGS (end_time));
3564 if (end_time + 2 * GST_SECOND < demux->segment.last_stop) {
3565 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
3566 GST_PAD_NAME (stream->pad));
3567 stream->sent_eos = TRUE;
3568 gst_pad_push_event (stream->pad, gst_event_new_eos ());
3573 /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return:
3575 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
3576 * GST_FLOW_UNEXPECTED: when all pads UNEXPECTED or NOT_LINKED.
3578 static GstFlowReturn
3579 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
3583 gboolean unexpected = FALSE, not_linked = TRUE;
3585 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
3587 /* store the value */
3588 stream->last_ret = ret;
3590 /* any other error that is not-linked or eos can be returned right away */
3591 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3594 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
3595 for (i = 0; i < demux->n_streams; i++) {
3596 QtDemuxStream *ostream = demux->streams[i];
3598 ret = ostream->last_ret;
3600 /* no unexpected or unlinked, return */
3601 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
3604 /* we check to see if we have at least 1 unexpected or all unlinked */
3605 unexpected |= (ret == GST_FLOW_UNEXPECTED);
3606 not_linked &= (ret == GST_FLOW_NOT_LINKED);
3609 /* when we get here, we all have unlinked or unexpected */
3611 ret = GST_FLOW_NOT_LINKED;
3612 else if (unexpected)
3613 ret = GST_FLOW_UNEXPECTED;
3615 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
3619 /* the input buffer metadata must be writable. Returns NULL when the buffer is
3620 * completely cliped */
3622 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3625 gint64 start, stop, cstart, cstop, diff;
3626 GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
3629 gint num_rate, denom_rate;
3633 data = GST_BUFFER_DATA (buf);
3634 size = GST_BUFFER_SIZE (buf);
3636 /* depending on the type, setup the clip parameters */
3637 if (stream->subtype == FOURCC_soun) {
3638 frame_size = stream->bytes_per_frame;
3639 num_rate = GST_SECOND;
3640 denom_rate = (gint) stream->rate;
3642 } else if (stream->subtype == FOURCC_vide) {
3644 num_rate = stream->fps_n;
3645 denom_rate = stream->fps_d;
3650 /* we can only clip if we have a valid timestamp */
3651 timestamp = GST_BUFFER_TIMESTAMP (buf);
3652 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
3655 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) {
3656 duration = GST_BUFFER_DURATION (buf);
3659 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
3663 stop = start + duration;
3665 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
3666 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
3669 /* see if some clipping happened */
3670 diff = cstart - start;
3676 /* bring clipped time to samples and to bytes */
3677 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3680 GST_DEBUG_OBJECT (qtdemux,
3681 "clipping start to %" GST_TIME_FORMAT " %"
3682 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
3688 diff = stop - cstop;
3693 /* bring clipped time to samples and then to bytes */
3694 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
3696 GST_DEBUG_OBJECT (qtdemux,
3697 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
3698 " bytes", GST_TIME_ARGS (cstop), diff);
3703 GST_BUFFER_TIMESTAMP (buf) = timestamp;
3704 GST_BUFFER_DURATION (buf) = duration;
3705 GST_BUFFER_SIZE (buf) = size;
3706 GST_BUFFER_DATA (buf) = data;
3710 /* dropped buffer */
3713 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
3718 GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer");
3723 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
3724 gst_buffer_unref (buf);
3729 /* the input buffer metadata must be writable,
3730 * but time/duration etc not yet set and need not be preserved */
3732 gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
3736 guint size, nsize = 0;
3739 data = GST_BUFFER_DATA (buf);
3740 size = GST_BUFFER_SIZE (buf);
3742 /* not many cases for now */
3743 if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
3744 /* send a one time dvd clut event */
3745 if (stream->pending_event && stream->pad)
3746 gst_pad_push_event (stream->pad, stream->pending_event);
3747 stream->pending_event = NULL;
3748 /* no further processing needed */
3749 stream->need_process = FALSE;
3752 if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
3756 if (G_LIKELY (size >= 2)) {
3757 nsize = GST_READ_UINT16_BE (data);
3758 nsize = MIN (nsize, size - 2);
3761 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%d", nsize, size);
3763 /* takes care of UTF-8 validation or UTF-16 recognition,
3764 * no other encoding expected */
3765 str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
3767 gst_buffer_unref (buf);
3768 buf = gst_buffer_new ();
3769 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;
3770 GST_BUFFER_SIZE (buf) = strlen (str);
3772 /* may be 0-size subtitle, which is also sent to keep pipeline going */
3773 GST_BUFFER_DATA (buf) = data + 2;
3774 GST_BUFFER_SIZE (buf) = nsize;
3777 /* FIXME ? convert optional subsequent style info to markup */
3782 /* Sets a buffer's attributes properly and pushes it downstream.
3783 * Also checks for additional actions and custom processing that may
3784 * need to be done first.
3787 gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
3788 QtDemuxStream * stream, GstBuffer * buf,
3789 guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
3790 guint64 byte_position)
3792 GstFlowReturn ret = GST_FLOW_OK;
3794 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
3797 url = g_strndup ((gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
3798 if (url != NULL && strlen (url) != 0) {
3799 /* we have RTSP redirect now */
3800 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
3801 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
3802 gst_structure_new ("redirect",
3803 "new-location", G_TYPE_STRING, url, NULL)));
3804 qtdemux->posted_redirect = TRUE;
3806 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
3812 /* position reporting */
3813 if (qtdemux->segment.rate >= 0) {
3814 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, position);
3815 gst_qtdemux_sync_streams (qtdemux);
3818 if (G_UNLIKELY (!stream->pad)) {
3819 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
3820 gst_buffer_unref (buf);
3824 /* send out pending buffers */
3825 while (stream->buffers) {
3826 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
3828 if (G_UNLIKELY (stream->discont)) {
3829 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
3830 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
3831 stream->discont = FALSE;
3833 gst_buffer_set_caps (buffer, stream->caps);
3835 gst_pad_push (stream->pad, buffer);
3837 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
3840 /* we're going to modify the metadata */
3841 buf = gst_buffer_make_metadata_writable (buf);
3843 if (G_UNLIKELY (stream->need_process))
3844 buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);
3846 GST_BUFFER_TIMESTAMP (buf) = timestamp;
3847 GST_BUFFER_DURATION (buf) = duration;
3848 GST_BUFFER_OFFSET (buf) = -1;
3849 GST_BUFFER_OFFSET_END (buf) = -1;
3851 if (G_UNLIKELY (stream->padding)) {
3852 GST_BUFFER_DATA (buf) += stream->padding;
3853 GST_BUFFER_SIZE (buf) -= stream->padding;
3856 if (G_UNLIKELY (qtdemux->element_index)) {
3857 GstClockTime stream_time;
3860 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
3862 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
3863 GST_LOG_OBJECT (qtdemux,
3864 "adding association %" GST_TIME_FORMAT "-> %"
3865 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
3866 gst_index_add_association (qtdemux->element_index,
3868 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
3869 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
3870 GST_FORMAT_BYTES, byte_position, NULL);
3874 if (stream->need_clip)
3875 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
3877 if (G_UNLIKELY (buf == NULL))
3880 if (G_UNLIKELY (stream->discont)) {
3881 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
3882 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
3883 stream->discont = FALSE;
3887 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
3889 gst_buffer_set_caps (buf, stream->caps);
3891 GST_LOG_OBJECT (qtdemux,
3892 "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
3893 GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3894 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
3896 ret = gst_pad_push (stream->pad, buf);
3902 static GstFlowReturn
3903 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
3905 GstFlowReturn ret = GST_FLOW_OK;
3906 GstBuffer *buf = NULL;
3907 QtDemuxStream *stream;
3910 guint64 timestamp = GST_CLOCK_TIME_NONE;
3911 guint64 duration = 0;
3912 gboolean keyframe = FALSE;
3917 gst_qtdemux_push_pending_newsegment (qtdemux);
3919 /* Figure out the next stream sample to output, min_time is expressed in
3920 * global time and runs over the edit list segments. */
3921 min_time = G_MAXUINT64;
3923 for (i = 0; i < qtdemux->n_streams; i++) {
3926 stream = qtdemux->streams[i];
3927 position = stream->time_position;
3929 /* position of -1 is EOS */
3930 if (position != -1 && position < min_time) {
3931 min_time = position;
3936 if (G_UNLIKELY (index == -1)) {
3937 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
3941 /* check for segment end */
3942 if (G_UNLIKELY (qtdemux->segment.stop != -1
3943 && qtdemux->segment.stop < min_time)) {
3944 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
3948 stream = qtdemux->streams[index];
3950 /* fetch info for the current sample of this stream */
3951 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
3952 &size, ×tamp, &duration, &keyframe)))
3955 GST_LOG_OBJECT (qtdemux,
3956 "pushing from stream %d, offset %" G_GUINT64_FORMAT
3957 ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
3958 index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
3960 /* hmm, empty sample, skip and move to next sample */
3961 if (G_UNLIKELY (size <= 0))
3964 /* last pushed sample was out of boundary, goto next sample */
3965 if (G_UNLIKELY (stream->last_ret == GST_FLOW_UNEXPECTED))
3968 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
3971 ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf);
3972 if (G_UNLIKELY (ret != GST_FLOW_OK))
3975 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
3976 timestamp, duration, keyframe, min_time, offset);
3979 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
3980 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
3981 * we have no more data for the pad to push */
3982 if (ret == GST_FLOW_UNEXPECTED)
3986 gst_qtdemux_advance_sample (qtdemux, stream);
3994 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
3995 ret = GST_FLOW_UNEXPECTED;
4000 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
4001 /* EOS will be raised if all are EOS */
4008 gst_qtdemux_loop (GstPad * pad)
4010 GstQTDemux *qtdemux;
4014 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
4016 cur_offset = qtdemux->offset;
4017 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
4018 cur_offset, qtdemux->state);
4020 switch (qtdemux->state) {
4021 case QTDEMUX_STATE_INITIAL:
4022 case QTDEMUX_STATE_HEADER:
4023 ret = gst_qtdemux_loop_state_header (qtdemux);
4025 case QTDEMUX_STATE_MOVIE:
4026 ret = gst_qtdemux_loop_state_movie (qtdemux);
4027 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_UNEXPECTED) {
4028 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
4036 /* if something went wrong, pause */
4037 if (ret != GST_FLOW_OK)
4041 gst_object_unref (qtdemux);
4047 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4048 (NULL), ("streaming stopped, invalid state"));
4049 qtdemux->segment_running = FALSE;
4050 gst_pad_pause_task (pad);
4051 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4056 const gchar *reason = gst_flow_get_name (ret);
4058 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
4060 qtdemux->segment_running = FALSE;
4061 gst_pad_pause_task (pad);
4063 /* fatal errors need special actions */
4065 if (ret == GST_FLOW_UNEXPECTED) {
4066 if (qtdemux->n_streams == 0) {
4067 /* we have no streams, post an error */
4068 gst_qtdemux_post_no_playable_stream_error (qtdemux);
4070 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4073 /* FIXME: I am not sure this is the right fix. If the sinks are
4074 * supposed to detect the segment is complete and accumulate
4075 * automatically, it does not seem to work here. Need more work */
4076 qtdemux->segment_running = TRUE;
4078 if ((stop = qtdemux->segment.stop) == -1)
4079 stop = qtdemux->segment.duration;
4081 if (qtdemux->segment.rate >= 0) {
4082 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
4083 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4084 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4085 GST_FORMAT_TIME, stop));
4087 /* For Reverse Playback */
4088 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
4089 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
4090 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
4091 GST_FORMAT_TIME, qtdemux->segment.start));
4094 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
4095 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4097 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
4098 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
4099 (NULL), ("streaming stopped, reason %s", reason));
4100 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
4109 * Returns the size of the first entry at the current offset.
4110 * If -1, there are none (which means EOS or empty file).
4113 next_entry_size (GstQTDemux * demux)
4115 QtDemuxStream *stream;
4118 guint64 smalloffs = (guint64) - 1;
4119 QtDemuxSample *sample;
4121 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
4124 for (i = 0; i < demux->n_streams; i++) {
4125 stream = demux->streams[i];
4127 if (stream->sample_index == -1)
4128 stream->sample_index = 0;
4130 if (stream->sample_index >= stream->n_samples) {
4131 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i);
4135 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
4136 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
4137 stream->sample_index);
4141 sample = &stream->samples[stream->sample_index];
4143 GST_LOG_OBJECT (demux,
4144 "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4145 " / size:%" G_GUINT32_FORMAT ")", i, stream->sample_index,
4146 sample->offset, sample->size);
4148 if (((smalloffs == -1)
4149 || (sample->offset < smalloffs)) && (sample->size)) {
4151 smalloffs = sample->offset;
4155 GST_LOG_OBJECT (demux,
4156 "stream %d offset %" G_GUINT64_FORMAT " demux->offset :%"
4157 G_GUINT64_FORMAT, smallidx, smalloffs, demux->offset);
4162 stream = demux->streams[smallidx];
4163 sample = &stream->samples[stream->sample_index];
4165 if (sample->offset >= demux->offset) {
4166 demux->todrop = sample->offset - demux->offset;
4167 return sample->size + demux->todrop;
4170 GST_DEBUG_OBJECT (demux,
4171 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
4176 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
4178 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
4180 gst_element_post_message (GST_ELEMENT_CAST (demux),
4181 gst_message_new_element (GST_OBJECT_CAST (demux),
4182 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
4186 qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
4191 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
4194 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
4195 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
4196 GST_SEEK_TYPE_NONE, -1);
4198 res = gst_pad_push_event (demux->sinkpad, event);
4203 /* check for seekable upstream, above and beyond a mere query */
4205 gst_qtdemux_check_seekability (GstQTDemux * demux)
4208 gboolean seekable = FALSE;
4209 gint64 start = -1, stop = -1;
4211 if (demux->upstream_size)
4214 query = gst_query_new_seeking (GST_FORMAT_BYTES);
4215 if (!gst_pad_peer_query (demux->sinkpad, query)) {
4216 GST_DEBUG_OBJECT (demux, "seeking query failed");
4220 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
4222 /* try harder to query upstream size if we didn't get it the first time */
4223 if (seekable && stop == -1) {
4224 GstFormat fmt = GST_FORMAT_BYTES;
4226 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
4227 gst_pad_query_peer_duration (demux->sinkpad, &fmt, &stop);
4230 /* if upstream doesn't know the size, it's likely that it's not seekable in
4231 * practice even if it technically may be seekable */
4232 if (seekable && (start != 0 || stop <= start)) {
4233 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
4238 gst_query_unref (query);
4240 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
4241 G_GUINT64_FORMAT ")", seekable, start, stop);
4242 demux->upstream_seekable = seekable;
4243 demux->upstream_size = seekable ? stop : -1;
4246 /* FIXME, unverified after edit list updates */
4247 static GstFlowReturn
4248 gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
4251 GstFlowReturn ret = GST_FLOW_OK;
4253 demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad));
4255 #ifdef QTDEMUX_MODIFICATION
4256 /* Added consideration of demuxer state */
4257 if (demux->file && demux->state == QTDEMUX_STATE_MOVIE) {
4258 if (fwrite (GST_BUFFER_DATA (inbuf), GST_BUFFER_SIZE (inbuf), 1, demux->file) != 1) {
4259 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4260 (_("Could not write to temporary buffer file.")),
4261 ("File Write Failure: '%s'",
4263 return GST_FLOW_ERROR;
4265 demux->filesize += GST_BUFFER_SIZE (inbuf);
4267 gst_buffer_unref (inbuf);
4269 gst_adapter_push (demux->adapter, inbuf);
4272 gst_adapter_push (demux->adapter, inbuf);
4275 /* we never really mean to buffer that much */
4276 if (demux->neededbytes == -1)
4279 GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
4280 inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
4282 #ifdef QTDEMUX_MODIFICATION
4283 /* Added consideration of demuxer state and file size*/
4284 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes
4285 || (demux->file && demux->state == QTDEMUX_STATE_MOVIE && demux->filesize >= demux->neededbytes))
4286 && ret == GST_FLOW_OK) {
4288 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
4289 (ret == GST_FLOW_OK)) {
4292 GST_DEBUG_OBJECT (demux,
4293 "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
4294 demux->state, demux->neededbytes, demux->offset);
4296 switch (demux->state) {
4297 case QTDEMUX_STATE_INITIAL:{
4302 gst_qtdemux_check_seekability (demux);
4304 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4306 /* get fourcc/length, set neededbytes */
4307 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
4309 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
4310 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
4312 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4313 (_("This file is invalid and cannot be played.")),
4314 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
4315 GST_FOURCC_ARGS (fourcc)));
4316 ret = GST_FLOW_ERROR;
4319 if (fourcc == FOURCC_mdat) {
4320 if (demux->n_streams > 0) {
4321 /* we have the headers, start playback */
4322 demux->state = QTDEMUX_STATE_MOVIE;
4323 demux->neededbytes = next_entry_size (demux);
4324 demux->mdatleft = size;
4326 /* Only post, event on pads is done after newsegment */
4327 qtdemux_post_global_tags (demux);
4330 /* no headers yet, try to get them */
4333 guint64 old, target;
4336 old = demux->offset;
4337 target = old + size;
4339 /* try to jump over the atom with a seek */
4340 /* only bother if it seems worth doing so,
4341 * and avoids possible upstream/server problems */
4342 if (demux->upstream_seekable &&
4343 demux->upstream_size > 4 * (1 << 20)) {
4344 res = qtdemux_seek_offset (demux, target);
4346 GST_DEBUG_OBJECT (demux, "skipping seek");
4351 GST_DEBUG_OBJECT (demux, "seek success");
4352 /* remember the offset fo the first mdat so we can seek back to it
4353 * after we have the headers */
4354 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
4355 demux->first_mdat = old;
4356 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
4359 /* seek worked, continue reading */
4360 demux->offset = target;
4361 demux->neededbytes = 16;
4362 demux->state = QTDEMUX_STATE_INITIAL;
4364 /* seek failed, need to buffer */
4365 demux->offset = old;
4366 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
4367 /* there may be multiple mdat (or alike) buffers */
4369 if (demux->mdatbuffer)
4370 bs = GST_BUFFER_SIZE (demux->mdatbuffer);
4371 #ifdef QTDEMUX_MODIFICATION
4372 else if (demux->file)
4373 bs = demux->filesize;
4377 #ifdef QTDEMUX_MODIFICATION
4378 if (size + bs > demux->maxbuffersize)
4380 if (size + bs > 10 * (1 << 20))
4383 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
4384 demux->neededbytes = size;
4385 #ifdef QTDEMUX_MODIFICATION
4386 if ((demux->filename && demux->file == NULL) || !demux->mdatbuffer)
4388 if (!demux->mdatbuffer)
4390 demux->mdatoffset = demux->offset;
4393 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
4394 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4395 (_("This file is invalid and cannot be played.")),
4396 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
4397 GST_FOURCC_ARGS (fourcc), size));
4398 ret = GST_FLOW_ERROR;
4401 /* this means we already started buffering and still no moov header,
4402 * let's continue buffering everything till we get moov */
4403 #ifdef QTDEMUX_MODIFICATION
4404 if ((demux->mdatbuffer || demux->file) && (fourcc != FOURCC_moov))
4406 if (demux->mdatbuffer && (fourcc != FOURCC_moov))
4409 demux->neededbytes = size;
4410 demux->state = QTDEMUX_STATE_HEADER;
4414 case QTDEMUX_STATE_HEADER:{
4418 GST_DEBUG_OBJECT (demux, "In header");
4420 data = gst_adapter_peek (demux->adapter, demux->neededbytes);
4422 /* parse the header */
4423 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
4425 if (fourcc == FOURCC_moov) {
4426 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
4428 demux->got_moov = TRUE;
4430 /* prepare newsegment to send when streaming actually starts */
4431 if (!demux->pending_newsegment) {
4432 demux->pending_newsegment =
4433 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
4434 0, GST_CLOCK_TIME_NONE, 0);
4437 qtdemux_parse_moov (demux, data, demux->neededbytes);
4438 qtdemux_node_dump (demux, demux->moov_node);
4439 qtdemux_parse_tree (demux);
4440 qtdemux_expose_streams (demux);
4442 g_node_destroy (demux->moov_node);
4443 demux->moov_node = NULL;
4444 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
4445 } else if (fourcc == FOURCC_moof) {
4446 if (demux->got_moov && demux->fragmented) {
4447 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
4448 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
4449 demux->offset, NULL)) {
4450 ret = GST_FLOW_ERROR;
4454 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
4456 } else if (fourcc == FOURCC_ftyp) {
4457 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
4458 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
4459 } else if (fourcc == FOURCC_uuid) {
4460 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
4461 qtdemux_parse_uuid (demux, data, demux->neededbytes);
4463 GST_WARNING_OBJECT (demux,
4464 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
4465 GST_FOURCC_ARGS (fourcc));
4466 /* Let's jump that one and go back to initial state */
4469 #ifdef QTDEMUX_MODIFICATION
4470 if ((demux->mdatbuffer || demux->file) && demux->n_streams) {
4473 if (demux->mdatbuffer && demux->n_streams) {
4476 /* the mdat was before the header */
4477 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
4478 demux->n_streams, demux->mdatbuffer);
4479 /* restore our adapter/offset view of things with upstream;
4480 * put preceding buffered data ahead of current moov data.
4481 * This should also handle evil mdat, moov, mdat cases and alike */
4482 #ifdef QTDEMUX_MODIFICATION
4483 if (demux->file == NULL) {
4484 // TODO: need to know what we need to do with buf, it is not freed
4485 //buf = gst_adapter_take_buffer (demux->adapter, gst_adapter_available (demux->adapter));
4487 gst_adapter_clear (demux->adapter);
4488 gst_adapter_push (demux->adapter, demux->mdatbuffer); /* need to check */
4489 demux->mdatbuffer = NULL;
4490 #ifdef QTDEMUX_MODIFICATION
4492 buf = gst_adapter_take_buffer (demux->adapter, gst_adapter_available (demux->adapter));
4493 gst_adapter_clear (demux->adapter);
4494 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, demux->file) != 1) {
4495 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4496 (_("Could not write to temporary buffer file.")),
4497 ("File Write Failure: '%s'", demux->filename));
4498 ret = GST_FLOW_ERROR;
4501 demux->filesize += GST_BUFFER_SIZE (buf);
4503 gst_buffer_unref (buf);
4506 demux->offset = demux->mdatoffset;
4507 demux->neededbytes = next_entry_size (demux);
4508 demux->state = QTDEMUX_STATE_MOVIE;
4509 demux->mdatleft = gst_adapter_available (demux->adapter);
4511 /* Only post, event on pads is done after newsegment */
4512 qtdemux_post_global_tags (demux);
4514 #ifdef QTDEMUX_MODIFICATION
4516 demux->ofile = fopen (demux->filename, "rb");
4517 if (demux->ofile == NULL) {
4518 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4519 (_("Could not open temporary buffer file.")),
4520 ("File Open Failure %d: '%s'", __LINE__, demux->filename));
4521 ret = GST_FLOW_ERROR;
4527 GST_DEBUG_OBJECT (demux, "Carrying on normally");
4528 gst_adapter_flush (demux->adapter, demux->neededbytes);
4530 if (demux->got_moov && demux->first_mdat != -1) {
4533 /* we need to seek back */
4534 res = qtdemux_seek_offset (demux, demux->first_mdat);
4536 demux->offset = demux->first_mdat;
4538 GST_DEBUG_OBJECT (demux, "Seek back failed");
4541 demux->offset += demux->neededbytes;
4543 demux->neededbytes = 16;
4544 demux->state = QTDEMUX_STATE_INITIAL;
4549 case QTDEMUX_STATE_BUFFER_MDAT:{
4552 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
4554 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4555 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
4556 GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4)));
4558 #ifdef QTDEMUX_MODIFICATION
4559 if (demux->filename == NULL) {
4561 if (demux->mdatbuffer)
4562 demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
4564 demux->mdatbuffer = buf;
4565 #ifdef QTDEMUX_MODIFICATION
4569 if (fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, demux->file) != 1) {
4570 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4571 (_("Could not write to temporary buffer file.")),
4572 ("File Write Failure: '%s'", demux->filename));
4573 ret = GST_FLOW_ERROR;
4576 demux->filesize += GST_BUFFER_SIZE (buf);
4578 gst_buffer_unref (buf);
4580 demux->file = fopen (demux->filename, "wb");
4581 if (demux->file == NULL) {
4582 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4583 (_("Could not create temporary buffer file.")),
4584 ("File Open Failure: '%s'", demux->filename));
4585 ret = GST_FLOW_ERROR;
4592 demux->offset += demux->neededbytes;
4593 demux->neededbytes = 16;
4594 demux->state = QTDEMUX_STATE_INITIAL;
4595 gst_qtdemux_post_progress (demux, 1, 1);
4599 case QTDEMUX_STATE_MOVIE:{
4601 QtDemuxStream *stream = NULL;
4602 QtDemuxSample *sample;
4604 guint64 timestamp, duration, position;
4607 GST_DEBUG_OBJECT (demux,
4608 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
4610 if (demux->fragmented) {
4611 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
4613 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
4614 /* if needed data starts within this atom,
4615 * then it should not exceed this atom */
4616 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
4617 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
4618 (_("This file is invalid and cannot be played.")),
4619 ("sample data crosses atom boundary"));
4620 ret = GST_FLOW_ERROR;
4623 demux->mdatleft -= demux->neededbytes;
4625 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
4626 /* so we are dropping more than left in this atom */
4627 demux->todrop -= demux->mdatleft;
4628 demux->neededbytes -= demux->mdatleft;
4629 demux->mdatleft = 0;
4630 /* need to resume atom parsing so we do not miss any other pieces */
4631 demux->state = QTDEMUX_STATE_INITIAL;
4632 demux->neededbytes = 16;
4637 if (demux->todrop) {
4638 GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);
4639 #ifdef QTDEMUX_MODIFICATION
4640 if (demux->file == NULL)
4642 gst_adapter_flush (demux->adapter, demux->todrop);
4643 #ifdef QTDEMUX_MODIFICATION
4645 if(!fseek (demux->ofile, (long) demux->todrop, SEEK_CUR))
4646 demux->filesize -= demux->todrop;
4649 demux->neededbytes -= demux->todrop;
4650 demux->offset += demux->todrop;
4654 /* initial newsegment sent here after having added pads,
4655 * possible others in sink_event */
4656 if (G_UNLIKELY (demux->pending_newsegment)) {
4657 gst_qtdemux_push_event (demux, demux->pending_newsegment);
4658 demux->pending_newsegment = NULL;
4659 /* clear to send tags on all streams */
4660 for (i = 0; i < demux->n_streams; i++) {
4661 gst_qtdemux_push_tags (demux, demux->streams[i]);
4665 /* Figure out which stream this is packet belongs to */
4666 for (i = 0; i < demux->n_streams; i++) {
4667 stream = demux->streams[i];
4668 if (stream->sample_index >= stream->n_samples)
4670 GST_LOG_OBJECT (demux,
4671 "Checking stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
4672 " / size:%d)", i, stream->sample_index,
4673 stream->samples[stream->sample_index].offset,
4674 stream->samples[stream->sample_index].size);
4676 if (stream->samples[stream->sample_index].offset == demux->offset)
4680 if (G_UNLIKELY (stream == NULL || i == demux->n_streams))
4681 goto unknown_stream;
4683 /* Put data in a buffer, set timestamps, caps, ... */
4684 #ifdef QTDEMUX_MODIFICATION
4685 if (demux->file == NULL) {
4687 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
4688 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
4689 GST_FOURCC_ARGS (stream->fourcc));
4690 #ifdef QTDEMUX_MODIFICATION
4692 outbuf = gst_buffer_new_and_alloc (demux->neededbytes);
4693 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4694 if (fread (GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf), 1, demux->ofile) != 1) {
4695 GST_ELEMENT_ERROR (demux, STREAM, DECODE,
4696 (_("Could not read from temporary buffer file.")),
4697 ("File Read Failure: '%s'", demux->filename));
4698 ret = GST_FLOW_ERROR;
4701 demux->filesize -= demux->neededbytes;
4704 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
4706 sample = &stream->samples[stream->sample_index];
4708 position = QTSAMPLE_DTS (stream, sample);
4709 timestamp = QTSAMPLE_PTS (stream, sample);
4710 duration = QTSAMPLE_DUR_DTS (stream, sample, position);
4711 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
4713 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
4714 timestamp, duration, keyframe, position, demux->offset);
4717 ret = gst_qtdemux_combine_flows (demux, stream, ret);
4719 stream->sample_index++;
4721 /* update current offset and figure out size of next buffer */
4722 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
4723 demux->offset, demux->neededbytes);
4724 demux->offset += demux->neededbytes;
4725 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
4728 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
4729 if (demux->fragmented) {
4730 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
4731 /* there may be more to follow, only finish this atom */
4732 demux->todrop = demux->mdatleft;
4733 demux->neededbytes = demux->todrop;
4745 /* when buffering movie data, at least show user something is happening */
4746 #ifdef QTDEMUX_MODIFICATION
4747 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT) {
4750 available = demux->filesize;
4752 available = gst_adapter_available (demux->adapter);
4754 if (available <= demux->neededbytes) {
4755 gst_qtdemux_post_progress (demux, available, demux->neededbytes);
4759 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
4760 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
4761 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
4762 demux->neededbytes);
4766 gst_object_unref (demux);
4773 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
4774 ret = GST_FLOW_ERROR;
4779 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
4780 ret = GST_FLOW_UNEXPECTED;
4785 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
4786 (NULL), ("qtdemuxer invalid state %d", demux->state));
4787 ret = GST_FLOW_ERROR;
4792 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
4793 (NULL), ("no 'moov' atom within the first 10 MB"));
4794 ret = GST_FLOW_ERROR;
4800 qtdemux_sink_activate (GstPad * sinkpad)
4802 if (gst_pad_check_pull_range (sinkpad))
4803 return gst_pad_activate_pull (sinkpad, TRUE);
4805 return gst_pad_activate_push (sinkpad, TRUE);
4809 qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4811 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
4814 demux->pullbased = TRUE;
4815 demux->segment_running = TRUE;
4816 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
4819 demux->segment_running = FALSE;
4820 return gst_pad_stop_task (sinkpad);
4825 qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
4827 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
4829 demux->pullbased = FALSE;
4836 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size)
4838 return g_malloc (items * size);
4842 qtdemux_zfree (void *opaque, void *addr)
4848 qtdemux_inflate (void *z_buffer, guint z_length, guint length)
4854 z = g_new0 (z_stream, 1);
4855 z->zalloc = qtdemux_zalloc;
4856 z->zfree = qtdemux_zfree;
4859 z->next_in = z_buffer;
4860 z->avail_in = z_length;
4862 buffer = (guint8 *) g_malloc (length);
4863 ret = inflateInit (z);
4864 while (z->avail_in > 0) {
4865 if (z->avail_out == 0) {
4867 buffer = (guint8 *) g_realloc (buffer, length);
4868 z->next_out = buffer + z->total_out;
4869 z->avail_out = 1024;
4871 ret = inflate (z, Z_SYNC_FLUSH);
4875 if (ret != Z_STREAM_END) {
4876 g_warning ("inflate() returned %d", ret);
4882 #endif /* HAVE_ZLIB */
4885 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
4887 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
4889 /* counts as header data */
4890 qtdemux->header_size += length;
4892 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
4893 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
4898 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
4901 while (G_UNLIKELY (buf < end)) {
4905 if (G_UNLIKELY (buf + 4 > end)) {
4906 GST_LOG_OBJECT (qtdemux, "buffer overrun");
4909 len = QT_UINT32 (buf);
4910 if (G_UNLIKELY (len == 0)) {
4911 GST_LOG_OBJECT (qtdemux, "empty container");
4914 if (G_UNLIKELY (len < 8)) {
4915 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
4918 if (G_UNLIKELY (len > (end - buf))) {
4919 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
4920 (gint) (end - buf));
4924 child = g_node_new ((guint8 *) buf);
4925 g_node_append (node, child);
4926 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
4927 qtdemux_parse_node (qtdemux, child, buf, len);
4935 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
4938 int len = QT_UINT32 (xdxt->data);
4939 guint8 *buf = xdxt->data;
4940 guint8 *end = buf + len;
4943 /* skip size and type */
4951 size = QT_UINT32 (buf);
4952 type = QT_FOURCC (buf + 4);
4954 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
4956 if (buf + size > end || size <= 0)
4962 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
4963 GST_FOURCC_ARGS (type));
4967 buffer = gst_buffer_new_and_alloc (size);
4968 memcpy (GST_BUFFER_DATA (buffer), buf, size);
4969 stream->buffers = g_slist_append (stream->buffers, buffer);
4970 GST_LOG_OBJECT (qtdemux, "parsing theora header");
4973 buffer = gst_buffer_new_and_alloc (size);
4974 memcpy (GST_BUFFER_DATA (buffer), buf, size);
4975 stream->buffers = g_slist_append (stream->buffers, buffer);
4976 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
4979 buffer = gst_buffer_new_and_alloc (size);
4980 memcpy (GST_BUFFER_DATA (buffer), buf, size);
4981 stream->buffers = g_slist_append (stream->buffers, buffer);
4982 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
4985 GST_WARNING_OBJECT (qtdemux,
4986 "unknown theora cookie %" GST_FOURCC_FORMAT,
4987 GST_FOURCC_ARGS (type));
4996 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
5000 guint32 node_length = 0;
5001 const QtNodeType *type;
5004 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
5006 if (G_UNLIKELY (length < 8))
5007 goto not_enough_data;
5009 node_length = QT_UINT32 (buffer);
5010 fourcc = QT_FOURCC (buffer + 4);
5012 /* ignore empty nodes */
5013 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
5016 type = qtdemux_type_get (fourcc);
5018 end = buffer + length;
5020 GST_LOG_OBJECT (qtdemux,
5021 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
5022 GST_FOURCC_ARGS (fourcc), node_length, type->name);
5024 if (node_length > length)
5025 goto broken_atom_size;
5027 if (type->flags & QT_FLAG_CONTAINER) {
5028 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
5033 if (node_length < 20) {
5034 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
5037 GST_DEBUG_OBJECT (qtdemux,
5038 "parsing stsd (sample table, sample description) atom");
5039 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
5049 /* also read alac (or whatever) in stead of mp4a in the following,
5050 * since a similar layout is used in other cases as well */
5051 if (fourcc == FOURCC_mp4a)
5056 /* There are two things we might encounter here: a true mp4a atom, and
5057 an mp4a entry in an stsd atom. The latter is what we're interested
5058 in, and it looks like an atom, but isn't really one. The true mp4a
5059 atom is short, so we detect it based on length here. */
5060 if (length < min_size) {
5061 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
5062 GST_FOURCC_ARGS (fourcc));
5066 /* 'version' here is the sound sample description version. Types 0 and
5067 1 are documented in the QTFF reference, but type 2 is not: it's
5068 described in Apple header files instead (struct SoundDescriptionV2
5070 version = QT_UINT16 (buffer + 16);
5072 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
5073 GST_FOURCC_ARGS (fourcc), version);
5075 /* parse any esds descriptors */
5087 GST_WARNING_OBJECT (qtdemux,
5088 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
5089 GST_FOURCC_ARGS (fourcc), version);
5094 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5106 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
5107 GST_FOURCC_ARGS (fourcc));
5108 version = QT_UINT32 (buffer + 16);
5109 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
5110 if (1 || version == 0x00000000) {
5111 buf = buffer + 0x32;
5113 /* FIXME Quicktime uses PASCAL string while
5114 * the iso format uses C strings. Check the file
5115 * type before attempting to parse the string here. */
5116 tlen = QT_UINT8 (buf);
5117 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen);
5119 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf);
5120 /* the string has a reserved space of 32 bytes so skip
5121 * the remaining 31 */
5123 buf += 4; /* and 4 bytes reserved */
5125 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf);
5127 qtdemux_parse_container (qtdemux, node, buf, end);
5133 GST_MEMDUMP_OBJECT (qtdemux, "avc1", buffer, end - buffer);
5134 qtdemux_parse_container (qtdemux, node, buffer + 0x56, end);
5139 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
5144 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
5145 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
5153 version = QT_UINT32 (buffer + 12);
5154 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
5161 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
5166 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
5171 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
5175 if (!strcmp (type->name, "unknown"))
5176 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
5180 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
5181 GST_FOURCC_ARGS (fourcc));
5187 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5188 (_("This file is corrupt and cannot be played.")),
5189 ("Not enough data for an atom header, got only %u bytes", length));
5194 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5195 (_("This file is corrupt and cannot be played.")),
5196 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
5197 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
5204 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
5208 guint32 child_fourcc;
5210 for (child = g_node_first_child (node); child;
5211 child = g_node_next_sibling (child)) {
5212 buffer = (guint8 *) child->data;
5214 child_fourcc = QT_FOURCC (buffer + 4);
5216 if (G_UNLIKELY (child_fourcc == fourcc)) {
5224 qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
5225 GstByteReader * parser)
5229 guint32 child_fourcc, child_len;
5231 for (child = g_node_first_child (node); child;
5232 child = g_node_next_sibling (child)) {
5233 buffer = (guint8 *) child->data;
5235 child_len = QT_UINT32 (buffer);
5236 child_fourcc = QT_FOURCC (buffer + 4);
5238 if (G_UNLIKELY (child_fourcc == fourcc)) {
5239 if (G_UNLIKELY (child_len < (4 + 4)))
5241 /* FIXME: must verify if atom length < parent atom length */
5242 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5250 qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
5251 GstByteReader * parser)
5255 guint32 child_fourcc, child_len;
5257 for (child = g_node_next_sibling (node); child;
5258 child = g_node_next_sibling (child)) {
5259 buffer = (guint8 *) child->data;
5261 child_fourcc = QT_FOURCC (buffer + 4);
5263 if (child_fourcc == fourcc) {
5265 child_len = QT_UINT32 (buffer);
5266 if (G_UNLIKELY (child_len < (4 + 4)))
5268 /* FIXME: must verify if atom length < parent atom length */
5269 gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
5278 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
5280 return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
5284 gst_qtdemux_add_stream (GstQTDemux * qtdemux,
5285 QtDemuxStream * stream, GstTagList * list)
5287 /* consistent default for push based mode */
5288 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
5289 gst_segment_set_newsegment (&stream->segment, FALSE, 1.0, GST_FORMAT_TIME,
5290 0, GST_CLOCK_TIME_NONE, 0);
5292 if (stream->subtype == FOURCC_vide) {
5293 gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
5296 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
5299 /* fps is calculated base on the duration of the first frames since
5300 * qt does not have a fixed framerate. */
5301 if ((stream->n_samples == 1) && (stream->min_duration == 0)) {
5306 stream->fps_n = stream->timescale;
5307 if (stream->min_duration == 0)
5310 stream->fps_d = stream->min_duration;
5315 gint depth, palette_count;
5316 const guint32 *palette_data = NULL;
5318 gst_caps_set_simple (stream->caps,
5319 "width", G_TYPE_INT, stream->width,
5320 "height", G_TYPE_INT, stream->height,
5321 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
5323 /* calculate pixel-aspect-ratio using display width and height */
5324 GST_DEBUG_OBJECT (qtdemux,
5325 "video size %dx%d, target display size %dx%d", stream->width,
5326 stream->height, stream->display_width, stream->display_height);
5328 if (stream->display_width > 0 && stream->display_height > 0 &&
5329 stream->width > 0 && stream->height > 0) {
5332 /* calculate the pixel aspect ratio using the display and pixel w/h */
5333 n = stream->display_width * stream->height;
5334 d = stream->display_height * stream->width;
5337 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
5338 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5339 GST_TYPE_FRACTION, n, d, NULL);
5342 /* qt file might have pasp atom */
5343 if (stream->par_w > 0 && stream->par_h > 0) {
5344 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
5345 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
5346 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
5349 depth = stream->bits_per_sample;
5351 /* more than 32 bits means grayscale */
5352 gray = (depth > 32);
5353 /* low 32 bits specify the depth */
5356 /* different number of palette entries is determined by depth. */
5358 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
5359 palette_count = (1 << depth);
5361 switch (palette_count) {
5365 palette_data = ff_qt_default_palette_2;
5368 palette_data = ff_qt_default_palette_4;
5372 palette_data = ff_qt_grayscale_palette_16;
5374 palette_data = ff_qt_default_palette_16;
5378 palette_data = ff_qt_grayscale_palette_256;
5380 palette_data = ff_qt_default_palette_256;
5383 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
5384 (_("The video in this file might not play correctly.")),
5385 ("unsupported palette depth %d", depth));
5391 /* make sure it's not writable. We leave MALLOCDATA to NULL so that we
5392 * don't free any of the buffer data. */
5393 palette = gst_buffer_new ();
5394 GST_BUFFER_FLAG_SET (palette, GST_BUFFER_FLAG_READONLY);
5395 GST_BUFFER_DATA (palette) = (guint8 *) palette_data;
5396 GST_BUFFER_SIZE (palette) = sizeof (guint32) * palette_count;
5398 gst_caps_set_simple (stream->caps, "palette_data",
5399 GST_TYPE_BUFFER, palette, NULL);
5400 gst_buffer_unref (palette);
5401 } else if (palette_count != 0) {
5402 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
5403 (NULL), ("Unsupported palette depth %d. Ignoring stream.", depth));
5405 gst_object_unref (stream->pad);
5409 qtdemux->n_video_streams++;
5410 } else if (stream->subtype == FOURCC_soun) {
5411 gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
5414 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
5417 gst_caps_set_simple (stream->caps,
5418 "rate", G_TYPE_INT, (int) stream->rate,
5419 "channels", G_TYPE_INT, stream->n_channels, NULL);
5421 qtdemux->n_audio_streams++;
5422 } else if (stream->subtype == FOURCC_strm) {
5423 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
5424 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
5425 gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams);
5428 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
5430 qtdemux->n_sub_streams++;
5432 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5437 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
5439 gst_pad_use_fixed_caps (stream->pad);
5440 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
5441 gst_pad_set_query_type_function (stream->pad,
5442 gst_qtdemux_get_src_query_types);
5443 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
5445 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
5446 gst_pad_set_caps (stream->pad, stream->caps);
5448 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
5449 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
5450 gst_pad_set_active (stream->pad, TRUE);
5451 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
5452 if (stream->pending_tags)
5453 gst_tag_list_free (stream->pending_tags);
5454 stream->pending_tags = list;
5456 /* post now, send event on pad later */
5457 GST_DEBUG_OBJECT (qtdemux, "Posting tags %" GST_PTR_FORMAT, list);
5458 gst_element_post_message (GST_ELEMENT (qtdemux),
5459 gst_message_new_tag_full (GST_OBJECT (qtdemux), stream->pad,
5460 gst_tag_list_copy (list)));
5462 /* global tags go on each pad anyway */
5463 stream->send_global_tags = TRUE;
5469 /* find next atom with @fourcc starting at @offset */
5470 static GstFlowReturn
5471 qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
5472 guint64 * length, guint32 fourcc)
5478 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
5479 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
5482 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
5483 if (G_UNLIKELY (ret != GST_FLOW_OK))
5485 if (G_LIKELY (GST_BUFFER_SIZE (buf) != 16)) {
5487 ret = GST_FLOW_UNEXPECTED;
5488 gst_buffer_unref (buf);
5491 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), 16, length,
5493 gst_buffer_unref (buf);
5495 if (G_UNLIKELY (*length == 0)) {
5496 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
5497 ret = GST_FLOW_ERROR;
5501 if (lfourcc == fourcc) {
5502 GST_DEBUG_OBJECT (qtdemux, "found fourcc at offset %" G_GUINT64_FORMAT,
5506 GST_LOG_OBJECT (qtdemux,
5507 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
5508 GST_FOURCC_ARGS (fourcc), *offset);
5517 /* might simply have had last one */
5518 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
5523 /* should only do something in pull mode */
5524 /* call with OBJECT lock */
5525 static GstFlowReturn
5526 qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
5528 guint64 length, offset;
5529 GstBuffer *buf = NULL;
5530 GstFlowReturn ret = GST_FLOW_OK;
5531 GstFlowReturn res = GST_FLOW_OK;
5533 offset = qtdemux->moof_offset;
5534 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
5537 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5538 return GST_FLOW_UNEXPECTED;
5541 /* best not do pull etc with lock held */
5542 GST_OBJECT_UNLOCK (qtdemux);
5544 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5545 if (ret != GST_FLOW_OK)
5548 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
5549 if (G_UNLIKELY (ret != GST_FLOW_OK))
5551 if (!qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf),
5552 GST_BUFFER_SIZE (buf), offset, NULL)) {
5553 gst_buffer_unref (buf);
5558 gst_buffer_unref (buf);
5562 /* look for next moof */
5563 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
5564 if (G_UNLIKELY (ret != GST_FLOW_OK))
5568 GST_OBJECT_LOCK (qtdemux);
5570 qtdemux->moof_offset = offset;
5576 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
5578 res = GST_FLOW_ERROR;
5583 /* maybe upstream temporarily flushing */
5584 if (ret != GST_FLOW_WRONG_STATE) {
5585 GST_DEBUG_OBJECT (qtdemux, "no next moof");
5588 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
5589 /* resume at current position next time */
5596 /* initialise bytereaders for stbl sub-atoms */
5598 qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
5600 stream->stbl_index = -1; /* no samples have yet been parsed */
5602 /* time-to-sample atom */
5603 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
5606 /* copy atom data into a new buffer for later use */
5607 stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);
5609 /* skip version + flags */
5610 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
5611 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
5613 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
5615 /* make sure there's enough data */
5616 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
5617 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
5618 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
5619 stream->n_sample_times);
5620 if (!stream->n_sample_times)
5624 /* sync sample atom */
5625 if ((stream->stss_present =
5626 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
5627 &stream->stss) ? TRUE : FALSE) == TRUE) {
5628 /* copy atom data into a new buffer for later use */
5629 stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
5631 /* skip version + flags */
5632 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
5633 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
5636 if (stream->n_sample_syncs) {
5637 /* make sure there's enough data */
5638 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
5644 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
5647 /* copy atom data into a new buffer for later use */
5648 stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);
5650 /* skip version + flags */
5651 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
5652 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
5655 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
5658 if (!stream->n_samples)
5661 /* sample-to-chunk atom */
5662 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
5665 /* copy atom data into a new buffer for later use */
5666 stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);
5668 /* skip version + flags */
5669 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
5670 !gst_byte_reader_get_uint32_be (&stream->stsc,
5671 &stream->n_samples_per_chunk))
5674 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
5675 stream->n_samples_per_chunk);
5677 /* make sure there's enough data */
5678 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
5684 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
5685 stream->co_size = sizeof (guint32);
5686 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
5688 stream->co_size = sizeof (guint64);
5692 /* copy atom data into a new buffer for later use */
5693 stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);
5695 /* skip version + flags */
5696 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
5699 /* chunks_are_chunks == 0 means treat chunks as samples */
5700 stream->chunks_are_chunks = !stream->sample_size || stream->sampled;
5701 if (stream->chunks_are_chunks) {
5702 /* skip number of entries */
5703 if (!gst_byte_reader_skip (&stream->stco, 4))
5706 /* make sure there are enough data in the stsz atom */
5707 if (!stream->sample_size) {
5708 /* different sizes for each sample */
5709 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
5713 /* treat chunks as samples */
5714 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
5718 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
5719 stream->n_samples, (guint) sizeof (QtDemuxSample),
5720 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
5722 if (stream->n_samples >=
5723 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
5724 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
5725 "be larger than %uMB (broken file?)", stream->n_samples,
5726 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
5730 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
5731 if (!stream->samples) {
5732 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
5738 /* composition time-to-sample */
5739 if ((stream->ctts_present =
5740 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
5741 &stream->ctts) ? TRUE : FALSE) == TRUE) {
5742 /* copy atom data into a new buffer for later use */
5743 stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
5745 /* skip version + flags */
5746 if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
5747 || !gst_byte_reader_get_uint32_be (&stream->ctts,
5748 &stream->n_composition_times))
5751 /* make sure there's enough data */
5752 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
5761 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
5762 (_("This file is corrupt and cannot be played.")), (NULL));
5767 gst_qtdemux_stbl_free (stream);
5768 if (!qtdemux->fragmented) {
5769 /* not quite good */
5770 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
5773 /* may pick up samples elsewhere */
5779 /* collect samples from the next sample to be parsed up to sample @n for @stream
5780 * by reading the info from @stbl
5782 * This code can be executed from both the streaming thread and the seeking
5783 * thread so it takes the object lock to protect itself
5786 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
5789 QtDemuxSample *samples, *first, *cur, *last;
5790 guint32 n_samples_per_chunk;
5793 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
5794 GST_FOURCC_FORMAT ", pad %s", GST_FOURCC_ARGS (stream->fourcc),
5795 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
5797 n_samples = stream->n_samples;
5800 goto out_of_samples;
5802 GST_OBJECT_LOCK (qtdemux);
5803 if (n <= stream->stbl_index)
5804 goto already_parsed;
5806 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
5808 if (!stream->stsz.data) {
5809 /* so we already parsed and passed all the moov samples;
5810 * onto fragmented ones */
5811 g_assert (qtdemux->fragmented);
5815 /* pointer to the sample table */
5816 samples = stream->samples;
5818 /* starts from -1, moves to the next sample index to parse */
5819 stream->stbl_index++;
5821 /* keep track of the first and last sample to fill */
5822 first = &samples[stream->stbl_index];
5825 if (stream->chunks_are_chunks) {
5826 /* set the sample sizes */
5827 if (stream->sample_size == 0) {
5828 /* different sizes for each sample */
5829 for (cur = first; cur <= last; cur++) {
5830 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
5831 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
5832 (guint) (cur - samples), cur->size);
5835 /* samples have the same size */
5836 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
5837 for (cur = first; cur <= last; cur++)
5838 cur->size = stream->sample_size;
5842 n_samples_per_chunk = stream->n_samples_per_chunk;
5845 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
5848 if (stream->stsc_chunk_index >= stream->last_chunk
5849 || stream->stsc_chunk_index < stream->first_chunk) {
5850 stream->first_chunk =
5851 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
5852 stream->samples_per_chunk =
5853 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
5854 gst_byte_reader_skip_unchecked (&stream->stsc, 4);
5856 /* chunk numbers are counted from 1 it seems */
5857 if (G_UNLIKELY (stream->first_chunk == 0))
5860 --stream->first_chunk;
5862 /* the last chunk of each entry is calculated by taking the first chunk
5863 * of the next entry; except if there is no next, where we fake it with
5865 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
5866 stream->last_chunk = G_MAXUINT32;
5868 stream->last_chunk =
5869 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
5870 if (G_UNLIKELY (stream->last_chunk == 0))
5873 --stream->last_chunk;
5876 GST_LOG_OBJECT (qtdemux,
5877 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i,
5878 stream->first_chunk, stream->last_chunk, stream->samples_per_chunk);
5880 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
5883 if (stream->last_chunk != G_MAXUINT32) {
5884 if (!qt_atom_parser_peek_sub (&stream->stco,
5885 stream->first_chunk * stream->co_size,
5886 (stream->last_chunk - stream->first_chunk) * stream->co_size,
5891 stream->co_chunk = stream->stco;
5892 if (!gst_byte_reader_skip (&stream->co_chunk,
5893 stream->first_chunk * stream->co_size))
5897 stream->stsc_chunk_index = stream->first_chunk;
5900 last_chunk = stream->last_chunk;
5902 if (stream->chunks_are_chunks) {
5903 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
5904 guint32 samples_per_chunk;
5905 guint64 chunk_offset;
5907 if (!stream->stsc_sample_index
5908 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
5909 &stream->chunk_offset))
5912 samples_per_chunk = stream->samples_per_chunk;
5913 chunk_offset = stream->chunk_offset;
5915 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
5916 GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %"
5917 G_GUINT64_FORMAT, (guint) (cur - samples), stream->chunk_offset);
5919 cur->offset = chunk_offset;
5920 chunk_offset += cur->size;
5923 if (G_UNLIKELY (cur > last)) {
5925 stream->stsc_sample_index = k + 1;
5926 stream->chunk_offset = chunk_offset;
5927 stream->stsc_chunk_index = j;
5931 stream->stsc_sample_index = 0;
5933 stream->stsc_chunk_index = j;
5935 cur = &samples[stream->stsc_chunk_index];
5937 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
5940 stream->stsc_chunk_index = j;
5945 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
5948 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
5949 "%" G_GUINT64_FORMAT, j, cur->offset);
5951 if (stream->samples_per_frame * stream->bytes_per_frame) {
5953 (stream->samples_per_chunk * stream->n_channels) /
5954 stream->samples_per_frame * stream->bytes_per_frame;
5956 cur->size = stream->samples_per_chunk;
5959 GST_DEBUG_OBJECT (qtdemux,
5960 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
5961 j, GST_TIME_ARGS (gst_util_uint64_scale (stream->stco_sample_index,
5962 GST_SECOND, stream->timescale)), cur->size);
5964 cur->timestamp = stream->stco_sample_index;
5965 cur->duration = stream->samples_per_chunk;
5966 cur->keyframe = TRUE;
5969 stream->stco_sample_index += stream->samples_per_chunk;
5971 stream->stsc_chunk_index = j;
5973 stream->stsc_index++;
5976 if (!stream->chunks_are_chunks)
5980 guint32 n_sample_times;
5982 n_sample_times = stream->n_sample_times;
5985 for (i = stream->stts_index; i < n_sample_times; i++) {
5986 guint32 stts_samples;
5987 gint32 stts_duration;
5990 if (stream->stts_sample_index >= stream->stts_samples
5991 || !stream->stts_sample_index) {
5993 stream->stts_samples =
5994 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
5995 stream->stts_duration =
5996 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
5998 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
5999 i, stream->stts_samples, stream->stts_duration);
6001 stream->stts_sample_index = 0;
6004 stts_samples = stream->stts_samples;
6005 stts_duration = stream->stts_duration;
6006 stts_time = stream->stts_time;
6008 for (j = stream->stts_sample_index; j < stts_samples; j++) {
6009 GST_DEBUG_OBJECT (qtdemux,
6010 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
6011 (guint) (cur - samples), j,
6012 GST_TIME_ARGS (gst_util_uint64_scale (stts_time, GST_SECOND,
6013 stream->timescale)));
6015 cur->timestamp = stts_time;
6016 cur->duration = stts_duration;
6018 /* avoid 32-bit wrap-around,
6019 * but still mind possible 'negative' duration */
6020 stts_time += (gint64) stts_duration;
6023 if (G_UNLIKELY (cur > last)) {
6025 stream->stts_time = stts_time;
6026 stream->stts_sample_index = j + 1;
6030 stream->stts_sample_index = 0;
6031 stream->stts_time = stts_time;
6032 stream->stts_index++;
6034 /* fill up empty timestamps with the last timestamp, this can happen when
6035 * the last samples do not decode and so we don't have timestamps for them.
6036 * We however look at the last timestamp to estimate the track length so we
6037 * need something in here. */
6038 for (; cur < last; cur++) {
6039 GST_DEBUG_OBJECT (qtdemux,
6040 "fill sample %d: timestamp %" GST_TIME_FORMAT,
6041 (guint) (cur - samples),
6042 GST_TIME_ARGS (gst_util_uint64_scale (stream->stts_time, GST_SECOND,
6043 stream->timescale)));
6044 cur->timestamp = stream->stts_time;
6050 /* sample sync, can be NULL */
6051 if (stream->stss_present == TRUE) {
6052 guint32 n_sample_syncs;
6054 n_sample_syncs = stream->n_sample_syncs;
6056 if (!n_sample_syncs) {
6057 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
6058 stream->all_keyframe = TRUE;
6060 for (i = stream->stss_index; i < n_sample_syncs; i++) {
6061 /* note that the first sample is index 1, not 0 */
6064 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
6066 if (G_LIKELY (index > 0 && index <= n_samples)) {
6068 samples[index].keyframe = TRUE;
6069 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
6070 /* and exit if we have enough samples */
6071 if (G_UNLIKELY (index >= n)) {
6078 stream->stss_index = i;
6081 /* no stss, all samples are keyframes */
6082 stream->all_keyframe = TRUE;
6083 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
6088 /* composition time to sample */
6089 if (stream->ctts_present == TRUE) {
6090 guint32 n_composition_times;
6092 gint32 ctts_soffset;
6094 /* Fill in the pts_offsets */
6096 n_composition_times = stream->n_composition_times;
6098 for (i = stream->ctts_index; i < n_composition_times; i++) {
6099 if (stream->ctts_sample_index >= stream->ctts_count
6100 || !stream->ctts_sample_index) {
6101 stream->ctts_count =
6102 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
6103 stream->ctts_soffset =
6104 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
6105 stream->ctts_sample_index = 0;
6108 ctts_count = stream->ctts_count;
6109 ctts_soffset = stream->ctts_soffset;
6111 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
6112 cur->pts_offset = ctts_soffset;
6115 if (G_UNLIKELY (cur > last)) {
6117 stream->ctts_sample_index = j + 1;
6121 stream->ctts_sample_index = 0;
6122 stream->ctts_index++;
6126 stream->stbl_index = n;
6127 /* if index has been completely parsed, free data that is no-longer needed */
6128 if (n + 1 == stream->n_samples) {
6129 gst_qtdemux_stbl_free (stream);
6130 GST_DEBUG_OBJECT (qtdemux,
6131 "parsed all available samples; checking for more");
6132 while (n + 1 == stream->n_samples)
6133 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
6136 GST_OBJECT_UNLOCK (qtdemux);
6143 GST_LOG_OBJECT (qtdemux,
6144 "Tried to parse up to sample %u but this sample has already been parsed",
6146 /* if fragmented, there may be more */
6147 if (qtdemux->fragmented && n == stream->stbl_index)
6149 GST_OBJECT_UNLOCK (qtdemux);
6155 GST_LOG_OBJECT (qtdemux,
6156 "Tried to parse up to sample %u but there are only %u samples", n + 1,
6158 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6159 (_("This file is corrupt and cannot be played.")), (NULL));
6164 GST_OBJECT_UNLOCK (qtdemux);
6165 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
6166 (_("This file is corrupt and cannot be played.")), (NULL));
6171 /* collect all segment info for @stream.
6174 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
6179 /* parse and prepare segment info from the edit list */
6180 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
6181 stream->n_segments = 0;
6182 stream->segments = NULL;
6183 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
6187 guint64 time, stime;
6190 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
6191 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
6194 buffer = elst->data;
6196 n_segments = QT_UINT32 (buffer + 12);
6198 /* we might allocate a bit too much, at least allocate 1 segment */
6199 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
6201 /* segments always start from 0 */
6205 for (i = 0; i < n_segments; i++) {
6208 QtDemuxSegment *segment;
6211 media_time = QT_UINT32 (buffer + 20 + i * 12);
6213 /* -1 media time is an empty segment, just ignore it */
6214 if (media_time == G_MAXUINT32)
6217 duration = QT_UINT32 (buffer + 16 + i * 12);
6219 segment = &stream->segments[count++];
6221 /* time and duration expressed in global timescale */
6222 segment->time = stime;
6223 /* add non scaled values so we don't cause roundoff errors */
6225 stime = gst_util_uint64_scale (time, GST_SECOND, qtdemux->timescale);
6226 segment->stop_time = stime;
6227 segment->duration = stime - segment->time;
6228 /* media_time expressed in stream timescale */
6229 segment->media_start =
6230 gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale);
6231 segment->media_stop = segment->media_start + segment->duration;
6232 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
6234 if (rate_int <= 1) {
6235 /* 0 is not allowed, some programs write 1 instead of the floating point
6237 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
6241 segment->rate = rate_int / 65536.0;
6244 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
6245 ", duration %" GST_TIME_FORMAT ", media_time %" GST_TIME_FORMAT
6246 ", rate %g, (%d)", i, GST_TIME_ARGS (segment->time),
6247 GST_TIME_ARGS (segment->duration),
6248 GST_TIME_ARGS (segment->media_start), segment->rate, rate_int);
6250 GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count);
6251 stream->n_segments = count;
6255 /* push based does not handle segments, so act accordingly here,
6256 * and warn if applicable */
6257 if (!qtdemux->pullbased) {
6258 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
6259 /* remove and use default one below, we stream like it anyway */
6260 g_free (stream->segments);
6261 stream->segments = NULL;
6262 stream->n_segments = 0;
6265 /* no segments, create one to play the complete trak */
6266 if (stream->n_segments == 0) {
6267 GstClockTime stream_duration =
6268 gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale);
6270 if (stream->segments == NULL)
6271 stream->segments = g_new (QtDemuxSegment, 1);
6273 /* represent unknown our way */
6274 if (stream_duration == 0)
6275 stream_duration = -1;
6277 stream->segments[0].time = 0;
6278 stream->segments[0].stop_time = stream_duration;
6279 stream->segments[0].duration = stream_duration;
6280 stream->segments[0].media_start = 0;
6281 stream->segments[0].media_stop = stream_duration;
6282 stream->segments[0].rate = 1.0;
6284 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
6285 GST_TIME_ARGS (stream_duration));
6286 stream->n_segments = 1;
6288 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
6294 * Parses the stsd atom of a svq3 trak looking for
6295 * the SMI and gama atoms.
6298 qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux, GNode * stsd,
6299 guint8 ** gamma, GstBuffer ** seqh)
6301 guint8 *_gamma = NULL;
6302 GstBuffer *_seqh = NULL;
6303 guint8 *stsd_data = stsd->data;
6304 guint32 length = QT_UINT32 (stsd_data);
6308 GST_WARNING_OBJECT (qtdemux, "stsd too short");
6314 version = QT_UINT16 (stsd_data);
6319 while (length > 8) {
6320 guint32 fourcc, size;
6322 size = QT_UINT32 (stsd_data);
6323 fourcc = QT_FOURCC (stsd_data + 4);
6324 data = stsd_data + 8;
6331 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
6332 " for gama atom, expected 12", size);
6337 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
6339 if (_seqh != NULL) {
6340 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
6341 " found, ignoring");
6343 seqh_size = QT_UINT32 (data + 4);
6344 if (seqh_size > 0) {
6345 _seqh = gst_buffer_new_and_alloc (seqh_size);
6346 memcpy (GST_BUFFER_DATA (_seqh), data + 8, seqh_size);
6353 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
6354 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
6358 if (size <= length) {
6364 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
6367 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
6368 G_GUINT16_FORMAT, version);
6379 gst_buffer_unref (_seqh);
6384 qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
6391 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
6392 * atom that might contain a 'data' atom with the rtsp uri.
6393 * This case was reported in bug #597497, some info about
6394 * the hndl atom can be found in TN1195
6396 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
6397 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
6400 guint32 dref_num_entries = 0;
6401 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
6402 gst_byte_reader_skip (&dref, 4) &&
6403 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
6406 /* search dref entries for hndl atom */
6407 for (i = 0; i < dref_num_entries; i++) {
6408 guint32 size = 0, type;
6409 guint8 string_len = 0;
6410 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
6411 qt_atom_parser_get_fourcc (&dref, &type)) {
6412 if (type == FOURCC_hndl) {
6413 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
6415 /* skip data reference handle bytes and the
6416 * following pascal string and some extra 4
6417 * bytes I have no idea what are */
6418 if (!gst_byte_reader_skip (&dref, 4) ||
6419 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
6420 !gst_byte_reader_skip (&dref, string_len + 4)) {
6421 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
6425 /* iterate over the atoms to find the data atom */
6426 while (gst_byte_reader_get_remaining (&dref) >= 8) {
6430 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
6431 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
6432 if (atom_type == FOURCC_data) {
6433 const guint8 *uri_aux = NULL;
6435 /* found the data atom that might contain the rtsp uri */
6436 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
6437 "hndl atom, interpreting it as an URI");
6438 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
6440 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
6441 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
6443 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
6444 "didn't contain a rtsp address");
6446 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
6451 /* skipping to the next entry */
6452 gst_byte_reader_skip (&dref, atom_size - 8);
6454 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
6461 /* skip to the next entry */
6462 gst_byte_reader_skip (&dref, size - 8);
6464 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
6467 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
6474 less_than (gconstpointer a, gconstpointer b)
6476 const guint32 *av = a, *bv = b;
6481 #define AMR_NB_ALL_MODES 0x81ff
6482 #define AMR_WB_ALL_MODES 0x83ff
6484 qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
6486 /* The 'damr' atom is of the form:
6488 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
6489 * 32 b 8 b 16 b 8 b 8 b
6491 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
6492 * represents the highest mode used in the stream (and thus the maximum
6493 * bitrate), with a couple of special cases as seen below.
6496 /* Map of frame type ID -> bitrate */
6497 static const guint nb_bitrates[] = {
6498 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
6500 static const guint wb_bitrates[] = {
6501 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
6503 const guint8 *data = GST_BUFFER_DATA (buf);
6504 guint size = QT_UINT32 (data), max_mode;
6507 if (GST_BUFFER_SIZE (buf) != 0x11) {
6508 GST_DEBUG ("Atom should have size 0x11, not %u", size);
6512 if (QT_FOURCC (data + 4) != GST_MAKE_FOURCC ('d', 'a', 'm', 'r')) {
6513 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
6514 GST_FOURCC_ARGS (QT_UINT32 (data + 4)));
6518 mode_set = QT_UINT16 (data + 13);
6520 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
6521 max_mode = 7 + (wb ? 1 : 0);
6523 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
6524 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
6526 if (max_mode == -1) {
6527 GST_DEBUG ("No mode indication was found (mode set) = %x",
6532 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
6539 * With each track we associate a new QtDemuxStream that contains all the info
6541 * traks that do not decode to something (like strm traks) will not have a pad.
6544 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
6559 QtDemuxStream *stream;
6560 GstTagList *list = NULL;
6561 gchar *codec = NULL;
6562 const guint8 *stsd_data;
6563 guint16 lang_code; /* quicktime lang code or packed iso code */
6565 guint32 tkhd_flags = 0;
6566 guint8 tkhd_version = 0;
6568 guint value_size, len;
6569 #ifdef QTDEMUX_MODIFICATION
6574 gboolean res = FALSE;
6576 guint16 alternate_group;
6578 GstClockTime duration;
6581 stream = g_new0 (QtDemuxStream, 1);
6582 /* new streams always need a discont */
6583 stream->discont = TRUE;
6584 /* we enable clipping for raw audio/video streams */
6585 stream->need_clip = FALSE;
6586 stream->need_process = FALSE;
6587 stream->segment_index = -1;
6588 stream->time_position = 0;
6589 stream->sample_index = -1;
6590 stream->last_ret = GST_FLOW_OK;
6591 #ifdef QTDEMUX_MODIFICATION
6592 stream->trickplay_info = g_new0 (TrickPlayInfo, 1);
6593 stream->trickplay_info->prev_kidx = 0;
6594 stream->trickplay_info->next_kidx = 0;
6595 stream->trickplay_info->kidxs_dur_diff = 0;
6596 stream->orientation = -1;
6598 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
6599 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
6600 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
6603 /* pick between 64 or 32 bits */
6604 value_size = tkhd_version == 1 ? 8 : 4;
6605 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
6606 !gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id))
6609 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
6610 tkhd_version, tkhd_flags, stream->track_id);
6611 #ifdef QTDEMUX_MODIFICATION
6612 if (!gst_byte_reader_skip (&tkhd, 4)) //skipping for reserved bits
6615 //reading duration from the tkhd atom, 64 bits if version is 1 and 32 bits if version is 0
6616 if(tkhd_version == 1) {
6617 if(gst_byte_reader_get_uint64_be (&tkhd, &duration)) {
6618 GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration));
6620 GST_LOG_OBJECT(qtdemux ,"no duration information present");
6624 if(gst_byte_reader_get_uint32_be (&tkhd, &duration)) {
6625 GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration));
6627 GST_LOG_OBJECT(qtdemux, "no duration information present");
6632 if(!gst_byte_reader_skip(&tkhd, 8)) { //skipping for reserved
6633 GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information");
6637 //reading layer, alternate_group and volume information from tkhd atom
6638 if(!gst_byte_reader_get_uint16_be(&tkhd, &layer) ||
6639 !gst_byte_reader_get_uint16_be(&tkhd, &alternate_group) ||
6640 !gst_byte_reader_get_uint16_be(&tkhd, &volume)) {
6641 GST_LOG_OBJECT(qtdemux, "tkhd doesn't have layer, group or volume information");
6644 GST_LOG_OBJECT(qtdemux, "the layer info is %d", layer);
6645 GST_LOG_OBJECT(qtdemux, "the alternate group info is %d", alternate_group);
6646 GST_LOG_OBJECT(qtdemux, "the volume information is %d", volume);
6648 if(!gst_byte_reader_skip(&tkhd, 2)) { //skipping for reserved
6649 GST_LOG_OBJECT(qtdemux, "no information present in tkhd");
6653 //reading the orientation matrix information
6654 if(!gst_byte_reader_get_uint32_be(&tkhd, &a) ||
6655 !gst_byte_reader_get_uint32_be(&tkhd, &b)) {
6656 GST_LOG_OBJECT(qtdemux, "No matrix information present");
6659 GST_LOG_OBJECT(qtdemux, "matrix a and b info is %08x %08x", a, b);
6662 if(!gst_byte_reader_skip(&tkhd, 4)) { //skipping some unrelevant information
6663 GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information");
6667 if(!gst_byte_reader_get_uint32_be(&tkhd, &c) ||
6668 !gst_byte_reader_get_uint32_be(&tkhd, &d)) {
6671 GST_LOG_OBJECT(qtdemux, "c and d info is %08x %08x", c, d);
6674 //Checking the orientation information with parsed matrix information
6675 if(a == 0x00010000 && b == 0x00000000 && c == 0x00000000 && d == 0x00010000)
6676 stream->orientation = 0;
6677 if(a == 0x00000000 && b == 0x00010000 && c == 0xFFFF0000 && d == 0x00000000)
6678 stream->orientation = 90;
6679 if(a == 0xFFFF0000 && b == 0x00000000 && c == 0x00000000 && d == 0xFFFF0000)
6680 stream->orientation = 180;
6681 if(a == 0x00000000 && b == 0xFFFF0000 && c == 0x00010000 && d == 0x00000000)
6682 stream->orientation = 270;
6684 GST_INFO("the orientation is %d", stream->orientation);
6686 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
6689 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
6690 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
6691 if (qtdemux->major_brand != FOURCC_mjp2 ||
6692 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
6696 len = QT_UINT32 ((guint8 *) mdhd->data);
6697 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
6698 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
6699 if (version == 0x01000000) {
6702 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
6703 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
6704 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
6708 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
6709 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
6710 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
6713 if (lang_code < 0x800) {
6714 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
6716 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
6717 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
6718 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
6719 stream->lang_id[3] = 0;
6722 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
6724 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
6726 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
6727 lang_code, stream->lang_id);
6729 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
6732 /* fragmented files may have bogus duration in moov */
6733 if (!qtdemux->fragmented &&
6734 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
6735 guint64 tdur1, tdur2;
6737 /* don't overflow */
6738 tdur1 = stream->timescale * (guint64) qtdemux->duration;
6739 tdur2 = qtdemux->timescale * (guint64) stream->duration;
6742 * some of those trailers, nowadays, have prologue images that are
6743 * themselves vide tracks as well. I haven't really found a way to
6744 * identify those yet, except for just looking at their duration. */
6745 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
6746 GST_WARNING_OBJECT (qtdemux,
6747 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
6748 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
6749 "found, assuming preview image or something; skipping track",
6750 stream->duration, stream->timescale, qtdemux->duration,
6751 qtdemux->timescale);
6752 #ifdef QTDEMUX_MODIFICATION
6753 g_free (stream->trickplay_info);
6760 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
6763 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
6764 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
6766 len = QT_UINT32 ((guint8 *) hdlr->data);
6768 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
6769 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
6770 GST_FOURCC_ARGS (stream->subtype));
6772 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
6775 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
6779 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
6781 stsd_data = (const guint8 *) stsd->data;
6783 /* stsd should at least have one entry */
6784 len = QT_UINT32 (stsd_data);
6788 /* and that entry should fit within stsd */
6789 len = QT_UINT32 (stsd_data + 16);
6790 if (len > QT_UINT32 (stsd_data) + 16)
6792 GST_LOG_OBJECT (qtdemux, "stsd len: %d", len);
6794 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
6795 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
6796 GST_FOURCC_ARGS (stream->fourcc));
6798 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) ||
6799 ((fourcc & 0xFFFFFF00) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)))
6800 goto error_encrypted;
6802 if (stream->subtype == FOURCC_vide) {
6803 guint32 w = 0, h = 0;
6805 stream->sampled = TRUE;
6807 /* version 1 uses some 64-bit ints */
6808 #ifdef QTDEMUX_MODIFICATION
6809 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
6810 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
6812 if (!gst_byte_reader_skip (&tkhd, 56 + value_size)
6813 || !gst_byte_reader_get_uint32_be (&tkhd, &w)
6814 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
6819 stream->display_width = w >> 16;
6820 stream->display_height = h >> 16;
6826 stream->width = QT_UINT16 (stsd_data + offset + 32);
6827 stream->height = QT_UINT16 (stsd_data + offset + 34);
6828 stream->fps_n = 0; /* this is filled in later */
6829 stream->fps_d = 0; /* this is filled in later */
6830 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
6831 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);
6833 GST_LOG_OBJECT (qtdemux, "frame count: %u",
6834 QT_UINT16 (stsd_data + offset + 48));
6837 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
6839 list = gst_tag_list_new ();
6840 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6841 GST_TAG_VIDEO_CODEC, codec, NULL);
6848 #ifdef QTDEMUX_MODIFICATION
6849 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4v);
6851 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_MP4V);
6852 /* H264 is MPEG-4 after all,
6853 * and qt seems to put MPEG-4 stuff in there as well */
6855 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_avc1);
6857 /* pick 'the' stsd child */
6858 mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc);
6861 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
6862 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
6866 const guint8 *pasp_data = (const guint8 *) pasp->data;
6868 stream->par_w = QT_UINT32 (pasp_data + 8);
6869 stream->par_h = QT_UINT32 (pasp_data + 12);
6876 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
6877 #ifdef QTDEMUX_MODIFICATION
6878 /* If the caps modified to H263 from MPEG-4, update the fourcc variable */
6879 fourcc = stream->fourcc;
6885 gint len = QT_UINT32 (stsd_data) - 0x66;
6886 const guint8 *avc_data = stsd_data + 0x66;
6889 while (len >= 0x8) {
6892 if (QT_UINT32 (avc_data) <= len)
6893 size = QT_UINT32 (avc_data) - 0x8;
6898 /* No real data, so break out */
6901 switch (QT_FOURCC (avc_data + 0x4)) {
6904 /* parse, if found */
6907 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
6909 /* First 4 bytes are the length of the atom, the next 4 bytes
6910 * are the fourcc, the next 1 byte is the version, and the
6911 * subsequent bytes are sequence parameter set like data. */
6912 gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
6913 avc_data + 8 + 1, size - 1);
6915 buf = gst_buffer_new_and_alloc (size);
6916 memcpy (GST_BUFFER_DATA (buf), avc_data + 0x8, size);
6917 gst_caps_set_simple (stream->caps,
6918 "codec_data", GST_TYPE_BUFFER, buf, NULL);
6919 gst_buffer_unref (buf);
6925 guint avg_bitrate, max_bitrate;
6927 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
6931 max_bitrate = QT_UINT32 (avc_data + 0xc);
6932 avg_bitrate = QT_UINT32 (avc_data + 0x10);
6934 if (!max_bitrate && !avg_bitrate)
6937 /* Some muxers seem to swap the average and maximum bitrates
6938 * (I'm looking at you, YouTube), so we swap for sanity. */
6939 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
6940 guint temp = avg_bitrate;
6942 avg_bitrate = max_bitrate;
6947 list = gst_tag_list_new ();
6949 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
6950 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6951 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
6953 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
6954 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
6955 GST_TAG_BITRATE, avg_bitrate, NULL);
6966 avc_data += size + 8;
6978 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
6979 GST_FOURCC_ARGS (fourcc));
6981 /* codec data might be in glbl extension atom */
6982 #ifdef QTDEMUX_MODIFICATION
6983 glbl = qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl);
6986 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
6993 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
6995 len = QT_UINT32 (data);
6998 buf = gst_buffer_new_and_alloc (len);
6999 memcpy (GST_BUFFER_DATA (buf), data + 8, len);
7000 gst_caps_set_simple (stream->caps,
7001 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7002 gst_buffer_unref (buf);
7009 /* see annex I of the jpeg2000 spec */
7010 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
7014 guint32 ncomp_map = 0;
7015 gint32 *comp_map = NULL;
7016 guint32 nchan_def = 0;
7017 gint32 *chan_def = NULL;
7019 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
7020 /* some required atoms */
7021 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
7024 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
7028 /* number of components; redundant with info in codestream, but useful
7030 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
7031 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
7033 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
7035 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
7038 GST_DEBUG_OBJECT (qtdemux, "found colr");
7039 /* extract colour space info */
7040 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
7041 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
7043 fourcc = GST_MAKE_FOURCC ('s', 'R', 'G', 'B');
7046 fourcc = GST_MAKE_FOURCC ('G', 'R', 'A', 'Y');
7049 fourcc = GST_MAKE_FOURCC ('s', 'Y', 'U', 'V');
7056 /* colr is required, and only values 16, 17, and 18 are specified,
7057 so error if we have no fourcc */
7060 /* extract component mapping */
7061 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
7063 guint32 cmap_len = 0;
7065 cmap_len = QT_UINT32 (cmap->data);
7066 if (cmap_len >= 8) {
7067 /* normal box, subtract off header */
7069 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
7070 if (cmap_len % 4 == 0) {
7071 ncomp_map = (cmap_len / 4);
7072 comp_map = g_new0 (gint32, ncomp_map);
7073 for (i = 0; i < ncomp_map; i++) {
7076 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
7077 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
7078 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
7079 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
7084 /* extract channel definitions */
7085 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
7087 guint32 cdef_len = 0;
7089 cdef_len = QT_UINT32 (cdef->data);
7090 if (cdef_len >= 10) {
7091 /* normal box, subtract off header and len */
7093 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
7094 if (cdef_len % 6 == 0) {
7095 nchan_def = (cdef_len / 6);
7096 chan_def = g_new0 (gint32, nchan_def);
7097 for (i = 0; i < nchan_def; i++)
7099 for (i = 0; i < nchan_def; i++) {
7100 guint16 cn, typ, asoc;
7101 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
7102 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
7103 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
7104 if (cn < nchan_def) {
7107 chan_def[cn] = asoc;
7110 chan_def[cn] = 0; /* alpha */
7113 chan_def[cn] = -typ;
7121 gst_caps_set_simple (stream->caps,
7122 "num-components", G_TYPE_INT, ncomp, NULL);
7123 gst_caps_set_simple (stream->caps,
7124 "fourcc", GST_TYPE_FOURCC, fourcc, NULL);
7127 GValue arr = { 0, };
7128 GValue elt = { 0, };
7130 g_value_init (&arr, GST_TYPE_ARRAY);
7131 g_value_init (&elt, G_TYPE_INT);
7132 for (i = 0; i < ncomp_map; i++) {
7133 g_value_set_int (&elt, comp_map[i]);
7134 gst_value_array_append_value (&arr, &elt);
7136 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7137 "component-map", &arr);
7138 g_value_unset (&elt);
7139 g_value_unset (&arr);
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 < nchan_def; i++) {
7150 g_value_set_int (&elt, chan_def[i]);
7151 gst_value_array_append_value (&arr, &elt);
7153 gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
7154 "channel-definitions", &arr);
7155 g_value_unset (&elt);
7156 g_value_unset (&arr);
7160 /* some optional atoms */
7161 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
7162 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
7164 /* indicate possible fields in caps */
7166 data = (guint8 *) field->data + 8;
7168 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
7169 (gint) * data, NULL);
7171 /* add codec_data if provided */
7176 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
7177 data = prefix->data;
7178 len = QT_UINT32 (data);
7181 buf = gst_buffer_new_and_alloc (len);
7182 memcpy (GST_BUFFER_DATA (buf), data + 8, len);
7183 gst_caps_set_simple (stream->caps,
7184 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7185 gst_buffer_unref (buf);
7194 GstBuffer *seqh = NULL;
7195 guint8 *gamma_data = NULL;
7196 gint len = QT_UINT32 (stsd_data);
7198 qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
7200 gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
7201 QT_FP32 (gamma_data), NULL);
7204 /* sorry for the bad name, but we don't know what this is, other
7205 * than its own fourcc */
7206 gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
7210 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
7211 buf = gst_buffer_new_and_alloc (len);
7212 memcpy (GST_BUFFER_DATA (buf), stsd_data, len);
7213 gst_caps_set_simple (stream->caps,
7214 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7215 gst_buffer_unref (buf);
7220 gst_caps_set_simple (stream->caps,
7221 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
7228 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
7229 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
7233 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
7237 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
7238 /* collect the headers and store them in a stream list so that we can
7239 * send them out first */
7240 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
7250 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
7251 ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
7254 ovc1_data = ovc1->data;
7255 ovc1_len = QT_UINT32 (ovc1_data);
7256 if (ovc1_len <= 198) {
7257 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
7260 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
7261 memcpy (GST_BUFFER_DATA (buf), ovc1_data + 198, ovc1_len - 198);
7262 gst_caps_set_simple (stream->caps,
7263 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7264 gst_buffer_unref (buf);
7272 GST_INFO_OBJECT (qtdemux,
7273 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7274 GST_FOURCC_ARGS (fourcc), stream->caps);
7276 } else if (stream->subtype == FOURCC_soun) {
7277 int version, samplesize;
7278 guint16 compression_id;
7279 gboolean amrwb = FALSE;
7285 version = QT_UINT32 (stsd_data + offset);
7286 stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
7287 samplesize = QT_UINT16 (stsd_data + offset + 10);
7288 compression_id = QT_UINT16 (stsd_data + offset + 12);
7289 stream->rate = QT_FP32 (stsd_data + offset + 16);
7291 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
7292 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
7293 QT_UINT32 (stsd_data + offset + 4));
7294 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7295 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
7296 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
7297 GST_LOG_OBJECT (qtdemux, "packet size: %d",
7298 QT_UINT16 (stsd_data + offset + 14));
7299 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7301 if (compression_id == 0xfffe)
7302 stream->sampled = TRUE;
7304 /* first assume uncompressed audio */
7305 stream->bytes_per_sample = samplesize / 8;
7306 stream->samples_per_frame = stream->n_channels;
7307 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
7308 stream->samples_per_packet = stream->samples_per_frame;
7309 stream->bytes_per_packet = stream->bytes_per_sample;
7313 /* Yes, these have to be hard-coded */
7316 stream->samples_per_packet = 6;
7317 stream->bytes_per_packet = 1;
7318 stream->bytes_per_frame = 1 * stream->n_channels;
7319 stream->bytes_per_sample = 1;
7320 stream->samples_per_frame = 6 * stream->n_channels;
7325 stream->samples_per_packet = 3;
7326 stream->bytes_per_packet = 1;
7327 stream->bytes_per_frame = 1 * stream->n_channels;
7328 stream->bytes_per_sample = 1;
7329 stream->samples_per_frame = 3 * stream->n_channels;
7334 stream->samples_per_packet = 64;
7335 stream->bytes_per_packet = 34;
7336 stream->bytes_per_frame = 34 * stream->n_channels;
7337 stream->bytes_per_sample = 2;
7338 stream->samples_per_frame = 64 * stream->n_channels;
7344 stream->samples_per_packet = 1;
7345 stream->bytes_per_packet = 1;
7346 stream->bytes_per_frame = 1 * stream->n_channels;
7347 stream->bytes_per_sample = 1;
7348 stream->samples_per_frame = 1 * stream->n_channels;
7353 stream->samples_per_packet = 160;
7354 stream->bytes_per_packet = 33;
7355 stream->bytes_per_frame = 33 * stream->n_channels;
7356 stream->bytes_per_sample = 2;
7357 stream->samples_per_frame = 160 * stream->n_channels;
7364 if (version == 0x00010000) {
7372 /* only parse extra decoding config for non-pcm audio */
7373 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7374 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
7375 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
7376 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);
7378 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7379 stream->samples_per_packet);
7380 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
7381 stream->bytes_per_packet);
7382 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
7383 stream->bytes_per_frame);
7384 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
7385 stream->bytes_per_sample);
7387 if (!stream->sampled && stream->bytes_per_packet) {
7388 stream->samples_per_frame = (stream->bytes_per_frame /
7389 stream->bytes_per_packet) * stream->samples_per_packet;
7390 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
7391 stream->samples_per_frame);
7396 } else if (version == 0x00020000) {
7403 stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
7404 qtfp.val = QT_UINT64 (stsd_data + offset + 4);
7405 stream->rate = qtfp.fp;
7406 stream->n_channels = QT_UINT32 (stsd_data + offset + 12);
7408 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
7409 stream->samples_per_packet);
7410 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate);
7411 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels);
7414 GST_WARNING_OBJECT (qtdemux, "unknown version %08x", version);
7417 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc, NULL, 0,
7426 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);
7428 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
7430 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
7432 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
7435 gst_caps_set_simple (stream->caps,
7436 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
7443 const gchar *owma_data, *codec_name = NULL;
7447 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
7448 /* FIXME this should also be gst_riff_strf_auds,
7449 * but the latter one is actually missing bits-per-sample :( */
7454 gint32 nSamplesPerSec;
7455 gint32 nAvgBytesPerSec;
7457 gint16 wBitsPerSample;
7462 GST_DEBUG_OBJECT (qtdemux, "parse owma");
7463 owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
7466 owma_data = owma->data;
7467 owma_len = QT_UINT32 (owma_data);
7468 if (owma_len <= 54) {
7469 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
7472 wfex = (WAVEFORMATEX *) (owma_data + 36);
7473 buf = gst_buffer_new_and_alloc (owma_len - 54);
7474 memcpy (GST_BUFFER_DATA (buf), owma_data + 54, owma_len - 54);
7475 if (wfex->wFormatTag == 0x0161) {
7476 codec_name = "Windows Media Audio";
7478 } else if (wfex->wFormatTag == 0x0162) {
7479 codec_name = "Windows Media Audio 9 Pro";
7481 } else if (wfex->wFormatTag == 0x0163) {
7482 codec_name = "Windows Media Audio 9 Lossless";
7483 /* is that correct? gstffmpegcodecmap.c is missing it, but
7484 * fluendo codec seems to support it */
7488 gst_caps_set_simple (stream->caps,
7489 "codec_data", GST_TYPE_BUFFER, buf,
7490 "wmaversion", G_TYPE_INT, version,
7491 "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
7492 "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
7493 "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7494 "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
7496 gst_buffer_unref (buf);
7500 codec = g_strdup (codec_name);
7512 list = gst_tag_list_new ();
7513 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7514 GST_TAG_AUDIO_CODEC, codec, NULL);
7518 /* some bitrate info may have ended up in caps */
7519 s = gst_caps_get_structure (stream->caps, 0);
7520 gst_structure_get_int (s, "bitrate", &bitrate);
7522 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
7526 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
7530 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
7532 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
7534 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
7538 /* If the fourcc's bottom 16 bits gives 'sm', then the top
7539 16 bits is a byte-swapped wave-style codec identifier,
7540 and we can find a WAVE header internally to a 'wave' atom here.
7541 This can more clearly be thought of as 'ms' as the top 16 bits, and a
7542 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
7545 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
7546 if (len < offset + 20) {
7547 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
7549 guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
7550 const guint8 *data = stsd_data + offset + 16;
7552 GNode *waveheadernode;
7554 wavenode = g_node_new ((guint8 *) data);
7555 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
7556 const guint8 *waveheader;
7559 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
7560 if (waveheadernode) {
7561 waveheader = (const guint8 *) waveheadernode->data;
7562 headerlen = QT_UINT32 (waveheader);
7564 if (headerlen > 8) {
7565 gst_riff_strf_auds *header = NULL;
7566 GstBuffer *headerbuf;
7572 headerbuf = gst_buffer_new ();
7573 GST_BUFFER_DATA (headerbuf) = (guint8 *) waveheader;
7574 GST_BUFFER_SIZE (headerbuf) = headerlen;
7576 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
7577 headerbuf, &header, &extra)) {
7578 gst_caps_unref (stream->caps);
7579 stream->caps = gst_riff_create_audio_caps (header->format, NULL,
7580 header, extra, NULL, NULL);
7583 gst_buffer_unref (extra);
7587 GST_DEBUG ("Didn't find waveheadernode for this codec");
7589 g_node_destroy (wavenode);
7592 gst_qtdemux_handle_esds (qtdemux, stream, esds, list);
7596 /* FIXME: what is in the chunk? */
7599 gint len = QT_UINT32 (stsd_data);
7601 /* seems to be always = 116 = 0x74 */
7607 gint len = QT_UINT32 (stsd_data);
7610 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);
7612 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x4C, len - 0x4C);
7613 gst_caps_set_simple (stream->caps,
7614 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7615 gst_buffer_unref (buf);
7617 gst_caps_set_simple (stream->caps,
7618 "samplesize", G_TYPE_INT, samplesize, NULL);
7623 GNode *alac, *wave = NULL;
7625 /* apparently, m4a has this atom appended directly in the stsd entry,
7626 * while mov has it in a wave atom */
7627 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
7629 /* alac now refers to stsd entry atom */
7630 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
7632 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
7634 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
7637 gint len = QT_UINT32 (alac->data);
7641 GST_DEBUG_OBJECT (qtdemux,
7642 "discarding alac atom with unexpected len %d", len);
7644 /* codec-data contains alac atom size and prefix,
7645 * ffmpeg likes it that way, not quite gst-ish though ...*/
7646 buf = gst_buffer_new_and_alloc (len);
7647 memcpy (GST_BUFFER_DATA (buf), alac->data, len);
7648 gst_caps_set_simple (stream->caps,
7649 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7650 gst_buffer_unref (buf);
7653 gst_caps_set_simple (stream->caps,
7654 "samplesize", G_TYPE_INT, samplesize, NULL);
7662 gint len = QT_UINT32 (stsd_data);
7665 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
7668 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x34, len - 0x34);
7670 /* If we have enough data, let's try to get the 'damr' atom. See
7671 * the 3GPP container spec (26.244) for more details. */
7672 if ((len - 0x34) > 8 &&
7673 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
7675 list = gst_tag_list_new ();
7676 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7677 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
7680 gst_caps_set_simple (stream->caps,
7681 "codec_data", GST_TYPE_BUFFER, buf, NULL);
7682 gst_buffer_unref (buf);
7690 GST_INFO_OBJECT (qtdemux,
7691 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
7692 GST_FOURCC_ARGS (fourcc), stream->caps);
7694 } else if (stream->subtype == FOURCC_strm) {
7695 if (fourcc == FOURCC_rtsp) {
7696 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
7698 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
7699 GST_FOURCC_ARGS (fourcc));
7700 goto unknown_stream;
7702 stream->sampled = TRUE;
7703 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
7705 stream->sampled = TRUE;
7710 qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
7712 list = gst_tag_list_new ();
7713 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7714 GST_TAG_SUBTITLE_CODEC, codec, NULL);
7719 /* hunt for sort-of codec data */
7726 /* look for palette */
7727 /* target mp4s atom */
7728 len = QT_UINT32 (stsd_data + offset);
7729 data = stsd_data + offset;
7730 /* verify sufficient length,
7731 * and esds present with decConfigDescr of expected size and position */
7732 if ((len >= 106 + 8)
7733 && (QT_FOURCC (data + 8 + 8 + 4) == FOURCC_esds)
7734 && (QT_UINT16 (data + 8 + 40) == 0x0540)) {
7739 /* move to decConfigDescr data */
7740 data = data + 8 + 42;
7741 for (i = 0; i < 16; i++) {
7742 clut[i] = QT_UINT32 (data);
7746 s = gst_structure_new ("application/x-gst-dvd", "event",
7747 G_TYPE_STRING, "dvd-spu-clut-change",
7748 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
7749 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
7750 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
7751 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
7752 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
7753 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
7754 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
7755 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
7758 /* store event and trigger custom processing */
7759 stream->pending_event =
7760 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
7761 stream->need_process = TRUE;
7769 goto unknown_stream;
7772 /* promote to sampled format */
7773 if (stream->fourcc == FOURCC_samr) {
7774 /* force mono 8000 Hz for AMR */
7775 stream->sampled = TRUE;
7776 stream->n_channels = 1;
7777 stream->rate = 8000;
7778 } else if (stream->fourcc == FOURCC_sawb) {
7779 /* force mono 16000 Hz for AMR-WB */
7780 stream->sampled = TRUE;
7781 stream->n_channels = 1;
7782 stream->rate = 16000;
7783 } else if (stream->fourcc == FOURCC_mp4a) {
7784 stream->sampled = TRUE;
7787 /* collect sample information */
7788 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
7789 goto samples_failed;
7791 if (qtdemux->fragmented) {
7795 /* need all moov samples as basis; probably not many if any at all */
7796 /* prevent moof parsing taking of at this time */
7797 offset = qtdemux->moof_offset;
7798 qtdemux->moof_offset = 0;
7799 if (stream->n_samples &&
7800 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
7801 qtdemux->moof_offset = offset;
7802 goto samples_failed;
7804 qtdemux->moof_offset = 0;
7805 /* movie duration more reliable in this case (e.g. mehd) */
7806 if (qtdemux->segment.duration &&
7807 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
7808 stream->duration = gst_util_uint64_scale (qtdemux->segment.duration,
7809 stream->timescale, GST_SECOND);
7810 /* need defaults for fragments */
7811 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
7814 /* configure segments */
7815 if (!qtdemux_parse_segments (qtdemux, stream, trak))
7816 goto segments_failed;
7818 /* add some language tag, if useful */
7819 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
7820 strcmp (stream->lang_id, "und")) {
7821 const gchar *lang_code;
7824 list = gst_tag_list_new ();
7826 /* convert ISO 639-2 code to ISO 639-1 */
7827 lang_code = gst_tag_get_language_code (stream->lang_id);
7828 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
7829 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
7832 /* now we are ready to add the stream */
7833 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
7834 goto too_many_streams;
7836 stream->pending_tags = list;
7837 qtdemux->streams[qtdemux->n_streams] = stream;
7838 qtdemux->n_streams++;
7839 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
7846 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
7847 (_("This file is corrupt and cannot be played.")), (NULL));
7848 #ifdef QTDEMUX_MODIFICATION
7849 g_free (stream->trickplay_info);
7856 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
7857 #ifdef QTDEMUX_MODIFICATION
7858 g_free (stream->trickplay_info);
7866 /* we posted an error already */
7867 /* free stbl sub-atoms */
7868 gst_qtdemux_stbl_free (stream);
7869 #ifdef QTDEMUX_MODIFICATION
7870 g_free (stream->trickplay_info);
7877 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
7878 GST_FOURCC_ARGS (stream->subtype));
7879 #ifdef QTDEMUX_MODIFICATION
7880 g_free (stream->trickplay_info);
7887 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
7888 (_("This file contains too many streams. Only playing first %d"),
7889 GST_QTDEMUX_MAX_STREAMS), (NULL));
7894 /* If we can estimate the overall bitrate, and don't have information about the
7895 * stream bitrate for exactly one stream, this guesses the stream bitrate as
7896 * the overall bitrate minus the sum of the bitrates of all other streams. This
7897 * should be useful for the common case where we have one audio and one video
7898 * stream and can estimate the bitrate of one, but not the other. */
7900 gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
7902 GstFormat format = GST_FORMAT_BYTES;
7903 QtDemuxStream *stream = NULL;
7904 gint64 size, duration, sys_bitrate, sum_bitrate = 0;
7908 if (qtdemux->fragmented)
7911 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
7913 if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &format, &size) ||
7914 format != GST_FORMAT_BYTES) {
7915 GST_DEBUG_OBJECT (qtdemux,
7916 "Size in bytes of the stream not known - bailing");
7920 /* Subtract the header size */
7921 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
7922 size, qtdemux->header_size);
7923 g_assert (size >= qtdemux->header_size);
7924 size = size - qtdemux->header_size;
7926 if (!gst_qtdemux_get_duration (qtdemux, &duration) ||
7927 duration == GST_CLOCK_TIME_NONE) {
7928 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
7932 for (i = 0; i < qtdemux->n_streams; i++) {
7933 switch (qtdemux->streams[i]->subtype) {
7936 /* retrieve bitrate, prefer avg then max */
7938 if (qtdemux->streams[i]->pending_tags) {
7939 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
7940 GST_TAG_MAXIMUM_BITRATE, &bitrate);
7941 gst_tag_list_get_uint (qtdemux->streams[i]->pending_tags,
7942 GST_TAG_BITRATE, &bitrate);
7945 sum_bitrate += bitrate;
7948 GST_DEBUG_OBJECT (qtdemux,
7949 ">1 stream with unknown bitrate - bailing");
7952 stream = qtdemux->streams[i];
7956 /* For other subtypes, we assume no significant impact on bitrate */
7962 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
7966 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
7968 if (sys_bitrate < sum_bitrate) {
7969 /* This can happen, since sum_bitrate might be derived from maximum
7970 * bitrates and not average bitrates */
7971 GST_DEBUG_OBJECT (qtdemux,
7972 "System bitrate less than sum bitrate - bailing");
7976 bitrate = sys_bitrate - sum_bitrate;
7977 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
7978 ", Stream bitrate = %u", sys_bitrate, bitrate);
7980 if (!stream->pending_tags)
7981 stream->pending_tags = gst_tag_list_new ();
7983 gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
7984 GST_TAG_BITRATE, bitrate, NULL);
7987 static GstFlowReturn
7988 qtdemux_expose_streams (GstQTDemux * qtdemux)
7991 GstFlowReturn ret = GST_FLOW_OK;
7993 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
7995 for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
7996 QtDemuxStream *stream = qtdemux->streams[i];
7997 guint32 sample_num = 0;
8002 GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
8003 i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
8005 if (qtdemux->fragmented) {
8006 /* need all moov samples first */
8007 GST_OBJECT_LOCK (qtdemux);
8008 while (stream->n_samples == 0)
8009 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
8011 GST_OBJECT_UNLOCK (qtdemux);
8013 /* discard any stray moof */
8014 qtdemux->moof_offset = 0;
8017 /* prepare braking */
8018 if (ret != GST_FLOW_ERROR)
8021 /* in pull mode, we should have parsed some sample info by now;
8022 * and quite some code will not handle no samples.
8023 * in push mode, we'll just have to deal with it */
8024 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
8025 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
8026 gst_qtdemux_stream_free (qtdemux, stream);
8027 memmove (&(qtdemux->streams[i]), &(qtdemux->streams[i + 1]),
8028 sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1));
8029 qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL;
8030 qtdemux->n_streams--;
8035 /* parse number of initial sample to set frame rate cap */
8036 while (sample_num < stream->n_samples && sample_num < samples) {
8037 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
8041 /* collect and sort durations */
8042 samples = MIN (stream->stbl_index + 1, samples);
8043 GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples);
8045 durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples);
8047 while (sample_num < samples) {
8048 g_array_append_val (durations, stream->samples[sample_num].duration);
8051 g_array_sort (durations, less_than);
8052 stream->min_duration = g_array_index (durations, guint32, samples / 2);
8053 g_array_free (durations, TRUE);
8056 /* now we have all info and can expose */
8057 list = stream->pending_tags;
8058 stream->pending_tags = NULL;
8059 gst_qtdemux_add_stream (qtdemux, stream, list);
8062 gst_qtdemux_guess_bitrate (qtdemux);
8064 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
8066 /* check if we should post a redirect in case there is a single trak
8067 * and it is a redirecting trak */
8068 if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
8071 qtdemux_post_global_tags (qtdemux);
8073 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
8074 "an external content");
8075 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
8076 gst_structure_new ("redirect",
8077 "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
8079 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
8080 qtdemux->posted_redirect = TRUE;
8086 /* check if major or compatible brand is 3GP */
8087 static inline gboolean
8088 qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
8091 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8092 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8093 } else if (qtdemux->comp_brands != NULL) {
8094 guint8 *data = GST_BUFFER_DATA (qtdemux->comp_brands);
8095 guint size = GST_BUFFER_SIZE (qtdemux->comp_brands);
8096 gboolean res = FALSE;
8099 res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
8100 GST_MAKE_FOURCC ('3', 'g', 0, 0));
8110 /* check if tag is a spec'ed 3GP tag keyword storing a string */
8111 static inline gboolean
8112 qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
8114 return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
8115 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
8116 || fourcc == FOURCC_albm;
8120 qtdemux_tag_add_location (GstQTDemux * qtdemux, const char *tag,
8121 const char *dummy, GNode * node)
8123 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8127 gdouble longitude, latitude, altitude;
8130 len = QT_UINT32 (node->data);
8137 /* TODO: language code skipped */
8139 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
8142 /* do not alarm in trivial case, but bail out otherwise */
8143 if (*(data + offset) != 0) {
8144 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
8148 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8149 GST_TAG_GEO_LOCATION_NAME, name, NULL);
8150 offset += strlen (name);
8154 if (len < offset + 2 + 4 + 4 + 4)
8157 /* +1 +1 = skip null-terminator and location role byte */
8159 /* table in spec says unsigned, semantics say negative has meaning ... */
8160 longitude = QT_SFP32 (data + offset);
8163 latitude = QT_SFP32 (data + offset);
8166 altitude = QT_SFP32 (data + offset);
8168 /* one invalid means all are invalid */
8169 if (longitude >= -180.0 && longitude <= 180.0 &&
8170 latitude >= -90.0 && latitude <= 90.0) {
8171 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8172 GST_TAG_GEO_LOCATION_LATITUDE, latitude,
8173 GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
8174 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
8177 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
8184 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
8191 qtdemux_tag_add_year (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8198 len = QT_UINT32 (node->data);
8202 y = QT_UINT16 ((guint8 *) node->data + 12);
8204 GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
8207 GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
8209 date = g_date_new_dmy (1, 1, y);
8210 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, date, NULL);
8215 qtdemux_tag_add_classification (GstQTDemux * qtdemux, const char *tag,
8216 const char *dummy, GNode * node)
8219 char *tag_str = NULL;
8224 len = QT_UINT32 (node->data);
8229 entity = (guint8 *) node->data + offset;
8230 if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
8231 GST_DEBUG_OBJECT (qtdemux,
8232 "classification info: %c%c%c%c invalid classification entity",
8233 entity[0], entity[1], entity[2], entity[3]);
8238 table = QT_UINT16 ((guint8 *) node->data + offset);
8240 /* Language code skipped */
8244 /* Tag format: "XXXX://Y[YYYY]/classification info string"
8245 * XXXX: classification entity, fixed length 4 chars.
8246 * Y[YYYY]: classification table, max 5 chars.
8248 tag_str = g_strdup_printf ("----://%u/%s",
8249 table, (char *) node->data + offset);
8251 /* memcpy To be sure we're preserving byte order */
8252 memcpy (tag_str, entity, 4);
8253 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
8255 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_APPEND, tag,
8265 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
8271 qtdemux_tag_add_str_full (GstQTDemux * qtdemux, const char *tag,
8272 const char *dummy, GNode * node)
8274 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8280 gboolean ret = TRUE;
8282 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8284 len = QT_UINT32 (data->data);
8285 type = QT_UINT32 ((guint8 *) data->data + 8);
8286 if (type == 0x00000001 && len > 16) {
8287 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
8290 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8291 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s,
8295 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8299 len = QT_UINT32 (node->data);
8300 type = QT_UINT32 ((guint8 *) node->data + 4);
8301 if ((type >> 24) == 0xa9) {
8302 /* Type starts with the (C) symbol, so the next 32 bits are
8303 * the language code, which we ignore */
8305 GST_DEBUG_OBJECT (qtdemux, "found international text tag");
8306 } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
8307 QT_FOURCC ((guint8 *) node->data + 4))) {
8308 guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
8310 /* we go for 3GP style encoding if major brands claims so,
8311 * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
8312 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8313 (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
8314 ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
8316 /* 16-bit Language code is ignored here as well */
8317 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
8324 GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
8325 ret = FALSE; /* may have to fallback */
8327 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8328 len - offset, env_vars);
8330 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
8331 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, NULL);
8335 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
8342 qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
8343 const char *dummy, GNode * node)
8345 qtdemux_tag_add_str_full (qtdemux, tag, dummy, node);
8349 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, const char *tag,
8350 const char *dummy, GNode * node)
8352 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
8354 char *s, *t, *k = NULL;
8359 /* first try normal string tag if major brand not 3GP */
8360 if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
8361 if (!qtdemux_tag_add_str_full (qtdemux, tag, dummy, node)) {
8362 /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
8363 * let's try it 3gpp way after minor safety check */
8365 if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
8371 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
8375 len = QT_UINT32 (data);
8379 count = QT_UINT8 (data + 14);
8381 for (; count; count--) {
8384 if (offset + 1 > len)
8386 slen = QT_UINT8 (data + offset);
8388 if (offset + slen > len)
8390 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
8393 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
8395 t = g_strjoin (",", k, s, NULL);
8403 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
8410 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
8411 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, k, NULL);
8420 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
8426 qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1,
8427 const char *tag2, GNode * node)
8434 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8436 len = QT_UINT32 (data->data);
8437 type = QT_UINT32 ((guint8 *) data->data + 8);
8438 if (type == 0x00000000 && len >= 22) {
8439 n1 = QT_UINT16 ((guint8 *) data->data + 18);
8440 n2 = QT_UINT16 ((guint8 *) data->data + 20);
8442 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
8443 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8447 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
8448 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8456 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8464 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8466 len = QT_UINT32 (data->data);
8467 type = QT_UINT32 ((guint8 *) data->data + 8);
8468 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
8469 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8470 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
8471 n1 = QT_UINT16 ((guint8 *) data->data + 16);
8473 /* do not add bpm=0 */
8474 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
8475 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8476 tag1, (gdouble) n1, NULL);
8483 qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, const char *tag1,
8484 const char *dummy, GNode * node)
8491 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8493 len = QT_UINT32 (data->data);
8494 type = QT_UINT32 ((guint8 *) data->data + 8);
8495 GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
8496 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
8497 if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
8498 num = QT_UINT32 ((guint8 *) data->data + 16);
8500 /* do not add num=0 */
8501 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
8502 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8510 qtdemux_tag_add_covr (GstQTDemux * qtdemux, const char *tag1, const char *dummy,
8518 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8520 len = QT_UINT32 (data->data);
8521 type = QT_UINT32 ((guint8 *) data->data + 8);
8522 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
8523 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
8524 if ((buf = gst_tag_image_data_to_image_buffer ((guint8 *) data->data + 16,
8525 len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
8526 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
8527 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8529 gst_buffer_unref (buf);
8536 qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8544 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8546 len = QT_UINT32 (data->data);
8547 type = QT_UINT32 ((guint8 *) data->data + 8);
8548 if (type == 0x00000001 && len > 16) {
8549 guint y, m = 1, d = 1;
8552 s = g_strndup ((char *) data->data + 16, len - 16);
8553 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
8554 ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
8555 if (ret >= 1 && y > 1500 && y < 3000) {
8558 date = g_date_new_dmy (d, m, y);
8559 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag,
8563 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
8571 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag, const char *dummy,
8576 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8578 #ifndef QTDEMUX_MODIFICATION
8579 /* re-route to normal string tag if major brand says so
8580 * or no data atom and compatible brand suggests so */
8581 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8582 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8583 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8591 len = QT_UINT32 (data->data);
8592 type = QT_UINT32 ((guint8 *) data->data + 8);
8593 if (type == 0x00000000 && len >= 18) {
8594 n = QT_UINT16 ((guint8 *) data->data + 16);
8598 genre = gst_tag_id3_genre_get (n - 1);
8599 if (genre != NULL) {
8600 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
8601 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
8606 #ifdef QTDEMUX_MODIFICATION
8608 /* re-route to normal string tag if major brand says so
8609 * or no data atom and compatible brand suggests so */
8610 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8611 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8612 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8618 #ifdef QTDEMUX_MODIFICATION
8620 /* re-route to normal string tag if major brand says so
8621 * or no data atom and compatible brand suggests so */
8622 if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
8623 (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
8624 qtdemux_tag_add_str (qtdemux, tag, dummy, node);
8632 qtdemux_add_double_tag_from_str (GstQTDemux * demux, const gchar * tag,
8633 guint8 * data, guint32 datasize)
8638 /* make a copy to have \0 at the end */
8639 datacopy = g_strndup ((gchar *) data, datasize);
8641 /* convert the str to double */
8642 if (sscanf (datacopy, "%lf", &value) == 1) {
8643 GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
8644 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_REPLACE, tag, value, NULL);
8646 GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
8654 qtdemux_tag_add_revdns (GstQTDemux * demux, const char *tag,
8655 const char *tag_bis, GNode * node)
8664 const gchar *meanstr;
8665 const gchar *namestr;
8667 /* checking the whole ---- atom size for consistency */
8668 if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
8669 GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
8673 mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
8675 GST_WARNING_OBJECT (demux, "No 'mean' atom found");
8679 meansize = QT_UINT32 (mean->data);
8680 if (meansize <= 12) {
8681 GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
8684 meanstr = ((gchar *) mean->data) + 12;
8686 name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
8688 GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
8692 namesize = QT_UINT32 (name->data);
8693 if (namesize <= 12) {
8694 GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
8697 namestr = ((gchar *) name->data) + 12;
8704 * uint24 - data type
8708 data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
8710 GST_WARNING_OBJECT (demux, "No data atom in this tag");
8713 datasize = QT_UINT32 (data->data);
8714 if (datasize <= 16) {
8715 GST_WARNING_OBJECT (demux, "Data atom too small");
8718 datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
8720 if (strncmp (meanstr, "com.apple.iTunes", meansize - 12) == 0) {
8723 const gchar name[28];
8724 const gchar tag[28];
8727 "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
8728 "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
8729 "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
8730 "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
8731 "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
8732 "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
8733 "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
8734 "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
8738 for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
8739 if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize - 12)) {
8740 switch (gst_tag_get_type (tags[i].tag)) {
8742 qtdemux_add_double_tag_from_str (demux, tags[i].tag,
8743 ((guint8 *) data->data) + 16, datasize - 16);
8746 qtdemux_tag_add_str (demux, tags[i].tag, NULL, node);
8755 if (i == G_N_ELEMENTS (tags))
8769 meanstr_dbg = g_strndup (meanstr, meansize - 12);
8770 namestr_dbg = g_strndup (namestr, namesize - 12);
8772 GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
8773 "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
8775 g_free (namestr_dbg);
8776 g_free (meanstr_dbg);
8782 qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag,
8783 const char *tag_bis, GNode * node)
8788 GstTagList *taglist = NULL;
8790 GST_LOG_OBJECT (demux, "parsing ID32");
8793 len = GST_READ_UINT32_BE (data);
8795 /* need at least full box and language tag */
8799 buf = gst_buffer_new ();
8800 GST_BUFFER_DATA (buf) = data + 14;
8801 GST_BUFFER_SIZE (buf) = len - 14;
8803 taglist = gst_tag_list_from_id3v2_tag (buf);
8805 GST_LOG_OBJECT (demux, "parsing ok");
8806 gst_tag_list_insert (demux->tag_list, taglist, GST_TAG_MERGE_KEEP);
8808 GST_LOG_OBJECT (demux, "parsing failed");
8812 gst_tag_list_free (taglist);
8814 gst_buffer_unref (buf);
8817 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux,
8818 const char *tag, const char *tag_bis, GNode * node);
8821 FOURCC_pcst -> if media is a podcast -> bool
8822 FOURCC_cpil -> if media is part of a compilation -> bool
8823 FOURCC_pgap -> if media is part of a gapless context -> bool
8824 FOURCC_tven -> the tv episode id e.g. S01E23 -> str
8830 const gchar *gst_tag;
8831 const gchar *gst_tag_bis;
8832 const GstQTDemuxAddTagFunc func;
8835 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8836 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
8837 FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
8838 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8839 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8840 FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
8841 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
8842 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
8843 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8844 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
8845 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8846 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
8847 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8848 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8849 FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8850 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
8851 FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
8852 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
8853 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
8854 FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8855 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
8856 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
8857 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8858 qtdemux_tag_add_num}, {
8859 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
8860 qtdemux_tag_add_num}, {
8861 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
8862 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
8863 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
8864 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
8865 FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
8866 FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
8867 FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8868 FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
8869 FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
8870 FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
8871 FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
8872 FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8873 FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
8874 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
8875 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
8876 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
8877 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
8878 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
8879 qtdemux_tag_add_classification}, {
8881 /* This is a special case, some tags are stored in this
8882 * 'reverse dns naming', according to:
8883 * http://atomicparsley.sourceforge.net/mpeg-4files.html and
8886 FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
8887 /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
8888 FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
8892 qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
8904 len = QT_UINT32 (data);
8905 buf = gst_buffer_new_and_alloc (len);
8906 memcpy (GST_BUFFER_DATA (buf), data, len);
8908 /* heuristic to determine style of tag */
8909 if (QT_FOURCC (data + 4) == FOURCC_____ ||
8910 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
8912 /* fall back to assuming iso/3gp tag style */
8916 /* santize the name for the caps. */
8917 for (i = 0; i < 4; i++) {
8918 guint8 d = data[4 + i];
8919 if (g_ascii_isalnum (d))
8920 ndata[i] = g_ascii_tolower (d);
8925 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
8926 ndata[0], ndata[1], ndata[2], ndata[3]);
8927 GST_DEBUG_OBJECT (demux, "media type %s", media_type);
8929 caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL);
8930 gst_buffer_set_caps (buf, caps);
8931 gst_caps_unref (caps);
8932 g_free (media_type);
8934 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT,
8935 GST_BUFFER_SIZE (buf), caps);
8937 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND,
8938 GST_QT_DEMUX_PRIVATE_TAG, buf, NULL);
8939 gst_buffer_unref (buf);
8943 qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta)
8951 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
8953 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
8955 GST_LOG_OBJECT (qtdemux, "no ilst");
8960 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
8963 GST_DEBUG_OBJECT (qtdemux, "new tag list");
8964 if (!qtdemux->tag_list)
8965 qtdemux->tag_list = gst_tag_list_new ();
8968 while (i < G_N_ELEMENTS (add_funcs)) {
8969 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
8973 len = QT_UINT32 (node->data);
8975 GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
8976 GST_FOURCC_ARGS (add_funcs[i].fourcc));
8978 add_funcs[i].func (qtdemux, add_funcs[i].gst_tag,
8979 add_funcs[i].gst_tag_bis, node);
8981 g_node_destroy (node);
8987 /* parsed nodes have been removed, pass along remainder as blob */
8988 g_node_children_foreach (ilst, G_TRAVERSE_ALL,
8989 (GNodeForeachFunc) qtdemux_tag_add_blob, qtdemux);
8991 /* parse up XMP_ node if existing */
8992 xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
8995 GstTagList *taglist;
8997 buf = gst_buffer_new ();
8998 GST_BUFFER_DATA (buf) = ((guint8 *) xmp_->data) + 8;
8999 GST_BUFFER_SIZE (buf) = QT_UINT32 ((guint8 *) xmp_->data) - 8;
9001 taglist = gst_tag_list_from_xmp_buffer (buf);
9002 gst_buffer_unref (buf);
9004 qtdemux_handle_xmp_taglist (qtdemux, taglist);
9006 GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
9013 GstStructure *structure; /* helper for sort function */
9015 guint min_req_bitrate;
9016 guint min_req_qt_version;
9020 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
9022 GstQtReference *ref_a = (GstQtReference *) a;
9023 GstQtReference *ref_b = (GstQtReference *) b;
9025 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
9026 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
9028 /* known bitrates go before unknown; higher bitrates go first */
9029 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
9032 /* sort the redirects and post a message for the application.
9035 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
9037 GstQtReference *best;
9040 GValue list_val = { 0, };
9043 g_assert (references != NULL);
9045 references = g_list_sort (references, qtdemux_redirects_sort_func);
9047 best = (GstQtReference *) references->data;
9049 g_value_init (&list_val, GST_TYPE_LIST);
9051 for (l = references; l != NULL; l = l->next) {
9052 GstQtReference *ref = (GstQtReference *) l->data;
9053 GValue struct_val = { 0, };
9055 ref->structure = gst_structure_new ("redirect",
9056 "new-location", G_TYPE_STRING, ref->location, NULL);
9058 if (ref->min_req_bitrate > 0) {
9059 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
9060 ref->min_req_bitrate, NULL);
9063 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
9064 g_value_set_boxed (&struct_val, ref->structure);
9065 gst_value_list_append_value (&list_val, &struct_val);
9066 g_value_unset (&struct_val);
9067 /* don't free anything here yet, since we need best->structure below */
9070 g_assert (best != NULL);
9071 s = gst_structure_copy (best->structure);
9073 if (g_list_length (references) > 1) {
9074 gst_structure_set_value (s, "locations", &list_val);
9077 g_value_unset (&list_val);
9079 for (l = references; l != NULL; l = l->next) {
9080 GstQtReference *ref = (GstQtReference *) l->data;
9082 gst_structure_free (ref->structure);
9083 g_free (ref->location);
9086 g_list_free (references);
9088 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
9089 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
9090 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
9091 qtdemux->posted_redirect = TRUE;
9094 /* look for redirect nodes, collect all redirect information and
9098 qtdemux_parse_redirects (GstQTDemux * qtdemux)
9104 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
9109 tags = gst_tag_list_new ();
9111 if (qtdemux->major_brand == FOURCC_mjp2)
9112 fmt = "Motion JPEG 2000";
9113 else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0))
9115 else if (qtdemux->fragmented)
9118 fmt = "ISO MP4/M4A";
9120 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
9121 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
9123 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
9129 /* we have read th complete moov node now.
9130 * This function parses all of the relevant info, creates the traks and
9131 * prepares all data structures for playback
9134 qtdemux_parse_tree (GstQTDemux * qtdemux)
9141 guint64 creation_time;
9142 GstDateTime *datetime = NULL;
9145 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
9147 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
9148 return qtdemux_parse_redirects (qtdemux);
9151 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
9153 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
9154 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
9155 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
9156 } else if (version == 0) {
9157 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
9158 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
9159 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
9161 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
9165 /* Moving qt creation time (secs since 1904) to unix time */
9166 if (creation_time != 0) {
9167 if (creation_time > QTDEMUX_SECONDS_FROM_1904_TO_1970) {
9170 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
9171 /* some data cleansing sanity */
9172 g_get_current_time (&now);
9173 if (now.tv_sec + 24 * 3600 < creation_time) {
9174 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
9176 datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time);
9179 GST_WARNING_OBJECT (qtdemux, "Can't handle datetimes before 1970 yet, "
9180 "please file a bug at http://bugzilla.gnome.org");
9184 if (!qtdemux->tag_list)
9185 qtdemux->tag_list = gst_tag_list_new ();
9187 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
9188 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
9190 gst_date_time_unref (datetime);
9193 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
9194 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
9196 /* check for fragmented file and get some (default) data */
9197 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
9200 GstByteReader mehd_data;
9202 /* let track parsing or anyone know weird stuff might happen ... */
9203 qtdemux->fragmented = TRUE;
9205 /* compensate for total duration */
9206 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
9208 qtdemux_parse_mehd (qtdemux, &mehd_data);
9211 /* set duration in the segment info */
9212 gst_qtdemux_get_duration (qtdemux, &duration);
9214 gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
9215 /* also do not exceed duration; stop is set that way post seek anyway,
9216 * and segment activation falls back to duration,
9217 * whereas loop only checks stop, so let's align this here as well */
9218 qtdemux->segment.stop = duration;
9221 /* parse all traks */
9222 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
9224 qtdemux_parse_trak (qtdemux, trak);
9225 /* iterate all siblings */
9226 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
9230 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
9232 qtdemux_parse_udta (qtdemux, udta);
9234 GST_LOG_OBJECT (qtdemux, "No udta node found.");
9237 /* maybe also some tags in meta box */
9238 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
9240 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
9241 qtdemux_parse_udta (qtdemux, udta);
9243 GST_LOG_OBJECT (qtdemux, "No meta node found.");
9246 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
9251 /* taken from ffmpeg */
9253 get_size (guint8 * ptr, guint8 ** end)
9262 len = (len << 7) | (c & 0x7f);
9271 /* this can change the codec originally present in @list */
9273 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
9274 GNode * esds, GstTagList * list)
9276 int len = QT_UINT32 (esds->data);
9277 guint8 *ptr = esds->data;
9278 guint8 *end = ptr + len;
9280 guint8 *data_ptr = NULL;
9282 guint8 object_type_id = 0;
9283 const char *codec_name = NULL;
9284 GstCaps *caps = NULL;
9286 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
9288 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
9291 tag = QT_UINT8 (ptr);
9292 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
9294 len = get_size (ptr, &ptr);
9295 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
9299 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
9300 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
9304 guint max_bitrate, avg_bitrate;
9306 object_type_id = QT_UINT8 (ptr);
9307 max_bitrate = QT_UINT32 (ptr + 5);
9308 avg_bitrate = QT_UINT32 (ptr + 9);
9309 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
9310 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
9311 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
9312 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
9313 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
9314 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
9315 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9316 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
9318 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
9319 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
9326 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
9332 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
9336 GST_ERROR_OBJECT (qtdemux, "parse error");
9341 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
9342 * in use, and should also be used to override some other parameters for some
9344 switch (object_type_id) {
9345 case 0x20: /* MPEG-4 */
9346 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
9347 * profile_and_level_indication */
9348 if (data_ptr != NULL && data_len >= 5 &&
9349 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
9350 gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
9351 data_ptr + 4, data_len - 4);
9353 break; /* Nothing special needed here */
9354 case 0x21: /* H.264 */
9355 codec_name = "H.264 / AVC";
9356 caps = gst_caps_new_simple ("video/x-h264",
9357 "stream-format", G_TYPE_STRING, "avc",
9358 "alignment", G_TYPE_STRING, "au", NULL);
9360 case 0x40: /* AAC (any) */
9361 case 0x66: /* AAC Main */
9362 case 0x67: /* AAC LC */
9363 case 0x68: /* AAC SSR */
9364 /* Override channels and rate based on the codec_data, as it's often
9366 /* Only do so for basic setup without HE-AAC extension */
9367 if (data_ptr && data_len == 2) {
9368 guint channels, rateindex, rate;
9370 /* FIXME: add gst_codec_utils_aac_get_{channels|sample_rate}()? */
9371 channels = (data_ptr[1] & 0x7f) >> 3;
9372 if (channels > 0 && channels < 7) {
9373 stream->n_channels = channels;
9374 } else if (channels == 7) {
9375 stream->n_channels = 8;
9378 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7);
9379 rate = gst_codec_utils_aac_get_sample_rate_from_index (rateindex);
9381 stream->rate = rate;
9384 /* Set level and profile if possible */
9385 if (data_ptr != NULL && data_len >= 2) {
9386 gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
9387 data_ptr, data_len);
9390 case 0x60: /* MPEG-2, various profiles */
9396 codec_name = "MPEG-2 video";
9398 gst_caps_unref (stream->caps);
9399 stream->caps = gst_caps_new_simple ("video/mpeg",
9400 "mpegversion", G_TYPE_INT, 2,
9401 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9403 case 0x69: /* MP3 has two different values, accept either */
9405 /* change to mpeg1 layer 3 audio */
9406 gst_caps_set_simple (stream->caps, "layer", G_TYPE_INT, 3,
9407 "mpegversion", G_TYPE_INT, 1, NULL);
9408 codec_name = "MPEG-1 layer 3";
9410 case 0x6A: /* MPEG-1 */
9411 codec_name = "MPEG-1 video";
9413 gst_caps_unref (stream->caps);
9414 stream->caps = gst_caps_new_simple ("video/mpeg",
9415 "mpegversion", G_TYPE_INT, 1,
9416 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9418 case 0x6C: /* MJPEG */
9419 caps = gst_caps_new_simple ("image/jpeg", NULL);
9420 codec_name = "Motion-JPEG";
9422 case 0x6D: /* PNG */
9423 caps = gst_caps_new_simple ("image/png", NULL);
9424 codec_name = "PNG still images";
9426 case 0x6E: /* JPEG2000 */
9427 codec_name = "JPEG-2000";
9428 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9430 case 0xA4: /* Dirac */
9431 codec_name = "Dirac";
9432 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9434 case 0xA5: /* AC3 */
9435 codec_name = "AC-3 audio";
9436 caps = gst_caps_new_simple ("audio/x-ac3",
9437 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
9439 case 0xE1: /* QCELP */
9440 /* QCELP, the codec_data is a riff tag (little endian) with
9441 * 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). */
9442 caps = gst_caps_new_simple ("audio/qcelp", NULL);
9443 codec_name = "QCELP";
9449 /* If we have a replacement caps, then change our caps for this stream */
9451 gst_caps_unref (stream->caps);
9452 stream->caps = caps;
9455 if (codec_name && list)
9456 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
9457 GST_TAG_AUDIO_CODEC, codec_name, NULL);
9459 /* Add the codec_data attribute to caps, if we have it */
9463 buffer = gst_buffer_new_and_alloc (data_len);
9464 memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len);
9466 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
9467 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
9469 #ifdef QTDEMUX_MODIFICATION
9470 /* Check the possiblity of H263 in case of mp4v fourCC */
9471 if(stream->fourcc == FOURCC_mp4v) {
9472 gboolean err = TRUE;
9475 GST_ERROR_OBJECT (qtdemux, "Checking for the Possibility of H263");
9476 for(i=0; i<data_len-4; i++) {
9477 if(QT_UINT32(data_ptr+i) == 0x00000120) {
9478 GST_ERROR_OBJECT (qtdemux, "Found the VOL Marker in DCI Info, It is MPEG-4 Content");
9484 GST_ERROR_OBJECT (qtdemux, "Not Found the VOL Marker in DCI Info, Modifying the CAPS to H263");
9485 stream->fourcc = GST_MAKE_FOURCC ('h', '2', '6', '3');
9486 stream->caps = gst_caps_from_string ("video/x-h263");
9487 gst_buffer_unref (buffer);
9493 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
9495 gst_buffer_unref (buffer);
9500 #define _codec(name) \
9503 *codec_name = g_strdup (name); \
9508 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9509 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
9512 const GstStructure *s;
9516 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '):
9517 _codec ("PNG still images");
9518 caps = gst_caps_new_simple ("image/png", NULL);
9520 case GST_MAKE_FOURCC ('j', 'p', 'e', 'g'):
9521 _codec ("JPEG still images");
9522 caps = gst_caps_new_simple ("image/jpeg", NULL);
9524 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
9525 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
9526 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
9527 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
9528 _codec ("Motion-JPEG");
9529 caps = gst_caps_new_simple ("image/jpeg", NULL);
9531 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
9532 _codec ("Motion-JPEG format B");
9533 caps = gst_caps_new_simple ("video/x-mjpeg-b", NULL);
9535 case GST_MAKE_FOURCC ('m', 'j', 'p', '2'):
9536 _codec ("JPEG-2000");
9537 /* override to what it should be according to spec, avoid palette_data */
9538 stream->bits_per_sample = 24;
9539 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
9541 case GST_MAKE_FOURCC ('S', 'V', 'Q', '3'):
9542 _codec ("Sorensen video v.3");
9543 caps = gst_caps_new_simple ("video/x-svq",
9544 "svqversion", G_TYPE_INT, 3, NULL);
9546 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
9547 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
9548 _codec ("Sorensen video v.1");
9549 caps = gst_caps_new_simple ("video/x-svq",
9550 "svqversion", G_TYPE_INT, 1, NULL);
9552 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9556 _codec ("Raw RGB video");
9557 bps = QT_UINT16 (stsd_data + 98);
9558 /* set common stuff */
9559 caps = gst_caps_new_simple ("video/x-raw-rgb",
9560 "endianness", G_TYPE_INT, G_BYTE_ORDER, "depth", G_TYPE_INT, bps,
9565 gst_caps_set_simple (caps,
9566 "bpp", G_TYPE_INT, 16,
9567 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9568 "red_mask", G_TYPE_INT, 0x7c00,
9569 "green_mask", G_TYPE_INT, 0x03e0,
9570 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9573 gst_caps_set_simple (caps,
9574 "bpp", G_TYPE_INT, 16,
9575 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9576 "red_mask", G_TYPE_INT, 0xf800,
9577 "green_mask", G_TYPE_INT, 0x07e0,
9578 "blue_mask", G_TYPE_INT, 0x001f, NULL);
9581 gst_caps_set_simple (caps,
9582 "bpp", G_TYPE_INT, 24,
9583 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9584 "red_mask", G_TYPE_INT, 0xff0000,
9585 "green_mask", G_TYPE_INT, 0x00ff00,
9586 "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
9589 gst_caps_set_simple (caps,
9590 "bpp", G_TYPE_INT, 32,
9591 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9592 "alpha_mask", G_TYPE_INT, 0xff000000,
9593 "red_mask", G_TYPE_INT, 0x00ff0000,
9594 "green_mask", G_TYPE_INT, 0x0000ff00,
9595 "blue_mask", G_TYPE_INT, 0x000000ff, NULL);
9603 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
9604 _codec ("Raw planar YUV 4:2:0");
9605 caps = gst_caps_new_simple ("video/x-raw-yuv",
9606 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
9609 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
9610 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
9611 _codec ("Raw packed YUV 4:2:2");
9612 caps = gst_caps_new_simple ("video/x-raw-yuv",
9613 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
9616 case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'):
9617 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
9618 _codec ("Raw packed YUV 4:2:2");
9619 caps = gst_caps_new_simple ("video/x-raw-yuv",
9620 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'),
9623 case GST_MAKE_FOURCC ('v', '2', '1', '0'):
9624 _codec ("Raw packed YUV 10-bit 4:2:2");
9625 caps = gst_caps_new_simple ("video/x-raw-yuv",
9626 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('v', '2', '1', '0'),
9629 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
9630 _codec ("Raw packed RGB 10-bit 4:4:4");
9631 caps = gst_caps_new_simple ("video/x-raw-rgb",
9632 "endianness", G_TYPE_INT, G_BIG_ENDIAN, "depth", G_TYPE_INT, 30,
9633 "bpp", G_TYPE_INT, 32,
9634 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9635 "red_mask", G_TYPE_INT, 0x3ff00000,
9636 "green_mask", G_TYPE_INT, 0x000ffc00,
9637 "blue_mask", G_TYPE_INT, 0x000003ff, NULL);
9639 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
9640 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
9641 _codec ("MPEG-1 video");
9642 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
9643 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9645 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
9646 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
9647 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
9648 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
9649 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080i60 */
9650 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
9651 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
9652 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
9653 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
9654 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
9655 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
9656 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 */
9657 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
9658 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
9659 _codec ("MPEG-2 video");
9660 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
9661 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9663 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
9664 _codec ("GIF still images");
9665 caps = gst_caps_new_simple ("image/gif", NULL);
9667 case GST_MAKE_FOURCC ('h', '2', '6', '3'):
9668 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
9669 case GST_MAKE_FOURCC ('s', '2', '6', '3'):
9670 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
9672 /* ffmpeg uses the height/width props, don't know why */
9673 caps = gst_caps_new_simple ("video/x-h263", NULL);
9675 case GST_MAKE_FOURCC ('m', 'p', '4', 'v'):
9676 case GST_MAKE_FOURCC ('M', 'P', '4', 'V'):
9677 _codec ("MPEG-4 video");
9678 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
9679 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9681 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
9682 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
9683 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
9684 caps = gst_caps_new_simple ("video/x-msmpeg",
9685 "msmpegversion", G_TYPE_INT, 43, NULL);
9687 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
9688 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
9689 _codec ("3ivX video");
9690 caps = gst_caps_new_simple ("video/x-3ivx", NULL);
9692 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
9694 caps = gst_caps_new_simple ("video/x-divx",
9695 "divxversion", G_TYPE_INT, 3, NULL);
9697 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
9698 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
9700 caps = gst_caps_new_simple ("video/x-divx",
9701 "divxversion", G_TYPE_INT, 4, NULL);
9703 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
9705 caps = gst_caps_new_simple ("video/x-divx",
9706 "divxversion", G_TYPE_INT, 5, NULL);
9708 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
9709 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
9710 _codec ("XVID MPEG-4");
9711 caps = gst_caps_new_simple ("video/x-xvid", NULL);
9714 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
9715 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
9716 caps = gst_caps_new_simple ("video/mpeg",
9717 "mpegversion", G_TYPE_INT, 4, NULL);
9719 *codec_name = g_strdup ("FFmpeg MPEG-4");
9722 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
9724 caps = gst_caps_new_simple ("video/x-cinepak", NULL);
9726 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
9727 _codec ("Apple QuickDraw");
9728 caps = gst_caps_new_simple ("video/x-qdrw", NULL);
9730 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
9731 _codec ("Apple video");
9732 caps = gst_caps_new_simple ("video/x-apple-video", NULL);
9734 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
9735 _codec ("H.264 / AVC");
9736 caps = gst_caps_new_simple ("video/x-h264",
9737 "stream-format", G_TYPE_STRING, "avc",
9738 "alignment", G_TYPE_STRING, "au", NULL);
9740 case GST_MAKE_FOURCC ('r', 'l', 'e', ' '):
9741 _codec ("Run-length encoding");
9742 caps = gst_caps_new_simple ("video/x-rle",
9743 "layout", G_TYPE_STRING, "quicktime", NULL);
9745 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
9746 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
9747 _codec ("Indeo Video 3");
9748 caps = gst_caps_new_simple ("video/x-indeo",
9749 "indeoversion", G_TYPE_INT, 3, NULL);
9751 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
9752 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
9753 _codec ("Intel Video 4");
9754 caps = gst_caps_new_simple ("video/x-indeo",
9755 "indeoversion", G_TYPE_INT, 4, NULL);
9757 case GST_MAKE_FOURCC ('d', 'v', 'c', 'p'):
9758 case GST_MAKE_FOURCC ('d', 'v', 'c', ' '):
9759 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
9760 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
9761 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
9762 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
9763 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
9764 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
9765 _codec ("DV Video");
9766 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
9767 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9769 case GST_MAKE_FOURCC ('d', 'v', '5', 'n'): /* DVCPRO50 NTSC */
9770 case GST_MAKE_FOURCC ('d', 'v', '5', 'p'): /* DVCPRO50 PAL */
9771 _codec ("DVCPro50 Video");
9772 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
9773 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9775 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
9776 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
9777 _codec ("DVCProHD Video");
9778 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
9779 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
9781 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
9782 _codec ("Apple Graphics (SMC)");
9783 caps = gst_caps_new_simple ("video/x-smc", NULL);
9785 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
9787 caps = gst_caps_new_simple ("video/x-vp3", NULL);
9789 case GST_MAKE_FOURCC ('X', 'i', 'T', 'h'):
9791 caps = gst_caps_new_simple ("video/x-theora", NULL);
9792 /* theora uses one byte of padding in the data stream because it does not
9793 * allow 0 sized packets while theora does */
9794 stream->padding = 1;
9796 case GST_MAKE_FOURCC ('d', 'r', 'a', 'c'):
9798 caps = gst_caps_new_simple ("video/x-dirac", NULL);
9800 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
9801 _codec ("TIFF still images");
9802 caps = gst_caps_new_simple ("image/tiff", NULL);
9804 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
9805 _codec ("Apple Intermediate Codec");
9806 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
9808 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
9809 _codec ("AVID DNxHD");
9810 caps = gst_caps_from_string ("video/x-dnxhd");
9812 case GST_MAKE_FOURCC ('V', 'P', '8', '0'):
9814 caps = gst_caps_from_string ("video/x-vp8");
9818 caps = gst_caps_new_simple ("video/x-wmv",
9819 "wmvversion", G_TYPE_INT, 3,
9820 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
9823 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
9828 s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
9829 GST_FOURCC_ARGS (fourcc));
9830 caps = gst_caps_new_simple (s, NULL);
9835 #ifdef QTDEMUX_MODIFICATION
9836 if(stream->orientation != -1) {
9837 gst_caps_set_simple (caps, "orientation", G_TYPE_INT, stream->orientation, NULL);
9841 /* enable clipping for raw video streams */
9842 s = gst_caps_get_structure (caps, 0);
9843 name = gst_structure_get_name (s);
9844 if (g_str_has_prefix (name, "video/x-raw-")) {
9845 stream->need_clip = TRUE;
9851 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
9852 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
9855 const GstStructure *s;
9859 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
9862 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
9863 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
9864 _codec ("Raw 8-bit PCM audio");
9865 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 8,
9866 "depth", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
9868 case GST_MAKE_FOURCC ('t', 'w', 'o', 's'):
9869 endian = G_BIG_ENDIAN;
9871 case GST_MAKE_FOURCC ('s', 'o', 'w', 't'):
9877 endian = G_LITTLE_ENDIAN;
9879 depth = stream->bytes_per_packet * 8;
9880 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
9883 caps = gst_caps_new_simple ("audio/x-raw-int",
9884 "width", G_TYPE_INT, depth, "depth", G_TYPE_INT, depth,
9885 "endianness", G_TYPE_INT, endian,
9886 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9889 case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
9890 _codec ("Raw 64-bit floating-point audio");
9891 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 64,
9892 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9894 case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
9895 _codec ("Raw 32-bit floating-point audio");
9896 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 32,
9897 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
9900 _codec ("Raw 24-bit PCM audio");
9901 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
9903 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 24,
9904 "depth", G_TYPE_INT, 24,
9905 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9906 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9908 case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
9909 _codec ("Raw 32-bit PCM audio");
9910 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 32,
9911 "depth", G_TYPE_INT, 32,
9912 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
9913 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
9915 case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
9916 _codec ("Mu-law audio");
9917 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
9919 case GST_MAKE_FOURCC ('a', 'l', 'a', 'w'):
9920 _codec ("A-law audio");
9921 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
9925 _codec ("Microsoft ADPCM");
9926 /* Microsoft ADPCM-ACM code 2 */
9927 caps = gst_caps_new_simple ("audio/x-adpcm",
9928 "layout", G_TYPE_STRING, "microsoft", NULL);
9932 _codec ("DVI/IMA ADPCM");
9933 caps = gst_caps_new_simple ("audio/x-adpcm",
9934 "layout", G_TYPE_STRING, "dvi", NULL);
9938 _codec ("DVI/Intel IMA ADPCM");
9939 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
9940 caps = gst_caps_new_simple ("audio/x-adpcm",
9941 "layout", G_TYPE_STRING, "quicktime", NULL);
9945 /* MPEG layer 3, CBR only (pre QT4.1) */
9946 case GST_MAKE_FOURCC ('.', 'm', 'p', '3'):
9947 _codec ("MPEG-1 layer 3");
9948 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
9949 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
9950 "mpegversion", G_TYPE_INT, 1, NULL);
9953 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
9954 _codec ("EAC-3 audio");
9955 caps = gst_caps_new_simple ("audio/x-eac3",
9956 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
9957 stream->sampled = TRUE;
9959 case GST_MAKE_FOURCC ('a', 'c', '-', '3'):
9960 _codec ("AC-3 audio");
9961 caps = gst_caps_new_simple ("audio/x-ac3",
9962 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
9963 stream->sampled = TRUE;
9965 case GST_MAKE_FOURCC ('M', 'A', 'C', '3'):
9967 caps = gst_caps_new_simple ("audio/x-mace",
9968 "maceversion", G_TYPE_INT, 3, NULL);
9970 case GST_MAKE_FOURCC ('M', 'A', 'C', '6'):
9972 caps = gst_caps_new_simple ("audio/x-mace",
9973 "maceversion", G_TYPE_INT, 6, NULL);
9975 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
9977 caps = gst_caps_new_simple ("application/ogg", NULL);
9979 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
9980 _codec ("DV audio");
9981 caps = gst_caps_new_simple ("audio/x-dv", NULL);
9983 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
9984 _codec ("MPEG-4 AAC audio");
9985 caps = gst_caps_new_simple ("audio/mpeg",
9986 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
9987 "stream-format", G_TYPE_STRING, "raw", NULL);
9989 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
9990 _codec ("QDesign Music");
9991 caps = gst_caps_new_simple ("audio/x-qdm", NULL);
9993 case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'):
9994 _codec ("QDesign Music v.2");
9995 /* FIXME: QDesign music version 2 (no constant) */
9997 caps = gst_caps_new_simple ("audio/x-qdm2",
9998 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
9999 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
10000 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
10002 caps = gst_caps_new_simple ("audio/x-qdm2", NULL);
10005 case GST_MAKE_FOURCC ('a', 'g', 's', 'm'):
10006 _codec ("GSM audio");
10007 caps = gst_caps_new_simple ("audio/x-gsm", NULL);
10009 case GST_MAKE_FOURCC ('s', 'a', 'm', 'r'):
10010 _codec ("AMR audio");
10011 caps = gst_caps_new_simple ("audio/AMR", NULL);
10013 case GST_MAKE_FOURCC ('s', 'a', 'w', 'b'):
10014 _codec ("AMR-WB audio");
10015 caps = gst_caps_new_simple ("audio/AMR-WB", NULL);
10017 case GST_MAKE_FOURCC ('i', 'm', 'a', '4'):
10018 _codec ("Quicktime IMA ADPCM");
10019 caps = gst_caps_new_simple ("audio/x-adpcm",
10020 "layout", G_TYPE_STRING, "quicktime", NULL);
10022 case GST_MAKE_FOURCC ('a', 'l', 'a', 'c'):
10023 _codec ("Apple lossless audio");
10024 caps = gst_caps_new_simple ("audio/x-alac", NULL);
10026 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
10027 _codec ("QualComm PureVoice");
10028 caps = gst_caps_from_string ("audio/qcelp");
10032 caps = gst_caps_new_simple ("audio/x-wma", NULL);
10034 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
10040 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10041 GST_FOURCC_ARGS (fourcc));
10042 caps = gst_caps_new_simple (s, NULL);
10047 /* enable clipping for raw audio streams */
10048 s = gst_caps_get_structure (caps, 0);
10049 name = gst_structure_get_name (s);
10050 if (g_str_has_prefix (name, "audio/x-raw-")) {
10051 stream->need_clip = TRUE;
10057 qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
10058 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name)
10062 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc);
10065 case GST_MAKE_FOURCC ('m', 'p', '4', 's'):
10066 _codec ("DVD subtitle");
10067 caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
10069 case GST_MAKE_FOURCC ('t', 'e', 'x', 't'):
10070 _codec ("Quicktime timed text");
10072 case GST_MAKE_FOURCC ('t', 'x', '3', 'g'):
10073 _codec ("3GPP timed text");
10075 caps = gst_caps_new_simple ("text/plain", NULL);
10076 /* actual text piece needs to be extracted */
10077 stream->need_process = TRUE;
10083 s = g_strdup_printf ("text/x-gst-fourcc-%" GST_FOURCC_FORMAT,
10084 GST_FOURCC_ARGS (fourcc));
10085 caps = gst_caps_new_simple (s, NULL);
10092 #ifdef QTDEMUX_MODIFICATION
10094 gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp)
10096 guint32 nsamples = 0; /* Number of samples between two consecutive keyframes */
10097 QtDemuxSample *sample;
10099 if ((*timestamp < stream->trickplay_info->start_pos) && !qtdemux->fwdtrick_mode) {
10100 GST_LOG_OBJECT (qtdemux, "Received shown sample... not applying trickplay algo");
10104 if ((stream->trickplay_info->next_kidx == 0 ) && (stream->sample_index < stream->to_sample)) {
10105 stream->trickplay_info->next_kidx = stream->trickplay_info->prev_kidx = stream->sample_index;
10107 GST_DEBUG_OBJECT (qtdemux, " Previous index : %d and ts = %"GST_TIME_FORMAT,
10108 stream->trickplay_info->prev_kidx, GST_TIME_ARGS(QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])));
10110 while(1) { /* while loop to handle multiple consecutive key frames */
10111 /* find previous key frame */
10112 stream->trickplay_info->next_kidx = gst_qtdemux_find_next_keyframe (qtdemux, stream, stream->trickplay_info->next_kidx + 1);
10114 /* based no.of sample between key frame and rate, drop frames */
10115 GST_DEBUG_OBJECT (qtdemux, "current index : %d, next key index : %d", stream->sample_index, stream->trickplay_info->next_kidx);
10117 if (qtdemux->fwdtrick_mode) {
10118 /* sending only key frames */
10119 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10120 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10121 stream->trickplay_info->next_kidx = 0;
10122 stream->discont = TRUE;
10126 /* find no.of samples between present and previous key frames */
10127 nsamples = stream->trickplay_info->next_kidx - stream->trickplay_info->prev_kidx;
10129 /* find average duration between key frames */
10130 stream->trickplay_info->kidxs_dur_diff = (QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->next_kidx]) -
10131 QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])) /
10134 stream->trickplay_info->show_samples = nsamples / qtdemux->segment.rate;
10136 GST_DEBUG_OBJECT (qtdemux, "duration 2 between consecutive frames : %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->trickplay_info->kidxs_dur_diff));
10138 if(stream->trickplay_info->show_samples) {
10139 GST_DEBUG_OBJECT (qtdemux, "samples to display between two key frames = %d", stream->trickplay_info->show_samples);
10140 /* found no. of samples to show between key frames */
10141 *timestamp = QTSAMPLE_PTS (stream, &stream->samples[stream->sample_index]);
10143 } else if (!stream->trickplay_info->show_samples &&
10144 (stream->trickplay_info->next_kidx >= (stream->n_samples-1))) {
10145 /* shown samples required to show between 2 key frames */
10146 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10147 if (stream->trickplay_info->next_kidx == stream->n_samples - 1)
10148 stream->sample_index = stream->trickplay_info->next_kidx;
10150 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10151 stream->trickplay_info->next_kidx = 0;
10152 stream->discont = TRUE;
10157 stream->discont = TRUE;
10158 } else if (stream->trickplay_info->next_kidx != 0) {
10159 GST_LOG_OBJECT (qtdemux, "next_kidx = %d and show_samples = %d", stream->trickplay_info->next_kidx, stream->trickplay_info->show_samples);
10161 stream->trickplay_info->show_samples--;
10162 sample = &stream->samples[stream->trickplay_info->prev_kidx];
10163 *timestamp = QTSAMPLE_PTS (stream, sample) +
10164 ((stream->sample_index -stream->trickplay_info->prev_kidx) * (abs (qtdemux->segment.rate)) * (stream->trickplay_info->kidxs_dur_diff));
10166 if (stream->trickplay_info->show_samples == 0) {
10167 /* shown samples required to show between 2 key frames */
10168 GST_DEBUG_OBJECT (qtdemux, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
10169 if (stream->trickplay_info->next_kidx == stream->n_samples - 1)
10170 stream->sample_index = stream->trickplay_info->next_kidx;
10172 stream->sample_index = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
10173 stream->trickplay_info->next_kidx = 0;
10174 stream->discont = TRUE;
10180 gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp)
10182 if (stream->samples[stream->sample_index].keyframe) {
10183 stream->sample_index = stream->to_sample;
10184 GST_LOG_OBJECT (qtdemux, "Received key frame...Jump to end of the segment (idx = %d)...", stream->sample_index);
10188 /* searches next immediate key frame after index */
10190 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index)
10192 gint32 new_index = index;
10194 if (index > str->n_samples) {
10195 GST_WARNING_OBJECT (qtdemux, "index is greater than n_samples...");
10196 new_index = str->n_samples - 1;
10200 /* all keyframes, return index */
10201 if (str->all_keyframe) {
10206 /* else go back until we have a keyframe */
10208 if (new_index == str->n_samples) {
10209 GST_INFO_OBJECT (qtdemux, "Reached n_samples, taking last frame as reference...");
10210 new_index = str->n_samples - 1;
10214 if (str->samples[new_index].keyframe) {
10215 GST_LOG ("Found keyframe...");
10222 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
10223 "gave %d", index, new_index);