blob: 13a537fc54464f4462098238607be2dd77a980cd [file] [log] [blame]
zengliang.li5f31ef42024-05-16 08:27:38 +00001/* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
4 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
5 * Copyright (C) <2007> Julien Moutte <julien@fluendo.com>
6 * Copyright (C) <2009> Tim-Philipp Müller <tim centricular net>
7 * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.com>
8 * Copyright (C) <2013> Sreerenj Balachandran <sreerenj.balachandran@intel.com>
9 * Copyright (C) <2013> Intel Corporation
10 * Copyright (C) <2014> Centricular Ltd
11 * Copyright (C) <2015> YouView TV Ltd.
12 * Copyright (C) <2016> British Broadcasting Corporation
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with this library; if not, write to the
26 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
28 */
29
30/**
31 * SECTION:element-qtdemux
32 * @title: qtdemux
33 *
34 * Demuxes a .mov file into raw or compressed audio and/or video streams.
35 *
36 * This element supports both push and pull-based scheduling, depending on the
37 * capabilities of the upstream elements.
38 *
39 * ## Example launch line
40 * |[
41 * gst-launch-1.0 filesrc location=test.mov ! qtdemux name=demux demux.audio_0 ! queue ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
42 * ]| Play (parse and decode) a .mov file and try to output it to
43 * an automatically detected soundcard and videosink. If the MOV file contains
44 * compressed audio or video data, this will only work if you have the
45 * right decoder elements/plugins installed.
46 *
47 */
48
49#ifdef HAVE_CONFIG_H
50#include "config.h"
51#endif
52
53#include "gst/gst-i18n-plugin.h"
54
55#include <glib/gprintf.h>
56#include <gst/base/base.h>
57#include <gst/tag/tag.h>
58#include <gst/audio/audio.h>
59#include <gst/riff/riff.h>
60#include <gst/pbutils/pbutils.h>
61
62#include "gstisomp4elements.h"
63#include "qtatomparser.h"
64#include "qtdemux_types.h"
65#include "qtdemux_dump.h"
66#include "fourcc.h"
67#include "descriptors.h"
68#include "qtdemux_lang.h"
69#include "qtdemux.h"
70#include "qtpalette.h"
71#include "qtdemux_tags.h"
72#include "qtdemux_tree.h"
73#include "qtdemux-webvtt.h"
74
75#include <stdlib.h>
76#include <string.h>
77
78#include <math.h>
79#include <gst/math-compat.h>
80
81#ifdef HAVE_ZLIB
82# include <zlib.h>
83#endif
84
85/* max. size considered 'sane' for non-mdat atoms */
86#define QTDEMUX_MAX_ATOM_SIZE (32*1024*1024)
87
88/* if the sample index is larger than this, something is likely wrong */
89#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
90
91/* For converting qt creation times to unix epoch times */
92#define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
93#define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
94#define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
95 QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
96
97#define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))
98
99#define STREAM_IS_EOS(s) ((s)->time_position == GST_CLOCK_TIME_NONE)
100
101#define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
102
103#define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s))
104#define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
105#define QTDEMUX_NTH_STREAM(demux,idx) \
106 QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
107#define QTDEMUX_NTH_OLD_STREAM(demux,idx) \
108 QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
109
110#define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
111
112GST_DEBUG_CATEGORY (qtdemux_debug);
113#define GST_CAT_DEFAULT qtdemux_debug
114
115typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
116typedef struct _QtDemuxAavdEncryptionInfo QtDemuxAavdEncryptionInfo;
117
118/* Macros for converting to/from timescale */
119#define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
120#define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
121
122#define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
123#define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
124
125/* timestamp is the DTS */
126#define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
127/* timestamp + offset + cslg_shift is the outgoing PTS */
128#define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
129/* timestamp + offset is the PTS used for internal seek calculations */
130#define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
131/* timestamp + duration - dts is the duration */
132#define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
133
134#define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
135
136#define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
137#define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
138 GST_TRACE("Locking from thread %p", g_thread_self()); \
139 g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
140 GST_TRACE("Locked from thread %p", g_thread_self()); \
141 } G_STMT_END
142
143#define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
144 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
145 g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
146 } G_STMT_END
147
148/*
149 * Quicktime has tracks and segments. A track is a continuous piece of
150 * multimedia content. The track is not always played from start to finish but
151 * instead, pieces of the track are 'cut out' and played in sequence. This is
152 * what the segments do.
153 *
154 * Inside the track we have keyframes (K) and delta frames. The track has its
155 * own timing, which starts from 0 and extends to end. The position in the track
156 * is called the media_time.
157 *
158 * The segments now describe the pieces that should be played from this track
159 * and are basically tuples of media_time/duration/rate entries. We can have
160 * multiple segments and they are all played after one another. An example:
161 *
162 * segment 1: media_time: 1 second, duration: 1 second, rate 1
163 * segment 2: media_time: 3 second, duration: 2 second, rate 2
164 *
165 * To correctly play back this track, one must play: 1 second of media starting
166 * from media_time 1 followed by 2 seconds of media starting from media_time 3
167 * at a rate of 2.
168 *
169 * Each of the segments will be played at a specific time, the first segment at
170 * time 0, the second one after the duration of the first one, etc.. Note that
171 * the time in resulting playback is not identical to the media_time of the
172 * track anymore.
173 *
174 * Visually, assuming the track has 4 second of media_time:
175 *
176 * (a) (b) (c) (d)
177 * .-----------------------------------------------------------.
178 * track: | K.....K.........K........K.......K.......K...........K... |
179 * '-----------------------------------------------------------'
180 * 0 1 2 3 4
181 * .------------^ ^ .----------^ ^
182 * / .-------------' / .------------------'
183 * / / .-----' /
184 * .--------------. .--------------.
185 * | segment 1 | | segment 2 |
186 * '--------------' '--------------'
187 *
188 * The challenge here is to cut out the right pieces of the track for each of
189 * the playback segments. This fortunately can easily be done with the SEGMENT
190 * events of GStreamer.
191 *
192 * For playback of segment 1, we need to provide the decoder with the keyframe
193 * (a), in the above figure, but we must instruct it only to output the decoded
194 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
195 * position set to the time of the segment: 0.
196 *
197 * We then proceed to push data from keyframe (a) to frame (b). The decoder
198 * decodes but clips all before media_time 1.
199 *
200 * After finishing a segment, we push out a new SEGMENT event with the clipping
201 * boundaries of the new data.
202 *
203 * This is a good usecase for the GStreamer accumulated SEGMENT events.
204 */
205
206struct _QtDemuxSegment
207{
208 /* global time and duration, all gst time */
209 GstClockTime time;
210 GstClockTime stop_time;
211 GstClockTime duration;
212 /* media time of trak, all gst time */
213 GstClockTime media_start;
214 GstClockTime media_stop;
215 gdouble rate;
216 /* Media start time in trak timescale units */
217 guint32 trak_media_start;
218};
219
220#define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
221
222/* Used with fragmented MP4 files (mfra atom) */
223struct _QtDemuxRandomAccessEntry
224{
225 GstClockTime ts;
226 guint64 moof_offset;
227};
228
229
230/* Contains properties and cryptographic info for a set of samples from a
231 * track protected using Common Encryption (cenc) */
232struct _QtDemuxCencSampleSetInfo
233{
234 GstStructure *default_properties;
235
236 /* @crypto_info holds one GstStructure per sample */
237 GPtrArray *crypto_info;
238};
239
240struct _QtDemuxAavdEncryptionInfo
241{
242 GstStructure *default_properties;
243};
244
245static const gchar *
246qt_demux_state_string (enum QtDemuxState state)
247{
248 switch (state) {
249 case QTDEMUX_STATE_INITIAL:
250 return "<INITIAL>";
251 case QTDEMUX_STATE_HEADER:
252 return "<HEADER>";
253 case QTDEMUX_STATE_MOVIE:
254 return "<MOVIE>";
255 case QTDEMUX_STATE_BUFFER_MDAT:
256 return "<BUFFER_MDAT>";
257 default:
258 return "<UNKNOWN>";
259 }
260}
261
262static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
263
264static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);
265
266static GstStaticPadTemplate gst_qtdemux_sink_template =
267 GST_STATIC_PAD_TEMPLATE ("sink",
268 GST_PAD_SINK,
269 GST_PAD_ALWAYS,
270 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
271 "application/x-3gp")
272 );
273
274static GstStaticPadTemplate gst_qtdemux_videosrc_template =
275GST_STATIC_PAD_TEMPLATE ("video_%u",
276 GST_PAD_SRC,
277 GST_PAD_SOMETIMES,
278 GST_STATIC_CAPS_ANY);
279
280static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
281GST_STATIC_PAD_TEMPLATE ("audio_%u",
282 GST_PAD_SRC,
283 GST_PAD_SOMETIMES,
284 GST_STATIC_CAPS_ANY);
285
286static GstStaticPadTemplate gst_qtdemux_subsrc_template =
287GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
288 GST_PAD_SRC,
289 GST_PAD_SOMETIMES,
290 GST_STATIC_CAPS_ANY);
291
292#define gst_qtdemux_parent_class parent_class
293G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
294GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qtdemux, "qtdemux",
295 GST_RANK_PRIMARY, GST_TYPE_QTDEMUX, isomp4_element_init (plugin));
296
297static void gst_qtdemux_dispose (GObject * object);
298static void gst_qtdemux_finalize (GObject * object);
299
300static guint32
301gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
302 GstClockTime media_time);
303static guint32
304gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
305 QtDemuxStream * str, gint64 media_offset);
306
307#if 0
308static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
309static GstIndex *gst_qtdemux_get_index (GstElement * element);
310#endif
311static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
312 GstStateChange transition);
313static void gst_qtdemux_set_context (GstElement * element,
314 GstContext * context);
315static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
316static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
317 GstObject * parent, GstPadMode mode, gboolean active);
318
319static void gst_qtdemux_loop (GstPad * pad);
320static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
321 GstBuffer * inbuf);
322static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
323 GstEvent * event);
324static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
325 GstQuery * query);
326static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
327static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
328 QtDemuxStream * stream);
329static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
330 QtDemuxStream * stream);
331static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
332 gboolean force);
333
334static void gst_qtdemux_check_seekability (GstQTDemux * demux);
335
336static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
337 const guint8 * buffer, guint length);
338static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
339 const guint8 * buffer, guint length);
340static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
341
342static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
343 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
344 GstTagList * list);
345static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
346 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
347 const guint8 * stsd_entry_data, gchar ** codec_name);
348static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
349 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
350 const guint8 * data, int len, gchar ** codec_name);
351static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
352 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
353 gchar ** codec_name);
354static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
355 QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
356 const guint8 * stsd_entry_data, gchar ** codec_name);
357
358static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
359 QtDemuxStream * stream, guint32 n);
360static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
361static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
362static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
363static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
364static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
365static void qtdemux_do_allocation (QtDemuxStream * stream,
366 GstQTDemux * qtdemux);
367static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
368 QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
369static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
370 QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
371 GstClockTime * _start, GstClockTime * _stop);
372static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
373 QtDemuxStream * stream, gint segment_index, GstClockTime pos);
374
375static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
376static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
377
378static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
379
380static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
381 QtDemuxStream * stream, guint sample_index);
382static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
383 const gchar * id);
384static void qtdemux_gst_structure_free (GstStructure * gststructure);
385static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
386
387static void
388gst_qtdemux_class_init (GstQTDemuxClass * klass)
389{
390 GObjectClass *gobject_class;
391 GstElementClass *gstelement_class;
392
393 gobject_class = (GObjectClass *) klass;
394 gstelement_class = (GstElementClass *) klass;
395
396 parent_class = g_type_class_peek_parent (klass);
397
398 gobject_class->dispose = gst_qtdemux_dispose;
399 gobject_class->finalize = gst_qtdemux_finalize;
400
401 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
402#if 0
403 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
404 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
405#endif
406 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
407
408 gst_tag_register_musicbrainz_tags ();
409
410 gst_element_class_add_static_pad_template (gstelement_class,
411 &gst_qtdemux_sink_template);
412 gst_element_class_add_static_pad_template (gstelement_class,
413 &gst_qtdemux_videosrc_template);
414 gst_element_class_add_static_pad_template (gstelement_class,
415 &gst_qtdemux_audiosrc_template);
416 gst_element_class_add_static_pad_template (gstelement_class,
417 &gst_qtdemux_subsrc_template);
418 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
419 "Codec/Demuxer",
420 "Demultiplex a QuickTime file into audio and video streams",
421 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
422
423 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
424 gst_riff_init ();
425}
426
427static void
428gst_qtdemux_init (GstQTDemux * qtdemux)
429{
430 qtdemux->sinkpad =
431 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
432 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
433 gst_pad_set_activatemode_function (qtdemux->sinkpad,
434 qtdemux_sink_activate_mode);
435 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
436 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
437 gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
438 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
439
440 qtdemux->adapter = gst_adapter_new ();
441 g_queue_init (&qtdemux->protection_event_queue);
442 qtdemux->flowcombiner = gst_flow_combiner_new ();
443 g_mutex_init (&qtdemux->expose_lock);
444
445 qtdemux->active_streams = g_ptr_array_new_with_free_func
446 ((GDestroyNotify) gst_qtdemux_stream_unref);
447 qtdemux->old_streams = g_ptr_array_new_with_free_func
448 ((GDestroyNotify) gst_qtdemux_stream_unref);
449
450 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
451
452 gst_qtdemux_reset (qtdemux, TRUE);
zengliang.lid0c84c32024-05-17 03:33:20 +0000453 qtdemux->cal_discontinuity_pos = FALSE;
454 qtdemux->discontinuity_base_pos = 0;
zengliang.li5f31ef42024-05-16 08:27:38 +0000455}
456
457static void
458gst_qtdemux_finalize (GObject * object)
459{
460 GstQTDemux *qtdemux = GST_QTDEMUX (object);
461
462 g_free (qtdemux->redirect_location);
463
464 G_OBJECT_CLASS (parent_class)->finalize (object);
465}
466
467static void
468gst_qtdemux_dispose (GObject * object)
469{
470 GstQTDemux *qtdemux = GST_QTDEMUX (object);
471
472 if (qtdemux->adapter) {
473 g_object_unref (G_OBJECT (qtdemux->adapter));
474 qtdemux->adapter = NULL;
475 }
476 gst_tag_list_unref (qtdemux->tag_list);
477 gst_flow_combiner_free (qtdemux->flowcombiner);
478 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
479 NULL);
480 g_queue_clear (&qtdemux->protection_event_queue);
481
482 g_free (qtdemux->cenc_aux_info_sizes);
483 qtdemux->cenc_aux_info_sizes = NULL;
484 g_mutex_clear (&qtdemux->expose_lock);
485
486 g_ptr_array_free (qtdemux->active_streams, TRUE);
487 g_ptr_array_free (qtdemux->old_streams, TRUE);
488
489 G_OBJECT_CLASS (parent_class)->dispose (object);
490}
491
492static void
493gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
494{
495 if (qtdemux->redirect_location) {
496 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
497 (_("This file contains no playable streams.")),
498 ("no known streams found, a redirect message has been posted"),
499 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
500 } else {
501 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
502 (_("This file contains no playable streams.")),
503 ("no known streams found"));
504 }
505}
506
507static GstBuffer *
508_gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
509{
510 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
511 mem, size, 0, size, mem, free_func);
512}
513
514static GstFlowReturn
515gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
516 GstBuffer ** buf)
517{
518 GstFlowReturn flow;
519 GstMapInfo map;
520 gsize bsize;
521
522 if (G_UNLIKELY (size == 0)) {
523 GstFlowReturn ret;
524 GstBuffer *tmp = NULL;
525
526 ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
527 if (ret != GST_FLOW_OK)
528 return ret;
529
530 gst_buffer_map (tmp, &map, GST_MAP_READ);
531 size = QT_UINT32 (map.data);
532 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
533
534 gst_buffer_unmap (tmp, &map);
535 gst_buffer_unref (tmp);
536 }
537
538 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
539 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
540 if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
541 /* we're pulling header but already got most interesting bits,
542 * so never mind the rest (e.g. tags) (that much) */
543 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
544 size);
545 return GST_FLOW_EOS;
546 } else {
547 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
548 (_("This file is invalid and cannot be played.")),
549 ("atom has bogus size %" G_GUINT64_FORMAT, size));
550 return GST_FLOW_ERROR;
551 }
552 }
553
554 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
555
556 if (G_UNLIKELY (flow != GST_FLOW_OK))
557 return flow;
558
559 bsize = gst_buffer_get_size (*buf);
560 /* Catch short reads - we don't want any partial atoms */
561 if (G_UNLIKELY (bsize < size)) {
562 GST_WARNING_OBJECT (qtdemux,
563 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
564 gst_buffer_unref (*buf);
565 *buf = NULL;
566 return GST_FLOW_EOS;
567 }
568
569 return flow;
570}
571
572#if 1
573static gboolean
574gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
575 GstFormat src_format, gint64 src_value, GstFormat dest_format,
576 gint64 * dest_value)
577{
578 gboolean res = TRUE;
579 QtDemuxStream *stream = gst_pad_get_element_private (pad);
580 gint32 index;
581
582 if (stream->subtype != FOURCC_vide) {
583 res = FALSE;
584 goto done;
585 }
586
587 switch (src_format) {
588 case GST_FORMAT_TIME:
589 switch (dest_format) {
590 case GST_FORMAT_BYTES:{
591 index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
592 if (-1 == index) {
593 res = FALSE;
594 goto done;
595 }
596
597 *dest_value = stream->samples[index].offset;
598
599 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
600 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
601 GST_TIME_ARGS (src_value), *dest_value);
602 break;
603 }
604 default:
605 res = FALSE;
606 break;
607 }
608 break;
609 case GST_FORMAT_BYTES:
610 switch (dest_format) {
611 case GST_FORMAT_TIME:{
612 index =
613 gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
614 stream, src_value);
615
616 if (-1 == index) {
617 res = FALSE;
618 goto done;
619 }
620
621 *dest_value =
622 QTSTREAMTIME_TO_GSTTIME (stream,
623 stream->samples[index].timestamp);
624 GST_DEBUG_OBJECT (qtdemux,
625 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
626 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
627 break;
628 }
629 default:
630 res = FALSE;
631 break;
632 }
633 break;
634 default:
635 res = FALSE;
636 break;
637 }
638
639done:
640 return res;
641}
642#endif
643
644static gboolean
645gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
646{
647 gboolean res = FALSE;
648
649 *duration = GST_CLOCK_TIME_NONE;
650
651 if (qtdemux->duration != 0 &&
652 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
653 *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
654 res = TRUE;
655 } else {
656 *duration = GST_CLOCK_TIME_NONE;
657 }
658
659 return res;
660}
661
662static gboolean
663gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
664 GstQuery * query)
665{
666 gboolean res = FALSE;
667 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
668
669 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
670
671 switch (GST_QUERY_TYPE (query)) {
672 case GST_QUERY_POSITION:{
673 GstFormat fmt;
674
675 gst_query_parse_position (query, &fmt, NULL);
676 if (fmt == GST_FORMAT_TIME
677 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
678 gst_query_set_position (query, GST_FORMAT_TIME,
679 qtdemux->segment.position);
680 res = TRUE;
681 }
682 }
683 break;
684 case GST_QUERY_DURATION:{
685 GstFormat fmt;
686
687 gst_query_parse_duration (query, &fmt, NULL);
688 if (fmt == GST_FORMAT_TIME) {
689 /* First try to query upstream */
690 res = gst_pad_query_default (pad, parent, query);
691 if (!res) {
692 GstClockTime duration;
693 if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
694 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
695 res = TRUE;
696 }
697 }
698 }
699 break;
700 }
701 case GST_QUERY_CONVERT:{
702 GstFormat src_fmt, dest_fmt;
703 gint64 src_value, dest_value = 0;
704
705 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
706
707 res = gst_qtdemux_src_convert (qtdemux, pad,
708 src_fmt, src_value, dest_fmt, &dest_value);
709 if (res)
710 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
711
712 break;
713 }
714 case GST_QUERY_FORMATS:
715 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
716 res = TRUE;
717 break;
718 case GST_QUERY_SEEKING:{
719 GstFormat fmt;
720 gboolean seekable;
721
722 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
723
724 if (fmt == GST_FORMAT_BYTES) {
725 /* We always refuse BYTES seeks from downstream */
726 break;
727 }
728
729 /* try upstream first */
730 res = gst_pad_query_default (pad, parent, query);
731
732 if (!res) {
733 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
734 if (fmt == GST_FORMAT_TIME) {
735 GstClockTime duration;
736
737 gst_qtdemux_get_duration (qtdemux, &duration);
738 seekable = TRUE;
739 if (!qtdemux->pullbased) {
740 GstQuery *q;
741
742 /* we might be able with help from upstream */
743 seekable = FALSE;
744 q = gst_query_new_seeking (GST_FORMAT_BYTES);
745 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
746 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
747 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
748 }
749 gst_query_unref (q);
750 }
751 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
752 res = TRUE;
753 }
754 }
755 break;
756 }
757 case GST_QUERY_SEGMENT:
758 {
759 GstFormat format;
760 gint64 start, stop;
761
762 format = qtdemux->segment.format;
763
764 start =
765 gst_segment_to_stream_time (&qtdemux->segment, format,
766 qtdemux->segment.start);
767 if ((stop = qtdemux->segment.stop) == -1)
768 stop = qtdemux->segment.duration;
769 else
770 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
771
772 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
773 res = TRUE;
774 break;
775 }
776 default:
777 res = gst_pad_query_default (pad, parent, query);
778 break;
779 }
780
781 return res;
782}
783
784static void
785gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
786{
787 if (G_LIKELY (stream->pad)) {
788 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
789 GST_DEBUG_PAD_NAME (stream->pad));
790
791 if (!gst_tag_list_is_empty (stream->stream_tags)) {
792 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
793 stream->stream_tags);
794 gst_pad_push_event (stream->pad,
795 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
796 }
797
798 if (G_UNLIKELY (stream->send_global_tags)) {
799 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
800 qtdemux->tag_list);
801 gst_pad_push_event (stream->pad,
802 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
803 stream->send_global_tags = FALSE;
804 }
805 }
806}
807
808/* push event on all source pads; takes ownership of the event */
809static void
810gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
811{
812 gboolean has_valid_stream = FALSE;
813 GstEventType etype = GST_EVENT_TYPE (event);
814 guint i;
815
816 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
817 GST_EVENT_TYPE_NAME (event));
818
819 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
820 GstPad *pad;
821 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
822 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
823
824 if ((pad = stream->pad)) {
825 has_valid_stream = TRUE;
826
827 if (etype == GST_EVENT_EOS) {
828 /* let's not send twice */
829 if (stream->sent_eos)
830 continue;
831 stream->sent_eos = TRUE;
832 }
833
834 gst_pad_push_event (pad, gst_event_ref (event));
835 }
836 }
837
838 gst_event_unref (event);
839
840 /* if it is EOS and there are no pads, post an error */
841 if (!has_valid_stream && etype == GST_EVENT_EOS) {
842 gst_qtdemux_post_no_playable_stream_error (qtdemux);
843 }
844}
845
846typedef struct
847{
848 guint64 media_time;
849} FindData;
850
851static gint
852find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
853{
854 if ((gint64) s1->timestamp > *media_time)
855 return 1;
856 if ((gint64) s1->timestamp == *media_time)
857 return 0;
858
859 return -1;
860}
861
862/* find the index of the sample that includes the data for @media_time using a
863 * binary search. Only to be called in optimized cases of linear search below.
864 *
865 * Returns the index of the sample with the corresponding *DTS*.
866 */
867static guint32
868gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
869 guint64 media_time)
870{
871 QtDemuxSample *result;
872 guint32 index;
873
874 /* convert media_time to mov format */
875 media_time =
876 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
877
878 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
879 sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
880 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
881
882 if (G_LIKELY (result))
883 index = result - str->samples;
884 else
885 index = 0;
886
887 return index;
888}
889
890
891
892/* find the index of the sample that includes the data for @media_offset using a
893 * linear search
894 *
895 * Returns the index of the sample.
896 */
897static guint32
898gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
899 QtDemuxStream * str, gint64 media_offset)
900{
901 QtDemuxSample *result = str->samples;
902 guint32 index = 0;
903
904 if (result == NULL || str->n_samples == 0)
905 return -1;
906
907 if (media_offset == result->offset)
908 return index;
909
910 result++;
911 while (index < str->n_samples - 1) {
912 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
913 goto parse_failed;
914
915 if (media_offset < result->offset)
916 break;
917
918 index++;
919 result++;
920 }
921 return index;
922
923 /* ERRORS */
924parse_failed:
925 {
926 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
927 return -1;
928 }
929}
930
931/* find the index of the sample that includes the data for @media_time using a
932 * linear search, and keeping in mind that not all samples may have been parsed
933 * yet. If possible, it will delegate to binary search.
934 *
935 * Returns the index of the sample.
936 */
937static guint32
938gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
939 GstClockTime media_time)
940{
941 guint32 index = 0;
942 guint64 mov_time;
943 QtDemuxSample *sample;
944
945 /* convert media_time to mov format */
946 mov_time =
947 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
948
949 sample = str->samples;
950 if (mov_time == sample->timestamp + sample->pts_offset)
951 return index;
952
953 /* use faster search if requested time in already parsed range */
954 sample = str->samples + str->stbl_index;
955 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
956 index = gst_qtdemux_find_index (qtdemux, str, media_time);
957 sample = str->samples + index;
958 } else {
959 while (index < str->n_samples - 1) {
960 if (!qtdemux_parse_samples (qtdemux, str, index + 1))
961 goto parse_failed;
962
963 sample = str->samples + index + 1;
964 if (mov_time < sample->timestamp) {
965 sample = str->samples + index;
966 break;
967 }
968
969 index++;
970 }
971 }
972
973 /* sample->timestamp is now <= media_time, need to find the corresponding
974 * PTS now by looking backwards */
975 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
976 index--;
977 sample = str->samples + index;
978 }
979
980 return index;
981
982 /* ERRORS */
983parse_failed:
984 {
985 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
986 return -1;
987 }
988}
989
990/* find the index of the keyframe needed to decode the sample at @index
991 * of stream @str, or of a subsequent keyframe (depending on @next)
992 *
993 * Returns the index of the keyframe.
994 */
995static guint32
996gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
997 guint32 index, gboolean next)
998{
999 guint32 new_index = index;
1000
1001 if (index >= str->n_samples) {
1002 new_index = str->n_samples;
1003 goto beach;
1004 }
1005
1006 /* all keyframes, return index */
1007 if (str->all_keyframe) {
1008 new_index = index;
1009 goto beach;
1010 }
1011
1012 /* else search until we have a keyframe */
1013 while (new_index < str->n_samples) {
1014 if (next && !qtdemux_parse_samples (qtdemux, str, new_index))
1015 goto parse_failed;
1016
1017 if (str->samples[new_index].keyframe)
1018 break;
1019
1020 if (new_index == 0)
1021 break;
1022
1023 if (next)
1024 new_index++;
1025 else
1026 new_index--;
1027 }
1028
1029 if (new_index == str->n_samples) {
1030 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1031 new_index = -1;
1032 }
1033
1034beach:
1035 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1036 "gave %u", next ? "after" : "before", index, new_index);
1037
1038 return new_index;
1039
1040 /* ERRORS */
1041parse_failed:
1042 {
1043 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1044 return -1;
1045 }
1046}
1047
1048/* find the segment for @time_position for @stream
1049 *
1050 * Returns the index of the segment containing @time_position.
1051 * Returns the last segment and sets the @eos variable to TRUE
1052 * if the time is beyond the end. @eos may be NULL
1053 */
1054static guint32
1055gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
1056 GstClockTime time_position)
1057{
1058 gint i;
1059 guint32 seg_idx;
1060
1061 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1062 GST_TIME_ARGS (time_position));
1063
1064 seg_idx = -1;
1065 for (i = 0; i < stream->n_segments; i++) {
1066 QtDemuxSegment *segment = &stream->segments[i];
1067
1068 GST_LOG_OBJECT (stream->pad,
1069 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1070 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1071
1072 /* For the last segment we include stop_time in the last segment */
1073 if (i < stream->n_segments - 1) {
1074 if (segment->time <= time_position && time_position < segment->stop_time) {
1075 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1076 seg_idx = i;
1077 break;
1078 }
1079 } else {
1080 /* Last segment always matches */
1081 seg_idx = i;
1082 break;
1083 }
1084 }
1085 return seg_idx;
1086}
1087
1088/* move the stream @str to the sample position @index.
1089 *
1090 * Updates @str->sample_index and marks discontinuity if needed.
1091 */
1092static void
1093gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
1094 guint32 index)
1095{
1096 /* no change needed */
1097 if (index == str->sample_index)
1098 return;
1099
1100 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1101 str->n_samples);
1102
1103 /* position changed, we have a discont */
1104 str->sample_index = index;
1105 str->offset_in_sample = 0;
1106 /* Each time we move in the stream we store the position where we are
1107 * starting from */
1108 str->from_sample = index;
1109 str->discont = TRUE;
1110}
1111
1112static void
1113gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
1114 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1115{
1116 guint64 min_offset;
1117 gint64 min_byte_offset = -1;
1118 guint i;
1119
1120 min_offset = desired_time;
1121
1122 /* for each stream, find the index of the sample in the segment
1123 * and move back to the previous keyframe. */
1124 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1125 QtDemuxStream *str;
1126 guint32 index, kindex;
1127 guint32 seg_idx;
1128 GstClockTime media_start;
1129 GstClockTime media_time;
1130 GstClockTime seg_time;
1131 QtDemuxSegment *seg;
1132 gboolean empty_segment = FALSE;
1133
1134 str = QTDEMUX_NTH_STREAM (qtdemux, i);
1135
1136 if (CUR_STREAM (str)->sparse && !use_sparse)
1137 continue;
1138
1139 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
1140 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1141
1142 /* get segment and time in the segment */
1143 seg = &str->segments[seg_idx];
1144 seg_time = (desired_time - seg->time) * seg->rate;
1145
1146 while (QTSEGMENT_IS_EMPTY (seg)) {
1147 seg_time = 0;
1148 empty_segment = TRUE;
1149 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1150 seg_idx);
1151 seg_idx++;
1152 if (seg_idx == str->n_segments)
1153 break;
1154 seg = &str->segments[seg_idx];
1155 }
1156
1157 if (seg_idx == str->n_segments) {
1158 /* FIXME track shouldn't have the last segment as empty, but if it
1159 * happens we better handle it */
1160 continue;
1161 }
1162
1163 /* get the media time in the segment */
1164 media_start = seg->media_start + seg_time;
1165
1166 /* get the index of the sample with media time */
1167 index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
1168 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1169 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1170 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1171 empty_segment);
1172
1173 /* shift to next frame if we are looking for next keyframe */
1174 if (next && QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
1175 && index < str->stbl_index)
1176 index++;
1177
1178 if (!empty_segment) {
1179 /* find previous keyframe */
1180 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
1181
1182 /* we will settle for one before if none found after */
1183 if (next && kindex == -1)
1184 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
1185
1186 /* Update the requested time whenever a keyframe was found, to make it
1187 * accurate and avoid having the first buffer fall outside of the segment
1188 */
1189 if (kindex != -1) {
1190 index = kindex;
1191
1192 /* get timestamp of keyframe */
1193 media_time = QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
1194 GST_DEBUG_OBJECT (qtdemux,
1195 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1196 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1197 str->samples[kindex].offset);
1198
1199 /* keyframes in the segment get a chance to change the
1200 * desired_offset. keyframes out of the segment are
1201 * ignored. */
1202 if (media_time >= seg->media_start) {
1203 GstClockTime seg_time;
1204
1205 /* this keyframe is inside the segment, convert back to
1206 * segment time */
1207 seg_time = (media_time - seg->media_start) + seg->time;
1208 if ((!next && (seg_time < min_offset)) ||
1209 (next && (seg_time > min_offset)))
1210 min_offset = seg_time;
1211 }
1212 }
1213 }
1214
1215 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1216 min_byte_offset = str->samples[index].offset;
1217 }
1218
1219 if (key_time)
1220 *key_time = min_offset;
1221 if (key_offset)
1222 *key_offset = min_byte_offset;
1223}
1224
1225static gboolean
1226gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
1227 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1228{
1229 gboolean res;
1230
1231 g_return_val_if_fail (format != NULL, FALSE);
1232 g_return_val_if_fail (cur != NULL, FALSE);
1233 g_return_val_if_fail (stop != NULL, FALSE);
1234
1235 if (*format == GST_FORMAT_TIME)
1236 return TRUE;
1237
1238 res = TRUE;
1239 if (cur_type != GST_SEEK_TYPE_NONE)
1240 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1241 if (res && stop_type != GST_SEEK_TYPE_NONE)
1242 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1243
1244 if (res)
1245 *format = GST_FORMAT_TIME;
1246
1247 return res;
1248}
1249
1250/* perform seek in push based mode:
1251 find BYTE position to move to based on time and delegate to upstream
1252*/
1253static gboolean
1254gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1255{
1256 gdouble rate;
1257 GstFormat format;
1258 GstSeekFlags flags;
1259 GstSeekType cur_type, stop_type;
1260 gint64 cur, stop, key_cur;
1261 gboolean res;
1262 gint64 byte_cur;
1263 gint64 original_stop;
1264 guint32 seqnum;
1265
1266 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1267
1268 gst_event_parse_seek (event, &rate, &format, &flags,
1269 &cur_type, &cur, &stop_type, &stop);
1270 seqnum = gst_event_get_seqnum (event);
1271
1272 /* Directly send the instant-rate-change event here before taking the
1273 * stream-lock so that it can be applied as soon as possible */
1274 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1275 GstEvent *ev;
1276
1277 /* instant rate change only supported if direction does not change. All
1278 * other requirements are already checked before creating the seek event
1279 * but let's double-check here to be sure */
1280 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1281 (qtdemux->segment.rate < 0 && rate > 0) ||
1282 cur_type != GST_SEEK_TYPE_NONE ||
1283 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1284 GST_ERROR_OBJECT (qtdemux,
1285 "Instant rate change seeks only supported in the "
1286 "same direction, without flushing and position change");
1287 return FALSE;
1288 }
1289
1290 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1291 (GstSegmentFlags) flags);
1292 gst_event_set_seqnum (ev, seqnum);
1293 gst_qtdemux_push_event (qtdemux, ev);
1294 return TRUE;
1295 }
1296
1297 /* only forward streaming and seeking is possible */
1298 if (rate <= 0)
1299 goto unsupported_seek;
1300
1301 /* convert to TIME if needed and possible */
1302 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1303 stop_type, &stop))
1304 goto no_format;
1305
1306 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1307 * the original stop position to use when upstream pushes the new segment
1308 * for this seek */
1309 original_stop = stop;
1310 stop = -1;
1311
1312 /* find reasonable corresponding BYTE position,
1313 * also try to mind about keyframes, since we can not go back a bit for them
1314 * later on */
1315 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1316 * mostly just work, but let's not yet boldly go there ... */
1317 gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
1318
1319 if (byte_cur == -1)
1320 goto abort_seek;
1321
1322 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1323 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1324 stop);
1325
1326 GST_OBJECT_LOCK (qtdemux);
1327 qtdemux->seek_offset = byte_cur;
1328 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1329 qtdemux->push_seek_start = cur;
1330 } else {
1331 qtdemux->push_seek_start = key_cur;
1332 }
1333
1334 if (stop_type == GST_SEEK_TYPE_NONE) {
1335 qtdemux->push_seek_stop = qtdemux->segment.stop;
1336 } else {
1337 qtdemux->push_seek_stop = original_stop;
1338 }
1339 GST_OBJECT_UNLOCK (qtdemux);
1340
1341 qtdemux->segment_seqnum = seqnum;
1342 /* BYTE seek event */
1343 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1344 stop_type, stop);
1345 gst_event_set_seqnum (event, seqnum);
1346 res = gst_pad_push_event (qtdemux->sinkpad, event);
1347
1348 return res;
1349
1350 /* ERRORS */
1351abort_seek:
1352 {
1353 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1354 "seek aborted.");
1355 return FALSE;
1356 }
1357unsupported_seek:
1358 {
1359 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1360 return FALSE;
1361 }
1362no_format:
1363 {
1364 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1365 return FALSE;
1366 }
1367}
1368
1369/* perform the seek.
1370 *
1371 * We set all segment_indexes in the streams to unknown and
1372 * adjust the time_position to the desired position. this is enough
1373 * to trigger a segment switch in the streaming thread to start
1374 * streaming from the desired position.
1375 *
1376 * Keyframe seeking is a little more complicated when dealing with
1377 * segments. Ideally we want to move to the previous keyframe in
1378 * the segment but there might not be a keyframe in the segment. In
1379 * fact, none of the segments could contain a keyframe. We take a
1380 * practical approach: seek to the previous keyframe in the segment,
1381 * if there is none, seek to the beginning of the segment.
1382 *
1383 * Called with STREAM_LOCK
1384 */
1385static gboolean
1386gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
1387 guint32 seqnum, GstSeekFlags flags)
1388{
1389 gint64 desired_offset;
1390 guint i;
1391
1392 desired_offset = segment->position;
1393
1394 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1395 GST_TIME_ARGS (desired_offset));
1396
1397 /* may not have enough fragmented info to do this adjustment,
1398 * and we can't scan (and probably should not) at this time with
1399 * possibly flushing upstream */
1400 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1401 gint64 min_offset;
1402 gboolean next, before, after;
1403
1404 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1405 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1406 next = after && !before;
1407 if (segment->rate < 0)
1408 next = !next;
1409
1410 gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
1411 NULL);
1412 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1413 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1414 desired_offset = min_offset;
1415 }
1416
1417 /* and set all streams to the final position */
1418 GST_OBJECT_LOCK (qtdemux);
1419 gst_flow_combiner_reset (qtdemux->flowcombiner);
1420 GST_OBJECT_UNLOCK (qtdemux);
1421 qtdemux->segment_seqnum = seqnum;
1422 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1423 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1424
1425 stream->time_position = desired_offset;
1426 stream->accumulated_base = 0;
1427 stream->sample_index = -1;
1428 stream->offset_in_sample = 0;
1429 stream->segment_index = -1;
1430 stream->sent_eos = FALSE;
1431 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1432
1433 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1434 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1435 }
1436 segment->position = desired_offset;
1437 if (segment->rate >= 0) {
1438 segment->start = desired_offset;
1439 /* We need to update time as we update start in that direction */
1440 segment->time = desired_offset;
1441
1442 /* we stop at the end */
1443 if (segment->stop == -1)
1444 segment->stop = segment->duration;
1445 } else {
1446 segment->stop = desired_offset;
1447 }
1448
1449 if (qtdemux->fragmented)
1450 qtdemux->fragmented_seek_pending = TRUE;
1451
1452 return TRUE;
1453}
1454
1455/* do a seek in pull based mode */
1456static gboolean
1457gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
1458{
1459 gdouble rate = 1.0;
1460 GstFormat format;
1461 GstSeekFlags flags;
1462 GstSeekType cur_type, stop_type;
1463 gint64 cur, stop;
1464 gboolean flush, instant_rate_change;
1465 gboolean update;
1466 GstSegment seeksegment;
1467 guint32 seqnum = GST_SEQNUM_INVALID;
1468 GstEvent *flush_event;
1469 gboolean ret;
1470
1471 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1472
1473 gst_event_parse_seek (event, &rate, &format, &flags,
1474 &cur_type, &cur, &stop_type, &stop);
1475 seqnum = gst_event_get_seqnum (event);
1476
1477 /* we have to have a format as the segment format. Try to convert
1478 * if not. */
1479 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
1480 stop_type, &stop))
1481 goto no_format;
1482
1483 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1484
1485 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1486 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1487
1488 /* Directly send the instant-rate-change event here before taking the
1489 * stream-lock so that it can be applied as soon as possible */
1490 if (instant_rate_change) {
1491 GstEvent *ev;
1492
1493 /* instant rate change only supported if direction does not change. All
1494 * other requirements are already checked before creating the seek event
1495 * but let's double-check here to be sure */
1496 if ((qtdemux->segment.rate > 0 && rate < 0) ||
1497 (qtdemux->segment.rate < 0 && rate > 0) ||
1498 cur_type != GST_SEEK_TYPE_NONE ||
1499 stop_type != GST_SEEK_TYPE_NONE || flush) {
1500 GST_ERROR_OBJECT (qtdemux,
1501 "Instant rate change seeks only supported in the "
1502 "same direction, without flushing and position change");
1503 return FALSE;
1504 }
1505
1506 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1507 (GstSegmentFlags) flags);
1508 gst_event_set_seqnum (ev, seqnum);
1509 gst_qtdemux_push_event (qtdemux, ev);
1510 return TRUE;
1511 }
1512
1513 /* stop streaming, either by flushing or by pausing the task */
1514 if (flush) {
1515 flush_event = gst_event_new_flush_start ();
1516 if (seqnum != GST_SEQNUM_INVALID)
1517 gst_event_set_seqnum (flush_event, seqnum);
1518 /* unlock upstream pull_range */
1519 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1520 /* make sure out loop function exits */
1521 gst_qtdemux_push_event (qtdemux, flush_event);
1522 } else {
1523 /* non flushing seek, pause the task */
1524 gst_pad_pause_task (qtdemux->sinkpad);
1525 }
1526
1527 /* wait for streaming to finish */
1528 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1529
1530 /* copy segment, we need this because we still need the old
1531 * segment when we close the current segment. */
1532 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1533
1534 /* configure the segment with the seek variables */
1535 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1536 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1537 cur_type, cur, stop_type, stop, &update)) {
1538 ret = FALSE;
1539 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1540 } else {
1541 /* now do the seek */
1542 ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
1543 }
1544
1545 /* prepare for streaming again */
1546 if (flush) {
1547 flush_event = gst_event_new_flush_stop (TRUE);
1548 if (seqnum != GST_SEQNUM_INVALID)
1549 gst_event_set_seqnum (flush_event, seqnum);
1550
1551 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1552 gst_qtdemux_push_event (qtdemux, flush_event);
1553 }
1554
1555 /* commit the new segment */
1556 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1557
1558 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1559 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1560 qtdemux->segment.format, qtdemux->segment.position);
1561 if (seqnum != GST_SEQNUM_INVALID)
1562 gst_message_set_seqnum (msg, seqnum);
1563 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1564 }
1565
1566 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
1567 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
1568 qtdemux->sinkpad, NULL);
1569
1570 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1571
1572 return ret;
1573
1574 /* ERRORS */
1575no_format:
1576 {
1577 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1578 return FALSE;
1579 }
1580}
1581
1582static gboolean
1583qtdemux_ensure_index (GstQTDemux * qtdemux)
1584{
1585 guint i;
1586
1587 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1588
1589 /* Build complete index */
1590 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
1591 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
1592
1593 if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
1594 GST_LOG_OBJECT (qtdemux,
1595 "Building complete index of track-id %u for seeking failed!",
1596 stream->track_id);
1597 return FALSE;
1598 }
1599 }
1600
1601 return TRUE;
1602}
1603
1604static gboolean
1605gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
1606 GstEvent * event)
1607{
1608 gboolean res = TRUE;
1609 GstQTDemux *qtdemux = GST_QTDEMUX (parent);
1610
1611 switch (GST_EVENT_TYPE (event)) {
1612 case GST_EVENT_RECONFIGURE:
1613 GST_OBJECT_LOCK (qtdemux);
1614 gst_flow_combiner_reset (qtdemux->flowcombiner);
1615 GST_OBJECT_UNLOCK (qtdemux);
1616 res = gst_pad_event_default (pad, parent, event);
1617 break;
1618 case GST_EVENT_SEEK:
1619 {
1620 GstSeekFlags flags = 0;
1621 GstFormat seek_format;
1622 gboolean instant_rate_change;
1623
1624#ifndef GST_DISABLE_GST_DEBUG
1625 GstClockTime ts = gst_util_get_timestamp ();
1626#endif
1627 guint32 seqnum = gst_event_get_seqnum (event);
1628
1629 qtdemux->received_seek = TRUE;
1630
1631 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1632 NULL);
1633 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1634
1635 if (seqnum == qtdemux->segment_seqnum) {
1636 GST_LOG_OBJECT (pad,
1637 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1638 gst_event_unref (event);
1639 return TRUE;
1640 }
1641
1642 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1643 /* seek should be handled by upstream, we might need to re-download fragments */
1644 GST_DEBUG_OBJECT (qtdemux,
1645 "let upstream handle seek for fragmented playback");
1646 goto upstream;
1647 }
1648
1649 if (seek_format == GST_FORMAT_BYTES) {
1650 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1651 gst_event_unref (event);
1652 return FALSE;
1653 }
1654
1655 gst_event_parse_seek_trickmode_interval (event,
1656 &qtdemux->trickmode_interval);
1657
1658 /* Build complete index for seeking;
1659 * if not a fragmented file at least and we're really doing a seek,
1660 * not just an instant-rate-change */
1661 if (!qtdemux->fragmented && !instant_rate_change) {
1662 if (!qtdemux_ensure_index (qtdemux))
1663 goto index_failed;
1664 }
1665#ifndef GST_DISABLE_GST_DEBUG
1666 ts = gst_util_get_timestamp () - ts;
1667 GST_INFO_OBJECT (qtdemux,
1668 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1669#endif
1670 if (qtdemux->pullbased) {
1671 res = gst_qtdemux_do_seek (qtdemux, pad, event);
1672 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1673 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1674 res = TRUE;
1675 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE
1676 && QTDEMUX_N_STREAMS (qtdemux)
1677 && !qtdemux->fragmented) {
1678 res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
1679 } else {
1680 GST_DEBUG_OBJECT (qtdemux,
1681 "ignoring seek in push mode in current state");
1682 res = FALSE;
1683 }
1684 gst_event_unref (event);
1685 }
1686 break;
1687 default:
1688 upstream:
1689 res = gst_pad_event_default (pad, parent, event);
1690 break;
1691 }
1692
1693done:
1694 return res;
1695
1696 /* ERRORS */
1697index_failed:
1698 {
1699 GST_ERROR_OBJECT (qtdemux, "Index failed");
1700 gst_event_unref (event);
1701 res = FALSE;
1702 goto done;
1703 }
1704}
1705
1706/* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1707 *
1708 * If @fw is false, the coding order is explored backwards.
1709 *
1710 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1711 * sample is found for that track.
1712 *
1713 * The stream and sample index of the sample with the minimum offset in the direction explored
1714 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1715 *
1716 * @_time is set to the QTSAMPLE_PTS of the matched sample with the minimum QTSAMPLE_PTS in the
1717 * direction explored, which may not always match the QTSAMPLE_PTS of the sample returned in
1718 * @_stream and @_index. */
1719static void
1720gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1721 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
1722{
1723 gint i, index;
1724 gint64 time, min_time;
1725 QtDemuxStream *stream;
1726 gint iter;
1727
1728 min_time = -1;
1729 stream = NULL;
1730 index = -1;
1731
1732 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
1733 QtDemuxStream *str;
1734 gint inc;
1735 gboolean set_sample;
1736
1737 str = QTDEMUX_NTH_STREAM (qtdemux, iter);
1738 set_sample = !set;
1739
1740 if (fw) {
1741 i = 0;
1742 inc = 1;
1743 } else {
1744 i = str->n_samples - 1;
1745 inc = -1;
1746 }
1747
1748 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1749 if (str->samples[i].size == 0)
1750 continue;
1751
1752 if (fw && (str->samples[i].offset < byte_pos))
1753 continue;
1754
1755 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1756 continue;
1757
1758 /* move stream to first available sample */
1759 if (set) {
1760 gst_qtdemux_move_stream (qtdemux, str, i);
1761 set_sample = TRUE;
1762 }
1763
1764 /* avoid index from sparse streams since they might be far away */
1765 if (!CUR_STREAM (str)->sparse) {
1766 /* determine min/max time */
1767 time = QTSAMPLE_PTS (str, &str->samples[i]);
1768 if (min_time == -1 || (!fw && time > min_time) ||
1769 (fw && time < min_time)) {
1770 min_time = time;
1771 }
1772
1773 /* determine stream with leading sample, to get its position */
1774 if (!stream ||
1775 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1776 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1777 stream = str;
1778 index = i;
1779 }
1780 }
1781 break;
1782 }
1783
1784 /* no sample for this stream, mark eos */
1785 if (!set_sample)
1786 gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
1787 }
1788
1789 if (_time)
1790 *_time = min_time;
1791 if (_stream)
1792 *_stream = stream;
1793 if (_index)
1794 *_index = index;
1795}
1796
1797/* Copied from mpegtsbase code */
1798/* FIXME: replace this function when we add new util function for stream-id creation */
1799static gchar *
1800_get_upstream_id (GstQTDemux * demux)
1801{
1802 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1803
1804 if (!upstream_id) {
1805 /* Try to create one from the upstream URI, else use a randome number */
1806 GstQuery *query;
1807 gchar *uri = NULL;
1808
1809 /* Try to generate one from the URI query and
1810 * if it fails take a random number instead */
1811 query = gst_query_new_uri ();
1812 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1813 gst_query_parse_uri (query, &uri);
1814 }
1815
1816 if (uri) {
1817 GChecksum *cs;
1818
1819 /* And then generate an SHA256 sum of the URI */
1820 cs = g_checksum_new (G_CHECKSUM_SHA256);
1821 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1822 g_free (uri);
1823 upstream_id = g_strdup (g_checksum_get_string (cs));
1824 g_checksum_free (cs);
1825 } else {
1826 /* Just get some random number if the URI query fails */
1827 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1828 "implementing a deterministic way of creating a stream-id");
1829 upstream_id =
1830 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1831 g_random_int (), g_random_int ());
1832 }
1833
1834 gst_query_unref (query);
1835 }
1836 return upstream_id;
1837}
1838
1839static QtDemuxStream *
1840_create_stream (GstQTDemux * demux, guint32 track_id)
1841{
1842 QtDemuxStream *stream;
1843 gchar *upstream_id;
1844
1845 stream = g_new0 (QtDemuxStream, 1);
1846 stream->demux = demux;
1847 stream->track_id = track_id;
1848 upstream_id = _get_upstream_id (demux);
1849 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1850 g_free (upstream_id);
1851 /* new streams always need a discont */
1852 stream->discont = TRUE;
1853 /* we enable clipping for raw audio/video streams */
1854 stream->need_clip = FALSE;
1855 stream->process_func = NULL;
1856 stream->segment_index = -1;
1857 stream->time_position = 0;
1858 stream->sample_index = -1;
1859 stream->offset_in_sample = 0;
1860 stream->new_stream = TRUE;
1861 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1862 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1863 stream->protected = FALSE;
1864 stream->protection_scheme_type = 0;
1865 stream->protection_scheme_version = 0;
1866 stream->protection_scheme_info = NULL;
1867 stream->n_samples_moof = 0;
1868 stream->duration_moof = 0;
1869 stream->duration_last_moof = 0;
1870 stream->alignment = 1;
1871 stream->stream_tags = gst_tag_list_new_empty ();
1872 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1873 g_queue_init (&stream->protection_scheme_event_queue);
1874 stream->ref_count = 1;
1875 /* consistent default for push based mode */
1876 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1877 return stream;
1878}
1879
1880static gboolean
1881gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
1882{
1883 GstStructure *structure;
1884 const gchar *variant;
1885 const GstCaps *mediacaps = NULL;
1886
1887 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1888
1889 structure = gst_caps_get_structure (caps, 0);
1890 variant = gst_structure_get_string (structure, "variant");
1891
1892 if (variant && strcmp (variant, "mse-bytestream") == 0) {
1893 demux->variant = VARIANT_MSE_BYTESTREAM;
1894 }
1895
1896 if (variant && strcmp (variant, "mss-fragmented") == 0) {
1897 QtDemuxStream *stream;
1898 const GValue *value;
1899
1900 demux->fragmented = TRUE;
1901 demux->variant = VARIANT_MSS_FRAGMENTED;
1902
1903 if (QTDEMUX_N_STREAMS (demux) > 1) {
1904 /* can't do this, we can only renegotiate for another mss format */
1905 return FALSE;
1906 }
1907
1908 value = gst_structure_get_value (structure, "media-caps");
1909 /* create stream */
1910 if (value) {
1911 const GValue *timescale_v;
1912
1913 /* TODO update when stream changes during playback */
1914
1915 if (QTDEMUX_N_STREAMS (demux) == 0) {
1916 stream = _create_stream (demux, 1);
1917 g_ptr_array_add (demux->active_streams, stream);
1918 /* mss has no stsd/stsd entry, use id 0 as default */
1919 stream->stsd_entries_length = 1;
1920 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
1921 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
1922 } else {
1923 stream = QTDEMUX_NTH_STREAM (demux, 0);
1924 }
1925
1926 timescale_v = gst_structure_get_value (structure, "timescale");
1927 if (timescale_v) {
1928 stream->timescale = g_value_get_uint64 (timescale_v);
1929 } else {
1930 /* default mss timescale */
1931 stream->timescale = 10000000;
1932 }
1933 demux->timescale = stream->timescale;
1934
1935 mediacaps = gst_value_get_caps (value);
1936 if (!CUR_STREAM (stream)->caps
1937 || !gst_caps_is_equal_fixed (mediacaps, CUR_STREAM (stream)->caps)) {
1938 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1939 mediacaps);
1940 stream->new_caps = TRUE;
1941 }
1942 gst_caps_replace (&CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
1943 structure = gst_caps_get_structure (mediacaps, 0);
1944 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
1945 stream->subtype = FOURCC_vide;
1946
1947 gst_structure_get_int (structure, "width", &CUR_STREAM (stream)->width);
1948 gst_structure_get_int (structure, "height",
1949 &CUR_STREAM (stream)->height);
1950 gst_structure_get_fraction (structure, "framerate",
1951 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
1952 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1953 gint rate = 0;
1954 stream->subtype = FOURCC_soun;
1955 gst_structure_get_int (structure, "channels",
1956 &CUR_STREAM (stream)->n_channels);
1957 gst_structure_get_int (structure, "rate", &rate);
1958 CUR_STREAM (stream)->rate = rate;
1959 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
1960 if (gst_structure_has_field (structure, "original-media-type")) {
1961 const gchar *media_type =
1962 gst_structure_get_string (structure, "original-media-type");
1963 if (g_str_has_prefix (media_type, "video")) {
1964 stream->subtype = FOURCC_vide;
1965 } else if (g_str_has_prefix (media_type, "audio")) {
1966 stream->subtype = FOURCC_soun;
1967 }
1968 }
1969 }
1970 }
1971 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1972 }
1973
1974 return TRUE;
1975}
1976
1977static void
1978gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
1979{
1980 gint i;
1981
1982 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
1983 gst_pad_stop_task (qtdemux->sinkpad);
1984
1985 if (hard || qtdemux->upstream_format_is_time) {
1986 qtdemux->state = QTDEMUX_STATE_INITIAL;
1987 qtdemux->neededbytes = 16;
1988 qtdemux->todrop = 0;
1989 qtdemux->pullbased = FALSE;
1990 g_clear_pointer (&qtdemux->redirect_location, g_free);
1991 qtdemux->first_mdat = -1;
1992 qtdemux->header_size = 0;
1993 qtdemux->mdatoffset = -1;
1994 qtdemux->restoredata_offset = -1;
1995 if (qtdemux->mdatbuffer)
1996 gst_buffer_unref (qtdemux->mdatbuffer);
1997 if (qtdemux->restoredata_buffer)
1998 gst_buffer_unref (qtdemux->restoredata_buffer);
1999 qtdemux->mdatbuffer = NULL;
2000 qtdemux->restoredata_buffer = NULL;
2001 qtdemux->mdatleft = 0;
2002 qtdemux->mdatsize = 0;
2003 if (qtdemux->comp_brands)
2004 gst_buffer_unref (qtdemux->comp_brands);
2005 qtdemux->comp_brands = NULL;
2006 qtdemux->last_moov_offset = -1;
2007 if (qtdemux->moov_node_compressed) {
2008 g_node_destroy (qtdemux->moov_node_compressed);
2009 if (qtdemux->moov_node)
2010 g_free (qtdemux->moov_node->data);
2011 }
2012 qtdemux->moov_node_compressed = NULL;
2013 if (qtdemux->moov_node)
2014 g_node_destroy (qtdemux->moov_node);
2015 qtdemux->moov_node = NULL;
2016 if (qtdemux->tag_list)
2017 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2018 qtdemux->tag_list = gst_tag_list_new_empty ();
2019 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2020#if 0
2021 if (qtdemux->element_index)
2022 gst_object_unref (qtdemux->element_index);
2023 qtdemux->element_index = NULL;
2024#endif
2025 qtdemux->major_brand = 0;
2026 qtdemux->upstream_format_is_time = FALSE;
2027 qtdemux->upstream_seekable = FALSE;
2028 qtdemux->upstream_size = 0;
2029
2030 qtdemux->fragment_start = -1;
2031 qtdemux->fragment_start_offset = -1;
2032 qtdemux->duration = 0;
2033 qtdemux->moof_offset = 0;
2034 qtdemux->chapters_track_id = 0;
2035 qtdemux->have_group_id = FALSE;
2036 qtdemux->group_id = G_MAXUINT;
2037
2038 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2039 NULL);
2040 g_queue_clear (&qtdemux->protection_event_queue);
2041
2042 qtdemux->received_seek = FALSE;
2043 qtdemux->first_moof_already_parsed = FALSE;
2044 }
2045 qtdemux->offset = 0;
2046 gst_adapter_clear (qtdemux->adapter);
2047 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2048 qtdemux->need_segment = TRUE;
2049
2050 if (hard) {
2051 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2052 qtdemux->trickmode_interval = 0;
2053 g_ptr_array_set_size (qtdemux->active_streams, 0);
2054 g_ptr_array_set_size (qtdemux->old_streams, 0);
2055 qtdemux->n_video_streams = 0;
2056 qtdemux->n_audio_streams = 0;
2057 qtdemux->n_sub_streams = 0;
2058 qtdemux->exposed = FALSE;
2059 qtdemux->fragmented = FALSE;
2060 qtdemux->variant = VARIANT_NONE;
2061 gst_caps_replace (&qtdemux->media_caps, NULL);
2062 qtdemux->timescale = 0;
2063 qtdemux->got_moov = FALSE;
2064 qtdemux->cenc_aux_info_offset = 0;
2065 qtdemux->cenc_aux_info_sizes = NULL;
2066 qtdemux->cenc_aux_sample_count = 0;
2067 if (qtdemux->protection_system_ids) {
2068 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2069 qtdemux->protection_system_ids = NULL;
2070 }
2071 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2072 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2073 GST_BIN_FLAG_STREAMS_AWARE);
2074
2075 if (qtdemux->preferred_protection_system_id) {
2076 g_free (qtdemux->preferred_protection_system_id);
2077 qtdemux->preferred_protection_system_id = NULL;
2078 }
2079 } else if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
2080 gst_flow_combiner_reset (qtdemux->flowcombiner);
2081 g_ptr_array_foreach (qtdemux->active_streams,
2082 (GFunc) gst_qtdemux_stream_clear, NULL);
2083 } else {
2084 gst_flow_combiner_reset (qtdemux->flowcombiner);
2085 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
2086 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
2087 stream->sent_eos = FALSE;
2088 stream->time_position = 0;
2089 stream->accumulated_base = 0;
2090 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2091 }
2092 }
2093}
2094
2095
2096/* Maps the @segment to the qt edts internal segments and pushes
2097 * the corresponding segment event.
2098 *
2099 * If it ends up being at a empty segment, a gap will be pushed and the next
2100 * edts segment will be activated in sequence.
2101 *
2102 * To be used in push-mode only */
2103static void
2104gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment)
2105{
2106 gint i, iter;
2107
2108 for (iter = 0; iter < QTDEMUX_N_STREAMS (qtdemux); iter++) {
2109 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, iter);
2110
2111 stream->time_position = segment->start;
2112
2113 /* in push mode we should be guaranteed that we will have empty segments
2114 * at the beginning and then one segment after, other scenarios are not
2115 * supported and are discarded when parsing the edts */
2116 for (i = 0; i < stream->n_segments; i++) {
2117 if (stream->segments[i].stop_time > segment->start) {
2118 /* push the empty segment and move to the next one */
2119 gst_qtdemux_activate_segment (qtdemux, stream, i,
2120 stream->time_position);
2121 if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2122 gst_qtdemux_send_gap_for_segment (qtdemux, stream, i,
2123 stream->time_position);
2124
2125 /* accumulate previous segments */
2126 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2127 stream->accumulated_base +=
2128 (stream->segment.stop -
2129 stream->segment.start) / ABS (stream->segment.rate);
2130 continue;
2131 }
2132
2133 g_assert (i == stream->n_segments - 1);
2134 }
2135 }
2136 }
2137}
2138
2139static void
2140gst_qtdemux_stream_concat (GstQTDemux * qtdemux, GPtrArray * dest,
2141 GPtrArray * src)
2142{
2143 guint i;
2144 guint len;
2145
2146 len = src->len;
2147
2148 if (len == 0)
2149 return;
2150
2151 for (i = 0; i < len; i++) {
2152 QtDemuxStream *stream = g_ptr_array_index (src, i);
2153
2154#ifndef GST_DISABLE_GST_DEBUG
2155 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2156 stream, GST_STR_NULL (stream->stream_id), dest);
2157#endif
2158 g_ptr_array_add (dest, gst_qtdemux_stream_ref (stream));
2159 }
2160
2161 g_ptr_array_set_size (src, 0);
2162}
2163
2164static gboolean
2165gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
2166 GstEvent * event)
2167{
2168 GstQTDemux *demux = GST_QTDEMUX (parent);
2169 gboolean res = TRUE;
2170
2171 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
2172
2173 switch (GST_EVENT_TYPE (event)) {
2174 case GST_EVENT_SEGMENT:
2175 {
2176 gint64 offset = 0;
2177 QtDemuxStream *stream;
2178 gint idx;
2179 GstSegment segment;
2180
2181 /* some debug output */
2182 gst_event_copy_segment (event, &segment);
2183 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2184 &segment);
2185
2186 if (segment.format == GST_FORMAT_TIME) {
2187 demux->upstream_format_is_time = TRUE;
2188 demux->segment_seqnum = gst_event_get_seqnum (event);
2189 } else {
2190 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2191 "not in time format");
2192
2193 /* chain will send initial newsegment after pads have been added */
2194 if (demux->state != QTDEMUX_STATE_MOVIE || !QTDEMUX_N_STREAMS (demux)) {
2195 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2196 goto exit;
2197 }
2198 }
2199
2200 /* check if this matches a time seek we received previously
2201 * FIXME for backwards compatibility reasons we use the
2202 * seek_offset here to compare. In the future we might want to
2203 * change this to use the seqnum as it uniquely should identify
2204 * the segment that corresponds to the seek. */
2205 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2206 ", received segment offset %" G_GINT64_FORMAT,
2207 demux->seek_offset, segment.start);
2208 if (segment.format == GST_FORMAT_BYTES
2209 && demux->seek_offset == segment.start) {
2210 GST_OBJECT_LOCK (demux);
2211 offset = segment.start;
2212
2213 segment.format = GST_FORMAT_TIME;
2214 segment.start = demux->push_seek_start;
2215 segment.stop = demux->push_seek_stop;
2216 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2217 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2218 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2219 GST_OBJECT_UNLOCK (demux);
2220 }
2221
2222 /* we only expect a BYTE segment, e.g. following a seek */
2223 if (segment.format == GST_FORMAT_BYTES) {
2224 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2225 offset = segment.start;
2226
2227 gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
2228 NULL, (gint64 *) & segment.start);
2229 if ((gint64) segment.start < 0)
2230 segment.start = 0;
2231 }
2232 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
2233 gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
2234 NULL, (gint64 *) & segment.stop);
2235 /* keyframe seeking should already arrange for start >= stop,
2236 * but make sure in other rare cases */
2237 segment.stop = MAX (segment.stop, segment.start);
2238 }
2239 } else if (segment.format == GST_FORMAT_TIME) {
2240 /* push all data on the adapter before starting this
2241 * new segment */
2242 gst_qtdemux_process_adapter (demux, TRUE);
2243 } else {
2244 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2245 goto exit;
2246 }
2247
2248 /* We shouldn't modify upstream driven TIME FORMAT segment */
2249 if (!demux->upstream_format_is_time) {
2250 /* accept upstream's notion of segment and distribute along */
2251 segment.format = GST_FORMAT_TIME;
2252 segment.position = segment.time = segment.start;
2253 segment.duration = demux->segment.duration;
2254 segment.base = gst_segment_to_running_time (&demux->segment,
2255 GST_FORMAT_TIME, demux->segment.position);
2256 }
2257
2258 gst_segment_copy_into (&segment, &demux->segment);
2259 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2260
2261 /* map segment to internal qt segments and push on each stream */
2262 if (QTDEMUX_N_STREAMS (demux)) {
2263 demux->need_segment = TRUE;
2264 gst_qtdemux_check_send_pending_segment (demux);
2265 }
2266
2267 /* clear leftover in current segment, if any */
2268 gst_adapter_clear (demux->adapter);
2269
2270 /* set up streaming thread */
2271 demux->offset = offset;
2272 if (demux->upstream_format_is_time) {
2273 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2274 "set values to restart reading from a new atom");
2275 demux->neededbytes = 16;
2276 demux->todrop = 0;
2277 } else {
2278 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
2279 NULL);
2280 if (stream) {
2281 demux->todrop = stream->samples[idx].offset - offset;
2282 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2283 } else {
2284 /* set up for EOS */
2285 demux->neededbytes = -1;
2286 demux->todrop = 0;
2287 }
2288 }
2289 exit:
2290 gst_event_unref (event);
2291 res = TRUE;
2292 goto drop;
2293 }
2294 case GST_EVENT_FLUSH_START:
2295 {
2296 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2297 gst_event_unref (event);
2298 goto drop;
2299 }
2300 QTDEMUX_EXPOSE_LOCK (demux);
2301 res = gst_pad_event_default (demux->sinkpad, parent, event);
2302 QTDEMUX_EXPOSE_UNLOCK (demux);
2303 goto drop;
2304 }
2305 case GST_EVENT_FLUSH_STOP:
2306 {
2307 guint64 dur;
2308
2309 dur = demux->segment.duration;
2310 gst_qtdemux_reset (demux, FALSE);
2311 demux->segment.duration = dur;
2312
2313 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2314 gst_event_unref (event);
2315 goto drop;
2316 }
2317 break;
2318 }
2319 case GST_EVENT_EOS:
2320 /* If we are in push mode, and get an EOS before we've seen any streams,
2321 * then error out - we have nowhere to send the EOS */
2322 if (!demux->pullbased) {
2323 gint i;
2324 gboolean has_valid_stream = FALSE;
2325 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
2326 if (QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
2327 has_valid_stream = TRUE;
2328 break;
2329 }
2330 }
2331 if (!has_valid_stream)
2332 gst_qtdemux_post_no_playable_stream_error (demux);
2333 else {
2334 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2335 (guint) gst_adapter_available (demux->adapter));
2336 if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
2337 res = FALSE;
2338 }
2339 }
2340 }
2341 break;
2342 case GST_EVENT_CAPS:{
2343 GstCaps *caps = NULL;
2344
2345 gst_event_parse_caps (event, &caps);
2346 gst_qtdemux_setcaps (demux, caps);
2347 res = TRUE;
2348 gst_event_unref (event);
2349 goto drop;
2350 }
2351 case GST_EVENT_PROTECTION:
2352 {
2353 const gchar *system_id = NULL;
2354
2355 gst_event_parse_protection (event, &system_id, NULL, NULL);
2356 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2357 system_id);
2358 gst_qtdemux_append_protection_system_id (demux, system_id);
2359 /* save the event for later, for source pads that have not been created */
2360 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2361 /* send it to all pads that already exist */
2362 gst_qtdemux_push_event (demux, event);
2363 res = TRUE;
2364 goto drop;
2365 }
2366 case GST_EVENT_STREAM_START:
2367 {
2368 res = TRUE;
2369 gst_event_unref (event);
2370
2371 /* Drain all the buffers */
2372 gst_qtdemux_process_adapter (demux, TRUE);
2373 gst_qtdemux_reset (demux, FALSE);
2374 /* We expect new moov box after new stream-start event */
2375 if (demux->exposed) {
2376 gst_qtdemux_stream_concat (demux,
2377 demux->old_streams, demux->active_streams);
2378 }
2379
2380 goto drop;
2381 }
zengliang.lid0c84c32024-05-17 03:33:20 +00002382 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:
2383 {
2384 if (gst_event_has_name(event, "AML-DISCONTINUITY-BASE-POS"))
2385 {
2386 GST_DEBUG_OBJECT (demux, "Handle event AML-DISCONTINUITY-BASE-POS");
2387 demux->cal_discontinuity_pos = TRUE;
2388
2389 res = TRUE;
2390 gst_event_unref(event);
2391 goto drop;
2392 }
2393 break;
2394 }
zengliang.li5f31ef42024-05-16 08:27:38 +00002395 default:
2396 break;
2397 }
2398
2399 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2400
2401drop:
2402 return res;
2403}
2404
2405static gboolean
2406gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
2407 GstQuery * query)
2408{
2409 GstQTDemux *demux = GST_QTDEMUX (parent);
2410 gboolean res = FALSE;
2411
2412 switch (GST_QUERY_TYPE (query)) {
2413 case GST_QUERY_BITRATE:
2414 {
2415 GstClockTime duration;
2416
2417 /* populate demux->upstream_size if not done yet */
2418 gst_qtdemux_check_seekability (demux);
2419
2420 if (demux->upstream_size != -1
2421 && gst_qtdemux_get_duration (demux, &duration)) {
2422 guint bitrate =
2423 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2424 duration);
2425
2426 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2427 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2428 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2429
2430 /* TODO: better results based on ranges/index tables */
2431 gst_query_set_bitrate (query, bitrate);
2432 res = TRUE;
2433 }
2434 break;
2435 }
2436 default:
2437 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2438 break;
2439 }
2440
2441 return res;
2442}
2443
2444
2445#if 0
2446static void
2447gst_qtdemux_set_index (GstElement * element, GstIndex * index)
2448{
2449 GstQTDemux *demux = GST_QTDEMUX (element);
2450
2451 GST_OBJECT_LOCK (demux);
2452 if (demux->element_index)
2453 gst_object_unref (demux->element_index);
2454 if (index) {
2455 demux->element_index = gst_object_ref (index);
2456 } else {
2457 demux->element_index = NULL;
2458 }
2459 GST_OBJECT_UNLOCK (demux);
2460 /* object lock might be taken again */
2461 if (index)
2462 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2463 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2464 demux->element_index, demux->index_id);
2465}
2466
2467static GstIndex *
2468gst_qtdemux_get_index (GstElement * element)
2469{
2470 GstIndex *result = NULL;
2471 GstQTDemux *demux = GST_QTDEMUX (element);
2472
2473 GST_OBJECT_LOCK (demux);
2474 if (demux->element_index)
2475 result = gst_object_ref (demux->element_index);
2476 GST_OBJECT_UNLOCK (demux);
2477
2478 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2479
2480 return result;
2481}
2482#endif
2483
2484static void
2485gst_qtdemux_stbl_free (QtDemuxStream * stream)
2486{
2487 g_free ((gpointer) stream->stco.data);
2488 stream->stco.data = NULL;
2489 g_free ((gpointer) stream->stsz.data);
2490 stream->stsz.data = NULL;
2491 g_free ((gpointer) stream->stsc.data);
2492 stream->stsc.data = NULL;
2493 g_free ((gpointer) stream->stts.data);
2494 stream->stts.data = NULL;
2495 g_free ((gpointer) stream->stss.data);
2496 stream->stss.data = NULL;
2497 g_free ((gpointer) stream->stps.data);
2498 stream->stps.data = NULL;
2499 g_free ((gpointer) stream->ctts.data);
2500 stream->ctts.data = NULL;
2501}
2502
2503static void
2504gst_qtdemux_stream_flush_segments_data (QtDemuxStream * stream)
2505{
2506 g_free (stream->segments);
2507 stream->segments = NULL;
2508 stream->segment_index = -1;
2509 stream->accumulated_base = 0;
2510}
2511
2512static void
2513gst_qtdemux_stream_flush_samples_data (QtDemuxStream * stream)
2514{
2515 g_free (stream->samples);
2516 stream->samples = NULL;
2517 gst_qtdemux_stbl_free (stream);
2518
2519 /* fragments */
2520 g_free (stream->ra_entries);
2521 stream->ra_entries = NULL;
2522 stream->n_ra_entries = 0;
2523
2524 stream->sample_index = -1;
2525 stream->stbl_index = -1;
2526 stream->n_samples = 0;
2527 stream->time_position = 0;
2528
2529 stream->n_samples_moof = 0;
2530 stream->duration_moof = 0;
2531 stream->duration_last_moof = 0;
2532}
2533
2534static void
2535gst_qtdemux_stream_clear (QtDemuxStream * stream)
2536{
2537 gint i;
2538 if (stream->allocator)
2539 gst_object_unref (stream->allocator);
2540 while (stream->buffers) {
2541 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2542 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2543 }
2544 for (i = 0; i < stream->stsd_entries_length; i++) {
2545 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2546 if (entry->rgb8_palette) {
2547 gst_memory_unref (entry->rgb8_palette);
2548 entry->rgb8_palette = NULL;
2549 }
2550 entry->sparse = FALSE;
2551 }
2552
2553 if (stream->stream_tags)
2554 gst_tag_list_unref (stream->stream_tags);
2555
2556 stream->stream_tags = gst_tag_list_new_empty ();
2557 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2558 g_free (stream->redirect_uri);
2559 stream->redirect_uri = NULL;
2560 stream->sent_eos = FALSE;
2561 stream->protected = FALSE;
2562 if (stream->protection_scheme_info) {
2563 if (stream->protection_scheme_type == FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +00002564 || stream->protection_scheme_type == FOURCC_cbcs
2565 || stream->protection_scheme_type == FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002566 QtDemuxCencSampleSetInfo *info =
2567 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2568 if (info->default_properties)
2569 gst_structure_free (info->default_properties);
2570 if (info->crypto_info)
2571 g_ptr_array_free (info->crypto_info, TRUE);
2572 }
2573 if (stream->protection_scheme_type == FOURCC_aavd) {
2574 QtDemuxAavdEncryptionInfo *info =
2575 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
2576 if (info->default_properties)
2577 gst_structure_free (info->default_properties);
2578 }
2579 g_free (stream->protection_scheme_info);
2580 stream->protection_scheme_info = NULL;
2581 }
2582 stream->protection_scheme_type = 0;
2583 stream->protection_scheme_version = 0;
2584 g_queue_foreach (&stream->protection_scheme_event_queue,
2585 (GFunc) gst_event_unref, NULL);
2586 g_queue_clear (&stream->protection_scheme_event_queue);
2587 gst_qtdemux_stream_flush_segments_data (stream);
2588 gst_qtdemux_stream_flush_samples_data (stream);
2589}
2590
2591static void
2592gst_qtdemux_stream_reset (QtDemuxStream * stream)
2593{
2594 gint i;
2595 gst_qtdemux_stream_clear (stream);
2596 for (i = 0; i < stream->stsd_entries_length; i++) {
2597 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
2598 if (entry->caps) {
2599 gst_caps_unref (entry->caps);
2600 entry->caps = NULL;
2601 }
2602 }
2603 g_free (stream->stsd_entries);
2604 stream->stsd_entries = NULL;
2605 stream->stsd_entries_length = 0;
2606}
2607
2608static QtDemuxStream *
2609gst_qtdemux_stream_ref (QtDemuxStream * stream)
2610{
2611 g_atomic_int_add (&stream->ref_count, 1);
2612
2613 return stream;
2614}
2615
2616static void
2617gst_qtdemux_stream_unref (QtDemuxStream * stream)
2618{
2619 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
2620 gst_qtdemux_stream_reset (stream);
2621 gst_tag_list_unref (stream->stream_tags);
2622 if (stream->pad) {
2623 GstQTDemux *demux = stream->demux;
2624 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2625 GST_OBJECT_LOCK (demux);
2626 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2627 GST_OBJECT_UNLOCK (demux);
2628 }
2629 g_free (stream->stream_id);
2630 g_free (stream);
2631 }
2632}
2633
2634static GstStateChangeReturn
2635gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
2636{
2637 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2638 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2639
2640 switch (transition) {
2641 case GST_STATE_CHANGE_READY_TO_PAUSED:
2642 gst_qtdemux_reset (qtdemux, TRUE);
2643 break;
2644 default:
2645 break;
2646 }
2647
2648 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2649
2650 switch (transition) {
2651 case GST_STATE_CHANGE_PAUSED_TO_READY:{
2652 gst_qtdemux_reset (qtdemux, TRUE);
2653 break;
2654 }
2655 default:
2656 break;
2657 }
2658
2659 return result;
2660}
2661
2662static void
2663gst_qtdemux_set_context (GstElement * element, GstContext * context)
2664{
2665 GstQTDemux *qtdemux = GST_QTDEMUX (element);
2666
2667 g_return_if_fail (GST_IS_CONTEXT (context));
2668
2669 if (gst_context_has_context_type (context,
2670 "drm-preferred-decryption-system-id")) {
2671 const GstStructure *s;
2672
2673 s = gst_context_get_structure (context);
2674 g_free (qtdemux->preferred_protection_system_id);
2675 qtdemux->preferred_protection_system_id =
2676 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2677 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2678 qtdemux->preferred_protection_system_id);
2679 }
2680
2681 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2682}
2683
2684static void
2685qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2686{
2687 /* counts as header data */
2688 qtdemux->header_size += length;
2689
2690 /* only consider at least a sufficiently complete ftyp atom */
2691 if (length >= 20) {
2692 GstBuffer *buf;
2693
2694 qtdemux->major_brand = QT_FOURCC (buffer + 8);
2695 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2696 GST_FOURCC_ARGS (qtdemux->major_brand));
2697 if (qtdemux->comp_brands)
2698 gst_buffer_unref (qtdemux->comp_brands);
2699 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2700 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2701 }
2702}
2703
2704static void
2705qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
2706 QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
2707 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
2708 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
2709 const guint8 * constant_iv)
2710{
2711 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2712 gst_buffer_fill (kid_buf, 0, kid, 16);
2713 if (info->default_properties)
2714 gst_structure_free (info->default_properties);
2715 info->default_properties =
2716 gst_structure_new ("application/x-cenc",
2717 "iv_size", G_TYPE_UINT, iv_size,
2718 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2719 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2720 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
zengliang.li125c3642024-05-17 06:06:08 +00002721 "is_encrypted=%u, iv_size=%u constant_iv_size=%u", is_encrypted, iv_size, constant_iv_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00002722 gst_buffer_unref (kid_buf);
2723 if (protection_scheme_type == FOURCC_cbcs) {
2724 if (crypt_byte_block != 0 || skip_byte_block != 0) {
2725 gst_structure_set (info->default_properties, "crypt_byte_block",
2726 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
2727 skip_byte_block, NULL);
2728 }
2729 if (constant_iv != NULL) {
2730 GstBuffer *constant_iv_buf =
2731 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
2732 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
2733 gst_structure_set (info->default_properties, "constant_iv_size",
2734 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
2735 NULL);
2736 gst_buffer_unref (constant_iv_buf);
2737 }
2738 gst_structure_set (info->default_properties, "cipher-mode",
2739 G_TYPE_STRING, "cbcs", NULL);
2740 } else {
2741 gst_structure_set (info->default_properties, "cipher-mode",
2742 G_TYPE_STRING, "cenc", NULL);
2743 }
2744}
2745
2746static gboolean
2747qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
2748 QtDemuxCencSampleSetInfo * info, GstByteReader * br)
2749{
2750 guint32 algorithm_id = 0;
2751 const guint8 *kid;
2752 gboolean is_encrypted = TRUE;
2753 guint8 iv_size = 8;
2754
2755 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2756 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2757 return FALSE;
2758 }
2759
2760 algorithm_id >>= 8;
2761 if (algorithm_id == 0) {
2762 is_encrypted = FALSE;
2763 } else if (algorithm_id == 1) {
2764 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2765 } else if (algorithm_id == 2) {
2766 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2767 }
2768
2769 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2770 return FALSE;
2771
2772 if (!gst_byte_reader_get_data (br, 16, &kid))
2773 return FALSE;
2774
2775 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
2776 is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
2777 gst_structure_set (info->default_properties, "piff_algorithm_id",
2778 G_TYPE_UINT, algorithm_id, NULL);
2779 return TRUE;
2780}
2781
2782
2783static void
2784qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
2785 guint offset)
2786{
2787 GstByteReader br;
2788 guint8 version;
2789 guint32 flags = 0;
2790 guint i;
2791 guint iv_size = 8;
2792 QtDemuxStream *stream;
2793 GstStructure *structure;
2794 QtDemuxCencSampleSetInfo *ss_info = NULL;
2795 const gchar *system_id;
2796 gboolean uses_sub_sample_encryption = FALSE;
2797 guint32 sample_count;
2798
2799 if (QTDEMUX_N_STREAMS (qtdemux) == 0)
2800 return;
2801
2802 stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
2803
2804 structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
2805 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2806 GST_WARNING_OBJECT (qtdemux,
2807 "Attempting PIFF box parsing on an unencrypted stream.");
2808 return;
2809 }
2810
2811 if (!gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2812 G_TYPE_STRING, &system_id, NULL)) {
2813 GST_WARNING_OBJECT (qtdemux, "%s field not present in caps",
2814 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD);
2815 return;
2816 }
2817
2818 gst_qtdemux_append_protection_system_id (qtdemux, system_id);
2819
2820 stream->protected = TRUE;
2821 stream->protection_scheme_type = FOURCC_cenc;
2822
2823 if (!stream->protection_scheme_info)
2824 stream->protection_scheme_info = g_new0 (QtDemuxCencSampleSetInfo, 1);
2825
2826 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
2827 if (!ss_info->default_properties) {
2828 ss_info->default_properties =
2829 gst_structure_new ("application/x-cenc",
2830 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2831 NULL);
2832
2833 }
2834
2835 if (ss_info->crypto_info) {
2836 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2837 g_ptr_array_free (ss_info->crypto_info, TRUE);
2838 ss_info->crypto_info = NULL;
2839 }
2840
2841 /* skip UUID */
2842 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2843
2844 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2845 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2846 return;
2847 }
2848
2849 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2850 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2851 return;
2852 }
2853
2854 if ((flags & 0x000001)) {
2855 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
2856 &br))
2857 return;
2858 } else if ((flags & 0x000002)) {
2859 uses_sub_sample_encryption = TRUE;
2860 }
2861
2862 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2863 &iv_size)) {
2864 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2865 return;
2866 }
2867
2868 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2869 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2870 return;
2871 }
2872
2873 ss_info->crypto_info =
2874 g_ptr_array_new_full (sample_count,
2875 (GDestroyNotify) qtdemux_gst_structure_free);
2876
2877 for (i = 0; i < sample_count; ++i) {
2878 GstStructure *properties;
2879 guint8 *data;
2880 GstBuffer *buf;
2881
2882 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
2883 if (properties == NULL) {
2884 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2885 qtdemux->cenc_aux_sample_count = i;
2886 return;
2887 }
2888
2889 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2890 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2891 gst_structure_free (properties);
2892 qtdemux->cenc_aux_sample_count = i;
2893 return;
2894 }
2895 buf = gst_buffer_new_wrapped (data, iv_size);
2896 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2897 gst_buffer_unref (buf);
2898
2899 if (uses_sub_sample_encryption) {
2900 guint16 n_subsamples;
2901 const GValue *kid_buf_value;
2902
2903 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2904 || n_subsamples == 0) {
2905 GST_ERROR_OBJECT (qtdemux,
2906 "failed to get subsample count for sample %u", i);
2907 gst_structure_free (properties);
2908 qtdemux->cenc_aux_sample_count = i;
2909 return;
2910 }
2911 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
2912 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
2913 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
2914 i);
2915 gst_structure_free (properties);
2916 qtdemux->cenc_aux_sample_count = i;
2917 return;
2918 }
2919 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
2920
2921 kid_buf_value =
2922 gst_structure_get_value (ss_info->default_properties, "kid");
2923
2924 gst_structure_set (properties,
2925 "subsample_count", G_TYPE_UINT, n_subsamples,
2926 "subsamples", GST_TYPE_BUFFER, buf, NULL);
2927 gst_structure_set_value (properties, "kid", kid_buf_value);
2928 gst_buffer_unref (buf);
2929 } else {
2930 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
2931 }
2932
2933 g_ptr_array_add (ss_info->crypto_info, properties);
2934 }
2935
2936 qtdemux->cenc_aux_sample_count = sample_count;
2937}
2938
2939static void
2940qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
2941{
2942 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2943 0x97, 0xA9, 0x42, 0xE8,
2944 0x9C, 0x71, 0x99, 0x94,
2945 0x91, 0xE3, 0xAF, 0xAC
2946 };
2947 static const guint8 playready_uuid[] = {
2948 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2949 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2950 };
2951
2952 static const guint8 piff_sample_encryption_uuid[] = {
2953 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
2954 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
2955 };
2956
2957 guint offset;
2958
2959 /* counts as header data */
2960 qtdemux->header_size += length;
2961
2962 offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
2963
2964 if (length <= offset + 16) {
2965 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
2966 return;
2967 }
2968
2969 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
2970 GstBuffer *buf;
2971 GstTagList *taglist;
2972
2973 buf = _gst_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
2974 length - offset - 16, NULL);
2975 taglist = gst_tag_list_from_xmp_buffer (buf);
2976 gst_buffer_unref (buf);
2977
2978 /* make sure we have a usable taglist */
2979 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
2980
2981 qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
2982
2983 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
2984 int len;
2985 const gunichar2 *s_utf16;
2986 char *contents;
2987
2988 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
2989 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
2990 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
2991 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
2992
2993 g_free (contents);
2994
2995 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
2996 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
2997 (NULL));
2998 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
2999 qtdemux_parse_piff (qtdemux, buffer, length, offset);
3000 } else {
3001 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3002 GST_READ_UINT32_LE (buffer + offset),
3003 GST_READ_UINT32_LE (buffer + offset + 4),
3004 GST_READ_UINT32_LE (buffer + offset + 8),
3005 GST_READ_UINT32_LE (buffer + offset + 12));
3006 }
3007}
3008
3009static void
3010qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
3011{
3012 GstSidxParser sidx_parser;
3013 GstIsoffParserResult res;
3014 guint consumed;
3015
3016 gst_isoff_qt_sidx_parser_init (&sidx_parser);
3017
3018 res =
3019 gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
3020 &consumed);
3021 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
3022 if (res == GST_ISOFF_QT_PARSER_DONE) {
3023 check_update_duration (qtdemux, sidx_parser.cumulative_pts);
3024 }
3025 gst_isoff_qt_sidx_parser_clear (&sidx_parser);
3026}
3027
3028/* caller verifies at least 8 bytes in buf */
3029static void
3030extract_initial_length_and_fourcc (const guint8 * data, guint size,
3031 guint64 * plength, guint32 * pfourcc)
3032{
3033 guint64 length;
3034 guint32 fourcc;
3035
3036 length = QT_UINT32 (data);
3037 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3038 fourcc = QT_FOURCC (data + 4);
3039 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3040
3041 if (length == 0) {
3042 length = G_MAXUINT64;
3043 } else if (length == 1 && size >= 16) {
3044 /* this means we have an extended size, which is the 64 bit value of
3045 * the next 8 bytes */
3046 length = QT_UINT64 (data + 8);
3047 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3048 }
3049
3050 if (plength)
3051 *plength = length;
3052 if (pfourcc)
3053 *pfourcc = fourcc;
3054}
3055
3056static gboolean
3057qtdemux_parse_mehd (GstQTDemux * qtdemux, GstByteReader * br)
3058{
3059 guint32 version = 0;
3060 GstClockTime duration = 0;
3061
3062 if (!gst_byte_reader_get_uint32_be (br, &version))
3063 goto failed;
3064
3065 version >>= 24;
3066 if (version == 1) {
3067 if (!gst_byte_reader_get_uint64_be (br, &duration))
3068 goto failed;
3069 } else {
3070 guint32 dur = 0;
3071
3072 if (!gst_byte_reader_get_uint32_be (br, &dur))
3073 goto failed;
3074 duration = dur;
3075 }
3076
3077 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3078 qtdemux->duration = duration;
3079
3080 return TRUE;
3081
3082failed:
3083 {
3084 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3085 return FALSE;
3086 }
3087}
3088
3089static gboolean
3090qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
3091 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3092{
3093 if (!stream->parsed_trex && qtdemux->moov_node) {
3094 GNode *mvex, *trex;
3095 GstByteReader trex_data;
3096
3097 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
3098 if (mvex) {
3099 trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
3100 &trex_data);
3101 while (trex) {
3102 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3103
3104 /* skip version/flags */
3105 if (!gst_byte_reader_skip (&trex_data, 4))
3106 goto next;
3107 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3108 goto next;
3109 if (id != stream->track_id)
3110 goto next;
3111 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3112 goto next;
3113 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3114 goto next;
3115 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3116 goto next;
3117 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3118 goto next;
3119
3120 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3121 "duration %d, size %d, flags 0x%x", stream->track_id,
3122 dur, size, flags);
3123
3124 stream->parsed_trex = TRUE;
3125 stream->def_sample_description_index = sdi;
3126 stream->def_sample_duration = dur;
3127 stream->def_sample_size = size;
3128 stream->def_sample_flags = flags;
3129
3130 next:
3131 /* iterate all siblings */
3132 trex = qtdemux_tree_get_sibling_by_type_full (trex, FOURCC_trex,
3133 &trex_data);
3134 }
3135 }
3136 }
3137
3138 *ds_duration = stream->def_sample_duration;
3139 *ds_size = stream->def_sample_size;
3140 *ds_flags = stream->def_sample_flags;
3141
3142 /* even then, above values are better than random ... */
3143 if (G_UNLIKELY (!stream->parsed_trex)) {
3144 GST_WARNING_OBJECT (qtdemux,
3145 "failed to find fragment defaults for stream %d", stream->track_id);
3146 return FALSE;
3147 }
3148
3149 return TRUE;
3150}
3151
3152/* This method should be called whenever a more accurate duration might
3153 * have been found. It will update all relevant variables if/where needed
3154 */
3155static void
3156check_update_duration (GstQTDemux * qtdemux, GstClockTime duration)
3157{
3158 guint i;
3159 guint64 movdur;
3160 GstClockTime prevdur;
3161
3162 movdur = GSTTIME_TO_QTTIME (qtdemux, duration);
3163
3164 if (movdur > qtdemux->duration) {
3165 prevdur = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
3166 GST_DEBUG_OBJECT (qtdemux,
3167 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3168 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3169 qtdemux->duration = movdur;
3170 GST_DEBUG_OBJECT (qtdemux,
3171 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3172 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3173 GST_TIME_ARGS (qtdemux->segment.stop));
3174 if (qtdemux->segment.duration == prevdur) {
3175 /* If the current segment has duration/stop identical to previous duration
3176 * update them also (because they were set at that point in time with
3177 * the wrong duration */
3178 /* We convert the value *from* the timescale version to avoid rounding errors */
3179 GstClockTime fixeddur = QTTIME_TO_GSTTIME (qtdemux, movdur);
3180 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3181 qtdemux->segment.duration = fixeddur;
3182 qtdemux->segment.stop = fixeddur;
3183 }
3184 }
3185
3186 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3187 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3188
3189 movdur = GSTTIME_TO_QTSTREAMTIME (stream, duration);
3190 if (movdur > stream->duration) {
3191 GST_DEBUG_OBJECT (qtdemux,
3192 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3193 GST_TIME_ARGS (duration));
3194 stream->duration = movdur;
3195 /* internal duration tracking state has been updated above, so */
3196 /* preserve an open-ended dummy segment rather than repeatedly updating
3197 * it and spamming downstream accordingly with segment events */
3198 /* also mangle the edit list end time when fragmented with a single edit
3199 * list that may only cover any non-fragmented data */
3200 if ((stream->dummy_segment ||
3201 (qtdemux->fragmented && stream->n_segments == 1)) &&
3202 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3203 /* Update all dummy values to new duration */
3204 stream->segments[0].stop_time = duration;
3205 stream->segments[0].duration = duration;
3206 stream->segments[0].media_stop = duration;
3207
3208 /* let downstream know we possibly have a new stop time */
3209 if (stream->segment_index != -1) {
3210 GstClockTime pos;
3211
3212 if (qtdemux->segment.rate >= 0) {
3213 pos = stream->segment.start;
3214 } else {
3215 pos = stream->segment.stop;
3216 }
3217
3218 gst_qtdemux_stream_update_segment (qtdemux, stream,
3219 stream->segment_index, pos, NULL, NULL);
3220 }
3221 }
3222 }
3223 }
3224}
3225
3226static gboolean
3227qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
3228 QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
3229 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3230 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3231 gboolean has_tfdt)
3232{
3233 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3234 guint64 timestamp;
3235 gint32 data_offset = 0;
3236 guint8 version;
3237 guint32 flags = 0, first_flags = 0, samples_count = 0;
3238 gint i;
3239 guint8 *data;
3240 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
3241 QtDemuxSample *sample;
3242 gboolean ismv = FALSE;
3243 gint64 initial_offset;
3244 gint32 min_ct = 0;
3245
3246 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3247 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3248 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3249 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3250
3251 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3252 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3253 return TRUE;
3254 }
3255
3256 /* presence of stss or not can't really tell us much,
3257 * and flags and so on tend to be marginally reliable in these files */
3258 if (stream->subtype == FOURCC_soun) {
3259 GST_DEBUG_OBJECT (qtdemux,
3260 "sound track in fragmented file; marking all keyframes");
3261 stream->all_keyframe = TRUE;
3262 }
3263
3264 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3265 !gst_byte_reader_get_uint24_be (trun, &flags))
3266 goto fail;
3267
3268 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3269 goto fail;
3270
3271 if (flags & TR_DATA_OFFSET) {
3272 /* note this is really signed */
3273 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3274 goto fail;
3275 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3276 /* default base offset = first byte of moof */
3277 if (*base_offset == -1) {
3278 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3279 *base_offset = moof_offset;
3280 }
3281 *running_offset = *base_offset + data_offset;
3282 } else {
3283 /* if no offset at all, that would mean data starts at moof start,
3284 * which is a bit wrong and is ismv crappy way, so compensate
3285 * assuming data is in mdat following moof */
3286 if (*base_offset == -1) {
3287 *base_offset = moof_offset + moof_length + 8;
3288 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3289 ismv = TRUE;
3290 }
3291 if (*running_offset == -1)
3292 *running_offset = *base_offset;
3293 }
3294
3295 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3296 *running_offset);
3297 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3298 data_offset, flags, samples_count);
3299
3300 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3301 if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
3302 GST_DEBUG_OBJECT (qtdemux,
3303 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
3304 flags ^= TR_FIRST_SAMPLE_FLAGS;
zengliang.li5f31ef42024-05-16 08:27:38 +00003305 }
zengliang.li34751a82024-05-17 02:52:12 +00003306
3307 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3308 goto fail;
3309 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
zengliang.li5f31ef42024-05-16 08:27:38 +00003310 }
3311
3312 /* FIXME ? spec says other bits should also be checked to determine
3313 * entry size (and prefix size for that matter) */
3314 entry_size = 0;
3315 dur_offset = size_offset = 0;
3316 if (flags & TR_SAMPLE_DURATION) {
3317 GST_LOG_OBJECT (qtdemux, "entry duration present");
3318 dur_offset = entry_size;
3319 entry_size += 4;
3320 }
3321 if (flags & TR_SAMPLE_SIZE) {
3322 GST_LOG_OBJECT (qtdemux, "entry size present");
3323 size_offset = entry_size;
3324 entry_size += 4;
3325 }
3326 if (flags & TR_SAMPLE_FLAGS) {
3327 GST_LOG_OBJECT (qtdemux, "entry flags present");
3328 flags_offset = entry_size;
3329 entry_size += 4;
3330 }
3331 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3332 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3333 ct_offset = entry_size;
3334 entry_size += 4;
3335 }
3336
3337 if (!qt_atom_parser_has_chunks (trun, samples_count, entry_size))
3338 goto fail;
3339 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3340
3341 if (stream->n_samples + samples_count >=
3342 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
3343 goto index_too_big;
3344
3345 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
3346 stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
3347 (stream->n_samples + samples_count) *
3348 sizeof (QtDemuxSample) / (1024.0 * 1024.0));
3349
3350 /* create a new array of samples if it's the first sample parsed */
3351 if (stream->n_samples == 0) {
3352 g_assert (stream->samples == NULL);
3353 stream->samples = g_try_new0 (QtDemuxSample, samples_count);
3354 /* or try to reallocate it with space enough to insert the new samples */
3355 } else
3356 stream->samples = g_try_renew (QtDemuxSample, stream->samples,
3357 stream->n_samples + samples_count);
3358 if (stream->samples == NULL)
3359 goto out_of_memory;
3360
3361 if (qtdemux->fragment_start != -1) {
3362 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
3363 qtdemux->fragment_start = -1;
3364 } else {
3365 if (stream->n_samples == 0) {
3366 if (decode_ts > 0) {
3367 timestamp = decode_ts;
3368 } else if (stream->pending_seek != NULL) {
3369 /* if we don't have a timestamp from a tfdt box, we'll use the one
3370 * from the mfra seek table */
3371 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3372 GST_TIME_ARGS (stream->pending_seek->ts));
3373
3374 /* FIXME: this is not fully correct, the timestamp refers to the random
3375 * access sample refered to in the tfra entry, which may not necessarily
3376 * be the first sample in the tfrag/trun (but hopefully/usually is) */
3377 timestamp = GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
3378 } else {
3379 timestamp = 0;
3380 }
3381
3382 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3383 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3384 GST_TIME_ARGS (gst_ts));
3385 } else {
3386 /* subsequent fragments extend stream */
3387 timestamp =
3388 stream->samples[stream->n_samples - 1].timestamp +
3389 stream->samples[stream->n_samples - 1].duration;
3390
3391 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3392 * difference (1 sec.) between decode_ts and timestamp, prefer the
3393 * former */
3394 if (has_tfdt && !qtdemux->upstream_format_is_time
3395 && ABSDIFF (decode_ts, timestamp) >
3396 MAX (stream->duration_last_moof / 2,
3397 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
3398 GST_INFO_OBJECT (qtdemux,
3399 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3400 ") are significantly different (more than %" GST_TIME_FORMAT
3401 "), using decode_ts",
3402 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3403 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3404 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
3405 MAX (stream->duration_last_moof / 2,
3406 GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
3407 timestamp = decode_ts;
3408 }
3409
3410 gst_ts = QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
3411 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3412 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3413 }
3414 }
3415
3416 initial_offset = *running_offset;
3417
3418 sample = stream->samples + stream->n_samples;
3419 for (i = 0; i < samples_count; i++) {
3420 guint32 dur, size, sflags;
3421 gint32 ct;
3422
3423 /* first read sample data */
3424 if (flags & TR_SAMPLE_DURATION) {
3425 dur = QT_UINT32 (data + dur_offset);
3426 } else {
3427 dur = d_sample_duration;
3428 }
3429 if (flags & TR_SAMPLE_SIZE) {
3430 size = QT_UINT32 (data + size_offset);
3431 } else {
3432 size = d_sample_size;
3433 }
3434 if (flags & TR_FIRST_SAMPLE_FLAGS) {
3435 if (i == 0) {
3436 sflags = first_flags;
3437 } else {
3438 sflags = d_sample_flags;
3439 }
3440 } else if (flags & TR_SAMPLE_FLAGS) {
3441 sflags = QT_UINT32 (data + flags_offset);
3442 } else {
3443 sflags = d_sample_flags;
3444 }
3445
3446 if (flags & TR_COMPOSITION_TIME_OFFSETS) {
3447 /* Read offsets as signed numbers regardless of trun version as very
3448 * high offsets are unlikely and there are files out there that use
3449 * version=0 truns with negative offsets */
3450 ct = QT_UINT32 (data + ct_offset);
3451
3452 /* FIXME: Set offset to 0 for "no decode samples". This needs
3453 * to be handled in a codec specific manner ideally. */
3454 if (ct == G_MININT32)
3455 ct = 0;
3456 } else {
3457 ct = 0;
3458 }
3459 data += entry_size;
3460
3461 /* fill the sample information */
3462 sample->offset = *running_offset;
3463 sample->pts_offset = ct;
3464 sample->size = size;
3465 sample->timestamp = timestamp;
3466 sample->duration = dur;
3467 /* sample-is-difference-sample */
3468 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3469 * now idea how it relates to bitfield other than massive LE/BE confusion */
3470 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3471 *running_offset += size;
3472 timestamp += dur;
3473 stream->duration_moof += dur;
3474 sample++;
3475
3476 if (ct < min_ct)
3477 min_ct = ct;
3478 }
3479
3480 /* Shift PTS/DTS to allow for negative composition offsets while keeping
3481 * A/V sync in place. This is similar to the code handling ctts/cslg in the
3482 * non-fragmented case.
3483 */
3484 if (min_ct < 0)
3485 stream->cslg_shift = -min_ct;
3486 else
3487 stream->cslg_shift = 0;
3488
3489 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
3490 stream->cslg_shift);
3491
3492 /* Update total duration if needed */
3493 check_update_duration (qtdemux, QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
3494
3495 /* Pre-emptively figure out size of mdat based on trun information.
3496 * If the [mdat] atom is effectively read, it will be replaced by the actual
3497 * size, else we will still be able to use this when dealing with gap'ed
3498 * input */
3499 qtdemux->mdatleft = *running_offset - initial_offset;
3500 qtdemux->mdatoffset = initial_offset;
3501 qtdemux->mdatsize = qtdemux->mdatleft;
3502
3503 stream->n_samples += samples_count;
3504 stream->n_samples_moof += samples_count;
3505
3506 if (stream->pending_seek != NULL)
3507 stream->pending_seek = NULL;
3508
3509 return TRUE;
3510
3511fail:
3512 {
3513 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3514 return FALSE;
3515 }
3516out_of_memory:
3517 {
3518 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3519 stream->n_samples);
3520 return FALSE;
3521 }
3522index_too_big:
3523 {
3524 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3525 "be larger than %uMB (broken file?)", stream->n_samples,
3526 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
3527 return FALSE;
3528 }
3529}
3530
3531/* find stream with @id */
3532static inline QtDemuxStream *
3533qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
3534{
3535 QtDemuxStream *stream;
3536 gint i;
3537
3538 /* check */
3539 if (G_UNLIKELY (!id)) {
3540 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3541 return NULL;
3542 }
3543
3544 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
3545 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
3546 if (stream->track_id == id)
3547 return stream;
3548 }
3549 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
3550 /* mss should have only 1 stream anyway */
3551 return QTDEMUX_NTH_STREAM (qtdemux, 0);
3552 }
3553
3554 return NULL;
3555}
3556
3557static gboolean
3558qtdemux_parse_mfhd (GstQTDemux * qtdemux, GstByteReader * mfhd,
3559 guint32 * fragment_number)
3560{
3561 if (!gst_byte_reader_skip (mfhd, 4))
3562 goto fail;
3563 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3564 goto fail;
3565 return TRUE;
3566fail:
3567 {
3568 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3569 return FALSE;
3570 }
3571}
3572
3573static gboolean
3574qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
3575 QtDemuxStream ** stream, guint32 * default_sample_duration,
3576 guint32 * default_sample_size, guint32 * default_sample_flags,
3577 gint64 * base_offset)
3578{
3579 guint32 flags = 0;
3580 guint32 track_id = 0;
3581
3582 if (!gst_byte_reader_skip (tfhd, 1) ||
3583 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3584 goto invalid_track;
3585
3586 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3587 goto invalid_track;
3588
3589 *stream = qtdemux_find_stream (qtdemux, track_id);
3590 if (G_UNLIKELY (!*stream))
3591 goto unknown_stream;
3592
3593 if (flags & TF_DEFAULT_BASE_IS_MOOF)
3594 *base_offset = qtdemux->moof_offset;
3595
3596 if (flags & TF_BASE_DATA_OFFSET)
3597 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3598 goto invalid_track;
3599
3600 /* obtain stream defaults */
3601 if (qtdemux_parse_trex (qtdemux, *stream,
3602 default_sample_duration, default_sample_size, default_sample_flags)) {
3603
3604 /* Default sample description index is only valid if trex parsing succeeded */
3605 (*stream)->stsd_sample_description_id =
3606 (*stream)->def_sample_description_index - 1;
3607 }
3608
3609 if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
3610 guint32 sample_description_index;
3611 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3612 goto invalid_track;
3613 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3614 }
3615
3616 if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
3617 /* mss has no stsd entry */
3618 (*stream)->stsd_sample_description_id = 0;
3619 }
3620
3621 if (flags & TF_DEFAULT_SAMPLE_DURATION)
3622 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3623 goto invalid_track;
3624
3625 if (flags & TF_DEFAULT_SAMPLE_SIZE)
3626 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3627 goto invalid_track;
3628
3629 if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3630 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3631 goto invalid_track;
3632
3633 return TRUE;
3634
3635invalid_track:
3636 {
3637 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3638 return FALSE;
3639 }
3640unknown_stream:
3641 {
3642 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
3643 return TRUE;
3644 }
3645}
3646
3647static gboolean
3648qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
3649 guint64 * decode_time)
3650{
3651 guint32 version = 0;
3652
3653 if (!gst_byte_reader_get_uint32_be (br, &version))
3654 return FALSE;
3655
3656 version >>= 24;
3657 if (version == 1) {
3658 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3659 goto failed;
3660 } else {
3661 guint32 dec_time = 0;
3662 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3663 goto failed;
3664 *decode_time = dec_time;
3665 }
3666
3667 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3668 *decode_time);
3669
3670 return TRUE;
3671
3672failed:
3673 {
3674 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3675 return FALSE;
3676 }
3677}
3678
3679/* Returns a pointer to a GstStructure containing the properties of
3680 * the stream sample identified by @sample_index. The caller must unref
3681 * the returned object after use. Returns NULL if unsuccessful. */
3682static GstStructure *
3683qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
3684 QtDemuxStream * stream, guint sample_index)
3685{
3686 QtDemuxCencSampleSetInfo *info = NULL;
3687
3688 g_return_val_if_fail (stream != NULL, NULL);
3689 g_return_val_if_fail (stream->protected, NULL);
3690 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3691
3692 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3693
3694 /* Currently, cenc properties for groups of samples are not supported, so
3695 * simply return a copy of the default sample properties */
3696 return gst_structure_copy (info->default_properties);
3697}
3698
3699/* Parses the sizes of sample auxiliary information contained within a stream,
3700 * as given in a saiz box. Returns array of sample_count guint8 size values,
3701 * or NULL on failure */
3702static guint8 *
3703qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
3704 GstByteReader * br, guint32 * sample_count)
3705{
3706 guint32 flags = 0;
3707 guint8 *info_sizes;
3708 guint8 default_info_size;
3709
3710 g_return_val_if_fail (qtdemux != NULL, NULL);
3711 g_return_val_if_fail (stream != NULL, NULL);
3712 g_return_val_if_fail (br != NULL, NULL);
3713 g_return_val_if_fail (sample_count != NULL, NULL);
3714
3715 if (!gst_byte_reader_get_uint32_be (br, &flags))
3716 return NULL;
3717
3718 if (flags & 0x1) {
3719 /* aux_info_type and aux_info_type_parameter are ignored */
3720 if (!gst_byte_reader_skip (br, 8))
3721 return NULL;
3722 }
3723
3724 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3725 return NULL;
3726 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3727
3728 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3729 return NULL;
3730 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3731
3732
3733 if (default_info_size == 0) {
3734 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3735 return NULL;
3736 }
3737 } else {
3738 info_sizes = g_new (guint8, *sample_count);
3739 memset (info_sizes, default_info_size, *sample_count);
3740 }
3741
3742 return info_sizes;
3743}
3744
3745/* Parses the offset of sample auxiliary information contained within a stream,
3746 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3747static gboolean
3748qtdemux_parse_saio (GstQTDemux * qtdemux, QtDemuxStream * stream,
3749 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3750 guint64 * offset)
3751{
3752 guint8 version = 0;
3753 guint32 flags = 0;
3754 guint32 aux_info_type = 0;
3755 guint32 aux_info_type_parameter = 0;
3756 guint32 entry_count;
3757 guint32 off_32;
3758 guint64 off_64;
3759 const guint8 *aux_info_type_data = NULL;
3760
3761 g_return_val_if_fail (qtdemux != NULL, FALSE);
3762 g_return_val_if_fail (stream != NULL, FALSE);
3763 g_return_val_if_fail (br != NULL, FALSE);
3764 g_return_val_if_fail (offset != NULL, FALSE);
3765
3766 if (!gst_byte_reader_get_uint8 (br, &version))
3767 return FALSE;
3768
3769 if (!gst_byte_reader_get_uint24_be (br, &flags))
3770 return FALSE;
3771
3772 if (flags & 0x1) {
3773
3774 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3775 return FALSE;
3776 aux_info_type = QT_FOURCC (aux_info_type_data);
3777
3778 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3779 return FALSE;
3780 } else if (stream->protected) {
3781 aux_info_type = stream->protection_scheme_type;
3782 } else {
3783 aux_info_type = CUR_STREAM (stream)->fourcc;
3784 }
3785
3786 if (info_type)
3787 *info_type = aux_info_type;
3788 if (info_type_parameter)
3789 *info_type_parameter = aux_info_type_parameter;
3790
3791 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3792 "aux_info_type_parameter: %#06x",
3793 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3794
3795 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3796 return FALSE;
3797
3798 if (entry_count != 1) {
3799 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3800 return FALSE;
3801 }
3802
3803 if (version == 0) {
3804 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3805 return FALSE;
3806 *offset = (guint64) off_32;
3807 } else {
3808 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3809 return FALSE;
3810 *offset = off_64;
3811 }
3812
3813 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3814 return TRUE;
3815}
3816
3817static void
3818qtdemux_gst_structure_free (GstStructure * gststructure)
3819{
3820 if (gststructure) {
3821 gst_structure_free (gststructure);
3822 }
3823}
3824
3825/* Parses auxiliary information relating to samples protected using
3826 * Common Encryption (cenc); the format of this information
3827 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
3828 * otherwise. */
3829static gboolean
3830qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
3831 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3832{
3833 QtDemuxCencSampleSetInfo *ss_info = NULL;
3834 guint8 size;
3835 gint i;
3836 GPtrArray *old_crypto_info = NULL;
3837 guint old_entries = 0;
3838
3839 g_return_val_if_fail (qtdemux != NULL, FALSE);
3840 g_return_val_if_fail (stream != NULL, FALSE);
3841 g_return_val_if_fail (br != NULL, FALSE);
3842 g_return_val_if_fail (stream->protected, FALSE);
3843 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3844
3845 ss_info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
3846
3847 if (ss_info->crypto_info) {
3848 old_crypto_info = ss_info->crypto_info;
3849 /* Count number of non-null entries remaining at the tail end */
3850 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3851 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3852 break;
3853 old_entries++;
3854 }
3855 }
3856
3857 ss_info->crypto_info =
3858 g_ptr_array_new_full (sample_count + old_entries,
3859 (GDestroyNotify) qtdemux_gst_structure_free);
3860
3861 /* We preserve old entries because we parse the next moof in advance
3862 * of consuming all samples from the previous moof, and otherwise
3863 * we'd discard the corresponding crypto info for the samples
3864 * from the previous fragment. */
3865 if (old_entries) {
3866 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3867 old_entries);
3868 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3869 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3870 i));
3871 g_ptr_array_index (old_crypto_info, i) = NULL;
3872 }
3873 }
3874
3875 if (old_crypto_info) {
3876 /* Everything now belongs to the new array */
3877 g_ptr_array_free (old_crypto_info, TRUE);
3878 }
3879
3880 for (i = 0; i < sample_count; ++i) {
3881 GstStructure *properties;
3882 guint16 n_subsamples = 0;
3883 guint8 *data;
3884 guint iv_size;
3885 GstBuffer *buf;
3886 gboolean could_read_iv;
3887
3888 properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
3889 if (properties == NULL) {
3890 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3891 return FALSE;
3892 }
3893 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3894 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3895 gst_structure_free (properties);
3896 return FALSE;
3897 }
3898 could_read_iv =
3899 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
3900 if (could_read_iv) {
3901 buf = gst_buffer_new_wrapped (data, iv_size);
3902 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3903 gst_buffer_unref (buf);
3904 } else if (stream->protection_scheme_type == FOURCC_cbcs) {
3905 const GValue *constant_iv_size_value =
3906 gst_structure_get_value (properties, "constant_iv_size");
3907 const GValue *constant_iv_value =
3908 gst_structure_get_value (properties, "iv");
3909 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
3910 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
3911 gst_structure_free (properties);
3912 return FALSE;
3913 }
3914 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
3915 gst_structure_remove_field (properties, "constant_iv_size");
3916 } else if (stream->protection_scheme_type == FOURCC_cenc) {
3917 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3918 gst_structure_free (properties);
3919 return FALSE;
3920 }
3921 size = info_sizes[i];
zengliang.li125c3642024-05-17 06:06:08 +00003922 GST_DEBUG_OBJECT (qtdemux, "size %u iv_size %u", size, iv_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00003923 if (size > iv_size) {
3924 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3925 || !(n_subsamples > 0)) {
3926 gst_structure_free (properties);
3927 GST_ERROR_OBJECT (qtdemux,
3928 "failed to get subsample count for sample %u", i);
3929 return FALSE;
3930 }
3931 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3932 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3933 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3934 i);
3935 gst_structure_free (properties);
3936 return FALSE;
3937 }
3938 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3939 if (!buf) {
3940 gst_structure_free (properties);
3941 return FALSE;
3942 }
3943 gst_structure_set (properties,
3944 "subsample_count", G_TYPE_UINT, n_subsamples,
3945 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3946 gst_buffer_unref (buf);
3947 } else {
3948 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3949 }
3950 g_ptr_array_add (ss_info->crypto_info, properties);
3951 }
3952 return TRUE;
3953}
3954
3955/* Converts a UUID in raw byte form to a string representation, as defined in
3956 * RFC 4122. The caller takes ownership of the returned string and is
3957 * responsible for freeing it after use. */
3958static gchar *
3959qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
3960{
3961 const guint8 *uuid = (const guint8 *) uuid_bytes;
3962
3963 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3964 "%02x%02x-%02x%02x%02x%02x%02x%02x",
3965 uuid[0], uuid[1], uuid[2], uuid[3],
3966 uuid[4], uuid[5], uuid[6], uuid[7],
3967 uuid[8], uuid[9], uuid[10], uuid[11],
3968 uuid[12], uuid[13], uuid[14], uuid[15]);
3969}
3970
3971/* Parses a Protection System Specific Header box (pssh), as defined in the
3972 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
3973 * information needed by a specific content protection system in order to
3974 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
3975 * otherwise. */
3976static gboolean
3977qtdemux_parse_pssh (GstQTDemux * qtdemux, GNode * node)
3978{
3979 gchar *sysid_string;
3980 guint32 pssh_size = QT_UINT32 (node->data);
3981 GstBuffer *pssh = NULL;
3982 GstEvent *event = NULL;
3983 guint32 parent_box_type;
3984 gint i;
3985
3986 if (G_UNLIKELY (pssh_size < 32U)) {
3987 GST_ERROR_OBJECT (qtdemux, "invalid box size");
3988 return FALSE;
3989 }
3990
3991 sysid_string =
3992 qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
3993
3994 gst_qtdemux_append_protection_system_id (qtdemux, sysid_string);
3995
3996 pssh = gst_buffer_new_memdup (node->data, pssh_size);
3997 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
3998 gst_buffer_get_size (pssh));
3999
4000 parent_box_type = QT_FOURCC ((const guint8 *) node->parent->data + 4);
4001
4002 /* Push an event containing the pssh box onto the queues of all streams. */
4003 event = gst_event_new_protection (sysid_string, pssh,
4004 (parent_box_type == FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4005 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4006 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4007 GST_TRACE_OBJECT (qtdemux,
4008 "adding protection event for stream %s and system %s",
4009 stream->stream_id, sysid_string);
4010 g_queue_push_tail (&stream->protection_scheme_event_queue,
4011 gst_event_ref (event));
4012 }
4013 g_free (sysid_string);
4014 gst_event_unref (event);
4015 gst_buffer_unref (pssh);
4016 return TRUE;
4017}
4018
4019static gboolean
4020qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
4021 guint64 moof_offset, QtDemuxStream * stream)
4022{
4023 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4024 GNode *uuid_node;
4025 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4026 GNode *saiz_node, *saio_node, *pssh_node;
4027 GstByteReader saiz_data, saio_data;
4028 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4029 gint64 base_offset, running_offset;
4030 guint32 frag_num;
4031 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
4032
4033 /* NOTE @stream ignored */
4034
4035 moof_node = g_node_new ((guint8 *) buffer);
4036 qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4037 qtdemux_node_dump (qtdemux, moof_node);
4038
4039 /* Get fragment number from mfhd and check it's valid */
4040 mfhd_node =
4041 qtdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
4042 if (mfhd_node == NULL)
4043 goto missing_mfhd;
4044 if (!qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
4045 goto fail;
4046 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4047
4048 /* unknown base_offset to start with */
4049 base_offset = running_offset = -1;
4050 traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
4051 while (traf_node) {
4052 guint64 decode_time = 0;
4053
4054 /* Fragment Header node */
4055 tfhd_node =
4056 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
4057 &tfhd_data);
4058 if (!tfhd_node)
4059 goto missing_tfhd;
4060 if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
4061 &ds_size, &ds_flags, &base_offset))
4062 goto missing_tfhd;
4063
4064 /* The following code assumes at most a single set of sample auxiliary
4065 * data in the fragment (consisting of a saiz box and a corresponding saio
4066 * box); in theory, however, there could be multiple sets of sample
4067 * auxiliary data in a fragment. */
4068 saiz_node =
4069 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saiz,
4070 &saiz_data);
4071 if (saiz_node) {
4072 guint32 info_type = 0;
4073 guint64 offset = 0;
4074 guint32 info_type_parameter = 0;
4075
4076 g_free (qtdemux->cenc_aux_info_sizes);
zengliang.li125c3642024-05-17 06:06:08 +00004077 GST_DEBUG_OBJECT (qtdemux, "parse saiz_node");
zengliang.li5f31ef42024-05-16 08:27:38 +00004078 qtdemux->cenc_aux_info_sizes =
4079 qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
4080 &qtdemux->cenc_aux_sample_count);
4081 if (qtdemux->cenc_aux_info_sizes == NULL) {
4082 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4083 goto fail;
4084 }
4085 saio_node =
4086 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_saio,
4087 &saio_data);
4088 if (!saio_node) {
4089 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4090 g_free (qtdemux->cenc_aux_info_sizes);
4091 qtdemux->cenc_aux_info_sizes = NULL;
4092 goto fail;
4093 }
4094
4095 if (G_UNLIKELY (!qtdemux_parse_saio (qtdemux, stream, &saio_data,
4096 &info_type, &info_type_parameter, &offset))) {
4097 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4098 g_free (qtdemux->cenc_aux_info_sizes);
4099 qtdemux->cenc_aux_info_sizes = NULL;
4100 goto fail;
4101 }
4102 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4103 offset += (guint64) (base_offset - qtdemux->moof_offset);
zengliang.li125c3642024-05-17 06:06:08 +00004104 if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs || info_type == FOURCC_cens)
zengliang.li5f31ef42024-05-16 08:27:38 +00004105 && info_type_parameter == 0U) {
4106 GstByteReader br;
4107 if (offset > length) {
4108 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4109 qtdemux->cenc_aux_info_offset = offset;
4110 } else {
zengliang.li125c3642024-05-17 06:06:08 +00004111 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
zengliang.li5f31ef42024-05-16 08:27:38 +00004112 gst_byte_reader_init (&br, buffer + offset, length - offset);
4113 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
4114 qtdemux->cenc_aux_info_sizes,
4115 qtdemux->cenc_aux_sample_count)) {
4116 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4117 g_free (qtdemux->cenc_aux_info_sizes);
4118 qtdemux->cenc_aux_info_sizes = NULL;
4119 goto fail;
4120 }
4121 }
4122 }
zengliang.li125c3642024-05-17 06:06:08 +00004123 } else {
4124 GST_WARNING_OBJECT (qtdemux, "no saiz_node, may cbc1 audio");
zengliang.li5f31ef42024-05-16 08:27:38 +00004125 }
4126
4127 tfdt_node =
4128 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
4129 &tfdt_data);
4130 if (tfdt_node) {
4131 /* We'll use decode_time to interpolate timestamps
4132 * in case the input timestamps are missing */
4133 qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
4134
4135 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4136 " (%" GST_TIME_FORMAT ")", decode_time,
4137 GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
4138 decode_time) : GST_CLOCK_TIME_NONE));
4139
4140 /* Discard the fragment buffer timestamp info to avoid using it.
4141 * Rely on tfdt instead as it is more accurate than the timestamp
4142 * that is fetched from a manifest/playlist and is usually
4143 * less accurate. */
4144 qtdemux->fragment_start = -1;
4145 }
4146
4147 if (G_UNLIKELY (!stream)) {
4148 /* we lost track of offset, we'll need to regain it,
4149 * but can delay complaining until later or avoid doing so altogether */
4150 base_offset = -2;
4151 goto next;
4152 }
4153 if (G_UNLIKELY (base_offset < -1))
4154 goto lost_offset;
4155
4156 min_dts = MIN (min_dts, QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
4157
4158 if (!qtdemux->pullbased) {
4159 /* Sample tables can grow enough to be problematic if the system memory
4160 * is very low (e.g. embedded devices) and the videos very long
4161 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4162 * Fortunately, we can easily discard them for each new fragment when
4163 * we know qtdemux will not receive seeks outside of the current fragment.
4164 * adaptivedemux honors this assumption.
4165 * This optimization is also useful for applications that use qtdemux as
4166 * a push-based simple demuxer, like Media Source Extensions. */
4167 gst_qtdemux_stream_flush_samples_data (stream);
4168 }
4169
4170 /* initialise moof sample data */
4171 stream->n_samples_moof = 0;
4172 stream->duration_last_moof = stream->duration_moof;
4173 stream->duration_moof = 0;
4174
4175 /* Track Run node */
4176 trun_node =
4177 qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
4178 &trun_data);
4179 while (trun_node) {
4180 qtdemux_parse_trun (qtdemux, &trun_data, stream,
4181 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4182 &running_offset, decode_time, (tfdt_node != NULL));
4183 /* iterate all siblings */
4184 trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
4185 &trun_data);
4186 /* don't use tfdt for subsequent trun as it only refers to the first */
4187 tfdt_node = NULL;
4188 }
4189
4190 uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
4191 if (uuid_node) {
4192 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
4193 guint32 box_length = QT_UINT32 (uuid_buffer);
4194
4195 qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
4196 }
4197
4198 /* if no new base_offset provided for next traf,
4199 * base is end of current traf */
4200 base_offset = running_offset;
4201 running_offset = -1;
4202
4203 if (stream->n_samples_moof && stream->duration_moof)
4204 stream->new_caps = TRUE;
4205
4206 next:
4207 /* iterate all siblings */
4208 traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
4209 }
4210
4211 /* parse any protection system info */
4212 pssh_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_pssh);
4213 while (pssh_node) {
4214 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
4215 qtdemux_parse_pssh (qtdemux, pssh_node);
4216 pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
4217 }
4218
4219 if (!qtdemux->upstream_format_is_time
4220 && qtdemux->variant != VARIANT_MSE_BYTESTREAM
4221 && !qtdemux->first_moof_already_parsed
4222 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4223 && min_dts != 0) {
4224 /* Unless the user has explicitly requested another seek, perform an
4225 * internal seek to the time specified in the tfdt.
4226 *
4227 * This way if the user opens a file where the first tfdt is 1 hour
4228 * into the presentation, they will not have to wait 1 hour for run
4229 * time to catch up and actual playback to start. */
4230 gint i;
4231
4232 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4233 "performing an internal seek to %" GST_TIME_FORMAT,
4234 GST_TIME_ARGS (min_dts));
4235
4236 qtdemux->segment.start = min_dts;
4237 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4238
4239 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4240 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
4241 stream->time_position = min_dts;
4242 }
4243
4244 /* Before this code was run a segment was already sent when the moov was
4245 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4246 * be emitted after a moov, and we can emit a second segment anyway for
4247 * special cases like this. */
4248 qtdemux->need_segment = TRUE;
4249 }
4250
4251 qtdemux->first_moof_already_parsed = TRUE;
4252
4253 g_node_destroy (moof_node);
4254 return TRUE;
4255
4256missing_tfhd:
4257 {
4258 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4259 goto fail;
4260 }
4261missing_mfhd:
4262 {
4263 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4264 goto fail;
4265 }
4266lost_offset:
4267 {
4268 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4269 goto fail;
4270 }
4271fail:
4272 {
4273 g_node_destroy (moof_node);
4274 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4275 (_("This file is corrupt and cannot be played.")), (NULL));
4276 return FALSE;
4277 }
4278}
4279
4280#if 0
4281/* might be used if some day we actually use mfra & co
4282 * for random access to fragments,
4283 * but that will require quite some modifications and much less relying
4284 * on a sample array */
4285#endif
4286
4287static gboolean
4288qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
4289{
4290 QtDemuxStream *stream;
4291 guint32 ver_flags, track_id, len, num_entries, i;
4292 guint value_size, traf_size, trun_size, sample_size;
4293 guint64 time = 0, moof_offset = 0;
4294#if 0
4295 GstBuffer *buf = NULL;
4296 GstFlowReturn ret;
4297#endif
4298 GstByteReader tfra;
4299
4300 gst_byte_reader_init (&tfra, tfra_node->data, QT_UINT32 (tfra_node->data));
4301
4302 if (!gst_byte_reader_skip (&tfra, 8))
4303 return FALSE;
4304
4305 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4306 return FALSE;
4307
4308 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4309 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4310 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4311 return FALSE;
4312
4313 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4314
4315 stream = qtdemux_find_stream (qtdemux, track_id);
4316 if (stream == NULL)
4317 goto unknown_trackid;
4318
4319 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4320 sample_size = (len & 3) + 1;
4321 trun_size = ((len & 12) >> 2) + 1;
4322 traf_size = ((len & 48) >> 4) + 1;
4323
4324 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4325 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4326
4327 if (num_entries == 0)
4328 goto no_samples;
4329
4330 if (!qt_atom_parser_has_chunks (&tfra, num_entries,
4331 value_size + value_size + traf_size + trun_size + sample_size))
4332 goto corrupt_file;
4333
4334 g_free (stream->ra_entries);
4335 stream->ra_entries = g_new (QtDemuxRandomAccessEntry, num_entries);
4336 stream->n_ra_entries = num_entries;
4337
4338 for (i = 0; i < num_entries; i++) {
4339 qt_atom_parser_get_offset (&tfra, value_size, &time);
4340 qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4341 qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4342 qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4343 qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
4344
4345 time = QTSTREAMTIME_TO_GSTTIME (stream, time);
4346
4347 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4348 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4349
4350 stream->ra_entries[i].ts = time;
4351 stream->ra_entries[i].moof_offset = moof_offset;
4352
4353 /* don't want to go through the entire file and read all moofs at startup */
4354#if 0
4355 ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
4356 if (ret != GST_FLOW_OK)
4357 goto corrupt_file;
4358 qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
4359 moof_offset, stream);
4360 gst_buffer_unref (buf);
4361#endif
4362 }
4363
4364 check_update_duration (qtdemux, time);
4365
4366 return TRUE;
4367
4368/* ERRORS */
4369unknown_trackid:
4370 {
4371 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4372 return FALSE;
4373 }
4374corrupt_file:
4375 {
4376 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4377 return FALSE;
4378 }
4379no_samples:
4380 {
4381 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4382 return FALSE;
4383 }
4384}
4385
4386static gboolean
4387qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
4388{
4389 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4390 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4391 GstBuffer *mfro = NULL, *mfra = NULL;
4392 GstFlowReturn flow;
4393 gboolean ret = FALSE;
4394 GNode *mfra_node, *tfra_node;
4395 guint64 mfra_offset = 0;
4396 guint32 fourcc, mfra_size;
4397 gint64 len;
4398
4399 /* query upstream size in bytes */
4400 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4401 goto size_query_failed;
4402
4403 /* mfro box should be at the very end of the file */
4404 flow = gst_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
4405 if (flow != GST_FLOW_OK)
4406 goto exit;
4407
4408 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4409
4410 fourcc = QT_FOURCC (mfro_map.data + 4);
4411 if (fourcc != FOURCC_mfro)
4412 goto exit;
4413
4414 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4415 if (mfro_map.size < 16)
4416 goto invalid_mfro_size;
4417
4418 mfra_size = QT_UINT32 (mfro_map.data + 12);
4419 if (mfra_size >= len)
4420 goto invalid_mfra_size;
4421
4422 mfra_offset = len - mfra_size;
4423
4424 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4425 mfra_offset, mfra_size);
4426
4427 /* now get and parse mfra box */
4428 flow = gst_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
4429 if (flow != GST_FLOW_OK)
4430 goto broken_file;
4431
4432 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4433
4434 mfra_node = g_node_new ((guint8 *) mfra_map.data);
4435 qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
4436
4437 tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
4438
4439 while (tfra_node) {
4440 qtdemux_parse_tfra (qtdemux, tfra_node);
4441 /* iterate all siblings */
4442 tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
4443 }
4444 g_node_destroy (mfra_node);
4445
4446 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4447 ret = TRUE;
4448
4449exit:
4450
4451 if (mfro) {
4452 if (mfro_map.memory != NULL)
4453 gst_buffer_unmap (mfro, &mfro_map);
4454 gst_buffer_unref (mfro);
4455 }
4456 if (mfra) {
4457 if (mfra_map.memory != NULL)
4458 gst_buffer_unmap (mfra, &mfra_map);
4459 gst_buffer_unref (mfra);
4460 }
4461 return ret;
4462
4463/* ERRORS */
4464size_query_failed:
4465 {
4466 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4467 goto exit;
4468 }
4469invalid_mfro_size:
4470 {
4471 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4472 goto exit;
4473 }
4474invalid_mfra_size:
4475 {
4476 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4477 goto exit;
4478 }
4479broken_file:
4480 {
4481 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4482 goto exit;
4483 }
4484}
4485
4486static guint64
4487add_offset (guint64 offset, guint64 advance)
4488{
4489 /* Avoid 64-bit overflow by clamping */
4490 if (offset > G_MAXUINT64 - advance)
4491 return G_MAXUINT64;
4492 return offset + advance;
4493}
4494
4495static GstFlowReturn
4496gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
4497{
4498 guint64 length = 0;
4499 guint32 fourcc = 0;
4500 GstBuffer *buf = NULL;
4501 GstFlowReturn ret = GST_FLOW_OK;
4502 guint64 cur_offset = qtdemux->offset;
4503 GstMapInfo map;
4504
4505 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4506 if (G_UNLIKELY (ret != GST_FLOW_OK))
4507 goto beach;
4508 gst_buffer_map (buf, &map, GST_MAP_READ);
4509 if (G_LIKELY (map.size >= 8))
4510 extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
4511 gst_buffer_unmap (buf, &map);
4512 gst_buffer_unref (buf);
4513
4514 /* maybe we already got most we needed, so only consider this eof */
4515 if (G_UNLIKELY (length == 0)) {
4516 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4517 (_("Invalid atom size.")),
4518 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4519 GST_FOURCC_ARGS (fourcc)));
4520 ret = GST_FLOW_EOS;
4521 goto beach;
4522 }
4523
4524 switch (fourcc) {
4525 case FOURCC_moof:
4526 /* record for later parsing when needed */
4527 if (!qtdemux->moof_offset) {
4528 qtdemux->moof_offset = qtdemux->offset;
4529 }
4530 if (qtdemux_pull_mfro_mfra (qtdemux)) {
4531 /* FIXME */
4532 } else {
4533 qtdemux->offset += length; /* skip moof and keep going */
4534 }
4535 if (qtdemux->got_moov) {
4536 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4537 ret = GST_FLOW_EOS;
4538 goto beach;
4539 }
4540 break;
4541 case FOURCC_mdat:
4542 case FOURCC_free:
4543 case FOURCC_skip:
4544 case FOURCC_wide:
4545 case FOURCC_PICT:
4546 case FOURCC_pnot:
4547 {
4548 GST_LOG_OBJECT (qtdemux,
4549 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4550 GST_FOURCC_ARGS (fourcc), cur_offset);
4551 qtdemux->offset = add_offset (qtdemux->offset, length);
4552 break;
4553 }
4554 case FOURCC_moov:
4555 {
4556 GstBuffer *moov = NULL;
4557
4558 if (qtdemux->got_moov) {
4559 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
4560 qtdemux->offset = add_offset (qtdemux->offset, length);
4561 goto beach;
4562 }
4563
4564 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4565 if (ret != GST_FLOW_OK)
4566 goto beach;
4567 gst_buffer_map (moov, &map, GST_MAP_READ);
4568
4569 if (length != map.size) {
4570 /* Some files have a 'moov' atom at the end of the file which contains
4571 * a terminal 'free' atom where the body of the atom is missing.
4572 * Check for, and permit, this special case.
4573 */
4574 if (map.size >= 8) {
4575 guint8 *final_data = map.data + (map.size - 8);
4576 guint32 final_length = QT_UINT32 (final_data);
4577 guint32 final_fourcc = QT_FOURCC (final_data + 4);
4578
4579 if (final_fourcc == FOURCC_free
4580 && map.size + final_length - 8 == length) {
4581 /* Ok, we've found that special case. Allocate a new buffer with
4582 * that free atom actually present. */
4583 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4584 gst_buffer_fill (newmoov, 0, map.data, map.size);
4585 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4586 gst_buffer_unmap (moov, &map);
4587 gst_buffer_unref (moov);
4588 moov = newmoov;
4589 gst_buffer_map (moov, &map, GST_MAP_READ);
4590 }
4591 }
4592 }
4593
4594 if (length != map.size) {
4595 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4596 (_("This file is incomplete and cannot be played.")),
4597 ("We got less than expected (received %" G_GSIZE_FORMAT
4598 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4599 (guint) length, cur_offset));
4600 gst_buffer_unmap (moov, &map);
4601 gst_buffer_unref (moov);
4602 ret = GST_FLOW_ERROR;
4603 goto beach;
4604 }
4605 qtdemux->offset += length;
4606
4607 qtdemux_parse_moov (qtdemux, map.data, length);
4608 qtdemux_node_dump (qtdemux, qtdemux->moov_node);
4609
4610 qtdemux_parse_tree (qtdemux);
4611 if (qtdemux->moov_node_compressed) {
4612 g_node_destroy (qtdemux->moov_node_compressed);
4613 g_free (qtdemux->moov_node->data);
4614 }
4615 qtdemux->moov_node_compressed = NULL;
4616 g_node_destroy (qtdemux->moov_node);
4617 qtdemux->moov_node = NULL;
4618 gst_buffer_unmap (moov, &map);
4619 gst_buffer_unref (moov);
4620 qtdemux->got_moov = TRUE;
4621
4622 break;
4623 }
4624 case FOURCC_ftyp:
4625 {
4626 GstBuffer *ftyp = NULL;
4627
4628 /* extract major brand; might come in handy for ISO vs QT issues */
4629 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
4630 if (ret != GST_FLOW_OK)
4631 goto beach;
4632 qtdemux->offset += length;
4633 gst_buffer_map (ftyp, &map, GST_MAP_READ);
4634 qtdemux_parse_ftyp (qtdemux, map.data, map.size);
4635 gst_buffer_unmap (ftyp, &map);
4636 gst_buffer_unref (ftyp);
4637 break;
4638 }
4639 case FOURCC_uuid:
4640 {
4641 GstBuffer *uuid = NULL;
4642
4643 /* uuid are extension atoms */
4644 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
4645 if (ret != GST_FLOW_OK)
4646 goto beach;
4647 qtdemux->offset += length;
4648 gst_buffer_map (uuid, &map, GST_MAP_READ);
4649 qtdemux_parse_uuid (qtdemux, map.data, map.size);
4650 gst_buffer_unmap (uuid, &map);
4651 gst_buffer_unref (uuid);
4652 break;
4653 }
4654 case FOURCC_sidx:
4655 {
4656 GstBuffer *sidx = NULL;
4657 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
4658 if (ret != GST_FLOW_OK)
4659 goto beach;
4660 qtdemux->offset += length;
4661 gst_buffer_map (sidx, &map, GST_MAP_READ);
4662 qtdemux_parse_sidx (qtdemux, map.data, map.size);
4663 gst_buffer_unmap (sidx, &map);
4664 gst_buffer_unref (sidx);
4665 break;
4666 }
4667 default:
4668 {
4669 GstBuffer *unknown = NULL;
4670
4671 GST_LOG_OBJECT (qtdemux,
4672 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4673 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4674 cur_offset);
4675 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
4676 if (ret != GST_FLOW_OK)
4677 goto beach;
4678 gst_buffer_map (unknown, &map, GST_MAP_READ);
4679 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4680 gst_buffer_unmap (unknown, &map);
4681 gst_buffer_unref (unknown);
4682 qtdemux->offset += length;
4683 break;
4684 }
4685 }
4686
4687beach:
4688 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4689 /* digested all data, show what we have */
4690 qtdemux_prepare_streams (qtdemux);
4691 QTDEMUX_EXPOSE_LOCK (qtdemux);
4692 ret = qtdemux_expose_streams (qtdemux);
4693 QTDEMUX_EXPOSE_UNLOCK (qtdemux);
4694
4695 qtdemux->state = QTDEMUX_STATE_MOVIE;
4696 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4697 qtdemux->state);
4698 return ret;
4699 }
4700 return ret;
4701}
4702
4703/* Seeks to the previous keyframe of the indexed stream and
4704 * aligns other streams with respect to the keyframe timestamp
4705 * of indexed stream. Only called in case of Reverse Playback
4706 */
4707static GstFlowReturn
4708gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
4709{
4710 guint32 seg_idx = 0, k_index = 0;
4711 guint32 ref_seg_idx, ref_k_index;
4712 GstClockTime k_pos = 0, last_stop = 0;
4713 QtDemuxSegment *seg = NULL;
4714 QtDemuxStream *ref_str = NULL;
4715 guint64 seg_media_start_mov; /* segment media start time in mov format */
4716 guint64 target_ts;
4717 gint i;
4718
4719 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4720 * and finally align all the other streams on that timestamp with their
4721 * respective keyframes */
4722 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4723 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4724
4725 /* No candidate yet, take the first stream */
4726 if (!ref_str) {
4727 ref_str = str;
4728 continue;
4729 }
4730
4731 /* So that stream has a segment, we prefer video streams */
4732 if (str->subtype == FOURCC_vide) {
4733 ref_str = str;
4734 break;
4735 }
4736 }
4737
4738 if (G_UNLIKELY (!ref_str)) {
4739 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4740 goto eos;
4741 }
4742
4743 if (G_UNLIKELY (!ref_str->from_sample)) {
4744 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4745 goto eos;
4746 }
4747
4748 /* So that stream has been playing from from_sample to to_sample. We will
4749 * get the timestamp of the previous sample and search for a keyframe before
4750 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
4751 if (ref_str->subtype == FOURCC_vide) {
4752 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str,
4753 ref_str->from_sample - 1, FALSE);
4754 } else {
4755 if (ref_str->from_sample >= 10)
4756 k_index = ref_str->from_sample - 10;
4757 else
4758 k_index = 0;
4759 }
4760
4761 target_ts =
4762 ref_str->samples[k_index].timestamp +
4763 ref_str->samples[k_index].pts_offset;
4764
4765 /* get current segment for that stream */
4766 seg = &ref_str->segments[ref_str->segment_index];
4767 /* Use segment start in original timescale for comparisons */
4768 seg_media_start_mov = seg->trak_media_start;
4769
4770 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4771 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4772 k_index, target_ts, seg_media_start_mov,
4773 GST_TIME_ARGS (seg->media_start));
4774
4775 /* Crawl back through segments to find the one containing this I frame */
4776 while (target_ts < seg_media_start_mov) {
4777 GST_DEBUG_OBJECT (qtdemux,
4778 "keyframe position (sample %u) is out of segment %u " " target %"
4779 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4780 ref_str->segment_index, target_ts, seg_media_start_mov);
4781
4782 if (G_UNLIKELY (!ref_str->segment_index)) {
4783 /* Reached first segment, let's consider it's EOS */
4784 goto eos;
4785 }
4786 ref_str->segment_index--;
4787 seg = &ref_str->segments[ref_str->segment_index];
4788 /* Use segment start in original timescale for comparisons */
4789 seg_media_start_mov = seg->trak_media_start;
4790 }
4791 /* Calculate time position of the keyframe and where we should stop */
4792 k_pos =
4793 QTSTREAMTIME_TO_GSTTIME (ref_str,
4794 target_ts - seg->trak_media_start) + seg->time;
4795 last_stop =
4796 QTSTREAMTIME_TO_GSTTIME (ref_str,
4797 ref_str->samples[ref_str->from_sample].timestamp -
4798 seg->trak_media_start) + seg->time;
4799
4800 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4801 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4802 k_index, GST_TIME_ARGS (k_pos));
4803
4804 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4805 qtdemux->segment.position = last_stop;
4806 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4807 GST_TIME_ARGS (last_stop));
4808
4809 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
4810 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
4811 goto eos;
4812 }
4813
4814 ref_seg_idx = ref_str->segment_index;
4815 ref_k_index = k_index;
4816
4817 /* Align them all on this */
4818 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
4819 guint32 index = 0;
4820 GstClockTime seg_time = 0;
4821 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
4822
4823 /* aligning reference stream again might lead to backing up to yet another
4824 * keyframe (due to timestamp rounding issues),
4825 * potentially putting more load on downstream; so let's try to avoid */
4826 if (str == ref_str) {
4827 seg_idx = ref_seg_idx;
4828 seg = &str->segments[seg_idx];
4829 k_index = ref_k_index;
4830 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
4831 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
4832 } else {
4833 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos);
4834 GST_DEBUG_OBJECT (qtdemux,
4835 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
4836 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
4837
4838 /* get segment and time in the segment */
4839 seg = &str->segments[seg_idx];
4840 seg_time = k_pos - seg->time;
4841
4842 /* get the media time in the segment.
4843 * No adjustment for empty "filler" segments */
4844 if (seg->media_start != GST_CLOCK_TIME_NONE)
4845 seg_time += seg->media_start;
4846
4847 /* get the index of the sample with media time */
4848 index = gst_qtdemux_find_index_linear (qtdemux, str, seg_time);
4849 GST_DEBUG_OBJECT (qtdemux,
4850 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
4851 GST_TIME_ARGS (seg_time), index);
4852
4853 /* find previous keyframe */
4854 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
4855 }
4856
4857 /* Remember until where we want to go */
4858 str->to_sample = str->from_sample - 1;
4859 /* Define our time position */
4860 target_ts =
4861 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
4862 str->time_position = QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
4863 if (seg->media_start != GST_CLOCK_TIME_NONE)
4864 str->time_position -= seg->media_start;
4865
4866 /* Now seek back in time */
4867 gst_qtdemux_move_stream (qtdemux, str, k_index);
4868 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
4869 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
4870 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
4871 }
4872
4873 return GST_FLOW_OK;
4874
4875eos:
4876 return GST_FLOW_EOS;
4877}
4878
4879/*
4880 * Gets the current qt segment start, stop and position for the
4881 * given time offset. This is used in update_segment()
4882 */
4883static void
4884gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
4885 QtDemuxStream * stream, GstClockTime offset,
4886 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
4887{
4888 GstClockTime seg_time;
4889 GstClockTime start, stop, time;
4890 QtDemuxSegment *segment;
4891
4892 segment = &stream->segments[stream->segment_index];
4893
4894 /* get time in this segment */
4895 seg_time = (offset - segment->time) * segment->rate;
4896
4897 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
4898 GST_TIME_ARGS (seg_time));
4899
4900 if (G_UNLIKELY (seg_time > segment->duration)) {
4901 GST_LOG_OBJECT (stream->pad,
4902 "seg_time > segment->duration %" GST_TIME_FORMAT,
4903 GST_TIME_ARGS (segment->duration));
4904 seg_time = segment->duration;
4905 }
4906
4907 /* qtdemux->segment.stop is in outside-time-realm, whereas
4908 * segment->media_stop is in track-time-realm.
4909 *
4910 * In order to compare the two, we need to bring segment.stop
4911 * into the track-time-realm
4912 *
4913 * FIXME - does this comment still hold? Don't see any conversion here */
4914
4915 stop = qtdemux->segment.stop;
4916 if (stop == GST_CLOCK_TIME_NONE)
4917 stop = qtdemux->segment.duration;
4918 if (stop == GST_CLOCK_TIME_NONE)
4919 stop = segment->media_stop;
4920 else
4921 stop =
4922 MIN (segment->media_stop, stop - segment->time + segment->media_start);
4923
4924 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
4925 start = segment->time + seg_time;
4926 time = offset;
4927 stop = start - seg_time + segment->duration;
4928 } else if (qtdemux->segment.rate >= 0) {
4929 start = MIN (segment->media_start + seg_time, stop);
4930 time = offset;
4931 } else {
4932 if (segment->media_start >= qtdemux->segment.start) {
4933 time = segment->time;
4934 } else {
4935 time = segment->time + (qtdemux->segment.start - segment->media_start);
4936 }
4937
4938 start = MAX (segment->media_start, qtdemux->segment.start);
4939 stop = MIN (segment->media_start + seg_time, stop);
4940 }
4941
4942 *_start = start;
4943 *_stop = stop;
4944 *_time = time;
4945}
4946
4947/*
4948 * Updates the qt segment used for the stream and pushes a new segment event
4949 * downstream on this stream's pad.
4950 */
4951static gboolean
4952gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
4953 gint seg_idx, GstClockTime offset, GstClockTime * _start,
4954 GstClockTime * _stop)
4955{
4956 QtDemuxSegment *segment;
4957 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
4958 gdouble rate;
4959 GstEvent *event;
4960
4961 /* update the current segment */
4962 stream->segment_index = seg_idx;
4963
4964 /* get the segment */
4965 segment = &stream->segments[seg_idx];
4966
4967 if (G_UNLIKELY (offset < segment->time)) {
4968 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
4969 GST_TIME_ARGS (segment->time));
4970 return FALSE;
4971 }
4972
4973 /* segment lies beyond total indicated duration */
4974 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
4975 segment->time > qtdemux->segment.duration)) {
4976 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
4977 " < segment->time %" GST_TIME_FORMAT,
4978 GST_TIME_ARGS (qtdemux->segment.duration),
4979 GST_TIME_ARGS (segment->time));
4980 return FALSE;
4981 }
4982
4983 gst_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
4984 &start, &stop, &time);
4985
4986 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
4987 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
4988 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
4989
4990 /* combine global rate with that of the segment */
4991 rate = segment->rate * qtdemux->segment.rate;
4992
4993 /* Copy flags from main segment */
4994 stream->segment.flags = qtdemux->segment.flags;
4995
4996 /* update the segment values used for clipping */
4997 stream->segment.offset = qtdemux->segment.offset;
4998 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
4999 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5000 stream->segment.rate = rate;
5001 stream->segment.start = start + QTSTREAMTIME_TO_GSTTIME (stream,
5002 stream->cslg_shift);
5003 if (stop != -1)
5004 stream->segment.stop = stop + QTSTREAMTIME_TO_GSTTIME (stream,
5005 stream->cslg_shift);
5006 else
5007 stream->segment.stop = stop;
5008 stream->segment.time = time;
5009 stream->segment.position = stream->segment.start;
5010
5011 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5012 &stream->segment);
5013
5014 /* now prepare and send the segment */
5015 if (stream->pad) {
5016 event = gst_event_new_segment (&stream->segment);
5017 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5018 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5019 }
5020 gst_pad_push_event (stream->pad, event);
5021 /* assume we can send more data now */
5022 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5023 /* clear to send tags on this pad now */
5024 gst_qtdemux_push_tags (qtdemux, stream);
5025 }
5026
5027 if (_start)
5028 *_start = start;
5029 if (_stop)
5030 *_stop = stop;
5031
5032 return TRUE;
5033}
5034
5035/* activate the given segment number @seg_idx of @stream at time @offset.
5036 * @offset is an absolute global position over all the segments.
5037 *
5038 * This will push out a NEWSEGMENT event with the right values and
5039 * position the stream index to the first decodable sample before
5040 * @offset.
5041 */
5042static gboolean
5043gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
5044 guint32 seg_idx, GstClockTime offset)
5045{
5046 QtDemuxSegment *segment;
5047 guint32 index, kf_index;
5048 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5049
5050 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5051 seg_idx, GST_TIME_ARGS (offset));
5052
5053 if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
5054 &start, &stop))
5055 return FALSE;
5056
5057 segment = &stream->segments[stream->segment_index];
5058
5059 /* in the fragmented case, we pick a fragment that starts before our
5060 * desired position and rely on downstream to wait for a keyframe
5061 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5062 * tfra entries tells us which trun/sample the key unit is in, but we don't
5063 * make use of this additional information at the moment) */
5064 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5065 stream->to_sample = G_MAXUINT32;
5066 return TRUE;
5067 } else {
5068 /* well, it will be taken care of below */
5069 qtdemux->fragmented_seek_pending = FALSE;
5070 /* FIXME ideally the do_fragmented_seek can be done right here,
5071 * rather than at loop level
5072 * (which might even allow handling edit lists in a fragmented file) */
5073 }
5074
5075 /* We don't need to look for a sample in push-based */
5076 if (!qtdemux->pullbased)
5077 return TRUE;
5078
5079 /* and move to the keyframe before the indicated media time of the
5080 * segment */
5081 if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
5082 if (qtdemux->segment.rate >= 0) {
5083 index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
5084 stream->to_sample = G_MAXUINT32;
5085 GST_DEBUG_OBJECT (stream->pad,
5086 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5087 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
5088 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5089 } else {
5090 index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
5091 stream->to_sample = index;
5092 GST_DEBUG_OBJECT (stream->pad,
5093 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5094 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
5095 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
5096 }
5097 } else {
5098 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5099 "this is an empty segment");
5100 return TRUE;
5101 }
5102
5103 /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
5104 * encountered an error and printed a message so we return appropriately */
5105 if (index == -1)
5106 return FALSE;
5107
5108 /* we're at the right spot */
5109 if (index == stream->sample_index) {
5110 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5111 return TRUE;
5112 }
5113
5114 /* find keyframe of the target index */
5115 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
5116
5117 /* go back two frames to provide lead-in for non-raw audio decoders */
5118 if (stream->subtype == FOURCC_soun && !stream->need_clip) {
5119 guint32 lead_in = 2;
5120 guint32 old_index = kf_index;
5121 GstStructure *s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
5122
5123 if (gst_structure_has_name (s, "audio/mpeg")) {
5124 gint mpegversion;
5125 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5126 && mpegversion == 1) {
5127 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5128 lead_in = 30;
5129 }
5130 }
5131
5132 kf_index = MAX (kf_index, lead_in) - lead_in;
5133 if (qtdemux_parse_samples (qtdemux, stream, kf_index)) {
5134 GST_DEBUG_OBJECT (stream->pad,
5135 "Moving backwards %u frames to ensure sufficient sound lead-in",
5136 old_index - kf_index);
5137 } else {
5138 kf_index = old_index;
5139 }
5140 }
5141
5142 /* if we move forwards, we don't have to go back to the previous
5143 * keyframe since we already sent that. We can also just jump to
5144 * the keyframe right before the target index if there is one. */
5145 if (index > stream->sample_index) {
5146 /* moving forwards check if we move past a keyframe */
5147 if (kf_index > stream->sample_index) {
5148 GST_DEBUG_OBJECT (stream->pad,
5149 "moving forwards to keyframe at %u "
5150 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5151 kf_index,
5152 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5153 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5154 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5155 } else {
5156 GST_DEBUG_OBJECT (stream->pad,
5157 "moving forwards, keyframe at %u "
5158 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5159 kf_index,
5160 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5161 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5162 }
5163 } else {
5164 GST_DEBUG_OBJECT (stream->pad,
5165 "moving backwards to %sframe at %u "
5166 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5167 (stream->subtype == FOURCC_soun) ? "audio " : "key", kf_index,
5168 GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5169 GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5170 gst_qtdemux_move_stream (qtdemux, stream, kf_index);
5171 }
5172
5173 return TRUE;
5174}
5175
5176/* prepare to get the current sample of @stream, getting essential values.
5177 *
5178 * This function will also prepare and send the segment when needed.
5179 *
5180 * Return FALSE if the stream is EOS.
5181 *
5182 * PULL-BASED
5183 */
5184static gboolean
5185gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
5186 QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
5187 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5188 gboolean * keyframe)
5189{
5190 QtDemuxSample *sample;
5191 GstClockTime time_position;
5192 guint32 seg_idx;
5193
5194 g_return_val_if_fail (stream != NULL, FALSE);
5195
5196 time_position = stream->time_position;
5197 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5198 goto eos;
5199
5200 seg_idx = stream->segment_index;
5201 if (G_UNLIKELY (seg_idx == -1)) {
5202 /* find segment corresponding to time_position if we are looking
5203 * for a segment. */
5204 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
5205 }
5206
5207 /* different segment, activate it, sample_index will be set. */
5208 if (G_UNLIKELY (stream->segment_index != seg_idx))
5209 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
5210
5211 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
5212 segments[stream->segment_index]))) {
5213 QtDemuxSegment *seg = &stream->segments[stream->segment_index];
5214
5215 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5216 " prepare empty sample");
5217
5218 *empty = TRUE;
5219 *pts = *dts = time_position;
5220 *duration = seg->duration - (time_position - seg->time);
5221
5222 return TRUE;
5223 }
5224
5225 *empty = FALSE;
5226
5227 if (stream->sample_index == -1)
5228 stream->sample_index = 0;
5229
5230 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5231 stream->sample_index, stream->n_samples);
5232
5233 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5234 if (!qtdemux->fragmented)
5235 goto eos;
5236
5237 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5238 do {
5239 GstFlowReturn flow;
5240
5241 GST_OBJECT_LOCK (qtdemux);
5242 flow = qtdemux_add_fragmented_samples (qtdemux);
5243 GST_OBJECT_UNLOCK (qtdemux);
5244
5245 if (flow != GST_FLOW_OK)
5246 goto eos;
5247 }
5248 while (stream->sample_index >= stream->n_samples);
5249 }
5250
5251 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5252 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5253 stream->sample_index);
5254 return FALSE;
5255 }
5256
5257 /* now get the info for the sample we're at */
5258 sample = &stream->samples[stream->sample_index];
5259
5260 *dts = QTSAMPLE_DTS (stream, sample);
5261 *pts = QTSAMPLE_PTS (stream, sample);
5262 *offset = sample->offset;
5263 *size = sample->size;
5264 *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
5265 *keyframe = QTSAMPLE_KEYFRAME (stream, sample);
5266
5267 return TRUE;
5268
5269 /* special cases */
5270eos:
5271 {
5272 stream->time_position = GST_CLOCK_TIME_NONE;
5273 return FALSE;
5274 }
5275}
5276
5277/* move to the next sample in @stream.
5278 *
5279 * Moves to the next segment when needed.
5280 */
5281static void
5282gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
5283{
5284 QtDemuxSample *sample;
5285 QtDemuxSegment *segment;
5286
5287 /* get current segment */
5288 segment = &stream->segments[stream->segment_index];
5289
5290 if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
5291 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5292 goto next_segment;
5293 }
5294
5295 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5296 /* Mark the stream as EOS */
5297 GST_DEBUG_OBJECT (qtdemux,
5298 "reached max allowed sample %u, mark EOS", stream->to_sample);
5299 stream->time_position = GST_CLOCK_TIME_NONE;
5300 return;
5301 }
5302
5303 /* move to next sample */
5304 stream->sample_index++;
5305 stream->offset_in_sample = 0;
5306
5307 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
5308 stream->n_samples);
5309
5310 /* reached the last sample, we need the next segment */
5311 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5312 goto next_segment;
5313
5314 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
5315 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5316 stream->sample_index);
5317 return;
5318 }
5319
5320 /* get next sample */
5321 sample = &stream->samples[stream->sample_index];
5322
5323 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
5324 GST_TIME_FORMAT, GST_TIME_ARGS (QTSAMPLE_DTS (stream, sample)),
5325 GST_TIME_ARGS (segment->media_stop));
5326
5327 /* see if we are past the segment */
zengliang.li1ab157f2024-05-16 12:12:48 +00005328 if (G_UNLIKELY (QTSAMPLE_PTS (stream, sample) >= segment->media_stop))
zengliang.li5f31ef42024-05-16 08:27:38 +00005329 goto next_segment;
5330
zengliang.li1ab157f2024-05-16 12:12:48 +00005331 if (QTSAMPLE_PTS (stream, sample) >= segment->media_start) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005332 /* inside the segment, update time_position, looks very familiar to
5333 * GStreamer segments, doesn't it? */
5334 stream->time_position =
zengliang.li1ab157f2024-05-16 12:12:48 +00005335 QTSAMPLE_PTS (stream, sample) - segment->media_start + segment->time;
zengliang.li5f31ef42024-05-16 08:27:38 +00005336 } else {
5337 /* not yet in segment, time does not yet increment. This means
5338 * that we are still prerolling keyframes to the decoder so it can
5339 * decode the first sample of the segment. */
5340 stream->time_position = segment->time;
5341 }
5342 return;
5343
5344 /* move to the next segment */
5345next_segment:
5346 {
5347 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5348
5349 if (stream->segment_index == stream->n_segments - 1) {
5350 /* are we at the end of the last segment, we're EOS */
5351 stream->time_position = GST_CLOCK_TIME_NONE;
5352 } else {
5353 /* else we're only at the end of the current segment */
5354 stream->time_position = segment->stop_time;
5355 }
5356 /* make sure we select a new segment */
5357
5358 /* accumulate previous segments */
5359 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5360 stream->accumulated_base +=
5361 (stream->segment.stop -
5362 stream->segment.start) / ABS (stream->segment.rate);
5363
5364 stream->segment_index = -1;
5365 }
5366}
5367
5368static void
5369gst_qtdemux_sync_streams (GstQTDemux * demux)
5370{
5371 gint i;
5372
5373 if (QTDEMUX_N_STREAMS (demux) <= 1)
5374 return;
5375
5376 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
5377 QtDemuxStream *stream;
5378 GstClockTime end_time;
5379
5380 stream = QTDEMUX_NTH_STREAM (demux, i);
5381
5382 if (!stream->pad)
5383 continue;
5384
5385 /* TODO advance time on subtitle streams here, if any some day */
5386
5387 /* some clips/trailers may have unbalanced streams at the end,
5388 * so send EOS on shorter stream to prevent stalling others */
5389
5390 /* do not mess with EOS if SEGMENT seeking */
5391 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5392 continue;
5393
5394 if (demux->pullbased) {
5395 /* loop mode is sample time based */
5396 if (!STREAM_IS_EOS (stream))
5397 continue;
5398 } else {
5399 /* push mode is byte position based */
5400 if (stream->n_samples &&
5401 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5402 continue;
5403 }
5404
5405 if (stream->sent_eos)
5406 continue;
5407
5408 /* only act if some gap */
5409 end_time = stream->segments[stream->n_segments - 1].stop_time;
5410 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5411 ", stream end: %" GST_TIME_FORMAT,
5412 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5413 if (GST_CLOCK_TIME_IS_VALID (end_time)
5414 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5415 GstEvent *event;
5416
5417 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5418 GST_PAD_NAME (stream->pad));
5419 stream->sent_eos = TRUE;
5420 event = gst_event_new_eos ();
5421 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5422 gst_event_set_seqnum (event, demux->segment_seqnum);
5423 gst_pad_push_event (stream->pad, event);
5424 }
5425 }
5426}
5427
5428/* EOS and NOT_LINKED need to be combined. This means that we return:
5429 *
5430 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5431 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5432 */
5433static GstFlowReturn
5434gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
5435 GstFlowReturn ret)
5436{
5437 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5438
5439 if (stream->pad)
5440 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5441 ret);
5442 else
5443 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5444
5445 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5446 return ret;
5447}
5448
5449/* the input buffer metadata must be writable. Returns NULL when the buffer is
5450 * completely clipped
5451 *
5452 * Should be used only with raw buffers */
5453static GstBuffer *
5454gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5455 GstBuffer * buf)
5456{
5457 guint64 start, stop, cstart, cstop, diff;
5458 GstClockTime pts, duration;
5459 gsize size, osize;
5460 gint num_rate, denom_rate;
5461 gint frame_size;
5462 gboolean clip_data;
5463 guint offset;
5464
5465 osize = size = gst_buffer_get_size (buf);
5466 offset = 0;
5467
5468 /* depending on the type, setup the clip parameters */
5469 if (stream->subtype == FOURCC_soun) {
5470 frame_size = CUR_STREAM (stream)->bytes_per_frame;
5471 num_rate = GST_SECOND;
5472 denom_rate = (gint) CUR_STREAM (stream)->rate;
5473 clip_data = TRUE;
5474 } else if (stream->subtype == FOURCC_vide) {
5475 frame_size = size;
5476 num_rate = CUR_STREAM (stream)->fps_n;
5477 denom_rate = CUR_STREAM (stream)->fps_d;
5478 clip_data = FALSE;
5479 } else
5480 goto wrong_type;
5481
5482 if (frame_size <= 0)
5483 goto bad_frame_size;
5484
5485 /* we can only clip if we have a valid pts */
5486 pts = GST_BUFFER_PTS (buf);
5487 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5488 goto no_pts;
5489
5490 duration = GST_BUFFER_DURATION (buf);
5491
5492 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5493 duration =
5494 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5495 }
5496
5497 start = pts;
5498 stop = start + duration;
5499
5500 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5501 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5502 goto clipped;
5503
5504 /* see if some clipping happened */
5505 diff = cstart - start;
5506 if (diff > 0) {
5507 pts += diff;
5508 duration -= diff;
5509
5510 if (clip_data) {
5511 /* bring clipped time to samples and to bytes */
5512 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5513 diff *= frame_size;
5514
5515 GST_DEBUG_OBJECT (qtdemux,
5516 "clipping start to %" GST_TIME_FORMAT " %"
5517 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5518
5519 offset = diff;
5520 size -= diff;
5521 }
5522 }
5523 diff = stop - cstop;
5524 if (diff > 0) {
5525 duration -= diff;
5526
5527 if (clip_data) {
5528 /* bring clipped time to samples and then to bytes */
5529 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5530 diff *= frame_size;
5531 GST_DEBUG_OBJECT (qtdemux,
5532 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5533 " bytes", GST_TIME_ARGS (cstop), diff);
5534 size -= diff;
5535 }
5536 }
5537
5538 if (offset != 0 || size != osize)
5539 gst_buffer_resize (buf, offset, size);
5540
5541 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5542 GST_BUFFER_PTS (buf) = pts;
5543 GST_BUFFER_DURATION (buf) = duration;
5544
5545 return buf;
5546
5547 /* dropped buffer */
5548wrong_type:
5549 {
5550 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5551 return buf;
5552 }
5553bad_frame_size:
5554 {
5555 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5556 return buf;
5557 }
5558no_pts:
5559 {
5560 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5561 return buf;
5562 }
5563clipped:
5564 {
5565 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5566 gst_buffer_unref (buf);
5567 return NULL;
5568 }
5569}
5570
5571static GstBuffer *
5572gst_qtdemux_align_buffer (GstQTDemux * demux,
5573 GstBuffer * buffer, gsize alignment)
5574{
5575 GstMapInfo map;
5576
5577 gst_buffer_map (buffer, &map, GST_MAP_READ);
5578
5579 if (map.size < sizeof (guintptr)) {
5580 gst_buffer_unmap (buffer, &map);
5581 return buffer;
5582 }
5583
5584 if (((guintptr) map.data) & (alignment - 1)) {
5585 GstBuffer *new_buffer;
5586 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5587
5588 new_buffer = gst_buffer_new_allocate (NULL,
5589 gst_buffer_get_size (buffer), &params);
5590
5591 /* Copy data "by hand", so ensure alignment is kept: */
5592 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5593
5594 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5595 GST_DEBUG_OBJECT (demux,
5596 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5597 alignment);
5598
5599 gst_buffer_unmap (buffer, &map);
5600 gst_buffer_unref (buffer);
5601
5602 return new_buffer;
5603 }
5604
5605 gst_buffer_unmap (buffer, &map);
5606 return buffer;
5607}
5608
5609static guint8 *
5610convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
5611 gsize * res)
5612{
5613 guint8 *storage;
5614 gsize i;
5615
5616 /* We are converting from pairs to triplets */
5617 *res = ccpair_size / 2 * 3;
5618 storage = g_malloc (*res);
5619 for (i = 0; i * 2 < ccpair_size; i += 1) {
5620 /* FIXME: Use line offset 0 as we simply can't know here */
5621 if (field == 1)
5622 storage[i * 3] = 0x80 | 0x00;
5623 else
5624 storage[i * 3] = 0x00 | 0x00;
5625 storage[i * 3 + 1] = ccpair[i * 2];
5626 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5627 }
5628
5629 return storage;
5630}
5631
5632static guint8 *
5633extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
5634 gsize * cclen)
5635{
5636 guint8 *res = NULL;
5637 guint32 atom_length, fourcc;
5638 QtDemuxStreamStsdEntry *stsd_entry;
5639
5640 GST_MEMDUMP ("caption atom", data, size);
5641
5642 /* There might be multiple atoms */
5643
5644 *cclen = 0;
5645 if (size < 8)
5646 goto invalid_cdat;
5647 atom_length = QT_UINT32 (data);
5648 fourcc = QT_FOURCC (data + 4);
5649 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5650 goto invalid_cdat;
5651
5652 GST_DEBUG_OBJECT (stream->pad, "here");
5653
5654 /* Check if we have something compatible */
5655 stsd_entry = CUR_STREAM (stream);
5656 switch (stsd_entry->fourcc) {
5657 case FOURCC_c608:{
5658 guint8 *cdat = NULL, *cdt2 = NULL;
5659 gsize cdat_size = 0, cdt2_size = 0;
5660 /* Should be cdat or cdt2 */
5661 if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
5662 GST_WARNING_OBJECT (stream->pad,
5663 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5664 GST_FOURCC_ARGS (fourcc));
5665 goto invalid_cdat;
5666 }
5667
5668 /* Convert to S334-1 Annex A byte triplet */
5669 if (fourcc == FOURCC_cdat)
5670 cdat = convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
5671 else
5672 cdt2 = convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
5673 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5674 size, atom_length);
5675
5676 /* Check for another atom ? */
5677 if (size > atom_length + 8) {
5678 guint32 new_atom_length = QT_UINT32 (data + atom_length);
5679 if (size >= atom_length + new_atom_length) {
5680 fourcc = QT_FOURCC (data + atom_length + 4);
5681 if (fourcc == FOURCC_cdat) {
5682 if (cdat == NULL)
5683 cdat =
5684 convert_to_s334_1a (data + atom_length + 8,
5685 new_atom_length - 8, 1, &cdat_size);
5686 else
5687 GST_WARNING_OBJECT (stream->pad,
5688 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5689 } else {
5690 if (cdt2 == NULL)
5691 cdt2 =
5692 convert_to_s334_1a (data + atom_length + 8,
5693 new_atom_length - 8, 2, &cdt2_size);
5694 else
5695 GST_WARNING_OBJECT (stream->pad,
5696 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5697 }
5698 }
5699 }
5700
5701 *cclen = cdat_size + cdt2_size;
5702 res = g_malloc (*cclen);
5703 if (cdat_size)
5704 memcpy (res, cdat, cdat_size);
5705 if (cdt2_size)
5706 memcpy (res + cdat_size, cdt2, cdt2_size);
5707 g_free (cdat);
5708 g_free (cdt2);
5709 }
5710 break;
5711 case FOURCC_c708:
5712 if (fourcc != FOURCC_ccdp) {
5713 GST_WARNING_OBJECT (stream->pad,
5714 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5715 GST_FOURCC_ARGS (fourcc));
5716 goto invalid_cdat;
5717 }
5718 *cclen = atom_length - 8;
5719 res = g_memdup2 (data + 8, *cclen);
5720 break;
5721 default:
5722 /* Keep this here in case other closed caption formats are added */
5723 g_assert_not_reached ();
5724 break;
5725 }
5726
5727 GST_MEMDUMP ("Output", res, *cclen);
5728 return res;
5729
5730 /* Errors */
5731invalid_cdat:
5732 GST_WARNING ("[cdat] atom is too small or invalid");
5733 return NULL;
5734}
5735
5736/* Handle Closed Caption sample buffers.
5737 * The input buffer metadata must be writable,
5738 * but time/duration etc not yet set and need not be preserved */
5739static GstBuffer *
5740gst_qtdemux_process_buffer_clcp (GstQTDemux * qtdemux, QtDemuxStream * stream,
5741 GstBuffer * buf)
5742{
5743 GstBuffer *outbuf = NULL;
5744 GstMapInfo map;
5745 guint8 *cc;
5746 gsize cclen = 0;
5747
5748 gst_buffer_map (buf, &map, GST_MAP_READ);
5749
5750 /* empty buffer is sent to terminate previous subtitle */
5751 if (map.size <= 2) {
5752 gst_buffer_unmap (buf, &map);
5753 gst_buffer_unref (buf);
5754 return NULL;
5755 }
5756
5757 /* For closed caption, we need to extract the information from the
5758 * [cdat],[cdt2] or [ccdp] atom */
5759 cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
5760 gst_buffer_unmap (buf, &map);
5761 if (cc) {
5762 outbuf = _gst_buffer_new_wrapped (cc, cclen, g_free);
5763 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5764 } else {
5765 /* Conversion failed or there's nothing */
5766 }
5767 gst_buffer_unref (buf);
5768
5769 return outbuf;
5770}
5771
5772/* DVD subpicture specific sample handling.
5773 * the input buffer metadata must be writable,
5774 * but time/duration etc not yet set and need not be preserved */
5775static GstBuffer *
5776gst_qtdemux_process_buffer_dvd (GstQTDemux * qtdemux, QtDemuxStream * stream,
5777 GstBuffer * buf)
5778{
5779 /* send a one time dvd clut event */
5780 if (stream->pending_event && stream->pad)
5781 gst_pad_push_event (stream->pad, stream->pending_event);
5782 stream->pending_event = NULL;
5783
5784 /* empty buffer is sent to terminate previous subtitle */
5785 if (gst_buffer_get_size (buf) <= 2) {
5786 gst_buffer_unref (buf);
5787 return NULL;
5788 }
5789
5790 /* That's all the processing needed for subpictures */
5791 return buf;
5792}
5793
5794/* Timed text formats
5795 * the input buffer metadata must be writable,
5796 * but time/duration etc not yet set and need not be preserved */
5797static GstBuffer *
5798gst_qtdemux_process_buffer_text (GstQTDemux * qtdemux, QtDemuxStream * stream,
5799 GstBuffer * buf)
5800{
5801 GstBuffer *outbuf = NULL;
5802 GstMapInfo map;
5803 guint nsize = 0;
5804 gchar *str;
5805
5806 /* not many cases for now */
5807 if (G_UNLIKELY (stream->subtype != FOURCC_text &&
5808 stream->subtype != FOURCC_sbtl)) {
5809 return buf;
5810 }
5811
5812 gst_buffer_map (buf, &map, GST_MAP_READ);
5813
5814 /* empty buffer is sent to terminate previous subtitle */
5815 if (map.size <= 2) {
5816 gst_buffer_unmap (buf, &map);
5817 gst_buffer_unref (buf);
5818 return NULL;
5819 }
5820
5821 nsize = GST_READ_UINT16_BE (map.data);
5822 nsize = MIN (nsize, map.size - 2);
5823
5824 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
5825 nsize, map.size);
5826
5827 /* takes care of UTF-8 validation or UTF-16 recognition,
5828 * no other encoding expected */
5829 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
5830 gst_buffer_unmap (buf, &map);
5831
5832 if (str) {
5833 outbuf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
5834 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5835 } else {
5836 /* this should not really happen unless the subtitle is corrupted */
5837 }
5838 gst_buffer_unref (buf);
5839
5840 /* FIXME ? convert optional subsequent style info to markup */
5841
5842 return outbuf;
5843}
5844
5845/* WebVTT sample handling according to 14496-30 */
5846static GstBuffer *
5847gst_qtdemux_process_buffer_wvtt (GstQTDemux * qtdemux, QtDemuxStream * stream,
5848 GstBuffer * buf)
5849{
5850 GstBuffer *outbuf = NULL;
5851 GstMapInfo map;
5852
5853 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
5854 g_assert_not_reached (); /* The buffer must be mappable */
5855 }
5856
5857 if (qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
5858 GstEvent *gap = NULL;
5859 /* Push a gap event */
5860 stream->segment.position = GST_BUFFER_PTS (buf);
5861 gap =
5862 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
5863 gst_pad_push_event (stream->pad, gap);
5864
5865 if (GST_BUFFER_DURATION_IS_VALID (buf))
5866 stream->segment.position += GST_BUFFER_DURATION (buf);
5867 } else {
5868 outbuf =
5869 qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
5870 GST_BUFFER_DURATION (buf), map.data, map.size);
5871 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
5872 }
5873
5874 gst_buffer_unmap (buf, &map);
5875 gst_buffer_unref (buf);
5876
5877 return outbuf;
5878}
5879
5880static GstFlowReturn
5881gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
5882 GstBuffer * buf)
5883{
5884 GstFlowReturn ret = GST_FLOW_OK;
5885 GstClockTime pts, duration;
5886
5887 if (stream->need_clip)
5888 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);
5889
5890 if (G_UNLIKELY (buf == NULL))
5891 goto exit;
5892
5893 if (G_UNLIKELY (stream->discont)) {
5894 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
5895 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5896 stream->discont = FALSE;
5897 } else {
5898 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5899 }
5900
zengliang.lid0c84c32024-05-17 03:33:20 +00005901 if (qtdemux->discontinuity_base_pos != 0 && GST_BUFFER_PTS (buf) < qtdemux->discontinuity_base_pos)
5902 {
5903 GST_BUFFER_PTS (buf) = GST_BUFFER_PTS (buf) + qtdemux->discontinuity_base_pos;
5904 GST_BUFFER_DTS (buf) = GST_BUFFER_DTS (buf) + qtdemux->discontinuity_base_pos;
5905 }
5906
zengliang.li5f31ef42024-05-16 08:27:38 +00005907 GST_LOG_OBJECT (qtdemux,
5908 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
zengliang.lid0c84c32024-05-17 03:33:20 +00005909 ", duration %" GST_TIME_FORMAT " on pad %s" ", discontinuity_base_pos %" GST_TIME_FORMAT,
zengliang.li5f31ef42024-05-16 08:27:38 +00005910 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
5911 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
zengliang.lid0c84c32024-05-17 03:33:20 +00005912 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad), GST_TIME_ARGS (qtdemux->discontinuity_base_pos));
zengliang.li5f31ef42024-05-16 08:27:38 +00005913
5914 if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
5915 GstStructure *crypto_info;
5916 QtDemuxAavdEncryptionInfo *info =
5917 (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
5918
5919 crypto_info = gst_structure_copy (info->default_properties);
5920 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
5921 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
5922 }
5923
5924 if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +00005925 || stream->protection_scheme_type == FOURCC_cbcs
5926 || stream->protection_scheme_type == FOURCC_cens)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005927 GstStructure *crypto_info;
5928 QtDemuxCencSampleSetInfo *info =
5929 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
5930 gint index;
5931 GstEvent *event;
zengliang.li125c3642024-05-17 06:06:08 +00005932 GstProtectionMeta *meta = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00005933
5934 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
5935 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
5936 GST_PTR_FORMAT, event);
5937 gst_pad_push_event (stream->pad, event);
5938 }
5939
5940 if (info->crypto_info == NULL) {
zengliang.li125c3642024-05-17 06:06:08 +00005941 /* cbc1 audio may has no saiz node, need update crypto info from default properties */
zengliang.li5f31ef42024-05-16 08:27:38 +00005942 if (stream->protection_scheme_type == FOURCC_cbcs) {
zengliang.li125c3642024-05-17 06:06:08 +00005943 guint crypt_byte_block = 0;
5944 guint skip_byte_block = 0;
5945 guint iv_size = 0;
5946 guint constant_iv_size = 0;
5947
zengliang.li5f31ef42024-05-16 08:27:38 +00005948 crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
zengliang.li125c3642024-05-17 06:06:08 +00005949 gst_structure_get (crypto_info,
5950 "crypt_byte_block", G_TYPE_UINT, &crypt_byte_block,
5951 "skip_byte_block", G_TYPE_UINT, &skip_byte_block,
5952 "iv_size", G_TYPE_UINT, &iv_size,
5953 NULL);
5954 if (iv_size == 0) {
5955 if (!gst_structure_get_uint (crypto_info, "constant_iv_size", &constant_iv_size)) {
5956 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size");
5957 } else {
5958 gst_structure_set (crypto_info, "iv_size", G_TYPE_UINT, constant_iv_size, NULL);
5959 }
5960 }
5961
5962 meta = gst_buffer_get_protection_meta(buf);
5963 if (meta) {
5964 GST_DEBUG_OBJECT (qtdemux, "protection metadata name %s", gst_structure_get_name(meta->info));
5965 gst_structure_set (meta->info,
5966 "crypt_byte_block", G_TYPE_UINT, crypt_byte_block,
5967 "skip_byte_block", G_TYPE_UINT, skip_byte_block,
5968 NULL);
5969 } else {
5970 GST_INFO_OBJECT (qtdemux, "no cbcs protection metadata");
5971 }
5972
5973 if (!meta && (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005974 GST_ERROR_OBJECT (qtdemux,
5975 "failed to attach cbcs metadata to buffer");
5976 qtdemux_gst_structure_free (crypto_info);
5977 } else {
5978 GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
5979 }
5980 } else {
5981 GST_DEBUG_OBJECT (qtdemux,
5982 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
5983 }
5984 } else {
5985 /* The end of the crypto_info array matches our n_samples position,
5986 * so count backward from there */
5987 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
5988 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
5989 /* steal structure from array */
5990 crypto_info = g_ptr_array_index (info->crypto_info, index);
5991 g_ptr_array_index (info->crypto_info, index) = NULL;
5992 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
5993 info->crypto_info->len);
zengliang.li125c3642024-05-17 06:06:08 +00005994
5995 if (stream->protection_scheme_type == FOURCC_cbcs) {
5996 guint subsample_count = 0;
5997 GstBuffer *subsamples = NULL;
5998 guint crypt_byte_block = 0;
5999 guint skip_byte_block = 0;
6000
6001 gst_structure_get (crypto_info,
6002 "subsample_count", G_TYPE_UINT, &subsample_count,
6003 "subsamples", GST_TYPE_BUFFER, &subsamples,
6004 "crypt_byte_block", G_TYPE_UINT, &crypt_byte_block,
6005 "skip_byte_block", G_TYPE_UINT, &skip_byte_block,
6006 NULL);
6007 meta = gst_buffer_get_protection_meta(buf);
6008 if (meta) {
6009 GST_DEBUG_OBJECT (qtdemux, "protection metadata name %s", gst_structure_get_name(meta->info));
6010 gst_structure_set (meta->info,
6011 "subsample_count", G_TYPE_UINT, subsample_count,
6012 "subsamples", GST_TYPE_BUFFER, subsamples,
6013 "crypt_byte_block", G_TYPE_UINT, crypt_byte_block,
6014 "skip_byte_block", G_TYPE_UINT, skip_byte_block,
6015 NULL);
6016 } else {
6017 GST_INFO_OBJECT (qtdemux, "no origin cbcs protection metadata");
6018 }
6019 }
6020
6021 if (!meta && (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)))
zengliang.li5f31ef42024-05-16 08:27:38 +00006022 GST_ERROR_OBJECT (qtdemux,
6023 "failed to attach cenc metadata to buffer");
6024 } else {
6025 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6026 index, stream->sample_index);
6027 }
6028 }
6029 }
6030
6031 if (stream->alignment > 1)
6032 buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
6033
6034 pts = GST_BUFFER_PTS (buf);
6035 duration = GST_BUFFER_DURATION (buf);
6036
6037 ret = gst_pad_push (stream->pad, buf);
6038
6039 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6040 /* mark position in stream, we'll need this to know when to send GAP event */
6041 stream->segment.position = pts + duration;
6042 }
6043
6044exit:
6045
6046 return ret;
6047}
6048
6049static GstFlowReturn
6050gst_qtdemux_split_and_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
6051 GstBuffer * buf)
6052{
6053 GstFlowReturn ret = GST_FLOW_OK;
6054
6055 if (stream->subtype == FOURCC_clcp
6056 && CUR_STREAM (stream)->fourcc == FOURCC_c608 && stream->need_split) {
6057 GstMapInfo map;
6058 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6059 guint n_triplets, i;
6060 guint field1_off = 0, field2_off = 0;
6061
6062 /* We have to split CEA608 buffers so that each outgoing buffer contains
6063 * one byte pair per field according to the framerate of the video track.
6064 *
6065 * If there is only a single byte pair per field we don't have to do
6066 * anything
6067 */
6068
6069 gst_buffer_map (buf, &map, GST_MAP_READ);
6070
6071 n_triplets = map.size / 3;
6072 for (i = 0; i < n_triplets; i++) {
6073 if (map.data[3 * i] & 0x80)
6074 n_field1++;
6075 else
6076 n_field2++;
6077 }
6078
6079 g_assert (n_field1 || n_field2);
6080
6081 /* If there's more than 1 frame we have to split, otherwise we can just
6082 * pass through */
6083 if (n_field1 > 1 || n_field2 > 1) {
6084 n_output_buffers =
6085 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
6086 CUR_STREAM (stream)->fps_n, GST_SECOND * CUR_STREAM (stream)->fps_d);
6087
6088 for (i = 0; i < n_output_buffers; i++) {
6089 GstBuffer *outbuf =
6090 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6091 GstMapInfo outmap;
6092 guint8 *outptr;
6093
6094 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6095 outptr = outmap.data;
6096
6097 if (n_field1) {
6098 gboolean found = FALSE;
6099
6100 while (map.data + field1_off < map.data + map.size) {
6101 if (map.data[field1_off] & 0x80) {
6102 memcpy (outptr, &map.data[field1_off], 3);
6103 field1_off += 3;
6104 found = TRUE;
6105 break;
6106 }
6107 field1_off += 3;
6108 }
6109
6110 if (!found) {
6111 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6112
6113 memcpy (outptr, empty, 3);
6114 }
6115
6116 outptr += 3;
6117 }
6118
6119 if (n_field2) {
6120 gboolean found = FALSE;
6121
6122 while (map.data + field2_off < map.data + map.size) {
6123 if ((map.data[field2_off] & 0x80) == 0) {
6124 memcpy (outptr, &map.data[field2_off], 3);
6125 field2_off += 3;
6126 found = TRUE;
6127 break;
6128 }
6129 field2_off += 3;
6130 }
6131
6132 if (!found) {
6133 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6134
6135 memcpy (outptr, empty, 3);
6136 }
6137
6138 outptr += 3;
6139 }
6140
6141 gst_buffer_unmap (outbuf, &outmap);
6142
6143 GST_BUFFER_PTS (outbuf) =
6144 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
6145 GST_SECOND * CUR_STREAM (stream)->fps_d,
6146 CUR_STREAM (stream)->fps_n);
6147 GST_BUFFER_DURATION (outbuf) =
6148 gst_util_uint64_scale (GST_SECOND, CUR_STREAM (stream)->fps_d,
6149 CUR_STREAM (stream)->fps_n);
6150 GST_BUFFER_OFFSET (outbuf) = -1;
6151 GST_BUFFER_OFFSET_END (outbuf) = -1;
6152
6153 ret = gst_qtdemux_push_buffer (qtdemux, stream, outbuf);
6154
6155 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6156 break;
6157 }
6158 gst_buffer_unmap (buf, &map);
6159 gst_buffer_unref (buf);
6160 } else {
6161 gst_buffer_unmap (buf, &map);
6162 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6163 }
6164 } else {
6165 ret = gst_qtdemux_push_buffer (qtdemux, stream, buf);
6166 }
6167
6168 return ret;
6169}
6170
6171/* Sets a buffer's attributes properly and pushes it downstream.
6172 * Also checks for additional actions and custom processing that may
6173 * need to be done first.
6174 */
6175static GstFlowReturn
6176gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
6177 QtDemuxStream * stream, GstBuffer * buf,
6178 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6179 gboolean keyframe, GstClockTime position, guint64 byte_position)
6180{
6181 GstFlowReturn ret = GST_FLOW_OK;
6182
6183 /* offset the timestamps according to the edit list */
6184
6185 if (G_UNLIKELY (CUR_STREAM (stream)->fourcc == FOURCC_rtsp)) {
6186 gchar *url;
6187 GstMapInfo map;
6188
6189 gst_buffer_map (buf, &map, GST_MAP_READ);
6190 url = g_strndup ((gchar *) map.data, map.size);
6191 gst_buffer_unmap (buf, &map);
6192 if (url != NULL && strlen (url) != 0) {
6193 /* we have RTSP redirect now */
6194 g_free (qtdemux->redirect_location);
6195 qtdemux->redirect_location = g_strdup (url);
6196 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6197 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6198 gst_structure_new ("redirect",
6199 "new-location", G_TYPE_STRING, url, NULL)));
6200 } else {
6201 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6202 "posting");
6203 }
6204 g_free (url);
6205 }
6206
6207 /* position reporting */
zengliang.li4681ee42024-05-16 12:25:30 +00006208 if (qtdemux->segment.rate >= 0 && GST_CLOCK_TIME_IS_VALID(position)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006209 qtdemux->segment.position = position;
6210 gst_qtdemux_sync_streams (qtdemux);
6211 }
6212
6213 if (G_UNLIKELY (!stream->pad)) {
6214 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6215 gst_buffer_unref (buf);
6216 goto exit;
6217 }
6218
6219 /* send out pending buffers */
6220 while (stream->buffers) {
6221 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6222
6223 if (G_UNLIKELY (stream->discont)) {
6224 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6225 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6226 stream->discont = FALSE;
6227 } else {
6228 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6229 }
6230
6231 if (stream->alignment > 1)
6232 buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
6233 gst_pad_push (stream->pad, buffer);
6234
6235 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6236 }
6237
6238 /* we're going to modify the metadata */
6239 buf = gst_buffer_make_writable (buf);
6240
6241 GST_BUFFER_DTS (buf) = dts;
6242 GST_BUFFER_PTS (buf) = pts;
6243 GST_BUFFER_DURATION (buf) = duration;
6244 GST_BUFFER_OFFSET (buf) = -1;
6245 GST_BUFFER_OFFSET_END (buf) = -1;
6246
6247 if (G_UNLIKELY (stream->process_func))
6248 buf = stream->process_func (qtdemux, stream, buf);
6249
6250 if (!buf) {
6251 goto exit;
6252 }
6253
6254 if (!keyframe) {
6255 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6256 stream->on_keyframe = FALSE;
6257 } else {
6258 stream->on_keyframe = TRUE;
6259 }
6260
6261 if (G_UNLIKELY (CUR_STREAM (stream)->rgb8_palette))
6262 gst_buffer_append_memory (buf,
6263 gst_memory_ref (CUR_STREAM (stream)->rgb8_palette));
6264
6265 if (G_UNLIKELY (CUR_STREAM (stream)->padding)) {
6266 gst_buffer_resize (buf, CUR_STREAM (stream)->padding, -1);
6267 }
6268#if 0
6269 if (G_UNLIKELY (qtdemux->element_index)) {
6270 GstClockTime stream_time;
6271
6272 stream_time =
6273 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6274 timestamp);
6275 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6276 GST_LOG_OBJECT (qtdemux,
6277 "adding association %" GST_TIME_FORMAT "-> %"
6278 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6279 gst_index_add_association (qtdemux->element_index,
6280 qtdemux->index_id,
6281 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6282 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6283 GST_FORMAT_BYTES, byte_position, NULL);
6284 }
6285 }
6286#endif
6287
6288 ret = gst_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
6289
6290exit:
6291 return ret;
6292}
6293
6294static const QtDemuxRandomAccessEntry *
6295gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
6296 GstClockTime pos, gboolean after)
6297{
6298 QtDemuxRandomAccessEntry *entries = stream->ra_entries;
6299 guint n_entries = stream->n_ra_entries;
6300 guint i;
6301
6302 /* we assume the table is sorted */
6303 for (i = 0; i < n_entries; ++i) {
6304 if (entries[i].ts > pos)
6305 break;
6306 }
6307
6308 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6309 * probably okay to assume that the index lists the very first fragment */
6310 if (i == 0)
6311 return &entries[0];
6312
6313 if (after)
6314 return &entries[i];
6315 else
6316 return &entries[i - 1];
6317}
6318
6319static gboolean
6320gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
6321{
6322 const QtDemuxRandomAccessEntry *best_entry = NULL;
6323 gint i;
6324
6325 GST_OBJECT_LOCK (qtdemux);
6326
6327 g_assert (QTDEMUX_N_STREAMS (qtdemux) > 0);
6328
6329 /* first see if we can determine where to go to using mfra,
6330 * before we start clearing things */
6331 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6332 const QtDemuxRandomAccessEntry *entry;
6333 QtDemuxStream *stream;
6334 gboolean is_audio_or_video;
6335
6336 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6337
6338 if (stream->ra_entries == NULL)
6339 continue;
6340
6341 if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
6342 is_audio_or_video = TRUE;
6343 else
6344 is_audio_or_video = FALSE;
6345
6346 entry =
6347 gst_qtdemux_stream_seek_fragment (qtdemux, stream,
6348 stream->time_position, !is_audio_or_video);
6349
6350 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6351 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6352
6353 stream->pending_seek = entry;
6354
6355 /* decide position to jump to just based on audio/video tracks, not subs */
6356 if (!is_audio_or_video)
6357 continue;
6358
6359 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6360 best_entry = entry;
6361 }
6362
6363 /* no luck, will handle seek otherwise */
6364 if (best_entry == NULL) {
6365 GST_OBJECT_UNLOCK (qtdemux);
6366 return FALSE;
6367 }
6368
6369 /* ok, now we can prepare for processing as of located moof */
6370 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6371 QtDemuxStream *stream;
6372
6373 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6374
6375 g_free (stream->samples);
6376 stream->samples = NULL;
6377 stream->n_samples = 0;
6378 stream->stbl_index = -1; /* no samples have yet been parsed */
6379 stream->sample_index = -1;
6380
6381 if (stream->protection_scheme_info) {
6382 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
6383 if (stream->protection_scheme_type == FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +00006384 || stream->protection_scheme_type == FOURCC_cbcs
6385 || stream->protection_scheme_type == FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006386 QtDemuxCencSampleSetInfo *info =
6387 (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
6388 if (info->crypto_info) {
6389 g_ptr_array_free (info->crypto_info, TRUE);
6390 info->crypto_info = NULL;
6391 }
6392 }
6393 }
6394 }
6395
6396 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6397 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
6398 GST_TIME_ARGS (QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
6399 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6400
6401 qtdemux->moof_offset = best_entry->moof_offset;
6402
6403 qtdemux_add_fragmented_samples (qtdemux);
6404
6405 GST_OBJECT_UNLOCK (qtdemux);
6406 return TRUE;
6407}
6408
6409static GstFlowReturn
6410gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
6411{
6412 GstFlowReturn ret = GST_FLOW_OK;
6413 GstBuffer *buf = NULL;
6414 QtDemuxStream *stream, *target_stream = NULL;
6415 GstClockTime min_time;
6416 guint64 offset = 0;
6417 GstClockTime dts = GST_CLOCK_TIME_NONE;
6418 GstClockTime pts = GST_CLOCK_TIME_NONE;
6419 GstClockTime duration = 0;
6420 gboolean keyframe = FALSE;
6421 guint sample_size = 0;
6422 guint num_samples = 1;
6423 gboolean empty = 0;
6424 guint size;
6425 gint i;
6426
6427 if (qtdemux->fragmented_seek_pending) {
6428 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
6429 if (gst_qtdemux_do_fragmented_seek (qtdemux)) {
6430 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6431 qtdemux->fragmented_seek_pending = FALSE;
6432 } else {
6433 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6434 }
6435 }
6436
6437 /* Figure out the next stream sample to output, min_time is expressed in
6438 * global time and runs over the edit list segments. */
6439 min_time = G_MAXUINT64;
6440 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6441 GstClockTime position;
6442
6443 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6444 position = stream->time_position;
6445
6446 /* position of -1 is EOS */
zengliang.li8c9c4092024-05-16 11:35:51 +00006447 if ((position != GST_CLOCK_TIME_NONE && position < min_time) ||
6448 || (stream->discont)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006449 min_time = position;
6450 target_stream = stream;
6451 }
6452 }
6453 /* all are EOS */
6454 if (G_UNLIKELY (target_stream == NULL)) {
6455 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6456 goto eos;
6457 }
6458
6459 /* check for segment end */
6460 if (G_UNLIKELY (qtdemux->segment.stop != -1
6461 && qtdemux->segment.rate >= 0
6462 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
6463 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6464 target_stream->time_position = GST_CLOCK_TIME_NONE;
6465 goto eos_stream;
6466 }
6467
6468 /* gap events for subtitle streams */
6469 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
6470 stream = QTDEMUX_NTH_STREAM (qtdemux, i);
6471 if (stream->pad) {
6472 GstClockTime gap_threshold;
6473
6474 /* Only send gap events on non-subtitle streams if lagging way behind. */
6475 if (stream->subtype == FOURCC_subp
6476 || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl ||
6477 stream->subtype == FOURCC_wvtt)
6478 gap_threshold = 1 * GST_SECOND;
6479 else
6480 gap_threshold = 3 * GST_SECOND;
6481
6482 /* send gap events until the stream catches up */
6483 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6484 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6485 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6486 stream->segment.position < (G_MAXUINT64 - gap_threshold) &&
6487 stream->segment.position + gap_threshold < min_time) {
6488 GstEvent *gap =
6489 gst_event_new_gap (stream->segment.position, gap_threshold);
6490 gst_pad_push_event (stream->pad, gap);
6491 stream->segment.position += gap_threshold;
6492 }
6493 }
6494 }
6495
6496 stream = target_stream;
6497 /* fetch info for the current sample of this stream */
6498 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
6499 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6500 goto eos_stream;
6501
6502 gst_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
6503 if (stream->new_caps) {
6504 gst_qtdemux_configure_stream (qtdemux, stream);
6505 qtdemux_do_allocation (stream, qtdemux);
6506 }
6507
6508 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6509 if (G_UNLIKELY (qtdemux->segment.
6510 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
6511 if (stream->subtype == FOURCC_vide) {
6512 if (!keyframe) {
6513 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6514 stream->track_id);
6515 goto next;
6516 } else if (qtdemux->trickmode_interval > 0) {
6517 GstClockTimeDiff interval;
6518
6519 if (qtdemux->segment.rate > 0)
6520 interval = stream->time_position - stream->last_keyframe_dts;
6521 else
6522 interval = stream->last_keyframe_dts - stream->time_position;
6523
6524 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
6525 && interval < qtdemux->trickmode_interval) {
6526 GST_LOG_OBJECT (qtdemux,
6527 "Skipping keyframe within interval on track-id %u",
6528 stream->track_id);
6529 goto next;
6530 } else {
6531 stream->last_keyframe_dts = stream->time_position;
6532 }
6533 }
6534 }
6535 }
6536
6537 GST_DEBUG_OBJECT (qtdemux,
6538 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6539 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6540 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6541 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6542 GST_TIME_ARGS (duration));
6543
6544 if (G_UNLIKELY (empty)) {
6545 /* empty segment, push a gap if there's a second or more
6546 * difference and move to the next one */
6547 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6548 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6549 stream->segment.position = pts + duration;
6550 goto next;
6551 }
6552
6553 /* hmm, empty sample, skip and move to next sample */
6554 if (G_UNLIKELY (sample_size <= 0))
6555 goto next;
6556
6557 /* last pushed sample was out of boundary, goto next sample */
6558 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6559 goto next;
6560
6561 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
6562 GST_DEBUG_OBJECT (qtdemux,
6563 "size %d larger than stream max_buffer_size %d, trimming",
6564 sample_size, stream->max_buffer_size);
6565 size =
6566 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6567 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
6568 && sample_size < stream->min_buffer_size) {
6569 guint start_sample_index = stream->sample_index;
6570 guint accumulated_size = sample_size;
6571 guint64 expected_next_offset = offset + sample_size;
6572
6573 GST_DEBUG_OBJECT (qtdemux,
6574 "size %d smaller than stream min_buffer_size %d, combining with the next",
6575 sample_size, stream->min_buffer_size);
6576
6577 while (stream->sample_index < stream->to_sample
6578 && stream->sample_index + 1 < stream->n_samples) {
6579 const QtDemuxSample *next_sample;
6580
6581 /* Increment temporarily */
6582 stream->sample_index++;
6583
6584 /* Failed to parse sample so let's go back to the previous one that was
6585 * still successful */
6586 if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
6587 stream->sample_index--;
6588 break;
6589 }
6590
6591 next_sample = &stream->samples[stream->sample_index];
6592
6593 /* Not contiguous with the previous sample so let's go back to the
6594 * previous one that was still successful */
6595 if (next_sample->offset != expected_next_offset) {
6596 stream->sample_index--;
6597 break;
6598 }
6599
6600 accumulated_size += next_sample->size;
6601 expected_next_offset += next_sample->size;
6602 if (accumulated_size >= stream->min_buffer_size)
6603 break;
6604 }
6605
6606 num_samples = stream->sample_index + 1 - start_sample_index;
6607 stream->sample_index = start_sample_index;
6608 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
6609 num_samples, accumulated_size);
6610 size = accumulated_size;
6611 } else {
6612 size = sample_size;
6613 }
6614
6615 if (qtdemux->cenc_aux_info_offset > 0) {
6616 GstMapInfo map;
6617 GstByteReader br;
6618 GstBuffer *aux_info = NULL;
6619
6620 /* pull the data stored before the sample */
6621 ret =
6622 gst_qtdemux_pull_atom (qtdemux, qtdemux->offset,
6623 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6624 if (G_UNLIKELY (ret != GST_FLOW_OK))
6625 goto beach;
6626 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6627 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6628 gst_byte_reader_init (&br, map.data + 8, map.size);
6629 if (!qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
6630 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6631 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6632 gst_buffer_unmap (aux_info, &map);
6633 gst_buffer_unref (aux_info);
6634 ret = GST_FLOW_ERROR;
6635 goto beach;
6636 }
6637 gst_buffer_unmap (aux_info, &map);
6638 gst_buffer_unref (aux_info);
6639 }
6640
6641 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6642 offset);
6643
6644 if (stream->use_allocator) {
6645 /* if we have a per-stream allocator, use it */
6646 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6647 }
6648
6649 ret = gst_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
6650 size, &buf);
6651 if (G_UNLIKELY (ret != GST_FLOW_OK))
6652 goto beach;
6653
6654 /* Update for both splitting and combining of samples */
6655 if (size != sample_size) {
6656 pts += gst_util_uint64_scale_int (GST_SECOND,
6657 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6658 stream->timescale);
6659 dts +=
6660 gst_util_uint64_scale_int (GST_SECOND,
6661 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame,
6662 stream->timescale);
6663 duration =
6664 gst_util_uint64_scale_int (GST_SECOND,
6665 size / CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
6666 }
6667
6668 ret = gst_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
6669 dts, pts, duration, keyframe, min_time, offset);
6670
6671 if (size < sample_size) {
6672 QtDemuxSample *sample = &stream->samples[stream->sample_index];
6673 QtDemuxSegment *segment = &stream->segments[stream->segment_index];
6674
6675 GstClockTime time_position = QTSTREAMTIME_TO_GSTTIME (stream,
6676 sample->timestamp +
6677 stream->offset_in_sample / CUR_STREAM (stream)->bytes_per_frame);
6678 if (time_position >= segment->media_start) {
6679 /* inside the segment, update time_position, looks very familiar to
6680 * GStreamer segments, doesn't it? */
6681 stream->time_position = (time_position - segment->media_start) +
6682 segment->time;
6683 } else {
6684 /* not yet in segment, time does not yet increment. This means
6685 * that we are still prerolling keyframes to the decoder so it can
6686 * decode the first sample of the segment. */
6687 stream->time_position = segment->time;
6688 }
6689 } else if (size > sample_size) {
6690 /* Increase to the last sample we already pulled so that advancing
6691 * below brings us to the next sample we need to pull */
6692 stream->sample_index += num_samples - 1;
6693 }
6694
6695 /* combine flows */
6696 GST_OBJECT_LOCK (qtdemux);
6697 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret);
6698 GST_OBJECT_UNLOCK (qtdemux);
6699 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6700 * we have no more data for the pad to push */
6701 if (ret == GST_FLOW_EOS)
6702 ret = GST_FLOW_OK;
6703
6704 stream->offset_in_sample += size;
6705 if (stream->offset_in_sample >= sample_size) {
6706 gst_qtdemux_advance_sample (qtdemux, stream);
6707 }
6708 goto beach;
6709
6710next:
6711 gst_qtdemux_advance_sample (qtdemux, stream);
6712
6713beach:
6714 return ret;
6715
6716 /* special cases */
6717eos:
6718 {
6719 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6720 ret = GST_FLOW_EOS;
6721 goto beach;
6722 }
6723eos_stream:
6724 {
6725 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6726 /* EOS will be raised if all are EOS */
6727 ret = GST_FLOW_OK;
6728 goto beach;
6729 }
6730}
6731
6732static void
6733gst_qtdemux_loop (GstPad * pad)
6734{
6735 GstQTDemux *qtdemux;
6736 guint64 cur_offset;
6737 GstFlowReturn ret;
6738
6739 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
6740
6741 cur_offset = qtdemux->offset;
6742 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
6743 cur_offset, qt_demux_state_string (qtdemux->state));
6744
6745 switch (qtdemux->state) {
6746 case QTDEMUX_STATE_INITIAL:
6747 case QTDEMUX_STATE_HEADER:
6748 ret = gst_qtdemux_loop_state_header (qtdemux);
6749 break;
6750 case QTDEMUX_STATE_MOVIE:
6751 ret = gst_qtdemux_loop_state_movie (qtdemux);
6752 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
6753 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux);
6754 }
6755 break;
6756 default:
6757 /* ouch */
6758 goto invalid_state;
6759 }
6760
6761 /* if something went wrong, pause */
6762 if (ret != GST_FLOW_OK)
6763 goto pause;
6764
6765done:
6766 gst_object_unref (qtdemux);
6767 return;
6768
6769 /* ERRORS */
6770invalid_state:
6771 {
6772 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
6773 (NULL), ("streaming stopped, invalid state"));
6774 gst_pad_pause_task (pad);
6775 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6776 goto done;
6777 }
6778pause:
6779 {
6780 const gchar *reason = gst_flow_get_name (ret);
6781
6782 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
6783
6784 gst_pad_pause_task (pad);
6785
6786 /* fatal errors need special actions */
6787 /* check EOS */
6788 if (ret == GST_FLOW_EOS) {
6789 if (QTDEMUX_N_STREAMS (qtdemux) == 0) {
6790 /* we have no streams, post an error */
6791 gst_qtdemux_post_no_playable_stream_error (qtdemux);
6792 }
6793 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
6794 gint64 stop;
6795
6796 if ((stop = qtdemux->segment.stop) == -1)
6797 stop = qtdemux->segment.duration;
6798
6799 if (qtdemux->segment.rate >= 0) {
6800 GstMessage *message;
6801 GstEvent *event;
6802
6803 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
6804 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6805 GST_FORMAT_TIME, stop);
6806 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
6807 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6808 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6809 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6810 }
6811 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6812 gst_qtdemux_push_event (qtdemux, event);
6813 } else {
6814 GstMessage *message;
6815 GstEvent *event;
6816
6817 /* For Reverse Playback */
6818 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
6819 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
6820 GST_FORMAT_TIME, qtdemux->segment.start);
6821 event = gst_event_new_segment_done (GST_FORMAT_TIME,
6822 qtdemux->segment.start);
6823 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
6824 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
6825 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6826 }
6827 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
6828 gst_qtdemux_push_event (qtdemux, event);
6829 }
6830 } else {
6831 GstEvent *event;
6832
6833 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
6834 event = gst_event_new_eos ();
6835 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
6836 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
6837 gst_qtdemux_push_event (qtdemux, event);
6838 }
6839 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
6840 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
6841 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
6842 }
6843 goto done;
6844 }
6845}
6846
6847/*
6848 * has_next_entry
6849 *
6850 * Returns if there are samples to be played.
6851 */
6852static gboolean
6853has_next_entry (GstQTDemux * demux)
6854{
6855 QtDemuxStream *stream;
6856 gint i;
6857
6858 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
6859
6860 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6861 stream = QTDEMUX_NTH_STREAM (demux, i);
6862
6863 if (stream->sample_index == -1) {
6864 stream->sample_index = 0;
6865 stream->offset_in_sample = 0;
6866 }
6867
6868 if (stream->sample_index >= stream->n_samples) {
6869 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6870 continue;
6871 }
6872 GST_DEBUG_OBJECT (demux, "Found a sample");
6873 return TRUE;
6874 }
6875
6876 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
6877 return FALSE;
6878}
6879
6880/*
6881 * next_entry_size
6882 *
6883 * Returns the size of the first entry at the current offset.
6884 * If -1, there are none (which means EOS or empty file).
6885 */
6886static guint64
6887next_entry_size (GstQTDemux * demux)
6888{
6889 QtDemuxStream *stream, *target_stream = NULL;
6890 guint64 smalloffs = (guint64) - 1;
6891 QtDemuxSample *sample;
6892 gint i;
6893
6894 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
6895 demux->offset);
6896
6897 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
6898 stream = QTDEMUX_NTH_STREAM (demux, i);
6899
6900 if (stream->sample_index == -1) {
6901 stream->sample_index = 0;
6902 stream->offset_in_sample = 0;
6903 }
6904
6905 if (stream->sample_index >= stream->n_samples) {
6906 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
6907 continue;
6908 }
6909
6910 if (!qtdemux_parse_samples (demux, stream, stream->sample_index)) {
6911 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
6912 stream->sample_index);
6913 return -1;
6914 }
6915
6916 sample = &stream->samples[stream->sample_index];
6917
6918 GST_LOG_OBJECT (demux,
6919 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
6920 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
6921 stream->sample_index, sample->offset, sample->size);
6922
6923 if (((smalloffs == -1)
6924 || (sample->offset < smalloffs)) && (sample->size)) {
6925 smalloffs = sample->offset;
6926 target_stream = stream;
6927 }
6928 }
6929
6930 if (!target_stream)
6931 return -1;
6932
6933 GST_LOG_OBJECT (demux,
6934 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
6935 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
6936
6937 stream = target_stream;
6938 sample = &stream->samples[stream->sample_index];
6939
6940 if (sample->offset >= demux->offset) {
6941 demux->todrop = sample->offset - demux->offset;
6942 return sample->size + demux->todrop;
6943 }
6944
6945 GST_DEBUG_OBJECT (demux,
6946 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
6947 return -1;
6948}
6949
6950static void
6951gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom)
6952{
6953 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
6954
6955 gst_element_post_message (GST_ELEMENT_CAST (demux),
6956 gst_message_new_element (GST_OBJECT_CAST (demux),
6957 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
6958}
6959
6960static gboolean
6961qtdemux_seek_offset (GstQTDemux * demux, guint64 offset)
6962{
6963 GstEvent *event;
6964 gboolean res = 0;
6965
6966 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
6967
6968 event =
6969 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
6970 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
6971 GST_SEEK_TYPE_NONE, -1);
6972
6973 /* store seqnum to drop flush events, they don't need to reach downstream */
6974 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
6975 res = gst_pad_push_event (demux->sinkpad, event);
6976 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
6977
6978 return res;
6979}
6980
6981/* check for seekable upstream, above and beyond a mere query */
6982static void
6983gst_qtdemux_check_seekability (GstQTDemux * demux)
6984{
6985 GstQuery *query;
6986 gboolean seekable = FALSE;
6987 gint64 start = -1, stop = -1;
6988
6989 if (demux->upstream_size)
6990 return;
6991
6992 if (demux->upstream_format_is_time)
6993 return;
6994
6995 query = gst_query_new_seeking (GST_FORMAT_BYTES);
6996 if (!gst_pad_peer_query (demux->sinkpad, query)) {
6997 GST_DEBUG_OBJECT (demux, "seeking query failed");
6998 goto done;
6999 }
7000
7001 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7002
7003 /* try harder to query upstream size if we didn't get it the first time */
7004 if (seekable && stop == -1) {
7005 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7006 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7007 }
7008
7009 /* if upstream doesn't know the size, it's likely that it's not seekable in
7010 * practice even if it technically may be seekable */
7011 if (seekable && (start != 0 || stop <= start)) {
7012 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7013 seekable = FALSE;
7014 }
7015
7016done:
7017 gst_query_unref (query);
7018
7019 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7020 G_GUINT64_FORMAT ")", seekable, start, stop);
7021 demux->upstream_seekable = seekable;
7022 demux->upstream_size = seekable ? stop : -1;
7023}
7024
7025static void
7026gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
7027{
7028 g_return_if_fail (bytes <= demux->todrop);
7029
7030 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7031 gst_adapter_flush (demux->adapter, bytes);
7032 demux->neededbytes -= bytes;
7033 demux->offset += bytes;
7034 demux->todrop -= bytes;
7035}
7036
7037/* PUSH-MODE only: Send a segment, if not done already. */
7038static void
7039gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
7040{
7041 if (G_UNLIKELY (demux->need_segment)) {
7042 gint i;
7043
7044 if (!demux->upstream_format_is_time) {
7045 gst_qtdemux_map_and_push_segments (demux, &demux->segment);
7046 } else {
7047 GstEvent *segment_event;
7048 segment_event = gst_event_new_segment (&demux->segment);
7049 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7050 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
7051 gst_qtdemux_push_event (demux, segment_event);
7052 }
7053
7054 demux->need_segment = FALSE;
7055
7056 /* clear to send tags on all streams */
7057 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7058 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7059 gst_qtdemux_push_tags (demux, stream);
7060 if (CUR_STREAM (stream)->sparse) {
7061 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7062 gst_pad_push_event (stream->pad,
7063 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7064 }
7065 }
7066 }
7067}
7068
7069/* Used for push mode only. */
7070static void
7071gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
7072 QtDemuxStream * stream, gint segment_index, GstClockTime pos)
7073{
7074 GstClockTime ts, dur;
7075
7076 ts = pos;
7077 dur =
7078 stream->segments[segment_index].duration - (pos -
7079 stream->segments[segment_index].time);
7080 stream->time_position += dur;
7081
7082 /* Only gaps with a duration of at least one second are propagated.
7083 * Same workaround as in pull mode.
7084 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7085 if (dur >= GST_SECOND) {
7086 GstEvent *gap;
7087 gap = gst_event_new_gap (ts, dur);
7088
7089 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7090 "segment: %" GST_PTR_FORMAT, gap);
7091 gst_pad_push_event (stream->pad, gap);
7092 }
7093}
7094
7095static GstFlowReturn
7096gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
7097{
7098 GstQTDemux *demux;
7099
7100 demux = GST_QTDEMUX (parent);
7101
7102 GST_DEBUG_OBJECT (demux,
7103 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7104 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7105 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7106 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7107 gst_buffer_get_size (inbuf), demux->offset);
7108
7109 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7110 gboolean is_gap_input = FALSE;
7111 gint i;
7112
7113 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7114
7115 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7116 QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
7117 }
7118
7119 /* Check if we can land back on our feet in the case where upstream is
7120 * handling the seeking/pushing of samples with gaps in between (like
7121 * in the case of trick-mode DASH for example) */
7122 if (demux->upstream_format_is_time
7123 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
7124 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7125 guint32 res;
7126 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (demux, i);
7127 GST_LOG_OBJECT (demux,
7128 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7129 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7130 res =
7131 gst_qtdemux_find_index_for_given_media_offset_linear (demux,
7132 stream, GST_BUFFER_OFFSET (inbuf));
7133 if (res != -1) {
7134 QtDemuxSample *sample = &stream->samples[res];
7135 GST_LOG_OBJECT (demux,
7136 "Checking if sample %d from track-id %u is valid (offset:%"
7137 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7138 stream->track_id, sample->offset, sample->size);
7139 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7140 GST_LOG_OBJECT (demux,
7141 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7142 res);
7143 is_gap_input = TRUE;
7144 /* We can go back to standard playback mode */
7145 demux->state = QTDEMUX_STATE_MOVIE;
7146 /* Remember which sample this stream is at */
7147 stream->sample_index = res;
7148 /* Finally update all push-based values to the expected values */
7149 demux->neededbytes = stream->samples[res].size;
7150 demux->offset = GST_BUFFER_OFFSET (inbuf);
7151 demux->mdatleft =
7152 demux->mdatsize - demux->offset + demux->mdatoffset;
7153 demux->todrop = 0;
7154 }
7155 }
7156 }
7157 if (!is_gap_input) {
7158 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7159 /* Reset state if it's a real discont */
7160 demux->neededbytes = 16;
7161 demux->state = QTDEMUX_STATE_INITIAL;
7162 demux->offset = GST_BUFFER_OFFSET (inbuf);
7163 gst_adapter_clear (demux->adapter);
7164 }
7165 }
7166 /* Reverse fragmented playback, need to flush all we have before
7167 * consuming a new fragment.
7168 * The samples array have the timestamps calculated by accumulating the
7169 * durations but this won't work for reverse playback of fragments as
7170 * the timestamps of a subsequent fragment should be smaller than the
7171 * previously received one. */
7172 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
7173 gst_qtdemux_process_adapter (demux, TRUE);
7174 g_ptr_array_foreach (demux->active_streams,
7175 (GFunc) gst_qtdemux_stream_flush_samples_data, NULL);
7176 }
7177 }
7178
7179 gst_adapter_push (demux->adapter, inbuf);
7180
7181 GST_DEBUG_OBJECT (demux,
7182 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7183 demux->neededbytes, gst_adapter_available (demux->adapter));
7184
7185 return gst_qtdemux_process_adapter (demux, FALSE);
7186}
7187
7188static GstFlowReturn
7189gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
7190{
7191 GstFlowReturn ret = GST_FLOW_OK;
7192
7193 /* we never really mean to buffer that much */
7194 if (demux->neededbytes == -1) {
7195 goto eos;
7196 }
7197
7198 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7199 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7200
7201#ifndef GST_DISABLE_GST_DEBUG
7202 {
7203 guint64 discont_offset, distance_from_discont;
7204
7205 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7206 distance_from_discont =
7207 gst_adapter_distance_from_discont (demux->adapter);
7208
7209 GST_DEBUG_OBJECT (demux,
7210 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7211 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
7212 " bytes)", qt_demux_state_string (demux->state), demux->neededbytes,
7213 demux->offset, discont_offset, distance_from_discont);
7214 }
7215#endif
7216
7217 switch (demux->state) {
7218 case QTDEMUX_STATE_INITIAL:{
7219 const guint8 *data;
7220 guint32 fourcc;
7221 guint64 size;
7222
7223 gst_qtdemux_check_seekability (demux);
7224
7225 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7226
7227 /* get fourcc/length, set neededbytes */
7228 extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
7229 &size, &fourcc);
7230 gst_adapter_unmap (demux->adapter);
7231 data = NULL;
7232 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7233 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7234 if (size == 0) {
7235 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7236 (_("This file is invalid and cannot be played.")),
7237 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7238 GST_FOURCC_ARGS (fourcc)));
7239 ret = GST_FLOW_ERROR;
7240 break;
7241 }
7242 if (fourcc == FOURCC_mdat) {
7243 gint next_entry = next_entry_size (demux);
7244 if (QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
7245 || !demux->fragmented)) {
7246 /* we have the headers, start playback */
7247 demux->state = QTDEMUX_STATE_MOVIE;
7248 demux->neededbytes = next_entry;
7249 demux->mdatleft = size;
7250 demux->mdatsize = demux->mdatleft;
7251 } else {
7252 /* no headers yet, try to get them */
7253 guint bs;
7254 gboolean res;
7255 guint64 old, target;
7256
7257 buffer_data:
7258 old = demux->offset;
7259 target = old + size;
7260
7261 /* try to jump over the atom with a seek */
7262 /* only bother if it seems worth doing so,
7263 * and avoids possible upstream/server problems */
7264 if (demux->upstream_seekable &&
7265 demux->upstream_size > 4 * (1 << 20)) {
7266 res = qtdemux_seek_offset (demux, target);
7267 } else {
7268 GST_DEBUG_OBJECT (demux, "skipping seek");
7269 res = FALSE;
7270 }
7271
7272 if (res) {
7273 GST_DEBUG_OBJECT (demux, "seek success");
7274 /* remember the offset fo the first mdat so we can seek back to it
7275 * after we have the headers */
7276 if (fourcc == FOURCC_mdat && demux->first_mdat == -1) {
7277 demux->first_mdat = old;
7278 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7279 demux->first_mdat);
7280 }
7281 /* seek worked, continue reading */
7282 demux->offset = target;
7283 demux->neededbytes = 16;
7284 demux->state = QTDEMUX_STATE_INITIAL;
7285 } else {
7286 /* seek failed, need to buffer */
7287 demux->offset = old;
7288 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7289 /* there may be multiple mdat (or alike) buffers */
7290 /* sanity check */
7291 if (demux->mdatbuffer)
7292 bs = gst_buffer_get_size (demux->mdatbuffer);
7293 else
7294 bs = 0;
7295 if (size + bs > 10 * (1 << 20))
7296 goto no_moov;
7297 demux->state = QTDEMUX_STATE_BUFFER_MDAT;
7298 demux->neededbytes = size;
7299 if (!demux->mdatbuffer)
7300 demux->mdatoffset = demux->offset;
7301 }
7302 }
7303 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
7304 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7305 (_("This file is invalid and cannot be played.")),
7306 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7307 GST_FOURCC_ARGS (fourcc), size));
7308 ret = GST_FLOW_ERROR;
7309 break;
7310 } else {
7311 /* this means we already started buffering and still no moov header,
7312 * let's continue buffering everything till we get moov */
7313 if (demux->mdatbuffer && !(fourcc == FOURCC_moov
7314 || fourcc == FOURCC_moof))
7315 goto buffer_data;
7316 demux->neededbytes = size;
7317 demux->state = QTDEMUX_STATE_HEADER;
7318 }
7319 break;
7320 }
7321 case QTDEMUX_STATE_HEADER:{
7322 const guint8 *data;
7323 guint32 fourcc;
7324
7325 GST_DEBUG_OBJECT (demux, "In header");
7326
7327 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7328
7329 /* parse the header */
7330 extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
7331 &fourcc);
7332 if (fourcc == FOURCC_moov) {
7333 /* in usual fragmented setup we could try to scan for more
7334 * and end up at the the moov (after mdat) again */
7335 if (demux->got_moov && QTDEMUX_N_STREAMS (demux) > 0 &&
7336 (!demux->fragmented
7337 || demux->last_moov_offset == demux->offset)) {
7338 GST_DEBUG_OBJECT (demux,
7339 "Skipping moov atom as we have (this) one already");
7340 } else {
7341 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7342
7343 if (demux->got_moov && demux->fragmented) {
7344 GST_DEBUG_OBJECT (demux,
7345 "Got a second moov, clean up data from old one");
7346 if (demux->moov_node_compressed) {
7347 g_node_destroy (demux->moov_node_compressed);
7348 if (demux->moov_node)
7349 g_free (demux->moov_node->data);
7350 }
7351 demux->moov_node_compressed = NULL;
7352 if (demux->moov_node)
7353 g_node_destroy (demux->moov_node);
7354 demux->moov_node = NULL;
7355 }
7356
7357 demux->last_moov_offset = demux->offset;
7358
7359 /* Update streams with new moov */
7360 gst_qtdemux_stream_concat (demux,
7361 demux->old_streams, demux->active_streams);
7362
7363 qtdemux_parse_moov (demux, data, demux->neededbytes);
7364 qtdemux_node_dump (demux, demux->moov_node);
7365 qtdemux_parse_tree (demux);
7366 qtdemux_prepare_streams (demux);
7367 QTDEMUX_EXPOSE_LOCK (demux);
7368 qtdemux_expose_streams (demux);
7369 QTDEMUX_EXPOSE_UNLOCK (demux);
7370
7371 demux->got_moov = TRUE;
7372
7373 gst_qtdemux_check_send_pending_segment (demux);
7374
zengliang.lif09b73f2024-05-17 06:22:11 +00007375 /* check sync and set multiqueue */
7376 if (2 == QTDEMUX_N_STREAMS (demux))
7377 {
7378 QtDemuxStream *stream_1 = QTDEMUX_NTH_STREAM (demux, 0);
7379 QtDemuxStream *stream_2 = QTDEMUX_NTH_STREAM (demux, 1);
7380 if ((0 == strncmp(GST_PAD_NAME (stream_1->pad), "video", 5) && 0 == strncmp(GST_PAD_NAME (stream_2->pad), "audio", 5))
7381 || (0 == strncmp(GST_PAD_NAME (stream_1->pad), "audio", 5) && 0 == strncmp(GST_PAD_NAME (stream_2->pad), "video", 5)))
7382 {
7383 GstStructure *s;
7384 GstEvent *event;
7385 s = gst_structure_new_empty ("AML-SET-MAX-BYTE-SIZE");
7386 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, s);
7387 GST_DEBUG("Send SET-MAX-BYTE-SIZE Event");
7388 gst_pad_push_event (stream_1->pad, event);
7389 }
7390 }
7391
zengliang.li5f31ef42024-05-16 08:27:38 +00007392 if (demux->moov_node_compressed) {
7393 g_node_destroy (demux->moov_node_compressed);
7394 g_free (demux->moov_node->data);
7395 }
7396 demux->moov_node_compressed = NULL;
7397 g_node_destroy (demux->moov_node);
7398 demux->moov_node = NULL;
7399 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7400 }
7401 } else if (fourcc == FOURCC_moof) {
7402 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7403 guint64 dist = 0;
7404 GstClockTime prev_pts;
7405 guint64 prev_offset;
7406 guint64 adapter_discont_offset, adapter_discont_dist;
7407
7408 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7409
7410 /*
7411 * The timestamp of the moof buffer is relevant as some scenarios
7412 * won't have the initial timestamp in the atoms. Whenever a new
7413 * buffer has started, we get that buffer's PTS and use it as a base
7414 * timestamp for the trun entries.
7415 *
7416 * To keep track of the current buffer timestamp and starting point
7417 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7418 * from the beginning of the buffer, with the distance and demux->offset
7419 * we know if it is still the same buffer or not.
7420 */
7421 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7422 prev_offset = demux->offset - dist;
7423 if (demux->fragment_start_offset == -1
7424 || prev_offset > demux->fragment_start_offset) {
7425 demux->fragment_start_offset = prev_offset;
7426 demux->fragment_start = prev_pts;
zengliang.lid0c84c32024-05-17 03:33:20 +00007427 if (demux->cal_discontinuity_pos)
7428 {
7429 demux->discontinuity_base_pos = demux->fragment_start;
7430 }
7431
zengliang.li5f31ef42024-05-16 08:27:38 +00007432 GST_DEBUG_OBJECT (demux,
7433 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7434 GST_TIME_FORMAT, demux->fragment_start_offset,
7435 GST_TIME_ARGS (demux->fragment_start));
7436 }
7437
7438 /* We can't use prev_offset() here because this would require
7439 * upstream to set consistent and correct offsets on all buffers
7440 * since the discont. Nothing ever did that in the past and we
7441 * would break backwards compatibility here then.
7442 * Instead take the offset we had at the last discont and count
7443 * the bytes from there. This works with old code as there would
7444 * be no discont between moov and moof, and also works with
7445 * adaptivedemux which correctly sets offset and will set the
7446 * DISCONT flag accordingly when needed.
7447 *
7448 * We also only do this for upstream TIME segments as otherwise
7449 * there are potential backwards compatibility problems with
7450 * seeking in PUSH mode and upstream providing inconsistent
7451 * timestamps. */
7452 adapter_discont_offset =
7453 gst_adapter_offset_at_discont (demux->adapter);
7454 adapter_discont_dist =
7455 gst_adapter_distance_from_discont (demux->adapter);
7456
7457 GST_DEBUG_OBJECT (demux,
7458 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7459 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7460 demux->offset, adapter_discont_offset, adapter_discont_dist);
7461
7462 if (demux->upstream_format_is_time) {
7463 demux->moof_offset = adapter_discont_offset;
7464 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7465 demux->moof_offset += adapter_discont_dist;
7466 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7467 demux->moof_offset = demux->offset;
7468 } else {
7469 demux->moof_offset = demux->offset;
7470 }
7471
7472 if (!qtdemux_parse_moof (demux, data, demux->neededbytes,
7473 demux->moof_offset, NULL)) {
7474 gst_adapter_unmap (demux->adapter);
7475 ret = GST_FLOW_ERROR;
7476 goto done;
7477 }
7478
7479 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
7480 if (demux->variant == VARIANT_MSS_FRAGMENTED && !demux->exposed) {
7481 QTDEMUX_EXPOSE_LOCK (demux);
7482 qtdemux_expose_streams (demux);
7483 QTDEMUX_EXPOSE_UNLOCK (demux);
7484 }
7485
7486 gst_qtdemux_check_send_pending_segment (demux);
7487 } else {
7488 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7489 }
7490 } else if (fourcc == FOURCC_ftyp) {
7491 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
7492 qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7493 } else if (fourcc == FOURCC_uuid) {
7494 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
7495 qtdemux_parse_uuid (demux, data, demux->neededbytes);
7496 } else if (fourcc == FOURCC_sidx) {
7497 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
7498 qtdemux_parse_sidx (demux, data, demux->neededbytes);
7499 } else {
7500 switch (fourcc) {
7501 case FOURCC_styp:
7502 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7503 * FALLTHROUGH */
7504 case FOURCC_skip:
7505 case FOURCC_free:
7506 /* [free] and [skip] are padding atoms */
7507 GST_DEBUG_OBJECT (demux,
7508 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7509 GST_FOURCC_ARGS (fourcc));
7510 break;
7511 default:
7512 GST_WARNING_OBJECT (demux,
7513 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7514 GST_FOURCC_ARGS (fourcc));
7515 /* Let's jump that one and go back to initial state */
7516 break;
7517 }
7518 }
7519 gst_adapter_unmap (demux->adapter);
7520 data = NULL;
7521
7522 if (demux->mdatbuffer && QTDEMUX_N_STREAMS (demux)) {
7523 gsize remaining_data_size = 0;
7524
7525 /* the mdat was before the header */
7526 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
7527 QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
7528 /* restore our adapter/offset view of things with upstream;
7529 * put preceding buffered data ahead of current moov data.
7530 * This should also handle evil mdat, moov, mdat cases and alike */
7531 gst_adapter_flush (demux->adapter, demux->neededbytes);
7532
7533 /* Store any remaining data after the mdat for later usage */
7534 remaining_data_size = gst_adapter_available (demux->adapter);
7535 if (remaining_data_size > 0) {
7536 g_assert (demux->restoredata_buffer == NULL);
7537 demux->restoredata_buffer =
7538 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7539 demux->restoredata_offset = demux->offset + demux->neededbytes;
7540 GST_DEBUG_OBJECT (demux,
7541 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7542 G_GUINT64_FORMAT, remaining_data_size,
7543 demux->restoredata_offset);
7544 }
7545
7546 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7547 demux->mdatbuffer = NULL;
7548 demux->offset = demux->mdatoffset;
7549 demux->neededbytes = next_entry_size (demux);
7550 demux->state = QTDEMUX_STATE_MOVIE;
7551 demux->mdatleft = gst_adapter_available (demux->adapter);
7552 demux->mdatsize = demux->mdatleft;
7553 } else {
7554 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7555 gst_adapter_flush (demux->adapter, demux->neededbytes);
7556
7557 /* only go back to the mdat if there are samples to play */
7558 if (demux->got_moov && demux->first_mdat != -1
7559 && has_next_entry (demux)) {
7560 gboolean res;
7561
7562 /* we need to seek back */
7563 res = qtdemux_seek_offset (demux, demux->first_mdat);
7564 if (res) {
7565 demux->offset = demux->first_mdat;
7566 } else {
7567 GST_DEBUG_OBJECT (demux, "Seek back failed");
7568 }
7569 } else {
7570 demux->offset += demux->neededbytes;
7571 }
7572 demux->neededbytes = 16;
7573 demux->state = QTDEMUX_STATE_INITIAL;
7574 }
7575
7576 break;
7577 }
7578 case QTDEMUX_STATE_BUFFER_MDAT:{
7579 GstBuffer *buf;
7580 guint8 fourcc[4];
7581
7582 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7583 demux->offset);
7584 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7585 gst_buffer_extract (buf, 0, fourcc, 4);
7586 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
7587 GST_FOURCC_ARGS (QT_FOURCC (fourcc)));
7588 if (demux->mdatbuffer)
7589 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7590 else
7591 demux->mdatbuffer = buf;
7592 demux->offset += demux->neededbytes;
7593 demux->neededbytes = 16;
7594 demux->state = QTDEMUX_STATE_INITIAL;
7595 gst_qtdemux_post_progress (demux, 1, 1);
7596
7597 break;
7598 }
7599 case QTDEMUX_STATE_MOVIE:{
7600 QtDemuxStream *stream = NULL;
7601 QtDemuxSample *sample;
7602 GstClockTime dts, pts, duration;
7603 gboolean keyframe;
7604 gint i;
7605
7606 GST_DEBUG_OBJECT (demux,
7607 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7608
7609 if (demux->fragmented) {
7610 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7611 demux->mdatleft);
7612 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7613 /* if needed data starts within this atom,
7614 * then it should not exceed this atom */
7615 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7616 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7617 (_("This file is invalid and cannot be played.")),
7618 ("sample data crosses atom boundary"));
7619 ret = GST_FLOW_ERROR;
7620 break;
7621 }
7622 demux->mdatleft -= demux->neededbytes;
7623 } else {
7624 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7625 /* so we are dropping more than left in this atom */
7626 gst_qtdemux_drop_data (demux, demux->mdatleft);
7627 demux->mdatleft = 0;
7628
7629 /* need to resume atom parsing so we do not miss any other pieces */
7630 demux->state = QTDEMUX_STATE_INITIAL;
7631 demux->neededbytes = 16;
7632
7633 /* check if there was any stored post mdat data from previous buffers */
7634 if (demux->restoredata_buffer) {
7635 g_assert (gst_adapter_available (demux->adapter) == 0);
7636
7637 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7638 demux->restoredata_buffer = NULL;
7639 demux->offset = demux->restoredata_offset;
7640 }
7641
7642 break;
7643 }
7644 }
7645
7646 if (demux->todrop) {
7647 if (demux->cenc_aux_info_offset > 0) {
7648 GstByteReader br;
7649 const guint8 *data;
7650
7651 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7652 data = gst_adapter_map (demux->adapter, demux->todrop);
7653 gst_byte_reader_init (&br, data + 8, demux->todrop);
7654 if (!qtdemux_parse_cenc_aux_info (demux,
7655 QTDEMUX_NTH_STREAM (demux, 0), &br,
7656 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7657 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7658 ret = GST_FLOW_ERROR;
7659 gst_adapter_unmap (demux->adapter);
7660 g_free (demux->cenc_aux_info_sizes);
7661 demux->cenc_aux_info_sizes = NULL;
7662 goto done;
7663 }
7664 demux->cenc_aux_info_offset = 0;
7665 g_free (demux->cenc_aux_info_sizes);
7666 demux->cenc_aux_info_sizes = NULL;
7667 gst_adapter_unmap (demux->adapter);
7668 }
7669 gst_qtdemux_drop_data (demux, demux->todrop);
7670 }
7671
7672 /* first buffer? */
7673 /* initial newsegment sent here after having added pads,
7674 * possible others in sink_event */
7675 gst_qtdemux_check_send_pending_segment (demux);
7676
7677 /* Figure out which stream this packet belongs to */
7678 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7679 stream = QTDEMUX_NTH_STREAM (demux, i);
7680 if (stream->sample_index >= stream->n_samples) {
7681 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7682 stream = NULL;
7683 continue;
7684 }
7685 GST_LOG_OBJECT (demux,
7686 "Checking track-id %u (sample_index:%d / offset:%"
7687 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7688 stream->sample_index,
7689 stream->samples[stream->sample_index].offset,
7690 stream->samples[stream->sample_index].size);
7691
7692 if (stream->samples[stream->sample_index].offset == demux->offset)
7693 break;
7694 }
7695
7696 if (G_UNLIKELY (stream == NULL))
7697 goto unknown_stream;
7698
7699 gst_qtdemux_stream_check_and_change_stsd_index (demux, stream);
7700
7701 if (stream->new_caps) {
7702 gst_qtdemux_configure_stream (demux, stream);
7703 }
7704
7705 /* Put data in a buffer, set timestamps, caps, ... */
7706 sample = &stream->samples[stream->sample_index];
7707
7708 if (G_LIKELY (!(STREAM_IS_EOS (stream)))) {
7709 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
7710 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
7711
7712 dts = QTSAMPLE_DTS (stream, sample);
7713 pts = QTSAMPLE_PTS (stream, sample);
7714 duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
7715 keyframe = QTSAMPLE_KEYFRAME (stream, sample);
7716
7717 /* check for segment end */
zengliang.li4681ee42024-05-16 12:25:30 +00007718 if (G_UNLIKELY (demux->segment.stop != -1 && GST_CLOCK_TIME_IS_VALID(dts)
zengliang.li5f31ef42024-05-16 08:27:38 +00007719 && demux->segment.stop <= pts && stream->on_keyframe)
7720 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7721 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7722 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7723
7724 /* skip this data, stream is EOS */
7725 gst_adapter_flush (demux->adapter, demux->neededbytes);
7726 demux->offset += demux->neededbytes;
7727
7728 /* check if all streams are eos */
7729 ret = GST_FLOW_EOS;
7730 for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
7731 if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
7732 ret = GST_FLOW_OK;
7733 break;
7734 }
7735 }
7736 } else {
7737 GstBuffer *outbuf;
7738
7739 outbuf =
7740 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7741
7742 /* FIXME: should either be an assert or a plain check */
7743 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
7744
7745 ret = gst_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
7746 dts, pts, duration, keyframe, dts, demux->offset);
7747 }
7748
7749 /* combine flows */
7750 GST_OBJECT_LOCK (demux);
7751 ret = gst_qtdemux_combine_flows (demux, stream, ret);
7752 GST_OBJECT_UNLOCK (demux);
7753 } else {
7754 /* skip this data, stream is EOS */
7755 gst_adapter_flush (demux->adapter, demux->neededbytes);
7756 }
7757
7758 stream->sample_index++;
7759 stream->offset_in_sample = 0;
7760
7761 /* update current offset and figure out size of next buffer */
7762 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
7763 demux->offset, demux->neededbytes);
7764 demux->offset += demux->neededbytes;
7765 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
7766 demux->offset);
7767
7768
7769 if (ret == GST_FLOW_EOS) {
7770 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
7771 demux->neededbytes = -1;
7772 goto eos;
7773 }
7774
7775 if ((demux->neededbytes = next_entry_size (demux)) == -1) {
7776 if (demux->fragmented) {
7777 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
7778 /* there may be more to follow, only finish this atom */
7779 demux->todrop = demux->mdatleft;
7780 demux->neededbytes = demux->todrop;
7781 break;
7782 }
7783 goto eos;
7784 }
7785 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
7786 goto non_ok_unlinked_flow;
7787 }
7788 break;
7789 }
7790 default:
7791 goto invalid_state;
7792 }
7793 }
7794
7795 /* when buffering movie data, at least show user something is happening */
7796 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
7797 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
7798 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
7799 demux->neededbytes);
7800 }
7801done:
7802
7803 return ret;
7804
7805 /* ERRORS */
7806non_ok_unlinked_flow:
7807 {
7808 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
7809 gst_flow_get_name (ret));
7810 return ret;
7811 }
7812unknown_stream:
7813 {
7814 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
7815 ret = GST_FLOW_ERROR;
7816 goto done;
7817 }
7818eos:
7819 {
7820 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
7821 ret = GST_FLOW_EOS;
7822 goto done;
7823 }
7824invalid_state:
7825 {
7826 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7827 (NULL), ("qtdemuxer invalid state %d", demux->state));
7828 ret = GST_FLOW_ERROR;
7829 goto done;
7830 }
7831no_moov:
7832 {
7833 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
7834 (NULL), ("no 'moov' atom within the first 10 MB"));
7835 ret = GST_FLOW_ERROR;
7836 goto done;
7837 }
7838}
7839
7840static gboolean
7841qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
7842{
7843 GstQuery *query;
7844 gboolean pull_mode;
7845
7846 query = gst_query_new_scheduling ();
7847
7848 if (!gst_pad_peer_query (sinkpad, query)) {
7849 gst_query_unref (query);
7850 goto activate_push;
7851 }
7852
7853 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
7854 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
7855 gst_query_unref (query);
7856
7857 if (!pull_mode)
7858 goto activate_push;
7859
7860 GST_DEBUG_OBJECT (sinkpad, "activating pull");
7861 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
7862
7863activate_push:
7864 {
7865 GST_DEBUG_OBJECT (sinkpad, "activating push");
7866 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
7867 }
7868}
7869
7870static gboolean
7871qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
7872 GstPadMode mode, gboolean active)
7873{
7874 gboolean res;
7875 GstQTDemux *demux = GST_QTDEMUX (parent);
7876
7877 switch (mode) {
7878 case GST_PAD_MODE_PUSH:
7879 demux->pullbased = FALSE;
7880 res = TRUE;
7881 break;
7882 case GST_PAD_MODE_PULL:
7883 if (active) {
7884 demux->pullbased = TRUE;
7885 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
7886 sinkpad, NULL);
7887 } else {
7888 res = gst_pad_stop_task (sinkpad);
7889 }
7890 break;
7891 default:
7892 res = FALSE;
7893 break;
7894 }
7895 return res;
7896}
7897
7898#ifdef HAVE_ZLIB
7899static void *
7900qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
7901{
7902 guint8 *buffer;
7903 z_stream z;
7904 int ret;
7905
7906 memset (&z, 0, sizeof (z));
7907 z.zalloc = NULL;
7908 z.zfree = NULL;
7909 z.opaque = NULL;
7910
7911 if ((ret = inflateInit (&z)) != Z_OK) {
7912 GST_ERROR ("inflateInit() returned %d", ret);
7913 return NULL;
7914 }
7915
7916 z.next_in = z_buffer;
7917 z.avail_in = z_length;
7918
7919 buffer = (guint8 *) g_malloc (*length);
7920 z.avail_out = *length;
7921 z.next_out = (Bytef *) buffer;
7922 do {
7923 ret = inflate (&z, Z_NO_FLUSH);
7924 if (ret == Z_STREAM_END) {
7925 break;
7926 } else if (ret != Z_OK) {
7927 GST_WARNING ("inflate() returned %d", ret);
7928 break;
7929 }
7930
7931 if (*length > G_MAXUINT - 4096 || *length > QTDEMUX_MAX_SAMPLE_INDEX_SIZE) {
7932 GST_WARNING ("too big decompressed data");
7933 ret = Z_MEM_ERROR;
7934 break;
7935 }
7936
7937 *length += 4096;
7938 buffer = (guint8 *) g_realloc (buffer, *length);
7939 z.next_out = (Bytef *) (buffer + z.total_out);
7940 z.avail_out += *length - z.total_out;
7941 } while (z.avail_in > 0);
7942
7943 if (ret != Z_STREAM_END) {
7944 g_free (buffer);
7945 buffer = NULL;
7946 *length = 0;
7947 } else {
7948 *length = z.total_out;
7949 }
7950
7951 inflateEnd (&z);
7952
7953 return buffer;
7954}
7955#endif /* HAVE_ZLIB */
7956
7957static gboolean
7958qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
7959{
7960 GNode *cmov;
7961
7962 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
7963
7964 /* counts as header data */
7965 qtdemux->header_size += length;
7966
7967 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
7968 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
7969
7970 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
7971 if (cmov) {
7972 guint32 method;
7973 GNode *dcom;
7974 GNode *cmvd;
7975 guint32 dcom_len;
7976
7977 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
7978 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
7979 if (dcom == NULL || cmvd == NULL)
7980 goto invalid_compression;
7981
7982 dcom_len = QT_UINT32 (dcom->data);
7983 if (dcom_len < 12)
7984 goto invalid_compression;
7985
7986 method = QT_FOURCC ((guint8 *) dcom->data + 8);
7987 switch (method) {
7988#ifdef HAVE_ZLIB
7989 case FOURCC_zlib:{
7990 guint uncompressed_length;
7991 guint compressed_length;
7992 guint8 *buf;
7993 guint32 cmvd_len;
7994
7995 cmvd_len = QT_UINT32 ((guint8 *) cmvd->data);
7996 if (cmvd_len < 12)
7997 goto invalid_compression;
7998
7999 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
8000 compressed_length = cmvd_len - 12;
8001 GST_LOG ("length = %u", uncompressed_length);
8002
8003 buf =
8004 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8005 compressed_length, &uncompressed_length);
8006
8007 if (buf) {
8008 qtdemux->moov_node_compressed = qtdemux->moov_node;
8009 qtdemux->moov_node = g_node_new (buf);
8010
8011 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
8012 uncompressed_length);
8013 }
8014 break;
8015 }
8016#endif /* HAVE_ZLIB */
8017 default:
8018 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8019 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8020 break;
8021 }
8022 }
8023 return TRUE;
8024
8025 /* ERRORS */
8026invalid_compression:
8027 {
8028 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8029 return FALSE;
8030 }
8031}
8032
8033static gboolean
8034qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
8035 const guint8 * end)
8036{
8037 while (G_UNLIKELY (buf < end)) {
8038 GNode *child;
8039 guint32 len;
8040
8041 if (G_UNLIKELY (buf + 4 > end)) {
8042 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8043 break;
8044 }
8045 len = QT_UINT32 (buf);
8046 if (G_UNLIKELY (len == 0)) {
8047 GST_LOG_OBJECT (qtdemux, "empty container");
8048 break;
8049 }
8050 if (G_UNLIKELY (len < 8)) {
8051 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8052 break;
8053 }
8054 if (G_UNLIKELY (len > (end - buf))) {
8055 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8056 (gint) (end - buf));
8057 break;
8058 }
8059
8060 child = g_node_new ((guint8 *) buf);
8061 g_node_append (node, child);
8062 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
8063 qtdemux_parse_node (qtdemux, child, buf, len);
8064
8065 buf += len;
8066 }
8067 return TRUE;
8068}
8069
8070static gboolean
8071qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
8072 GNode * xdxt)
8073{
8074 int len = QT_UINT32 (xdxt->data);
8075 guint8 *buf = xdxt->data;
8076 guint8 *end = buf + len;
8077 GstBuffer *buffer;
8078
8079 /* skip size and type */
8080 buf += 8;
8081 end -= 8;
8082
8083 while (buf < end) {
8084 gint size;
8085 guint32 type;
8086
8087 size = QT_UINT32 (buf);
8088 type = QT_FOURCC (buf + 4);
8089
8090 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8091
8092 if (buf + size > end || size <= 0)
8093 break;
8094
8095 buf += 8;
8096 size -= 8;
8097
8098 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8099 GST_FOURCC_ARGS (type));
8100
8101 switch (type) {
8102 case FOURCC_tCtH:
8103 buffer = gst_buffer_new_and_alloc (size);
8104 gst_buffer_fill (buffer, 0, buf, size);
8105 stream->buffers = g_slist_append (stream->buffers, buffer);
8106 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8107 break;
8108 case FOURCC_tCt_:
8109 buffer = gst_buffer_new_and_alloc (size);
8110 gst_buffer_fill (buffer, 0, buf, size);
8111 stream->buffers = g_slist_append (stream->buffers, buffer);
8112 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8113 break;
8114 case FOURCC_tCtC:
8115 buffer = gst_buffer_new_and_alloc (size);
8116 gst_buffer_fill (buffer, 0, buf, size);
8117 stream->buffers = g_slist_append (stream->buffers, buffer);
8118 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8119 break;
8120 default:
8121 GST_WARNING_OBJECT (qtdemux,
8122 "unknown theora cookie %" GST_FOURCC_FORMAT,
8123 GST_FOURCC_ARGS (type));
8124 break;
8125 }
8126 buf += size;
8127 }
8128 return TRUE;
8129}
8130
8131static gboolean
8132qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
8133 guint length)
8134{
8135 guint32 fourcc = 0;
8136 guint32 node_length = 0;
8137 const QtNodeType *type;
8138 const guint8 *end;
8139
8140 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8141
8142 if (G_UNLIKELY (length < 8))
8143 goto not_enough_data;
8144
8145 node_length = QT_UINT32 (buffer);
8146 fourcc = QT_FOURCC (buffer + 4);
8147
8148 /* ignore empty nodes */
8149 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8150 return TRUE;
8151
8152 type = qtdemux_type_get (fourcc);
8153
8154 end = buffer + length;
8155
8156 GST_LOG_OBJECT (qtdemux,
8157 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8158 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8159
8160 if (node_length > length)
8161 goto broken_atom_size;
8162
8163 if (type->flags & QT_FLAG_CONTAINER) {
8164 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8165 } else {
8166 switch (fourcc) {
8167 case FOURCC_stsd:
8168 {
8169 if (node_length < 20) {
8170 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8171 break;
8172 }
8173 GST_DEBUG_OBJECT (qtdemux,
8174 "parsing stsd (sample table, sample description) atom");
8175 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
8176 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8177 break;
8178 }
8179 case FOURCC_mp4a:
8180 case FOURCC_alac:
8181 case FOURCC_fLaC:
8182 case FOURCC_aavd:
8183 {
8184 guint32 version;
8185 guint32 offset;
8186 guint min_size;
8187
8188 /* also read alac (or whatever) in stead of mp4a in the following,
8189 * since a similar layout is used in other cases as well */
8190 if (fourcc == FOURCC_mp4a)
8191 min_size = 20;
8192 else if (fourcc == FOURCC_fLaC)
8193 min_size = 86;
8194 else
8195 min_size = 40;
8196
8197 /* There are two things we might encounter here: a true mp4a atom, and
8198 an mp4a entry in an stsd atom. The latter is what we're interested
8199 in, and it looks like an atom, but isn't really one. The true mp4a
8200 atom is short, so we detect it based on length here. */
8201 if (length < min_size) {
8202 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8203 GST_FOURCC_ARGS (fourcc));
8204 break;
8205 }
8206
8207 /* 'version' here is the sound sample description version. Types 0 and
8208 1 are documented in the QTFF reference, but type 2 is not: it's
8209 described in Apple header files instead (struct SoundDescriptionV2
8210 in Movies.h) */
8211 version = QT_UINT16 (buffer + 16);
8212
8213 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8214 GST_FOURCC_ARGS (fourcc), version);
8215
8216 /* parse any esds descriptors */
8217 switch (version) {
8218 case 0:
8219 offset = 0x24;
8220 break;
8221 case 1:
8222 offset = 0x34;
8223 break;
8224 case 2:
8225 offset = 0x48;
8226 break;
8227 default:
8228 GST_WARNING_OBJECT (qtdemux,
8229 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8230 GST_FOURCC_ARGS (fourcc), version);
8231 offset = 0;
8232 break;
8233 }
8234 if (offset)
8235 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8236 break;
8237 }
8238 case FOURCC_mp4v:
8239 case FOURCC_MP4V:
8240 case FOURCC_fmp4:
8241 case FOURCC_FMP4:
8242 case FOURCC_apcs:
8243 case FOURCC_apch:
8244 case FOURCC_apcn:
8245 case FOURCC_apco:
8246 case FOURCC_ap4h:
8247 case FOURCC_xvid:
8248 case FOURCC_XVID:
8249 case FOURCC_H264:
8250 case FOURCC_avc1:
8251 case FOURCC_avc3:
8252 case FOURCC_H265:
8253 case FOURCC_hvc1:
8254 case FOURCC_hev1:
8255 case FOURCC_dvh1:
8256 case FOURCC_dvhe:
8257 case FOURCC_mjp2:
8258 case FOURCC_encv:
8259 {
8260 guint32 version;
8261 guint32 str_len;
8262
8263 /* codec_data is contained inside these atoms, which all have
8264 * the same format. */
8265 /* video sample description size is 86 bytes without extension.
8266 * node_length have to be bigger than 86 bytes because video sample
8267 * description can include extensions such as esds, fiel, glbl, etc. */
8268 if (node_length < 86) {
8269 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8270 " sample description length too short (%u < 86)",
8271 GST_FOURCC_ARGS (fourcc), node_length);
8272 break;
8273 }
8274
8275 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8276 GST_FOURCC_ARGS (fourcc));
8277
8278 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8279 * its data format.
8280 * revision level (2 bytes) : must be set to 0. */
8281 version = QT_UINT32 (buffer + 16);
8282 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8283
8284 /* compressor name : PASCAL string and informative purposes
8285 * first byte : the number of bytes to be displayed.
8286 * it has to be less than 32 because it is reserved
8287 * space of 32 bytes total including itself. */
8288 str_len = QT_UINT8 (buffer + 50);
8289 if (str_len < 32)
8290 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8291 (char *) buffer + 51);
8292 else
8293 GST_WARNING_OBJECT (qtdemux,
8294 "compressorname length too big (%u > 31)", str_len);
8295
8296 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8297 end - buffer);
8298 qtdemux_parse_container (qtdemux, node, buffer + 86, end);
8299 break;
8300 }
8301 case FOURCC_meta:
8302 {
8303 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8304
8305 /* You are reading this correctly. QTFF specifies that the
8306 * metadata atom is a short atom, whereas ISO BMFF specifies
8307 * it's a full atom. But since so many people are doing things
8308 * differently, we actually peek into the atom to see which
8309 * variant it is */
8310 if (length < 16) {
8311 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8312 GST_FOURCC_ARGS (fourcc));
8313 break;
8314 }
8315 if (QT_FOURCC (buffer + 12) == FOURCC_hdlr) {
8316 /* Variant 1: What QTFF specifies. 'meta' is a short header which
8317 * starts with a 'hdlr' atom */
8318 qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8319 } else if (QT_UINT32 (buffer + 8) == 0x00000000) {
8320 /* Variant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
8321 * with version/flags both set to zero */
8322 qtdemux_parse_container (qtdemux, node, buffer + 12, end);
8323 } else
8324 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8325 break;
8326 }
8327 case FOURCC_mp4s:
8328 {
8329 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8330 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
8331 qtdemux_parse_container (qtdemux, node, buffer + 16, end);
8332 break;
8333 }
8334 case FOURCC_XiTh:
8335 {
8336 guint32 version;
8337 guint32 offset;
8338
8339 if (length < 16) {
8340 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8341 GST_FOURCC_ARGS (fourcc));
8342 break;
8343 }
8344
8345 version = QT_UINT32 (buffer + 12);
8346 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8347
8348 switch (version) {
8349 case 0x00000001:
8350 offset = 0x62;
8351 break;
8352 default:
8353 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8354 offset = 0;
8355 break;
8356 }
8357 if (offset) {
8358 if (length < offset) {
8359 GST_WARNING_OBJECT (qtdemux,
8360 "skipping too small %" GST_FOURCC_FORMAT " box",
8361 GST_FOURCC_ARGS (fourcc));
8362 break;
8363 }
8364 qtdemux_parse_container (qtdemux, node, buffer + offset, end);
8365 }
8366 break;
8367 }
8368 case FOURCC_in24:
8369 {
8370 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
8371 break;
8372 }
8373 case FOURCC_uuid:
8374 {
8375 qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
8376 break;
8377 }
8378 case FOURCC_enca:
8379 {
8380 qtdemux_parse_container (qtdemux, node, buffer + 36, end);
8381 break;
8382 }
zengliang.li54713f02024-05-17 02:46:54 +00008383 case FOURCC_dvcC:
8384 case FOURCC_dvvC:
8385 case FOURCC_dvwC:
8386 {
8387 guint32 dv_version_major = QT_UINT8(buffer+8);
8388 guint32 dv_version_minor = QT_UINT8(buffer+9);
8389 guint32 tmp = QT_UINT16(buffer+10);
8390 guint32 dv_profile = (tmp >> 9) & 0x7f; // 7 bits
8391 guint32 dv_level = (tmp >> 3) & 0x3f; // 6 bits
8392 guint32 rpu_present_flag = (tmp >> 2) & 0x01;
8393 guint32 el_present_flag = (tmp >> 1) & 0x01; // 1 bit
8394 guint32 bl_present_flag = tmp & 0x01; // 1 bit
8395
8396 guint32 dv_bl_signal_compatibility_id = 0;// 0 stands for None
8397 if (node_length >= 24) {
8398 guint32 id = QT_UINT8(buffer+12);
8399 dv_bl_signal_compatibility_id = (id >> 4) & 0x0f; // 4 bits
8400 }
8401 GST_DEBUG_OBJECT (qtdemux,"DOVI in dvcC/dvvC box, version: %d.%d, profile: %d, level: %d, "
8402 "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n",
8403 dv_version_major, dv_version_minor,
8404 dv_profile, dv_level,
8405 rpu_present_flag,
8406 el_present_flag,
8407 bl_present_flag,
8408 dv_bl_signal_compatibility_id
8409 );
8410 /*
8411 Expected results are:
8412 • If the test vector (MP4) carries an undefined/unknown Base Layer Signal Compatibility ID (dv_bl_signal_
8413 compatibility_id), the device under test rejects the playback.
8414 • If the test vector (MP4) is of an undefined/unknown Dolby Vision profile, but its Base Layer Signal
8415 Compatibility ID (dv_bl_signal_compatibility_id) is valid, the device under test plays the content
8416 properly and TV displays in Dolby Vision picture mode.
8417 Note: Level 11 metadata changes when the embedded label on the left side of the test pattern
8418 changes.
8419 • If the Dolby Vision configuration box contains unknown elements (reserved fields with non-zero value),
8420 the device under test ignores the unknown elements properly and play the MP4 test vector properly and
8421 TV displays in Dolby Vision picture mode.
8422 */
8423 if (dv_bl_signal_compatibility_id != 0 &&
8424 dv_bl_signal_compatibility_id != 1 &&
8425 dv_bl_signal_compatibility_id != 2 &&
8426 dv_bl_signal_compatibility_id != 4 &&
8427 dv_bl_signal_compatibility_id != 6) {
8428 GST_ERROR_OBJECT(qtdemux,"Not support dolby vision config box");
8429 goto broken_atom_size;
8430 }
8431
8432 } break;
zengliang.li5f31ef42024-05-16 08:27:38 +00008433 default:
8434 if (!strcmp (type->name, "unknown"))
8435 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8436 break;
8437 }
8438 }
8439 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8440 GST_FOURCC_ARGS (fourcc));
8441 return TRUE;
8442
8443/* ERRORS */
8444not_enough_data:
8445 {
8446 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8447 (_("This file is corrupt and cannot be played.")),
8448 ("Not enough data for an atom header, got only %u bytes", length));
8449 return FALSE;
8450 }
8451broken_atom_size:
8452 {
8453 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8454 (_("This file is corrupt and cannot be played.")),
8455 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8456 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8457 length));
8458 return FALSE;
8459 }
8460}
8461
8462static void
8463qtdemux_do_allocation (QtDemuxStream * stream, GstQTDemux * qtdemux)
8464{
8465/* FIXME: This can only reliably work if demuxers have a
8466 * separate streaming thread per srcpad. This should be
8467 * done in a demuxer base class, which integrates parts
8468 * of multiqueue
8469 *
8470 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8471 */
8472#if 0
8473 GstQuery *query;
8474
8475 query = gst_query_new_allocation (stream->caps, FALSE);
8476
8477 if (!gst_pad_peer_query (stream->pad, query)) {
8478 /* not a problem, just debug a little */
8479 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8480 }
8481
8482 if (stream->allocator)
8483 gst_object_unref (stream->allocator);
8484
8485 if (gst_query_get_n_allocation_params (query) > 0) {
8486 /* try the allocator */
8487 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8488 &stream->params);
8489 stream->use_allocator = TRUE;
8490 } else {
8491 stream->allocator = NULL;
8492 gst_allocation_params_init (&stream->params);
8493 stream->use_allocator = FALSE;
8494 }
8495 gst_query_unref (query);
8496#endif
8497}
8498
8499static gboolean
8500pad_query (const GValue * item, GValue * value, gpointer user_data)
8501{
8502 GstPad *pad = g_value_get_object (item);
8503 GstQuery *query = user_data;
8504 gboolean res;
8505
8506 res = gst_pad_peer_query (pad, query);
8507
8508 if (res) {
8509 g_value_set_boolean (value, TRUE);
8510 return FALSE;
8511 }
8512
8513 GST_INFO_OBJECT (pad, "pad peer query failed");
8514 return TRUE;
8515}
8516
8517static gboolean
8518gst_qtdemux_run_query (GstElement * element, GstQuery * query,
8519 GstPadDirection direction)
8520{
8521 GstIterator *it;
8522 GstIteratorFoldFunction func = pad_query;
8523 GValue res = { 0, };
8524
8525 g_value_init (&res, G_TYPE_BOOLEAN);
8526 g_value_set_boolean (&res, FALSE);
8527
8528 /* Ask neighbor */
8529 if (direction == GST_PAD_SRC)
8530 it = gst_element_iterate_src_pads (element);
8531 else
8532 it = gst_element_iterate_sink_pads (element);
8533
8534 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8535 gst_iterator_resync (it);
8536
8537 gst_iterator_free (it);
8538
8539 return g_value_get_boolean (&res);
8540}
8541
8542static void
8543gst_qtdemux_request_protection_context (GstQTDemux * qtdemux,
8544 QtDemuxStream * stream)
8545{
8546 GstQuery *query;
8547 GstContext *ctxt;
8548 GstElement *element = GST_ELEMENT (qtdemux);
8549 GstStructure *st;
8550 gchar **filtered_sys_ids;
8551 GValue event_list = G_VALUE_INIT;
8552 GList *walk;
8553
8554 /* 1. Check if we already have the context. */
8555 if (qtdemux->preferred_protection_system_id != NULL) {
8556 GST_LOG_OBJECT (element,
8557 "already have the protection context, no need to request it again");
8558 return;
8559 }
8560
8561 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8562 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8563 (const gchar **) qtdemux->protection_system_ids->pdata);
8564
8565 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8566 qtdemux->protection_system_ids->len - 1);
8567 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8568 "decryptors for %u of them, running context request",
8569 qtdemux->protection_system_ids->len,
8570 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8571
8572
8573 if (stream->protection_scheme_event_queue.length) {
8574 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8575 stream->protection_scheme_event_queue.length);
8576 walk = stream->protection_scheme_event_queue.tail;
8577 } else {
8578 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8579 qtdemux->protection_event_queue.length);
8580 walk = qtdemux->protection_event_queue.tail;
8581 }
8582
8583 g_value_init (&event_list, GST_TYPE_LIST);
8584 for (; walk; walk = g_list_previous (walk)) {
8585 GValue *event_value = g_new0 (GValue, 1);
8586 g_value_init (event_value, GST_TYPE_EVENT);
8587 g_value_set_boxed (event_value, walk->data);
8588 gst_value_list_append_and_take_value (&event_list, event_value);
8589 }
8590
8591 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8592 * check if downstream already has a context of the specific type
8593 * 2b) Query upstream as above.
8594 */
8595 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8596 st = gst_query_writable_structure (query);
8597 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8598 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8599 NULL);
8600 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8601 if (gst_qtdemux_run_query (element, query, GST_PAD_SRC)) {
8602 gst_query_parse_context (query, &ctxt);
8603 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8604 gst_element_set_context (element, ctxt);
8605 } else if (gst_qtdemux_run_query (element, query, GST_PAD_SINK)) {
8606 gst_query_parse_context (query, &ctxt);
8607 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8608 gst_element_set_context (element, ctxt);
8609 } else {
8610 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8611 * the required context type and afterwards check if a
8612 * usable context was set now as in 1). The message could
8613 * be handled by the parent bins of the element and the
8614 * application.
8615 */
8616 GstMessage *msg;
8617
8618 GST_INFO_OBJECT (element, "posting need context message");
8619 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8620 "drm-preferred-decryption-system-id");
8621 st = (GstStructure *) gst_message_get_structure (msg);
8622 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8623 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8624 NULL);
8625
8626 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8627 gst_element_post_message (element, msg);
8628 }
8629
8630 g_strfreev (filtered_sys_ids);
8631 g_value_unset (&event_list);
8632 gst_query_unref (query);
8633}
8634
8635static gboolean
8636gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
8637 QtDemuxStream * stream)
8638{
8639 GstStructure *s;
8640 const gchar *selected_system = NULL;
8641
8642 g_return_val_if_fail (qtdemux != NULL, FALSE);
8643 g_return_val_if_fail (stream != NULL, FALSE);
8644 g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
8645 FALSE);
8646
8647 if (stream->protection_scheme_type == FOURCC_aavd) {
8648 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8649 if (!gst_structure_has_name (s, "application/x-aavd")) {
8650 gst_structure_set (s,
8651 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8652 NULL);
8653 gst_structure_set_name (s, "application/x-aavd");
8654 }
8655 return TRUE;
8656 }
8657
8658 if (stream->protection_scheme_type != FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +00008659 && stream->protection_scheme_type != FOURCC_cbcs
8660 && stream->protection_scheme_type != FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008661 GST_ERROR_OBJECT (qtdemux,
8662 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8663 GST_FOURCC_ARGS (stream->protection_scheme_type));
8664 return FALSE;
8665 }
8666
8667 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8668 if (!gst_structure_has_name (s, "application/x-cenc")) {
8669 gst_structure_set (s,
8670 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
8671 gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
8672 (stream->protection_scheme_type == FOURCC_cbcs) ? "cbcs" : "cenc",
8673 NULL);
8674 gst_structure_set_name (s, "application/x-cenc");
8675 }
8676
8677 if (qtdemux->protection_system_ids == NULL) {
8678 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
8679 "cenc protection system information has been found, not setting a "
8680 "protection system UUID");
8681 return TRUE;
8682 }
8683
8684 gst_qtdemux_request_protection_context (qtdemux, stream);
8685 if (qtdemux->preferred_protection_system_id != NULL) {
8686 const gchar *preferred_system_array[] =
8687 { qtdemux->preferred_protection_system_id, NULL };
8688
8689 selected_system = gst_protection_select_system (preferred_system_array);
8690
8691 if (selected_system) {
8692 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8693 qtdemux->preferred_protection_system_id);
8694 } else {
8695 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8696 "because there is no available decryptor",
8697 qtdemux->preferred_protection_system_id);
8698 }
8699 }
8700
8701 if (!selected_system) {
8702 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8703 selected_system = gst_protection_select_system ((const gchar **)
8704 qtdemux->protection_system_ids->pdata);
8705 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8706 qtdemux->protection_system_ids->len - 1);
8707 }
8708
8709 if (!selected_system) {
8710 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8711 "suitable decryptor element has been found");
8712 return FALSE;
8713 }
8714
8715 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8716 selected_system);
8717
8718 gst_structure_set (s,
8719 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8720 NULL);
8721
8722 return TRUE;
8723}
8724
8725static gboolean
8726gst_qtdemux_guess_framerate (GstQTDemux * qtdemux, QtDemuxStream * stream)
8727{
8728 /* fps is calculated base on the duration of the average framerate since
8729 * qt does not have a fixed framerate. */
8730 gboolean fps_available = TRUE;
8731 guint32 first_duration = 0;
8732
8733 if (stream->n_samples > 0)
8734 first_duration = stream->samples[0].duration;
8735
8736 if ((stream->n_samples == 1 && first_duration == 0)
8737 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8738 /* still frame */
8739 CUR_STREAM (stream)->fps_n = 0;
8740 CUR_STREAM (stream)->fps_d = 1;
8741 } else {
8742 if (stream->duration == 0 || stream->n_samples < 2) {
8743 CUR_STREAM (stream)->fps_n = stream->timescale;
8744 CUR_STREAM (stream)->fps_d = 1;
8745 fps_available = FALSE;
8746 } else {
8747 GstClockTime avg_duration;
8748 guint64 duration;
8749 guint32 n_samples;
8750
8751 /* duration and n_samples can be updated for fragmented format
8752 * so, framerate of fragmented format is calculated using data in a moof */
8753 if (qtdemux->fragmented && stream->n_samples_moof > 0
8754 && stream->duration_moof > 0) {
8755 n_samples = stream->n_samples_moof;
8756 duration = stream->duration_moof;
8757 } else {
8758 n_samples = stream->n_samples;
8759 duration = stream->duration;
8760 }
8761
8762 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8763 /* stream->duration is guint64, timescale, n_samples are guint32 */
8764 avg_duration =
8765 gst_util_uint64_scale_round (duration -
8766 first_duration, GST_SECOND,
8767 (guint64) (stream->timescale) * (n_samples - 1));
8768
8769 GST_LOG_OBJECT (qtdemux,
8770 "Calculating avg sample duration based on stream (or moof) duration %"
8771 G_GUINT64_FORMAT
8772 " minus first sample %u, leaving %d samples gives %"
8773 GST_TIME_FORMAT, duration, first_duration,
8774 n_samples - 1, GST_TIME_ARGS (avg_duration));
8775
8776 fps_available =
8777 gst_video_guess_framerate (avg_duration,
8778 &CUR_STREAM (stream)->fps_n, &CUR_STREAM (stream)->fps_d);
8779
8780 GST_DEBUG_OBJECT (qtdemux,
8781 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
8782 stream->timescale, CUR_STREAM (stream)->fps_n,
8783 CUR_STREAM (stream)->fps_d);
8784 }
8785 }
8786
8787 return fps_available;
8788}
8789
8790static gboolean
8791gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
8792{
8793 if (stream->subtype == FOURCC_vide) {
8794 gboolean fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8795
8796 if (CUR_STREAM (stream)->caps) {
8797 CUR_STREAM (stream)->caps =
8798 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8799
8800 if (CUR_STREAM (stream)->width && CUR_STREAM (stream)->height)
8801 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8802 "width", G_TYPE_INT, CUR_STREAM (stream)->width,
8803 "height", G_TYPE_INT, CUR_STREAM (stream)->height, NULL);
8804
8805 /* set framerate if calculated framerate is reliable */
8806 if (fps_available) {
8807 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8808 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8809 CUR_STREAM (stream)->fps_d, NULL);
8810 }
8811
8812 /* calculate pixel-aspect-ratio using display width and height */
8813 GST_DEBUG_OBJECT (qtdemux,
8814 "video size %dx%d, target display size %dx%d",
8815 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height,
8816 stream->display_width, stream->display_height);
8817 /* qt file might have pasp atom */
8818 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8819 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", CUR_STREAM (stream)->par_w,
8820 CUR_STREAM (stream)->par_h);
8821 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8822 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8823 CUR_STREAM (stream)->par_h, NULL);
8824 } else if (stream->display_width > 0 && stream->display_height > 0
8825 && CUR_STREAM (stream)->width > 0
8826 && CUR_STREAM (stream)->height > 0) {
8827 gint n, d;
8828
8829 /* calculate the pixel aspect ratio using the display and pixel w/h */
8830 n = stream->display_width * CUR_STREAM (stream)->height;
8831 d = stream->display_height * CUR_STREAM (stream)->width;
8832 if (n == d)
8833 n = d = 1;
8834 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
8835 CUR_STREAM (stream)->par_w = n;
8836 CUR_STREAM (stream)->par_h = d;
8837 gst_caps_set_simple (CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
8838 GST_TYPE_FRACTION, CUR_STREAM (stream)->par_w,
8839 CUR_STREAM (stream)->par_h, NULL);
8840 }
8841
8842 if (CUR_STREAM (stream)->interlace_mode > 0) {
8843 if (CUR_STREAM (stream)->interlace_mode == 1) {
8844 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8845 G_TYPE_STRING, "progressive", NULL);
8846 } else if (CUR_STREAM (stream)->interlace_mode == 2) {
8847 gst_caps_set_simple (CUR_STREAM (stream)->caps, "interlace-mode",
8848 G_TYPE_STRING, "interleaved", NULL);
8849 if (CUR_STREAM (stream)->field_order == 9) {
8850 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8851 G_TYPE_STRING, "top-field-first", NULL);
8852 } else if (CUR_STREAM (stream)->field_order == 14) {
8853 gst_caps_set_simple (CUR_STREAM (stream)->caps, "field-order",
8854 G_TYPE_STRING, "bottom-field-first", NULL);
8855 }
8856 }
8857 }
8858
8859 /* Create incomplete colorimetry here if needed */
8860 if (CUR_STREAM (stream)->colorimetry.range ||
8861 CUR_STREAM (stream)->colorimetry.matrix ||
8862 CUR_STREAM (stream)->colorimetry.transfer
8863 || CUR_STREAM (stream)->colorimetry.primaries) {
8864 gchar *colorimetry =
8865 gst_video_colorimetry_to_string (&CUR_STREAM (stream)->colorimetry);
8866 gst_caps_set_simple (CUR_STREAM (stream)->caps, "colorimetry",
8867 G_TYPE_STRING, colorimetry, NULL);
8868 g_free (colorimetry);
8869 }
8870
8871 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
8872 guint par_w = 1, par_h = 1;
8873
8874 if (CUR_STREAM (stream)->par_w > 0 && CUR_STREAM (stream)->par_h > 0) {
8875 par_w = CUR_STREAM (stream)->par_w;
8876 par_h = CUR_STREAM (stream)->par_h;
8877 }
8878
8879 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
8880 CUR_STREAM (stream)->width, CUR_STREAM (stream)->height, par_w,
8881 par_h)) {
8882 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
8883 }
8884
8885 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8886 "multiview-mode", G_TYPE_STRING,
8887 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
8888 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
8889 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
8890 }
8891 }
8892 }
8893
8894 else if (stream->subtype == FOURCC_soun) {
8895 if (CUR_STREAM (stream)->caps) {
8896 CUR_STREAM (stream)->caps =
8897 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8898 if (CUR_STREAM (stream)->rate > 0)
8899 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8900 "rate", G_TYPE_INT, (int) CUR_STREAM (stream)->rate, NULL);
8901 if (CUR_STREAM (stream)->n_channels > 0)
8902 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8903 "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
8904 if (CUR_STREAM (stream)->n_channels > 2) {
8905 /* FIXME: Need to parse the 'chan' atom to get channel layouts
8906 * correctly; this is just the minimum we can do - assume
8907 * we don't actually have any channel positions. */
8908 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8909 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
8910 }
8911 }
8912 }
8913
8914 else if (stream->subtype == FOURCC_clcp && CUR_STREAM (stream)->caps) {
8915 const GstStructure *s;
8916 QtDemuxStream *fps_stream = NULL;
8917 gboolean fps_available = FALSE;
8918
8919 /* CEA608 closed caption tracks are a bit special in that each sample
8920 * can contain CCs for multiple frames, and CCs can be omitted and have to
8921 * be inferred from the duration of the sample then.
8922 *
8923 * As such we take the framerate from the (first) video track here for
8924 * CEA608 as there must be one CC byte pair for every video frame
8925 * according to the spec.
8926 *
8927 * For CEA708 all is fine and there is one sample per frame.
8928 */
8929
8930 s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
8931 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
8932 gint i;
8933
8934 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
8935 QtDemuxStream *tmp = QTDEMUX_NTH_STREAM (qtdemux, i);
8936
8937 if (tmp->subtype == FOURCC_vide) {
8938 fps_stream = tmp;
8939 break;
8940 }
8941 }
8942
8943 if (fps_stream) {
8944 fps_available = gst_qtdemux_guess_framerate (qtdemux, fps_stream);
8945 CUR_STREAM (stream)->fps_n = CUR_STREAM (fps_stream)->fps_n;
8946 CUR_STREAM (stream)->fps_d = CUR_STREAM (fps_stream)->fps_d;
8947 }
8948 } else {
8949 fps_available = gst_qtdemux_guess_framerate (qtdemux, stream);
8950 fps_stream = stream;
8951 }
8952
8953 CUR_STREAM (stream)->caps =
8954 gst_caps_make_writable (CUR_STREAM (stream)->caps);
8955
8956 /* set framerate if calculated framerate is reliable */
8957 if (fps_available) {
8958 gst_caps_set_simple (CUR_STREAM (stream)->caps,
8959 "framerate", GST_TYPE_FRACTION, CUR_STREAM (stream)->fps_n,
8960 CUR_STREAM (stream)->fps_d, NULL);
8961 }
8962 }
8963
8964 if (stream->pad) {
8965 GstCaps *prev_caps = NULL;
8966
8967 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
8968 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
8969 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
8970 gst_pad_set_active (stream->pad, TRUE);
8971
8972 gst_pad_use_fixed_caps (stream->pad);
8973
8974 if (stream->protected) {
8975 if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
8976 GST_ERROR_OBJECT (qtdemux,
8977 "Failed to configure protected stream caps.");
8978 return FALSE;
8979 }
8980 }
8981
8982 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
8983 CUR_STREAM (stream)->caps);
8984 if (stream->new_stream) {
8985 GstEvent *event;
8986 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
8987
8988 event =
8989 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
8990 0);
8991 if (event) {
8992 gst_event_parse_stream_flags (event, &stream_flags);
8993 if (gst_event_parse_group_id (event, &qtdemux->group_id))
8994 qtdemux->have_group_id = TRUE;
8995 else
8996 qtdemux->have_group_id = FALSE;
8997 gst_event_unref (event);
8998 } else if (!qtdemux->have_group_id) {
8999 qtdemux->have_group_id = TRUE;
9000 qtdemux->group_id = gst_util_group_id_next ();
9001 }
9002
9003 stream->new_stream = FALSE;
9004 event = gst_event_new_stream_start (stream->stream_id);
9005 if (qtdemux->have_group_id)
9006 gst_event_set_group_id (event, qtdemux->group_id);
9007 if (stream->disabled)
9008 stream_flags |= GST_STREAM_FLAG_UNSELECT;
9009 if (CUR_STREAM (stream)->sparse) {
9010 stream_flags |= GST_STREAM_FLAG_SPARSE;
9011 } else {
9012 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9013 }
9014 gst_event_set_stream_flags (event, stream_flags);
9015 gst_pad_push_event (stream->pad, event);
9016 }
9017
9018 prev_caps = gst_pad_get_current_caps (stream->pad);
9019
9020 if (CUR_STREAM (stream)->caps) {
9021 if (!prev_caps
9022 || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
9023 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
9024 CUR_STREAM (stream)->caps);
9025 gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
9026 } else {
9027 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9028 }
9029 } else {
9030 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9031 }
9032
9033 if (prev_caps)
9034 gst_caps_unref (prev_caps);
9035 stream->new_caps = FALSE;
9036 }
9037 return TRUE;
9038}
9039
9040static void
9041gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
9042 QtDemuxStream * stream)
9043{
9044 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9045 return;
9046
9047 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9048 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9049 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9050 stream->stsd_entries_length)) {
9051 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9052 (_("This file is invalid and cannot be played.")),
9053 ("New sample description id is out of bounds (%d >= %d)",
9054 stream->stsd_sample_description_id, stream->stsd_entries_length));
9055 } else {
9056 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9057 stream->new_caps = TRUE;
9058 }
9059}
9060
9061static gboolean
9062gst_qtdemux_add_stream (GstQTDemux * qtdemux,
9063 QtDemuxStream * stream, GstTagList * list)
9064{
9065 gboolean ret = TRUE;
9066
9067 if (stream->subtype == FOURCC_vide) {
9068 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9069
9070 stream->pad =
9071 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9072 g_free (name);
9073
9074 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9075 gst_object_unref (stream->pad);
9076 stream->pad = NULL;
9077 ret = FALSE;
9078 goto done;
9079 }
9080
9081 qtdemux->n_video_streams++;
9082 } else if (stream->subtype == FOURCC_soun) {
9083 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9084
9085 stream->pad =
9086 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
9087 g_free (name);
9088 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9089 gst_object_unref (stream->pad);
9090 stream->pad = NULL;
9091 ret = FALSE;
9092 goto done;
9093 }
9094 qtdemux->n_audio_streams++;
9095 } else if (stream->subtype == FOURCC_strm) {
9096 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
9097 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
9098 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
9099 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
9100 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9101
9102 stream->pad =
9103 gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
9104 g_free (name);
9105 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9106 gst_object_unref (stream->pad);
9107 stream->pad = NULL;
9108 ret = FALSE;
9109 goto done;
9110 }
9111 qtdemux->n_sub_streams++;
9112 } else if (CUR_STREAM (stream)->caps) {
9113 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9114
9115 stream->pad =
9116 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
9117 g_free (name);
9118 if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
9119 gst_object_unref (stream->pad);
9120 stream->pad = NULL;
9121 ret = FALSE;
9122 goto done;
9123 }
9124 qtdemux->n_video_streams++;
9125 } else {
9126 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9127 goto done;
9128 }
9129
9130 if (stream->pad) {
9131 GList *l;
9132
9133 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9134 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9135 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9136 GST_OBJECT_LOCK (qtdemux);
9137 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9138 GST_OBJECT_UNLOCK (qtdemux);
9139
9140 if (stream->stream_tags)
9141 gst_tag_list_unref (stream->stream_tags);
9142 stream->stream_tags = list;
9143 list = NULL;
9144 /* global tags go on each pad anyway */
9145 stream->send_global_tags = TRUE;
9146 /* send upstream GST_EVENT_PROTECTION events that were received before
9147 this source pad was created */
9148 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9149 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9150 }
9151done:
9152 if (list)
9153 gst_tag_list_unref (list);
9154 return ret;
9155}
9156
9157/* find next atom with @fourcc starting at @offset */
9158static GstFlowReturn
9159qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
9160 guint64 * length, guint32 fourcc)
9161{
9162 GstFlowReturn ret;
9163 guint32 lfourcc;
9164 GstBuffer *buf;
9165
9166 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9167 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9168
9169 while (TRUE) {
9170 GstMapInfo map;
9171
9172 buf = NULL;
9173 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9174 if (G_UNLIKELY (ret != GST_FLOW_OK))
9175 goto locate_failed;
9176 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9177 /* likely EOF */
9178 ret = GST_FLOW_EOS;
9179 gst_buffer_unref (buf);
9180 goto locate_failed;
9181 }
9182 gst_buffer_map (buf, &map, GST_MAP_READ);
9183 extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
9184 gst_buffer_unmap (buf, &map);
9185 gst_buffer_unref (buf);
9186
9187 if (G_UNLIKELY (*length == 0)) {
9188 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9189 ret = GST_FLOW_ERROR;
9190 goto locate_failed;
9191 }
9192
9193 if (lfourcc == fourcc) {
9194 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9195 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9196 break;
9197 } else {
9198 GST_LOG_OBJECT (qtdemux,
9199 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9200 GST_FOURCC_ARGS (lfourcc), *offset);
9201 if (*offset == G_MAXUINT64)
9202 goto locate_failed;
9203 *offset += *length;
9204 }
9205 }
9206
9207 return GST_FLOW_OK;
9208
9209locate_failed:
9210 {
9211 /* might simply have had last one */
9212 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9213 return ret;
9214 }
9215}
9216
9217/* should only do something in pull mode */
9218/* call with OBJECT lock */
9219static GstFlowReturn
9220qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
9221{
9222 guint64 length, offset;
9223 GstBuffer *buf = NULL;
9224 GstFlowReturn ret = GST_FLOW_OK;
9225 GstFlowReturn res = GST_FLOW_OK;
9226 GstMapInfo map;
9227
9228 offset = qtdemux->moof_offset;
9229 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9230
9231 if (!offset) {
9232 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9233 return GST_FLOW_EOS;
9234 }
9235
9236 /* best not do pull etc with lock held */
9237 GST_OBJECT_UNLOCK (qtdemux);
9238
9239 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9240 if (ret != GST_FLOW_OK)
9241 goto flow_failed;
9242
9243 ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
9244 if (G_UNLIKELY (ret != GST_FLOW_OK))
9245 goto flow_failed;
9246 gst_buffer_map (buf, &map, GST_MAP_READ);
9247 if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
9248 gst_buffer_unmap (buf, &map);
9249 gst_buffer_unref (buf);
9250 buf = NULL;
9251 goto parse_failed;
9252 }
9253
9254 gst_buffer_unmap (buf, &map);
9255 gst_buffer_unref (buf);
9256 buf = NULL;
9257
9258 offset += length;
9259 /* look for next moof */
9260 ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
9261 if (G_UNLIKELY (ret != GST_FLOW_OK))
9262 goto flow_failed;
9263
9264exit:
9265 GST_OBJECT_LOCK (qtdemux);
9266
9267 qtdemux->moof_offset = offset;
9268
9269 return res;
9270
9271parse_failed:
9272 {
9273 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9274 offset = 0;
9275 res = GST_FLOW_ERROR;
9276 goto exit;
9277 }
9278flow_failed:
9279 {
9280 /* maybe upstream temporarily flushing */
9281 if (ret != GST_FLOW_FLUSHING) {
9282 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9283 offset = 0;
9284 } else {
9285 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9286 /* resume at current position next time */
9287 }
9288 res = ret;
9289 goto exit;
9290 }
9291}
9292
9293static void
9294qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
9295{
9296 guint i;
9297 guint32 num_chunks;
9298 gint32 stts_duration;
9299 GstByteWriter stsc, stts, stsz;
9300
9301 /* Each sample has a different size, which we don't support for merging */
9302 if (stream->sample_size == 0) {
9303 GST_DEBUG_OBJECT (qtdemux,
9304 "Not all samples have the same size, not merging");
9305 return;
9306 }
9307
9308 /* The stream has a ctts table, we don't support that */
9309 if (stream->ctts_present) {
9310 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
9311 return;
9312 }
9313
9314 /* If there's a sync sample table also ignore this stream */
9315 if (stream->stps_present || stream->stss_present) {
9316 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
9317 return;
9318 }
9319
9320 /* If chunks are considered samples already ignore this stream */
9321 if (stream->chunks_are_samples) {
9322 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
9323 return;
9324 }
9325
9326 /* Require that all samples have the same duration */
9327 if (stream->n_sample_times > 1) {
9328 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
9329 return;
9330 }
9331
9332 /* Parse the stts to get the sample duration and number of samples */
9333 gst_byte_reader_skip_unchecked (&stream->stts, 4);
9334 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9335
9336 /* Parse the number of chunks from the stco manually because the
9337 * reader is already behind that */
9338 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
9339
9340 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
9341 num_chunks);
9342
9343 /* Now parse stsc, convert chunks into single samples and generate a
9344 * new stsc, stts and stsz from this information */
9345 gst_byte_writer_init (&stsc);
9346 gst_byte_writer_init (&stts);
9347 gst_byte_writer_init (&stsz);
9348
9349 /* Note: we skip fourccs, size, version, flags and other fields of the new
9350 * atoms as the byte readers with them are already behind that position
9351 * anyway and only update the values of those inside the stream directly.
9352 */
9353 stream->n_sample_times = 0;
9354 stream->n_samples = 0;
9355 for (i = 0; i < stream->n_samples_per_chunk; i++) {
9356 guint j;
9357 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
9358
9359 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9360 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9361 sample_description_id =
9362 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9363
9364 if (i == stream->n_samples_per_chunk - 1) {
9365 /* +1 because first_chunk is 1-based */
9366 last_chunk = num_chunks + 1;
9367 } else {
9368 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9369 }
9370
9371 GST_DEBUG_OBJECT (qtdemux,
9372 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
9373 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
9374
9375 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
9376 /* One sample in this chunk */
9377 gst_byte_writer_put_uint32_be (&stsc, 1);
9378 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
9379
9380 /* For each chunk write a stts and stsz entry now */
9381 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
9382 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
9383 for (j = first_chunk; j < last_chunk; j++) {
9384 gst_byte_writer_put_uint32_be (&stsz,
9385 stream->sample_size * samples_per_chunk);
9386 }
9387
9388 stream->n_sample_times += 1;
9389 stream->n_samples += last_chunk - first_chunk;
9390 }
9391
9392 g_assert_cmpint (stream->n_samples, ==, num_chunks);
9393
9394 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
9395 stream->n_samples, stream->n_sample_times);
9396
9397 /* We don't have a fixed sample size anymore */
9398 stream->sample_size = 0;
9399
9400 /* Free old data for the atoms */
9401 g_free ((gpointer) stream->stsz.data);
9402 stream->stsz.data = NULL;
9403 g_free ((gpointer) stream->stsc.data);
9404 stream->stsc.data = NULL;
9405 g_free ((gpointer) stream->stts.data);
9406 stream->stts.data = NULL;
9407
9408 /* Store new data and replace byte readers */
9409 stream->stsz.size = gst_byte_writer_get_size (&stsz);
9410 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
9411 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
9412 stream->stts.size = gst_byte_writer_get_size (&stts);
9413 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
9414 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
9415 stream->stsc.size = gst_byte_writer_get_size (&stsc);
9416 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
9417 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
9418}
9419
9420/* initialise bytereaders for stbl sub-atoms */
9421static gboolean
9422qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
9423{
9424 stream->stbl_index = -1; /* no samples have yet been parsed */
9425 stream->sample_index = -1;
9426
9427 /* time-to-sample atom */
9428 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
9429 goto corrupt_file;
9430
9431 /* copy atom data into a new buffer for later use */
9432 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
9433
9434 /* skip version + flags */
9435 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9436 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9437 goto corrupt_file;
9438 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9439
9440 /* make sure there's enough data */
9441 if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
9442 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9443 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9444 stream->n_sample_times);
9445 if (!stream->n_sample_times)
9446 goto corrupt_file;
9447 }
9448
9449 /* sync sample atom */
9450 stream->stps_present = FALSE;
9451 if ((stream->stss_present =
9452 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
9453 &stream->stss) ? TRUE : FALSE) == TRUE) {
9454 /* copy atom data into a new buffer for later use */
9455 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
9456
9457 /* skip version + flags */
9458 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9459 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9460 goto corrupt_file;
9461
9462 if (stream->n_sample_syncs) {
9463 /* make sure there's enough data */
9464 if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
9465 goto corrupt_file;
9466 }
9467
9468 /* partial sync sample atom */
9469 if ((stream->stps_present =
9470 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
9471 &stream->stps) ? TRUE : FALSE) == TRUE) {
9472 /* copy atom data into a new buffer for later use */
9473 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
9474
9475 /* skip version + flags */
9476 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9477 !gst_byte_reader_get_uint32_be (&stream->stps,
9478 &stream->n_sample_partial_syncs))
9479 goto corrupt_file;
9480
9481 /* if there are no entries, the stss table contains the real
9482 * sync samples */
9483 if (stream->n_sample_partial_syncs) {
9484 /* make sure there's enough data */
9485 if (!qt_atom_parser_has_chunks (&stream->stps,
9486 stream->n_sample_partial_syncs, 4))
9487 goto corrupt_file;
9488 }
9489 }
9490 }
9491
9492 /* sample size */
9493 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
9494 goto no_samples;
9495
9496 /* copy atom data into a new buffer for later use */
9497 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
9498
9499 /* skip version + flags */
9500 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9501 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9502 goto corrupt_file;
9503
9504 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9505 goto corrupt_file;
9506
9507 if (!stream->n_samples)
9508 goto no_samples;
9509
9510 /* sample-to-chunk atom */
9511 if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
9512 goto corrupt_file;
9513
9514 /* copy atom data into a new buffer for later use */
9515 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
9516
9517 /* skip version + flags */
9518 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9519 !gst_byte_reader_get_uint32_be (&stream->stsc,
9520 &stream->n_samples_per_chunk))
9521 goto corrupt_file;
9522
9523 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9524 stream->n_samples_per_chunk);
9525
9526 /* make sure there's enough data */
9527 if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
9528 12))
9529 goto corrupt_file;
9530
9531
9532 /* chunk offset */
9533 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
9534 stream->co_size = sizeof (guint32);
9535 else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
9536 &stream->stco))
9537 stream->co_size = sizeof (guint64);
9538 else
9539 goto corrupt_file;
9540
9541 /* copy atom data into a new buffer for later use */
9542 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
9543
9544 /* skip version + flags */
9545 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9546 goto corrupt_file;
9547
9548 /* chunks_are_samples == TRUE means treat chunks as samples */
9549 stream->chunks_are_samples = stream->sample_size
9550 && !CUR_STREAM (stream)->sampled;
9551 if (stream->chunks_are_samples) {
9552 /* treat chunks as samples */
9553 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9554 goto corrupt_file;
9555 } else {
9556 /* skip number of entries */
9557 if (!gst_byte_reader_skip (&stream->stco, 4))
9558 goto corrupt_file;
9559
9560 /* make sure there are enough data in the stsz atom */
9561 if (!stream->sample_size) {
9562 /* different sizes for each sample */
9563 if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
9564 goto corrupt_file;
9565 }
9566 }
9567
9568 /* composition time-to-sample */
9569 if ((stream->ctts_present =
9570 ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
9571 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9572 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9573 guint8 ctts_version;
9574 gboolean checked_ctts = FALSE;
9575
9576 /* copy atom data into a new buffer for later use */
9577 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
9578
9579 /* version 1 has signed offsets */
9580 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
9581 goto corrupt_file;
9582
9583 /* flags */
9584 if (!gst_byte_reader_skip (&stream->ctts, 3)
9585 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9586 &stream->n_composition_times))
9587 goto corrupt_file;
9588
9589 /* make sure there's enough data */
9590 if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
9591 4 + 4))
9592 goto corrupt_file;
9593
9594 /* This is optional, if missing we iterate the ctts */
9595 if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
9596 guint8 cslg_version;
9597
9598 /* cslg version 1 has 64 bit fields */
9599 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
9600 goto corrupt_file;
9601
9602 /* skip flags */
9603 if (!gst_byte_reader_skip (&cslg, 3))
9604 goto corrupt_file;
9605
9606 if (cslg_version == 0) {
9607 gint32 composition_to_dts_shift;
9608
9609 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
9610 goto corrupt_file;
9611
9612 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9613 } else {
9614 gint64 composition_to_dts_shift;
9615
9616 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
9617 goto corrupt_file;
9618
9619 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9620 }
9621 } else {
9622 gint32 cslg_least = 0;
9623 guint num_entries, pos;
9624 gint i;
9625
9626 pos = gst_byte_reader_get_pos (&stream->ctts);
9627 num_entries = stream->n_composition_times;
9628
9629 checked_ctts = TRUE;
9630
9631 stream->cslg_shift = 0;
9632
9633 for (i = 0; i < num_entries; i++) {
9634 gint32 offset;
9635
9636 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9637 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9638 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9639 * slightly inaccurate PTS could be more usable than corrupted one */
9640 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
zengliang.li055f1112024-05-16 12:50:57 +00009641 && ABS (offset) * 2 > stream->duration)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009642 GST_WARNING_OBJECT (qtdemux,
9643 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9644 " larger than duration %" G_GUINT64_FORMAT, offset,
9645 stream->duration);
9646
9647 stream->cslg_shift = 0;
9648 stream->ctts_present = FALSE;
9649 goto done;
9650 }
9651
zengliang.licdc43772024-05-17 03:09:29 +00009652
zengliang.li5f31ef42024-05-16 08:27:38 +00009653 /* Don't consider "no decode samples" with offset G_MININT32
9654 * for the DTS/PTS shift */
zengliang.licdc43772024-05-17 03:09:29 +00009655 //if (offset != G_MININT32 && offset < cslg_least)
9656 //cslg_least = offset;
9657
zengliang.li5f31ef42024-05-16 08:27:38 +00009658 }
9659
9660 if (cslg_least < 0)
9661 stream->cslg_shift = -cslg_least;
9662 else
9663 stream->cslg_shift = 0;
9664
9665 /* reset the reader so we can generate sample table */
9666 gst_byte_reader_set_pos (&stream->ctts, pos);
9667 }
9668
9669 /* Check if ctts values are looking reasonable if that didn't happen above */
9670 if (!checked_ctts) {
9671 guint num_entries, pos;
9672 gint i;
9673
9674 pos = gst_byte_reader_get_pos (&stream->ctts);
9675 num_entries = stream->n_composition_times;
9676
9677 for (i = 0; i < num_entries; i++) {
9678 gint32 offset;
9679
9680 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9681 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9682 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9683 * slightly inaccurate PTS could be more usable than corrupted one */
9684 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
9685 && ABS (offset) / 2 > stream->duration)) {
9686 GST_WARNING_OBJECT (qtdemux,
9687 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9688 " larger than duration %" G_GUINT64_FORMAT, offset,
9689 stream->duration);
9690
9691 stream->cslg_shift = 0;
9692 stream->ctts_present = FALSE;
9693 goto done;
9694 }
9695 }
9696
9697 /* reset the reader so we can generate sample table */
9698 gst_byte_reader_set_pos (&stream->ctts, pos);
9699 }
9700 } else {
9701 /* Ensure the cslg_shift value is consistent so we can use it
9702 * unconditionally to produce TS and Segment */
9703 stream->cslg_shift = 0;
9704 }
9705
9706 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
9707 stream->cslg_shift);
9708
9709 /* For raw audio streams especially we might want to merge the samples
9710 * to not output one audio sample per buffer. We're doing this here
9711 * before allocating the sample tables so that from this point onwards
9712 * the number of container samples are static */
9713 if (stream->min_buffer_size > 0) {
9714 qtdemux_merge_sample_table (qtdemux, stream);
9715 }
9716
9717done:
9718 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
9719 stream->n_samples, (guint) sizeof (QtDemuxSample),
9720 stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
9721
9722 if (stream->n_samples >=
9723 QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
9724 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9725 "be larger than %uMB (broken file?)", stream->n_samples,
9726 QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
9727 return FALSE;
9728 }
9729
9730 g_assert (stream->samples == NULL);
9731 stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
9732 if (!stream->samples) {
9733 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9734 stream->n_samples);
9735 return FALSE;
9736 }
9737
9738 return TRUE;
9739
9740corrupt_file:
9741 {
9742 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9743 (_("This file is corrupt and cannot be played.")), (NULL));
9744 return FALSE;
9745 }
9746no_samples:
9747 {
9748 gst_qtdemux_stbl_free (stream);
9749 if (!qtdemux->fragmented) {
9750 /* not quite good */
9751 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9752 return FALSE;
9753 } else {
9754 /* may pick up samples elsewhere */
9755 return TRUE;
9756 }
9757 }
9758}
9759
9760/* collect samples from the next sample to be parsed up to sample @n for @stream
9761 * by reading the info from @stbl
9762 *
9763 * This code can be executed from both the streaming thread and the seeking
9764 * thread so it takes the object lock to protect itself
9765 */
9766static gboolean
9767qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
9768{
9769 gint i, j, k;
9770 QtDemuxSample *samples, *first, *cur, *last;
9771 guint32 n_samples_per_chunk;
9772 guint32 n_samples;
9773
9774 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9775 GST_FOURCC_FORMAT ", pad %s",
9776 GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc),
9777 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
9778
9779 n_samples = stream->n_samples;
9780
9781 if (n >= n_samples)
9782 goto out_of_samples;
9783
9784 GST_OBJECT_LOCK (qtdemux);
9785 if (n <= stream->stbl_index)
9786 goto already_parsed;
9787
9788 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
9789
9790 if (!stream->stsz.data) {
9791 /* so we already parsed and passed all the moov samples;
9792 * onto fragmented ones */
9793 g_assert (qtdemux->fragmented);
9794 goto done;
9795 }
9796
9797 /* pointer to the sample table */
9798 samples = stream->samples;
9799
9800 /* starts from -1, moves to the next sample index to parse */
9801 stream->stbl_index++;
9802
9803 /* keep track of the first and last sample to fill */
9804 first = &samples[stream->stbl_index];
9805 last = &samples[n];
9806
9807 if (!stream->chunks_are_samples) {
9808 /* set the sample sizes */
9809 if (stream->sample_size == 0) {
9810 /* different sizes for each sample */
9811 for (cur = first; cur <= last; cur++) {
9812 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
9813 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
9814 (guint) (cur - samples), cur->size);
9815 }
9816 } else {
9817 /* samples have the same size */
9818 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
9819 for (cur = first; cur <= last; cur++)
9820 cur->size = stream->sample_size;
9821 }
9822 }
9823
9824 n_samples_per_chunk = stream->n_samples_per_chunk;
9825 cur = first;
9826
9827 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
9828 guint32 last_chunk;
9829
9830 if (stream->stsc_chunk_index >= stream->last_chunk
9831 || stream->stsc_chunk_index < stream->first_chunk) {
9832 stream->first_chunk =
9833 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9834 stream->samples_per_chunk =
9835 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9836 /* starts from 1 */
9837 stream->stsd_sample_description_id =
9838 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
9839
9840 /* chunk numbers are counted from 1 it seems */
9841 if (G_UNLIKELY (stream->first_chunk == 0))
9842 goto corrupt_file;
9843
9844 --stream->first_chunk;
9845
9846 /* the last chunk of each entry is calculated by taking the first chunk
9847 * of the next entry; except if there is no next, where we fake it with
9848 * INT_MAX */
9849 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
9850 stream->last_chunk = G_MAXUINT32;
9851 } else {
9852 stream->last_chunk =
9853 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9854 if (G_UNLIKELY (stream->last_chunk == 0))
9855 goto corrupt_file;
9856
9857 --stream->last_chunk;
9858 }
9859
9860 GST_LOG_OBJECT (qtdemux,
9861 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
9862 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
9863 stream->samples_per_chunk, stream->stsd_sample_description_id);
9864
9865 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
9866 goto corrupt_file;
9867
9868 if (stream->last_chunk != G_MAXUINT32) {
9869 if (!qt_atom_parser_peek_sub (&stream->stco,
9870 stream->first_chunk * stream->co_size,
9871 (stream->last_chunk - stream->first_chunk) * stream->co_size,
9872 &stream->co_chunk))
9873 goto corrupt_file;
9874
9875 } else {
9876 stream->co_chunk = stream->stco;
9877 if (!gst_byte_reader_skip (&stream->co_chunk,
9878 stream->first_chunk * stream->co_size))
9879 goto corrupt_file;
9880 }
9881
9882 stream->stsc_chunk_index = stream->first_chunk;
9883 }
9884
9885 last_chunk = stream->last_chunk;
9886
9887 if (stream->chunks_are_samples) {
9888 cur = &samples[stream->stsc_chunk_index];
9889
9890 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9891 if (j > n) {
9892 /* save state */
9893 stream->stsc_chunk_index = j;
9894 goto done;
9895 }
9896
9897 cur->offset =
9898 qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
9899 stream->co_size);
9900
9901 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
9902 "%" G_GUINT64_FORMAT, j, cur->offset);
9903
9904 if (CUR_STREAM (stream)->samples_per_frame > 0 &&
9905 CUR_STREAM (stream)->bytes_per_frame > 0) {
9906 cur->size =
9907 (stream->samples_per_chunk * CUR_STREAM (stream)->n_channels) /
9908 CUR_STREAM (stream)->samples_per_frame *
9909 CUR_STREAM (stream)->bytes_per_frame;
9910 } else {
9911 cur->size = stream->samples_per_chunk;
9912 }
9913
9914 GST_DEBUG_OBJECT (qtdemux,
9915 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
9916 j, GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream,
9917 stream->stco_sample_index)), cur->size);
9918
9919 cur->timestamp = stream->stco_sample_index;
9920 cur->duration = stream->samples_per_chunk;
9921 cur->keyframe = TRUE;
9922 cur++;
9923
9924 stream->stco_sample_index += stream->samples_per_chunk;
9925 }
9926 stream->stsc_chunk_index = j;
9927 } else {
9928 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
9929 guint32 samples_per_chunk;
9930 guint64 chunk_offset;
9931
9932 if (!stream->stsc_sample_index
9933 && !qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
9934 &stream->chunk_offset))
9935 goto corrupt_file;
9936
9937 samples_per_chunk = stream->samples_per_chunk;
9938 chunk_offset = stream->chunk_offset;
9939
9940 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
9941 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
9942 G_GUINT64_FORMAT " and size %d",
9943 (guint) (cur - samples), chunk_offset, cur->size);
9944
9945 cur->offset = chunk_offset;
9946 chunk_offset += cur->size;
9947 cur++;
9948
9949 if (G_UNLIKELY (cur > last)) {
9950 /* save state */
9951 stream->stsc_sample_index = k + 1;
9952 stream->chunk_offset = chunk_offset;
9953 stream->stsc_chunk_index = j;
9954 goto done2;
9955 }
9956 }
9957 stream->stsc_sample_index = 0;
9958 }
9959 stream->stsc_chunk_index = j;
9960 }
9961 stream->stsc_index++;
9962 }
9963
9964 if (stream->chunks_are_samples)
9965 goto ctts;
9966done2:
9967 {
9968 guint32 n_sample_times;
9969
9970 n_sample_times = stream->n_sample_times;
9971 cur = first;
9972
9973 for (i = stream->stts_index; i < n_sample_times; i++) {
9974 guint32 stts_samples;
9975 gint32 stts_duration;
9976 gint64 stts_time;
9977
9978 if (stream->stts_sample_index >= stream->stts_samples
9979 || !stream->stts_sample_index) {
9980
9981 stream->stts_samples =
9982 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9983 stream->stts_duration =
9984 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9985
9986 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
9987 i, stream->stts_samples, stream->stts_duration);
9988
9989 stream->stts_sample_index = 0;
9990 }
9991
9992 stts_samples = stream->stts_samples;
9993 stts_duration = stream->stts_duration;
9994 stts_time = stream->stts_time;
9995
9996 for (j = stream->stts_sample_index; j < stts_samples; j++) {
9997 GST_DEBUG_OBJECT (qtdemux,
9998 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
9999 (guint) (cur - samples), j,
10000 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
10001
10002 cur->timestamp = stts_time;
10003 cur->duration = stts_duration;
10004
10005 /* avoid 32-bit wrap-around,
10006 * but still mind possible 'negative' duration */
10007 stts_time += (gint64) stts_duration;
10008 cur++;
10009
10010 if (G_UNLIKELY (cur > last)) {
10011 /* save values */
10012 stream->stts_time = stts_time;
10013 stream->stts_sample_index = j + 1;
10014 if (stream->stts_sample_index >= stream->stts_samples)
10015 stream->stts_index++;
10016 goto done3;
10017 }
10018 }
10019 stream->stts_sample_index = 0;
10020 stream->stts_time = stts_time;
10021 stream->stts_index++;
10022 }
10023 /* fill up empty timestamps with the last timestamp, this can happen when
10024 * the last samples do not decode and so we don't have timestamps for them.
10025 * We however look at the last timestamp to estimate the track length so we
10026 * need something in here. */
10027 for (; cur < last; cur++) {
10028 GST_DEBUG_OBJECT (qtdemux,
10029 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10030 (guint) (cur - samples),
10031 GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
10032 cur->timestamp = stream->stts_time;
10033 cur->duration = -1;
10034 }
10035 }
10036done3:
10037 {
10038 /* sample sync, can be NULL */
10039 if (stream->stss_present == TRUE) {
10040 guint32 n_sample_syncs;
10041
10042 n_sample_syncs = stream->n_sample_syncs;
10043
10044 if (!n_sample_syncs) {
10045 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10046 stream->all_keyframe = TRUE;
10047 } else {
10048 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10049 /* note that the first sample is index 1, not 0 */
10050 guint32 index;
10051
10052 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10053
10054 if (G_LIKELY (index > 0 && index <= n_samples)) {
10055 index -= 1;
10056 samples[index].keyframe = TRUE;
10057 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10058 /* and exit if we have enough samples */
10059 if (G_UNLIKELY (index >= n)) {
10060 i++;
10061 break;
10062 }
10063 }
10064 }
10065 /* save state */
10066 stream->stss_index = i;
10067 }
10068
10069 /* stps marks partial sync frames like open GOP I-Frames */
10070 if (stream->stps_present == TRUE) {
10071 guint32 n_sample_partial_syncs;
10072
10073 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10074
10075 /* if there are no entries, the stss table contains the real
10076 * sync samples */
10077 if (n_sample_partial_syncs) {
10078 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10079 /* note that the first sample is index 1, not 0 */
10080 guint32 index;
10081
10082 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10083
10084 if (G_LIKELY (index > 0 && index <= n_samples)) {
10085 index -= 1;
10086 samples[index].keyframe = TRUE;
10087 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10088 /* and exit if we have enough samples */
10089 if (G_UNLIKELY (index >= n)) {
10090 i++;
10091 break;
10092 }
10093 }
10094 }
10095 /* save state */
10096 stream->stps_index = i;
10097 }
10098 }
10099 } else {
10100 /* no stss, all samples are keyframes */
10101 stream->all_keyframe = TRUE;
10102 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10103 }
10104 }
10105
10106ctts:
10107 /* composition time to sample */
10108 if (stream->ctts_present == TRUE) {
10109 guint32 n_composition_times;
10110 guint32 ctts_count;
10111 gint32 ctts_soffset;
10112
10113 /* Fill in the pts_offsets */
10114 cur = first;
10115 n_composition_times = stream->n_composition_times;
10116
10117 for (i = stream->ctts_index; i < n_composition_times; i++) {
10118 if (stream->ctts_sample_index >= stream->ctts_count
10119 || !stream->ctts_sample_index) {
10120 stream->ctts_count =
10121 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10122 stream->ctts_soffset =
10123 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10124 stream->ctts_sample_index = 0;
10125 }
10126
10127 ctts_count = stream->ctts_count;
10128 ctts_soffset = stream->ctts_soffset;
10129
10130 /* FIXME: Set offset to 0 for "no decode samples". This needs
10131 * to be handled in a codec specific manner ideally. */
10132 if (ctts_soffset == G_MININT32)
10133 ctts_soffset = 0;
10134
10135 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
zengliang.li4681ee42024-05-16 12:25:30 +000010136 if (stream->elst_media_time != G_MAXUINT64 && FOURCC_vide == stream->subtype)
10137 {
10138 cur->pts_offset = ctts_soffset - stream->elst_media_time;
10139 GST_DEBUG_OBJECT (qtdemux, "elst_media_time: %lld, new pts_offset: %lld", stream->elst_media_time, cur->pts_offset);
10140 } else {
10141 cur->pts_offset = ctts_soffset;
10142 }
zengliang.li5f31ef42024-05-16 08:27:38 +000010143 cur++;
10144
10145 if (G_UNLIKELY (cur > last)) {
10146 /* save state */
10147 stream->ctts_sample_index = j + 1;
10148 goto done;
10149 }
10150 }
10151 stream->ctts_sample_index = 0;
10152 stream->ctts_index++;
10153 }
10154 }
10155done:
10156 stream->stbl_index = n;
10157 /* if index has been completely parsed, free data that is no-longer needed */
10158 if (n + 1 == stream->n_samples) {
10159 gst_qtdemux_stbl_free (stream);
10160 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10161 if (qtdemux->pullbased) {
10162 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10163 while (n + 1 == stream->n_samples)
10164 if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
10165 break;
10166 }
10167 }
10168 GST_OBJECT_UNLOCK (qtdemux);
10169
10170 return TRUE;
10171
10172 /* SUCCESS */
10173already_parsed:
10174 {
10175 GST_LOG_OBJECT (qtdemux,
10176 "Tried to parse up to sample %u but this sample has already been parsed",
10177 n);
10178 /* if fragmented, there may be more */
10179 if (qtdemux->fragmented && n == stream->stbl_index)
10180 goto done;
10181 GST_OBJECT_UNLOCK (qtdemux);
10182 return TRUE;
10183 }
10184 /* ERRORS */
10185out_of_samples:
10186 {
10187 GST_LOG_OBJECT (qtdemux,
10188 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10189 stream->n_samples);
10190 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10191 (_("This file is corrupt and cannot be played.")), (NULL));
10192 return FALSE;
10193 }
10194corrupt_file:
10195 {
10196 GST_OBJECT_UNLOCK (qtdemux);
10197 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10198 (_("This file is corrupt and cannot be played.")), (NULL));
10199 return FALSE;
10200 }
10201}
10202
10203/* collect all segment info for @stream.
10204 */
10205static gboolean
10206qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
10207 GNode * trak)
10208{
10209 GNode *edts;
10210 /* accept edts if they contain gaps at start and there is only
10211 * one media segment */
10212 gboolean allow_pushbased_edts = TRUE;
10213 gint media_segments_count = 0;
10214
10215 /* parse and prepare segment info from the edit list */
10216 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10217 stream->n_segments = 0;
10218 stream->segments = NULL;
zengliang.li4681ee42024-05-16 12:25:30 +000010219 stream->elst_media_time = G_MAXUINT64;
zengliang.li5f31ef42024-05-16 08:27:38 +000010220 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
10221 GNode *elst;
10222 guint n_segments;
10223 guint segment_number, entry_size;
10224 guint64 time;
10225 GstClockTime stime;
10226 const guint8 *buffer;
10227 guint8 version;
10228 guint32 size;
10229
10230 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
10231 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
10232 goto done;
10233
10234 buffer = elst->data;
10235
10236 size = QT_UINT32 (buffer);
10237 /* version, flags, n_segments */
10238 if (size < 16) {
10239 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10240 goto done;
10241 }
10242 version = QT_UINT8 (buffer + 8);
10243 entry_size = (version == 1) ? 20 : 12;
10244
10245 n_segments = QT_UINT32 (buffer + 12);
10246
10247 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10248 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10249 goto done;
10250 }
10251
10252 /* we might allocate a bit too much, at least allocate 1 segment */
10253 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));
10254
10255 /* segments always start from 0 */
10256 time = 0;
10257 stime = 0;
10258 buffer += 16;
10259 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10260 guint64 duration;
10261 guint64 media_time;
10262 gboolean empty_edit = FALSE;
10263 QtDemuxSegment *segment;
10264 guint32 rate_int;
10265 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10266
10267 if (version == 1) {
10268 media_time = QT_UINT64 (buffer + 8);
10269 duration = QT_UINT64 (buffer);
10270 if (media_time == G_MAXUINT64)
10271 empty_edit = TRUE;
10272 } else {
10273 media_time = QT_UINT32 (buffer + 4);
10274 duration = QT_UINT32 (buffer);
10275 if (media_time == G_MAXUINT32)
10276 empty_edit = TRUE;
10277 }
10278
10279 if (!empty_edit)
zengliang.li4681ee42024-05-16 12:25:30 +000010280 {
10281 stream->elst_media_time = MIN(media_time, stream->elst_media_time);
zengliang.li5f31ef42024-05-16 08:27:38 +000010282 media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
zengliang.li4681ee42024-05-16 12:25:30 +000010283 }
zengliang.li5f31ef42024-05-16 08:27:38 +000010284
10285 segment = &stream->segments[segment_number];
10286
10287 /* time and duration expressed in global timescale */
10288 segment->time = stime;
10289 if (duration != 0 || empty_edit) {
10290 /* edge case: empty edits with duration=zero are treated here.
10291 * (files should not have these anyway). */
10292
10293 /* add non scaled values so we don't cause roundoff errors */
10294 time += duration;
10295 stime = QTTIME_TO_GSTTIME (qtdemux, time);
10296 segment->duration = stime - segment->time;
10297 } else {
10298 /* zero duration does not imply media_start == media_stop
10299 * but, only specify media_start. The edit ends with the track. */
10300 stime = segment->duration = GST_CLOCK_TIME_NONE;
10301 /* Don't allow more edits after this one. */
10302 n_segments = segment_number + 1;
10303 }
10304 segment->stop_time = stime;
10305
10306 segment->trak_media_start = media_time;
10307 /* media_time expressed in stream timescale */
10308 if (!empty_edit) {
10309 segment->media_start = media_start;
10310 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10311 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10312 media_segments_count++;
10313 } else {
10314 segment->media_start = GST_CLOCK_TIME_NONE;
10315 segment->media_stop = GST_CLOCK_TIME_NONE;
10316 }
10317 rate_int = QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
10318
10319 if (rate_int <= 1) {
10320 /* 0 is not allowed, some programs write 1 instead of the floating point
10321 * value */
10322 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10323 rate_int);
10324 segment->rate = 1;
10325 } else {
10326 segment->rate = rate_int / 65536.0;
10327 }
10328
10329 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10330 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10331 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10332 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10333 segment_number, GST_TIME_ARGS (segment->time),
10334 GST_TIME_ARGS (segment->duration),
10335 GST_TIME_ARGS (segment->media_start), media_time,
10336 GST_TIME_ARGS (segment->media_stop),
10337 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10338 stream->timescale);
10339 if (segment->stop_time > qtdemux->segment.stop &&
10340 !qtdemux->upstream_format_is_time) {
10341 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10342 " extends to %" GST_TIME_FORMAT
10343 " past the end of the declared movie duration %" GST_TIME_FORMAT
10344 " movie segment will be extended", segment_number,
10345 GST_TIME_ARGS (segment->stop_time),
10346 GST_TIME_ARGS (qtdemux->segment.stop));
10347 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10348 }
10349
10350 buffer += entry_size;
10351 }
10352 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10353 stream->n_segments = n_segments;
10354 if (media_segments_count != 1)
10355 allow_pushbased_edts = FALSE;
10356 }
10357done:
10358
10359 /* push based does not handle segments, so act accordingly here,
10360 * and warn if applicable */
10361 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10362 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10363 /* remove and use default one below, we stream like it anyway */
10364 g_free (stream->segments);
10365 stream->segments = NULL;
10366 stream->n_segments = 0;
10367 }
10368
10369 /* no segments, create one to play the complete trak */
10370 if (stream->n_segments == 0) {
10371 GstClockTime stream_duration =
10372 QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
10373
10374 if (stream->segments == NULL)
10375 stream->segments = g_new (QtDemuxSegment, 1);
10376
10377 /* represent unknown our way */
10378 if (stream_duration == 0)
10379 stream_duration = GST_CLOCK_TIME_NONE;
10380
10381 stream->segments[0].time = 0;
10382 stream->segments[0].stop_time = stream_duration;
10383 stream->segments[0].duration = stream_duration;
10384 stream->segments[0].media_start = 0;
10385 stream->segments[0].media_stop = stream_duration;
10386 stream->segments[0].rate = 1.0;
10387 stream->segments[0].trak_media_start = 0;
10388
10389 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10390 GST_TIME_ARGS (stream_duration));
10391 stream->n_segments = 1;
10392 stream->dummy_segment = TRUE;
10393 }
10394 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10395
10396 return TRUE;
10397}
10398
10399/*
10400 * Parses the stsd atom of a svq3 trak looking for
10401 * the SMI and gama atoms.
10402 */
10403static void
10404qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
10405 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10406{
10407 const guint8 *_gamma = NULL;
10408 GstBuffer *_seqh = NULL;
10409 const guint8 *stsd_data = stsd_entry_data;
10410 guint32 length = QT_UINT32 (stsd_data);
10411 guint16 version;
10412
10413 if (length < 32) {
10414 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10415 goto end;
10416 }
10417
10418 stsd_data += 16;
10419 length -= 16;
10420 version = QT_UINT16 (stsd_data);
10421 if (version == 3) {
10422 if (length >= 70) {
10423 length -= 70;
10424 stsd_data += 70;
10425 while (length > 8) {
10426 guint32 fourcc, size;
10427 const guint8 *data;
10428 size = QT_UINT32 (stsd_data);
10429 fourcc = QT_FOURCC (stsd_data + 4);
10430 data = stsd_data + 8;
10431
10432 if (size == 0) {
10433 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10434 "svq3 atom parsing");
10435 goto end;
10436 }
10437
10438 switch (fourcc) {
10439 case FOURCC_gama:{
10440 if (size == 12) {
10441 _gamma = data;
10442 } else {
10443 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10444 " for gama atom, expected 12", size);
10445 }
10446 break;
10447 }
10448 case FOURCC_SMI_:{
10449 if (size > 16 && QT_FOURCC (data) == FOURCC_SEQH) {
10450 guint32 seqh_size;
10451 if (_seqh != NULL) {
10452 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10453 " found, ignoring");
10454 } else {
10455 seqh_size = QT_UINT32 (data + 4);
10456 if (seqh_size > 0) {
10457 _seqh = gst_buffer_new_and_alloc (seqh_size);
10458 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10459 }
10460 }
10461 }
10462 break;
10463 }
10464 default:{
10465 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10466 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10467 }
10468 }
10469
10470 if (size <= length) {
10471 length -= size;
10472 stsd_data += size;
10473 }
10474 }
10475 } else {
10476 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10477 }
10478 } else {
10479 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10480 G_GUINT16_FORMAT, version);
10481 goto end;
10482 }
10483
10484end:
10485 if (gamma) {
10486 *gamma = _gamma;
10487 }
10488 if (seqh) {
10489 *seqh = _seqh;
10490 } else if (_seqh) {
10491 gst_buffer_unref (_seqh);
10492 }
10493}
10494
10495static gchar *
10496qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
10497{
10498 GNode *dinf;
10499 GstByteReader dref;
10500 gchar *uri = NULL;
10501
10502 /*
10503 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10504 * atom that might contain a 'data' atom with the rtsp uri.
10505 * This case was reported in bug #597497, some info about
10506 * the hndl atom can be found in TN1195
10507 */
10508 dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
10509 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10510
10511 if (dinf) {
10512 guint32 dref_num_entries = 0;
10513 if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
10514 gst_byte_reader_skip (&dref, 4) &&
10515 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10516 gint i;
10517
10518 /* search dref entries for hndl atom */
10519 for (i = 0; i < dref_num_entries; i++) {
10520 guint32 size = 0, type;
10521 guint8 string_len = 0;
10522 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
10523 qt_atom_parser_get_fourcc (&dref, &type)) {
10524 if (type == FOURCC_hndl) {
10525 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10526
10527 /* skip data reference handle bytes and the
10528 * following pascal string and some extra 4
10529 * bytes I have no idea what are */
10530 if (!gst_byte_reader_skip (&dref, 4) ||
10531 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10532 !gst_byte_reader_skip (&dref, string_len + 4)) {
10533 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10534 break;
10535 }
10536
10537 /* iterate over the atoms to find the data atom */
10538 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10539 guint32 atom_size;
10540 guint32 atom_type;
10541
10542 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
10543 qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10544 if (atom_type == FOURCC_data) {
10545 const guint8 *uri_aux = NULL;
10546
10547 /* found the data atom that might contain the rtsp uri */
10548 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10549 "hndl atom, interpreting it as an URI");
10550 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10551 &uri_aux)) {
10552 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10553 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10554 else
10555 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10556 "didn't contain a rtsp address");
10557 } else {
10558 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10559 "atom contents");
10560 }
10561 break;
10562 }
10563 /* skipping to the next entry */
10564 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10565 break;
10566 } else {
10567 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10568 "atom header");
10569 break;
10570 }
10571 }
10572 break;
10573 }
10574 /* skip to the next entry */
10575 if (!gst_byte_reader_skip (&dref, size - 8))
10576 break;
10577 } else {
10578 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10579 }
10580 }
10581 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10582 }
10583 }
10584 return uri;
10585}
10586
10587#define AMR_NB_ALL_MODES 0x81ff
10588#define AMR_WB_ALL_MODES 0x83ff
10589static guint
10590qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
10591{
10592 /* The 'damr' atom is of the form:
10593 *
10594 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10595 * 32 b 8 b 16 b 8 b 8 b
10596 *
10597 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10598 * represents the highest mode used in the stream (and thus the maximum
10599 * bitrate), with a couple of special cases as seen below.
10600 */
10601
10602 /* Map of frame type ID -> bitrate */
10603 static const guint nb_bitrates[] = {
10604 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10605 };
10606 static const guint wb_bitrates[] = {
10607 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10608 };
10609 GstMapInfo map;
10610 gsize max_mode;
10611 guint16 mode_set;
10612
10613 gst_buffer_map (buf, &map, GST_MAP_READ);
10614
10615 if (map.size != 0x11) {
10616 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10617 goto bad_data;
10618 }
10619
10620 if (QT_FOURCC (map.data + 4) != FOURCC_damr) {
10621 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
10622 GST_FOURCC_ARGS (QT_UINT32 (map.data + 4)));
10623 goto bad_data;
10624 }
10625
10626 mode_set = QT_UINT16 (map.data + 13);
10627
10628 if (mode_set == (wb ? AMR_WB_ALL_MODES : AMR_NB_ALL_MODES))
10629 max_mode = 7 + (wb ? 1 : 0);
10630 else
10631 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10632 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10633
10634 if (max_mode == -1) {
10635 GST_DEBUG ("No mode indication was found (mode set) = %x",
10636 (guint) mode_set);
10637 goto bad_data;
10638 }
10639
10640 gst_buffer_unmap (buf, &map);
10641 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10642
10643bad_data:
10644 gst_buffer_unmap (buf, &map);
10645 return 0;
10646}
10647
10648static gboolean
10649qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
10650 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10651{
10652 /*
10653 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10654 * [0 1 2]
10655 * [3 4 5]
10656 * [6 7 8]
10657 */
10658
10659 if (gst_byte_reader_get_remaining (reader) < 36)
10660 return FALSE;
10661
10662 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10663 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10664 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10665 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10666 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10667 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10668 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10669 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10670 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10671
10672 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10673 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10674 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10675 matrix[2] & 0xFF);
10676 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10677 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10678 matrix[5] & 0xFF);
10679 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10680 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10681 matrix[8] & 0xFF);
10682
10683 return TRUE;
10684}
10685
10686static void
10687qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
10688 QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
10689{
10690
10691/* [a b c]
10692 * [d e f]
10693 * [g h i]
10694 *
10695 * This macro will only compare value abdegh, it expects cfi to have already
10696 * been checked
10697 */
10698#define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
10699 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10700
10701 /* only handle the cases where the last column has standard values */
10702 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10703 const gchar *rotation_tag = NULL;
10704
10705 /* no rotation needed */
10706 if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
10707 /* NOP */
10708 } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
10709 rotation_tag = "rotate-90";
10710 } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
10711 rotation_tag = "rotate-180";
10712 } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
10713 rotation_tag = "rotate-270";
10714 } else {
10715 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10716 }
10717
10718 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10719 GST_STR_NULL (rotation_tag));
10720 if (rotation_tag != NULL) {
10721 if (*taglist == NULL)
10722 *taglist = gst_tag_list_new_empty ();
10723 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10724 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10725 }
10726 } else {
10727 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10728 }
10729}
10730
10731static gboolean
10732qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
10733 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10734{
10735 GNode *adrm;
10736 guint32 adrm_size;
10737 GstBuffer *adrm_buf = NULL;
10738 QtDemuxAavdEncryptionInfo *info;
10739
10740 adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
10741 if (G_UNLIKELY (!adrm)) {
10742 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
10743 return FALSE;
10744 }
10745 adrm_size = QT_UINT32 (adrm->data);
10746 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
10747
10748 stream->protection_scheme_type = FOURCC_aavd;
10749
10750 if (!stream->protection_scheme_info)
10751 stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
10752
10753 info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
10754
10755 if (info->default_properties)
10756 gst_structure_free (info->default_properties);
10757 info->default_properties = gst_structure_new ("application/x-aavd",
10758 "encrypted", G_TYPE_BOOLEAN, TRUE,
10759 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
10760 gst_buffer_unref (adrm_buf);
10761
10762 *original_fmt = FOURCC_mp4a;
10763 return TRUE;
10764}
10765
10766/* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10767 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10768 * Common Encryption (cenc), the function will also parse the tenc box (defined
10769 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10770 * (typically an enc[v|a|t|s] sample entry); the function will set
10771 * @original_fmt to the fourcc of the original unencrypted stream format.
10772 * Returns TRUE if successful; FALSE otherwise. */
10773static gboolean
10774qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
10775 QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
10776{
10777 GNode *sinf;
10778 GNode *frma;
10779 GNode *schm;
10780 GNode *schi;
10781 QtDemuxCencSampleSetInfo *info;
10782 GNode *tenc;
10783 const guint8 *tenc_data;
10784
10785 g_return_val_if_fail (qtdemux != NULL, FALSE);
10786 g_return_val_if_fail (stream != NULL, FALSE);
10787 g_return_val_if_fail (container != NULL, FALSE);
10788 g_return_val_if_fail (original_fmt != NULL, FALSE);
10789
10790 sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
10791 if (G_UNLIKELY (!sinf)) {
10792 if (stream->protection_scheme_type == FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +000010793 || stream->protection_scheme_type == FOURCC_cbcs
10794 || stream->protection_scheme_type == FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010795 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
10796 "mandatory for Common Encryption");
10797 return FALSE;
10798 }
10799 return TRUE;
10800 }
10801
10802 frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
10803 if (G_UNLIKELY (!frma)) {
10804 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
10805 return FALSE;
10806 }
10807
10808 *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
10809 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
10810 GST_FOURCC_ARGS (*original_fmt));
10811
10812 schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
10813 if (!schm) {
10814 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
10815 return FALSE;
10816 }
10817 stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
10818 stream->protection_scheme_version =
10819 QT_UINT32 ((const guint8 *) schm->data + 16);
10820
10821 GST_DEBUG_OBJECT (qtdemux,
10822 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
10823 "protection_scheme_version: %#010x",
10824 GST_FOURCC_ARGS (stream->protection_scheme_type),
10825 stream->protection_scheme_version);
10826
10827 schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
10828 if (!schi) {
10829 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
10830 return FALSE;
10831 }
10832 if (stream->protection_scheme_type != FOURCC_cenc &&
10833 stream->protection_scheme_type != FOURCC_piff &&
zengliang.li125c3642024-05-17 06:06:08 +000010834 stream->protection_scheme_type != FOURCC_cbcs &&
10835 stream->protection_scheme_type != FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010836 GST_ERROR_OBJECT (qtdemux,
10837 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
10838 GST_FOURCC_ARGS (stream->protection_scheme_type));
10839 return FALSE;
10840 }
10841
10842 if (G_UNLIKELY (!stream->protection_scheme_info))
10843 stream->protection_scheme_info =
10844 g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));
10845
10846 info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
10847
10848 if (stream->protection_scheme_type == FOURCC_cenc
zengliang.li125c3642024-05-17 06:06:08 +000010849 || stream->protection_scheme_type == FOURCC_cbcs
10850 || stream->protection_scheme_type == FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010851 guint8 is_encrypted;
10852 guint8 iv_size;
10853 guint8 constant_iv_size = 0;
10854 const guint8 *default_kid;
10855 guint8 crypt_byte_block = 0;
10856 guint8 skip_byte_block = 0;
10857 const guint8 *constant_iv = NULL;
10858
10859 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
10860 if (!tenc) {
10861 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10862 "which is mandatory for Common Encryption");
10863 return FALSE;
10864 }
10865 tenc_data = (const guint8 *) tenc->data + 12;
10866 is_encrypted = QT_UINT8 (tenc_data + 2);
10867 iv_size = QT_UINT8 (tenc_data + 3);
10868 default_kid = (tenc_data + 4);
10869 if (stream->protection_scheme_type == FOURCC_cbcs) {
10870 guint8 possible_pattern_info;
10871 if (iv_size == 0) {
10872 constant_iv_size = QT_UINT8 (tenc_data + 20);
10873 if (constant_iv_size != 8 && constant_iv_size != 16) {
10874 GST_ERROR_OBJECT (qtdemux,
10875 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
10876 return FALSE;
10877 }
10878 constant_iv = (tenc_data + 21);
10879 }
10880 possible_pattern_info = QT_UINT8 (tenc_data + 1);
10881 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
10882 skip_byte_block = possible_pattern_info & 0x0f;
10883 }
10884 qtdemux_update_default_sample_cenc_settings (qtdemux, info,
10885 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
10886 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
10887 } else if (stream->protection_scheme_type == FOURCC_piff) {
10888 GstByteReader br;
10889 static const guint8 piff_track_encryption_uuid[] = {
10890 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
10891 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
10892 };
10893
10894 tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_uuid);
10895 if (!tenc) {
10896 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
10897 "which is mandatory for Common Encryption");
10898 return FALSE;
10899 }
10900
10901 tenc_data = (const guint8 *) tenc->data + 8;
10902 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
10903 gchar *box_uuid = qtdemux_uuid_bytes_to_string (tenc_data);
10904 GST_ERROR_OBJECT (qtdemux,
10905 "Unsupported track encryption box with uuid: %s", box_uuid);
10906 g_free (box_uuid);
10907 return FALSE;
10908 }
10909 tenc_data = (const guint8 *) tenc->data + 16 + 12;
10910 gst_byte_reader_init (&br, tenc_data, 20);
10911 if (!qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
10912 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
10913 return FALSE;
10914 }
10915 stream->protection_scheme_type = FOURCC_cenc;
10916 }
10917
10918 return TRUE;
10919}
10920
10921static gint
10922qtdemux_track_id_compare_func (QtDemuxStream ** stream1,
10923 QtDemuxStream ** stream2)
10924{
10925 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
10926}
10927
zengliang.lib0f27cd2024-05-17 06:58:54 +000010928
10929static gint
10930qtdemux_parse_dvcc (guint32 sampleEntry, guint32 dvConfig, const guint8 * data, GstCaps *caps)
10931{
10932 gint len;
10933 const guint8 *config_data;
10934 guint16 buf;
10935 guint8 dv_profile;
10936 guint8 dv_level;
10937 guint8 rpu_present_flag;
10938 gboolean dv_bl_present_flag = FALSE;
10939 gboolean dv_el_present_flag = FALSE;
10940 gboolean bRejectPlay = FALSE;
10941
10942 if (NULL == data || NULL == caps)
10943 {
10944 return 0;
10945 }
10946
10947 len = QT_UINT32 (data);
10948 config_data = data + 0x8;
10949
10950 if (((1<<30) < len) || (1 != config_data[0]) || (0 != config_data[1]))
10951 {
10952 return 0;
10953 }
10954
10955 buf = (config_data[2] << 8) | config_data[3];
10956
10957 dv_profile = (buf >> 9) & 0x7f; // 7 bits
10958 dv_level = (buf >> 3) & 0x3f; // 6 bits
10959 rpu_present_flag = (buf >> 2) & 0x01; // 1 bit
10960 dv_el_present_flag = (buf >> 1) & 0x01; // 1 bit
10961 dv_bl_present_flag = buf & 0x01; // 1 bit
10962
10963 GST_DEBUG ("entry type: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
10964 GST_DEBUG ("dvConfig type: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
10965 GST_DEBUG ("dv_profile: %d, dv_level: %d, dv_el_present_flag: %d, dv_bl_present_flag: %d",
10966 dv_profile, dv_level, dv_el_present_flag, dv_bl_present_flag);
10967
10968 if (sampleEntry == FOURCC_hev1 || sampleEntry == FOURCC_hvc1)
10969 {
10970 if (dvConfig == FOURCC_dvcC || dvConfig == FOURCC_dvvC || dvConfig == FOURCC_dvwC)
10971 {
10972 if (dv_profile == 4 || dv_profile == 7 || dv_profile == 8)
10973 {
10974 GST_DEBUG ("Playback Dolby Vision");
10975 }
10976 else
10977 {
10978 GST_DEBUG ("invalid profile, playback as non-dv.");
10979 dv_el_present_flag = 0;
10980 dv_bl_present_flag = 0;
10981 }
10982 }
10983 else
10984 {
10985 GST_DEBUG ("invalid dvConfig type, playback as non-dv.");
10986 dv_el_present_flag = 0;
10987 dv_bl_present_flag = 0;
10988 }
10989 }
10990 else if (sampleEntry == FOURCC_dvhe || sampleEntry == FOURCC_dvh1)
10991 {
10992 if (dvConfig == FOURCC_dvcC)
10993 {
10994 if (dv_profile == 5)
10995 GST_DEBUG ("Playback Dolby Vision");
10996 else
10997 {
10998 GST_WARNING("reject play for invalid profile:%d", dv_profile);
10999 bRejectPlay = TRUE;
11000 }
11001 }
11002 else
11003 {
11004 GST_WARNING ("reject play for invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11005 bRejectPlay = TRUE;
11006 }
11007 }
11008 else if (sampleEntry == FOURCC_avc1 || sampleEntry == FOURCC_avc3 || sampleEntry == FOURCC_avc2 || sampleEntry == FOURCC_avc4)
11009 {
11010 if (dvConfig == FOURCC_dvcC || dvConfig == FOURCC_dvvC || dvConfig == FOURCC_dvwC)
11011 {
11012 if (dv_profile == 9)
11013 GST_DEBUG ("Playback Dolby Vision");
11014 else
11015 {
11016 GST_DEBUG ("invalid profile, Playback HEVC bitstream using base-layer");
11017 dv_el_present_flag = 0;
11018 dv_bl_present_flag = 0;
11019 }
11020 }
11021 else
11022 {
11023 GST_DEBUG ("invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11024 dv_el_present_flag = 0;
11025 dv_bl_present_flag = 0;
11026 }
11027 }
11028 else if (sampleEntry == FOURCC_dvav || sampleEntry == FOURCC_dva1)
11029 {
11030 if (dvConfig == FOURCC_dvcC)
11031 {
11032 if (dv_profile == 1)
11033 {
11034 GST_WARNING("reject play for invalid profile:%d", dv_profile);
11035 bRejectPlay = TRUE;
11036 }
11037 }
11038 else
11039 {
11040 GST_WARNING ("reject play for invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11041 bRejectPlay = TRUE;
11042 }
11043 }
11044 else if (sampleEntry == FOURCC_av01)
11045 {
11046 GST_DEBUG ("ToDo sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
11047 }
11048 else if (sampleEntry == FOURCC_dav1)
11049 {
11050 if (dvConfig == FOURCC_dvvC || dvConfig == FOURCC_dvwC)
11051 {
11052 if (dv_profile != 10)
11053 {
11054 GST_WARNING("reject play for invalid profile:%d", dv_profile);
11055 bRejectPlay = TRUE;
11056 }
11057 }
11058 else
11059 {
11060 GST_WARNING ("reject play for invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11061 bRejectPlay = TRUE;
11062 }
11063 }
11064 else if (sampleEntry == FOURCC_vvcN || sampleEntry == FOURCC_vvc1 || sampleEntry == FOURCC_vvi1 || sampleEntry == FOURCC_vvs1)
11065 {
11066 GST_DEBUG ("invalid sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
11067 dv_el_present_flag = 0;
11068 dv_bl_present_flag = 0;
11069 }
11070
11071 gst_caps_set_simple (caps, "dv_bl_present_flag", G_TYPE_BOOLEAN, dv_bl_present_flag, NULL);
11072 gst_caps_set_simple (caps, "dv_el_present_flag", G_TYPE_BOOLEAN, dv_el_present_flag, NULL);
11073
11074 return bRejectPlay ? -1 : 0;
11075}
11076
11077static gint
11078qtdemux_parse_sgpd_av1M (GstQTDemux * qtdemux, GNode * node)
11079{
11080 GNode *sgpd;
11081 const guint8 *sgpd_data;
11082 gint sgpd_size;
11083 guint32 sgpd_fourcc;
11084 gint sgpd_version;
11085 gint sgpd_flag;
11086 guint32 sgpd_group_type;
11087 gint sgpd_default_len = -1;
11088 guint sgpd_entry_cnt;
11089 guint32 group_fourcc;
11090 guint metadata_type;
11091 guint metadata_specific_parameters;
11092 gboolean bRejectPlay = FALSE;
11093
11094 GST_DEBUG_OBJECT (qtdemux, "try to find sgpd");
11095
11096 sgpd = qtdemux_tree_get_child_by_type (node, FOURCC_sgpd);
11097 if (sgpd)
11098 {
11099 int rIdx = 0;
11100 int entryIdx = 0;
11101 sgpd_data = (const guint8 *) sgpd->data;
11102 sgpd_size = QT_UINT32 (sgpd_data + rIdx);
11103 rIdx += 4;
11104
11105 sgpd_fourcc = QT_FOURCC (sgpd_data + rIdx);
11106 rIdx += 4;
11107
11108 sgpd_version = QT_UINT8 (sgpd_data + rIdx);
11109 rIdx += 4; // 1 byte version + 3 byte flags
11110
11111 sgpd_group_type = QT_UINT32 (sgpd_data + rIdx);
11112 rIdx += 4;
11113
11114 if (sgpd_version == 1) {
11115 sgpd_default_len = QT_UINT32 (sgpd_data + rIdx);
11116 rIdx += 4;
11117 }
11118 else if (sgpd_version >= 2)
11119 rIdx += 4; // 4 byte default_sample_descriotion_index
11120
11121 sgpd_entry_cnt = QT_UINT32 (sgpd_data + rIdx);
11122 rIdx += 4;
11123
11124 GST_WARNING_OBJECT (qtdemux, "sgpd_size=0x%x, sgpd_fourcc=0x%x, sgpd_version=0x%x, sgpd_group_type=0x%x, sgpd_entry_cnt=0x%x",
11125 sgpd_size, sgpd_fourcc, sgpd_version, sgpd_group_type, sgpd_entry_cnt);
11126
11127 for (entryIdx = 0; entryIdx < sgpd_entry_cnt; entryIdx++)
11128 {
11129 if (sgpd_version == 1 && sgpd_default_len == 0) {
11130 sgpd_default_len = QT_UINT32 (sgpd_data + rIdx);
11131 rIdx += 4;
11132 }
11133
11134 group_fourcc = QT_FOURCC (sgpd_data + rIdx);
11135 rIdx += 4;
11136
11137 if (group_fourcc == FOURCC_av1M) {
11138 metadata_type = QT_UINT8 (sgpd_data + rIdx);
11139 rIdx += 1;
11140
11141 metadata_specific_parameters = QT_UINT32 (sgpd_data + rIdx);
11142 rIdx += 4;
11143
11144 if ( metadata_type != 4 || metadata_specific_parameters != 0xB5003B) {
11145 // METADATA_TYPE_ITUT_T35 and 0xB5003B
11146 bRejectPlay = TRUE;
11147 }
11148 }
11149 GST_WARNING_OBJECT (qtdemux, "group_fourcc=0x%x, metadata_type=0x%x, metadata_specific_parameters=0x%x",
11150 group_fourcc, metadata_type, metadata_specific_parameters);
11151
11152 }
11153 }
11154 else
11155 {
11156 GST_DEBUG_OBJECT (qtdemux, "no sgpd");
11157 }
11158
11159 return bRejectPlay ? -1 : 0;
11160}
11161
zengliang.li5f31ef42024-05-16 08:27:38 +000011162static gboolean
11163qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
11164 GNode * stbl)
11165{
11166 GNode *svmi;
11167
11168 /*parse svmi header if existing */
11169 svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
11170 if (svmi) {
11171 guint32 len = QT_UINT32 ((guint8 *) svmi->data);
11172 guint32 version = QT_UINT32 ((guint8 *) svmi->data + 8);
11173 if (!version) {
11174 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11175 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11176 guint8 frame_type, frame_layout;
11177 guint32 stereo_mono_change_count;
11178
11179 if (len < 18)
11180 return FALSE;
11181
11182 /* MPEG-A stereo video */
11183 if (qtdemux->major_brand == FOURCC_ss02)
11184 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11185
11186 frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
11187 frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11188 stereo_mono_change_count = QT_UINT32 ((guint8 *) svmi->data + 14);
11189
11190 switch (frame_type) {
11191 case 0:
11192 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11193 break;
11194 case 1:
11195 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11196 break;
11197 case 2:
11198 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11199 break;
11200 case 3:
11201 /* mode 3 is primary/secondary view sequence, ie
11202 * left/right views in separate tracks. See section 7.2
11203 * of ISO/IEC 23000-11:2009 */
11204 /* In the future this might be supported using related
11205 * streams, like an enhancement track - if files like this
11206 * ever exist */
11207 GST_FIXME_OBJECT (qtdemux,
11208 "Implement stereo video in separate streams");
11209 }
11210
11211 if ((frame_layout & 0x1) == 0)
11212 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11213
11214 GST_LOG_OBJECT (qtdemux,
11215 "StereoVideo: composition type: %u, is_left_first: %u",
11216 frame_type, frame_layout);
11217
11218 if (stereo_mono_change_count > 1) {
11219 GST_FIXME_OBJECT (qtdemux,
11220 "Mixed-mono flags are not yet supported in qtdemux.");
11221 }
11222
11223 stream->multiview_mode = mode;
11224 stream->multiview_flags = flags;
11225 }
11226 }
11227
11228 return TRUE;
11229}
11230
11231/* parse the traks.
11232 * With each track we associate a new QtDemuxStream that contains all the info
11233 * about the trak.
11234 * traks that do not decode to something (like strm traks) will not have a pad.
11235 */
11236static gboolean
11237qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
11238{
11239 GstByteReader tkhd;
11240 int offset;
11241 GNode *mdia;
11242 GNode *mdhd;
11243 GNode *hdlr;
11244 GNode *minf;
11245 GNode *stbl;
11246 GNode *stsd;
11247 GNode *mp4a;
11248 GNode *mp4v;
11249 GNode *esds;
11250 GNode *tref;
11251 GNode *udta;
11252
11253 QtDemuxStream *stream = NULL;
11254 const guint8 *stsd_data;
11255 const guint8 *stsd_entry_data;
11256 guint remaining_stsd_len;
11257 guint stsd_entry_count;
11258 guint stsd_index;
11259 guint16 lang_code; /* quicktime lang code or packed iso code */
11260 guint32 version;
11261 guint32 tkhd_flags = 0;
11262 guint8 tkhd_version = 0;
11263 guint32 w = 0, h = 0;
11264 guint value_size, stsd_len, len;
11265 guint32 track_id;
11266 guint32 dummy;
11267
11268 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11269
11270 if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
11271 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11272 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11273 goto corrupt_file;
11274
11275 /* pick between 64 or 32 bits */
11276 value_size = tkhd_version == 1 ? 8 : 4;
11277 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11278 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11279 goto corrupt_file;
11280
11281 /* Check if current moov has duplicated track_id */
11282 if (qtdemux_find_stream (qtdemux, track_id))
11283 goto existing_stream;
11284
11285 stream = _create_stream (qtdemux, track_id);
11286 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11287
11288 /* need defaults for fragments */
11289 qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
11290
11291 if ((tkhd_flags & 1) == 0)
11292 stream->disabled = TRUE;
11293
11294 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11295 tkhd_version, tkhd_flags, stream->track_id);
11296
11297 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
11298 goto corrupt_file;
11299
11300 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
11301 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
11302 if (qtdemux->major_brand != FOURCC_mjp2 ||
11303 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
11304 goto corrupt_file;
11305 }
11306
11307 len = QT_UINT32 ((guint8 *) mdhd->data);
11308 version = QT_UINT32 ((guint8 *) mdhd->data + 8);
11309 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11310 if (version == 0x01000000) {
11311 if (len < 42)
11312 goto corrupt_file;
11313 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
11314 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
11315 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 40);
11316 } else {
11317 if (len < 30)
11318 goto corrupt_file;
11319 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
11320 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
11321 lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
11322 }
11323
11324 if (lang_code < 0x400) {
11325 qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
11326 } else if (lang_code == 0x7fff) {
11327 stream->lang_id[0] = 0; /* unspecified */
11328 } else {
11329 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11330 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11331 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11332 stream->lang_id[3] = 0;
11333 }
11334
11335 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11336 stream->timescale);
11337 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11338 stream->duration);
11339 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11340 lang_code, stream->lang_id);
11341
11342 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11343 goto corrupt_file;
11344
11345 if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
11346 /* chapters track reference */
11347 GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
11348 if (chap) {
11349 gsize length = GST_READ_UINT32_BE (chap->data);
11350 if (qtdemux->chapters_track_id)
11351 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11352
11353 if (length >= 12) {
11354 qtdemux->chapters_track_id =
11355 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11356 }
11357 }
11358 }
11359
11360 /* fragmented files may have bogus duration in moov */
11361 if (!qtdemux->fragmented &&
11362 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11363 guint64 tdur1, tdur2;
11364
11365 /* don't overflow */
11366 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11367 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11368
11369 /* HACK:
11370 * some of those trailers, nowadays, have prologue images that are
11371 * themselves video tracks as well. I haven't really found a way to
11372 * identify those yet, except for just looking at their duration. */
11373 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11374 GST_WARNING_OBJECT (qtdemux,
11375 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11376 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11377 "found, assuming preview image or something; skipping track",
11378 stream->duration, stream->timescale, qtdemux->duration,
11379 qtdemux->timescale);
11380 gst_qtdemux_stream_unref (stream);
11381 return TRUE;
11382 }
11383 }
11384
11385 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
11386 goto corrupt_file;
11387
11388 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
11389 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));
11390
11391 len = QT_UINT32 ((guint8 *) hdlr->data);
11392 if (len >= 20)
11393 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
11394 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11395 GST_FOURCC_ARGS (stream->subtype));
11396
11397 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
11398 goto corrupt_file;
11399
11400 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
11401 goto corrupt_file;
11402
11403 /* Parse out svmi (and later st3d/sv3d) atoms */
11404 if (!qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
11405 goto corrupt_file;
11406
11407 /* parse rest of tkhd */
11408 if (stream->subtype == FOURCC_vide) {
11409 guint32 matrix[9];
11410
11411 /* version 1 uses some 64-bit ints */
11412 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11413 goto corrupt_file;
11414
11415 if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
11416 goto corrupt_file;
11417
11418 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11419 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11420 goto corrupt_file;
11421
11422 qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
11423 &stream->stream_tags);
11424 }
11425
11426 /* parse stsd */
11427 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
11428 goto corrupt_file;
11429 stsd_data = (const guint8 *) stsd->data;
11430
11431 /* stsd should at least have one entry */
11432 stsd_len = QT_UINT32 (stsd_data);
11433 if (stsd_len < 24) {
11434 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
11435 if (stream->subtype == FOURCC_vivo) {
11436 gst_qtdemux_stream_unref (stream);
11437 return TRUE;
11438 } else {
11439 goto corrupt_file;
11440 }
11441 }
11442
11443 stream->stsd_entries_length = stsd_entry_count = QT_UINT32 (stsd_data + 12);
11444 /* each stsd entry must contain at least 8 bytes */
11445 if (stream->stsd_entries_length == 0
11446 || stream->stsd_entries_length > stsd_len / 8) {
11447 stream->stsd_entries_length = 0;
11448 goto corrupt_file;
11449 }
11450 stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, stsd_entry_count);
11451 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11452 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11453
11454 stsd_entry_data = stsd_data + 16;
11455 remaining_stsd_len = stsd_len - 16;
11456 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11457 guint32 fourcc;
11458 gchar *codec = NULL;
11459 QtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
11460
11461 /* and that entry should fit within stsd */
11462 len = QT_UINT32 (stsd_entry_data);
11463 if (len > remaining_stsd_len)
11464 goto corrupt_file;
11465
11466 entry->fourcc = fourcc = QT_FOURCC (stsd_entry_data + 4);
11467 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11468 GST_FOURCC_ARGS (entry->fourcc));
11469 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11470
11471 if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
11472 goto error_encrypted;
11473
11474 if (fourcc == FOURCC_aavd) {
11475 if (stream->subtype != FOURCC_soun) {
11476 GST_ERROR_OBJECT (qtdemux,
11477 "Unexpeced stsd type 'aavd' outside 'soun' track");
11478 } else {
11479 /* encrypted audio with sound sample description v0 */
11480 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11481 stream->protected = TRUE;
11482 if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
11483 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11484 }
11485 }
11486
11487 if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
11488 /* FIXME this looks wrong, there might be multiple children
11489 * with the same type */
11490 GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
11491 stream->protected = TRUE;
11492 if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
11493 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11494 }
11495
11496 if (stream->subtype == FOURCC_vide) {
11497 GNode *colr;
11498 GNode *fiel;
11499 GNode *pasp;
11500 gboolean gray;
11501 gint depth, palette_size, palette_count;
11502 guint32 *palette_data = NULL;
11503
11504 entry->sampled = TRUE;
11505
11506 stream->display_width = w >> 16;
11507 stream->display_height = h >> 16;
11508
11509 offset = 16;
11510 if (len < 86) /* TODO verify */
11511 goto corrupt_file;
11512
11513 entry->width = QT_UINT16 (stsd_entry_data + offset + 16);
11514 entry->height = QT_UINT16 (stsd_entry_data + offset + 18);
11515 entry->fps_n = 0; /* this is filled in later */
11516 entry->fps_d = 0; /* this is filled in later */
11517 entry->bits_per_sample = QT_UINT16 (stsd_entry_data + offset + 66);
11518 entry->color_table_id = QT_UINT16 (stsd_entry_data + offset + 68);
11519
11520 /* if color_table_id is 0, ctab atom must follow; however some files
11521 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11522 * if color table is not present we'll correct the value */
11523 if (entry->color_table_id == 0 &&
11524 (len < 90
11525 || QT_FOURCC (stsd_entry_data + offset + 70) != FOURCC_ctab)) {
11526 entry->color_table_id = -1;
11527 }
11528
11529 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11530 entry->width, entry->height, entry->bits_per_sample,
11531 entry->color_table_id);
11532
11533 depth = entry->bits_per_sample;
11534
11535 /* more than 32 bits means grayscale */
11536 gray = (depth > 32);
11537 /* low 32 bits specify the depth */
11538 depth &= 0x1F;
11539
11540 /* different number of palette entries is determined by depth. */
11541 palette_count = 0;
11542 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11543 palette_count = (1 << depth);
11544 palette_size = palette_count * 4;
11545
11546 if (entry->color_table_id) {
11547 switch (palette_count) {
11548 case 0:
11549 break;
11550 case 2:
11551 palette_data = g_memdup2 (ff_qt_default_palette_2, palette_size);
11552 break;
11553 case 4:
11554 palette_data = g_memdup2 (ff_qt_default_palette_4, palette_size);
11555 break;
11556 case 16:
11557 if (gray)
11558 palette_data =
11559 g_memdup2 (ff_qt_grayscale_palette_16, palette_size);
11560 else
11561 palette_data = g_memdup2 (ff_qt_default_palette_16, palette_size);
11562 break;
11563 case 256:
11564 if (gray)
11565 palette_data =
11566 g_memdup2 (ff_qt_grayscale_palette_256, palette_size);
11567 else
11568 palette_data =
11569 g_memdup2 (ff_qt_default_palette_256, palette_size);
11570 break;
11571 default:
11572 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11573 (_("The video in this file might not play correctly.")),
11574 ("unsupported palette depth %d", depth));
11575 break;
11576 }
11577 } else {
11578 guint i, j, start, end;
11579
11580 if (len < 94)
11581 goto corrupt_file;
11582
11583 /* read table */
11584 start = QT_UINT32 (stsd_entry_data + offset + 70);
11585 palette_count = QT_UINT16 (stsd_entry_data + offset + 74);
11586 end = QT_UINT16 (stsd_entry_data + offset + 76);
11587
11588 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11589 start, end, palette_count);
11590
11591 if (end > 255)
11592 end = 255;
11593 if (start > end)
11594 start = end;
11595
11596 if (len < 94 + (end - start) * 8)
11597 goto corrupt_file;
11598
11599 /* palette is always the same size */
11600 palette_data = g_malloc0 (256 * 4);
11601 palette_size = 256 * 4;
11602
11603 for (j = 0, i = start; i <= end; j++, i++) {
11604 guint32 a, r, g, b;
11605
11606 a = QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11607 r = QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11608 g = QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11609 b = QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
11610
11611 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11612 (g & 0xff00) | (b >> 8);
11613 }
11614 }
11615
11616 if (entry->caps)
11617 gst_caps_unref (entry->caps);
11618
11619 entry->caps =
11620 qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
11621 &codec);
11622 if (G_UNLIKELY (!entry->caps)) {
11623 g_free (palette_data);
11624 goto unknown_stream;
11625 }
11626
11627 if (codec) {
11628 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11629 GST_TAG_VIDEO_CODEC, codec, NULL);
11630 g_free (codec);
11631 codec = NULL;
11632 }
11633
11634 if (palette_data) {
11635 GstStructure *s;
11636
11637 if (entry->rgb8_palette)
11638 gst_memory_unref (entry->rgb8_palette);
11639 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
11640 palette_data, palette_size, 0, palette_size, palette_data, g_free);
11641
11642 s = gst_caps_get_structure (entry->caps, 0);
11643
11644 /* non-raw video has a palette_data property. raw video has the palette as
11645 * an extra plane that we append to the output buffers before we push
11646 * them*/
11647 if (!gst_structure_has_name (s, "video/x-raw")) {
11648 GstBuffer *palette;
11649
11650 palette = gst_buffer_new ();
11651 gst_buffer_append_memory (palette, entry->rgb8_palette);
11652 entry->rgb8_palette = NULL;
11653
11654 gst_caps_set_simple (entry->caps, "palette_data",
11655 GST_TYPE_BUFFER, palette, NULL);
11656 gst_buffer_unref (palette);
11657 }
11658 } else if (palette_count != 0) {
11659 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
11660 (NULL), ("Unsupported palette depth %d", depth));
11661 }
11662
11663 GST_LOG_OBJECT (qtdemux, "frame count: %u",
11664 QT_UINT16 (stsd_entry_data + offset + 32));
11665
11666 esds = NULL;
11667 pasp = NULL;
11668 colr = NULL;
11669 fiel = NULL;
11670 /* pick 'the' stsd child */
11671 mp4v = qtdemux_tree_get_child_by_index (stsd, stsd_index);
11672 // We should skip parsing the stsd for non-protected streams if
11673 // the entry doesn't match the fourcc, since they don't change
11674 // format. However, for protected streams we can have partial
11675 // encryption, where parts of the stream are encrypted and parts
11676 // not. For both parts of such streams, we should ensure the
11677 // esds overrides are parsed for both from the stsd.
11678 if (QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
11679 if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4v) != FOURCC_encv)
11680 mp4v = NULL;
11681 else if (!stream->protected)
11682 mp4v = NULL;
11683 }
11684
11685 if (mp4v) {
11686 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
11687 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
11688 colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
11689 fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
11690 }
11691
11692 if (pasp) {
11693 const guint8 *pasp_data = (const guint8 *) pasp->data;
11694 guint len = QT_UINT32 (pasp_data);
11695
11696 if (len == 16) {
11697 CUR_STREAM (stream)->par_w = QT_UINT32 (pasp_data + 8);
11698 CUR_STREAM (stream)->par_h = QT_UINT32 (pasp_data + 12);
11699 } else {
11700 CUR_STREAM (stream)->par_w = 0;
11701 CUR_STREAM (stream)->par_h = 0;
11702 }
11703 } else {
11704 CUR_STREAM (stream)->par_w = 0;
11705 CUR_STREAM (stream)->par_h = 0;
11706 }
11707
11708 if (fiel) {
11709 const guint8 *fiel_data = (const guint8 *) fiel->data;
11710 guint len = QT_UINT32 (fiel_data);
11711
11712 if (len == 10) {
11713 CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
11714 CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
11715 }
11716 }
11717
11718 if (colr) {
11719 const guint8 *colr_data = (const guint8 *) colr->data;
11720 guint len = QT_UINT32 (colr_data);
11721
11722 if (len == 19 || len == 18) {
11723 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
11724
11725 if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
11726 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
11727 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
11728 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
11729 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
11730
11731 CUR_STREAM (stream)->colorimetry.primaries =
11732 gst_video_color_primaries_from_iso (primaries);
11733 CUR_STREAM (stream)->colorimetry.transfer =
11734 gst_video_transfer_function_from_iso (transfer_function);
11735 CUR_STREAM (stream)->colorimetry.matrix =
11736 gst_video_color_matrix_from_iso (matrix);
11737 CUR_STREAM (stream)->colorimetry.range =
11738 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
11739 GST_VIDEO_COLOR_RANGE_16_235;
11740 } else {
11741 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
11742 }
11743 } else {
11744 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
11745 }
11746 }
11747
11748 if (esds) {
11749 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
11750 stream->stream_tags);
11751 } else {
11752 switch (fourcc) {
11753 case FOURCC_H264:
11754 case FOURCC_avc1:
11755 case FOURCC_avc3:
zengliang.lib0f27cd2024-05-17 06:58:54 +000011756 case FOURCC_dav1:
zengliang.li5f31ef42024-05-16 08:27:38 +000011757 {
11758 guint len = QT_UINT32 (stsd_entry_data);
11759 len = len <= 0x56 ? 0 : len - 0x56;
11760 const guint8 *avc_data = stsd_entry_data + 0x56;
11761
11762 /* find avcC */
11763 while (len >= 0x8) {
11764 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011765 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000011766
11767 if (QT_UINT32 (avc_data) <= 0x8)
11768 size = 0;
11769 else if (QT_UINT32 (avc_data) <= len)
11770 size = QT_UINT32 (avc_data) - 0x8;
11771 else
11772 size = len - 0x8;
11773
11774 if (size < 1)
11775 /* No real data, so break out */
11776 break;
11777
zengliang.lib0f27cd2024-05-17 06:58:54 +000011778 dvconfig = QT_FOURCC (avc_data + 0x4);
11779 switch (dvconfig) {
11780 case FOURCC_dvcC:
11781 case FOURCC_dvvC:
11782 case FOURCC_dvwC:
11783 {
11784 qtdemux_parse_dvcc (fourcc, dvconfig, avc_data, entry->caps);
11785 break;
11786 }
zengliang.li5f31ef42024-05-16 08:27:38 +000011787 case FOURCC_avcC:
11788 {
11789 /* parse, if found */
11790 GstBuffer *buf;
11791
11792 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
11793
11794 /* First 4 bytes are the length of the atom, the next 4 bytes
11795 * are the fourcc, the next 1 byte is the version, and the
11796 * subsequent bytes are profile_tier_level structure like data. */
11797 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
11798 avc_data + 8 + 1, size - 1);
11799 buf = gst_buffer_new_and_alloc (size);
11800 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
11801 gst_caps_set_simple (entry->caps,
11802 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11803 gst_buffer_unref (buf);
11804
11805 break;
11806 }
zengliang.lib0f27cd2024-05-17 06:58:54 +000011807 case FOURCC_av1C:
11808 {
11809 GST_DEBUG_OBJECT (qtdemux, "found av1C in stsd,so try to find sgpd");
11810
11811 if (qtdemux_parse_sgpd_av1M(qtdemux, stbl) == -1)
11812 {
11813 GST_WARNING_OBJECT (qtdemux, "reject play for invalid metadata_type and specific_parameters");
11814 goto corrupt_file;
11815 }
11816 break;
11817 }
zengliang.li5f31ef42024-05-16 08:27:38 +000011818 case FOURCC_strf:
11819 {
11820 GstBuffer *buf;
11821
11822 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
11823
11824 /* First 4 bytes are the length of the atom, the next 4 bytes
11825 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
11826 * next 1 byte is the version, and the
11827 * subsequent bytes are sequence parameter set like data. */
11828
11829 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
11830 if (size > 1) {
11831 gst_codec_utils_h264_caps_set_level_and_profile
11832 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
11833
11834 buf = gst_buffer_new_and_alloc (size);
11835 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
11836 gst_caps_set_simple (entry->caps,
11837 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11838 gst_buffer_unref (buf);
11839 }
11840 break;
11841 }
11842 case FOURCC_btrt:
11843 {
11844 guint avg_bitrate, max_bitrate;
11845
11846 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
11847 if (size < 12)
11848 break;
11849
11850 max_bitrate = QT_UINT32 (avc_data + 0xc);
11851 avg_bitrate = QT_UINT32 (avc_data + 0x10);
11852
11853 if (!max_bitrate && !avg_bitrate)
11854 break;
11855
11856 /* Some muxers seem to swap the average and maximum bitrates
11857 * (I'm looking at you, YouTube), so we swap for sanity. */
11858 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
11859 guint temp = avg_bitrate;
11860
11861 avg_bitrate = max_bitrate;
11862 max_bitrate = temp;
11863 }
11864
11865 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
11866 gst_tag_list_add (stream->stream_tags,
11867 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
11868 max_bitrate, NULL);
11869 }
11870 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
11871 gst_tag_list_add (stream->stream_tags,
11872 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
11873 NULL);
11874 }
11875
11876 break;
11877 }
11878
11879 default:
11880 break;
11881 }
11882
11883 len -= size + 8;
11884 avc_data += size + 8;
11885 }
11886
11887 break;
11888 }
11889 case FOURCC_H265:
11890 case FOURCC_hvc1:
11891 case FOURCC_hev1:
11892 case FOURCC_dvh1:
11893 case FOURCC_dvhe:
11894 {
11895 guint len = QT_UINT32 (stsd_entry_data);
11896 len = len <= 0x56 ? 0 : len - 0x56;
11897 const guint8 *hevc_data = stsd_entry_data + 0x56;
11898
11899 /* find hevc */
11900 while (len >= 0x8) {
11901 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011902 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000011903
11904 if (QT_UINT32 (hevc_data) <= 0x8)
11905 size = 0;
11906 else if (QT_UINT32 (hevc_data) <= len)
11907 size = QT_UINT32 (hevc_data) - 0x8;
11908 else
11909 size = len - 0x8;
11910
11911 if (size < 1)
11912 /* No real data, so break out */
11913 break;
11914
zengliang.lib0f27cd2024-05-17 06:58:54 +000011915 dvconfig = QT_FOURCC (hevc_data + 0x4);
11916
11917 switch (dvconfig) {
11918 case FOURCC_dvcC:
11919 case FOURCC_dvvC:
11920 case FOURCC_dvwC:
11921 case FOURCC_dvzC:
11922 case FOURCC_dvxC:
11923 {
11924 gint ret = qtdemux_parse_dvcc (fourcc, dvconfig, hevc_data, entry->caps);
11925 if (ret == -1)
11926 {
11927 GST_WARNING_OBJECT (qtdemux, "reject play!");
11928 goto corrupt_file;
11929 }
11930 break;
11931 }
11932
zengliang.li5f31ef42024-05-16 08:27:38 +000011933 case FOURCC_hvcC:
11934 {
11935 /* parse, if found */
11936 GstBuffer *buf;
11937
11938 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
11939
11940 /* First 4 bytes are the length of the atom, the next 4 bytes
11941 * are the fourcc, the next 1 byte is the version, and the
11942 * subsequent bytes are sequence parameter set like data. */
11943 gst_codec_utils_h265_caps_set_level_tier_and_profile
11944 (entry->caps, hevc_data + 8 + 1, size - 1);
11945
11946 buf = gst_buffer_new_and_alloc (size);
11947 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
11948 gst_caps_set_simple (entry->caps,
11949 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11950 gst_buffer_unref (buf);
11951 break;
11952 }
11953 default:
11954 break;
11955 }
11956 len -= size + 8;
11957 hevc_data += size + 8;
11958 }
11959 break;
11960 }
11961 case FOURCC_mp4v:
11962 case FOURCC_MP4V:
11963 case FOURCC_fmp4:
11964 case FOURCC_FMP4:
11965 case FOURCC_xvid:
11966 case FOURCC_XVID:
11967 {
11968 GNode *glbl;
11969
11970 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
11971 GST_FOURCC_ARGS (fourcc));
11972
11973 /* codec data might be in glbl extension atom */
11974 glbl = mp4v ?
11975 qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
11976 if (glbl) {
11977 guint8 *data;
11978 GstBuffer *buf;
11979 guint len;
11980
11981 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
11982 data = glbl->data;
11983 len = QT_UINT32 (data);
11984 if (len > 0x8) {
11985 len -= 0x8;
11986 buf = gst_buffer_new_and_alloc (len);
11987 gst_buffer_fill (buf, 0, data + 8, len);
11988 gst_caps_set_simple (entry->caps,
11989 "codec_data", GST_TYPE_BUFFER, buf, NULL);
11990 gst_buffer_unref (buf);
11991 }
11992 }
11993 break;
11994 }
11995 case FOURCC_mjp2:
11996 {
11997 /* see annex I of the jpeg2000 spec */
11998 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
11999 const guint8 *data;
12000 const gchar *colorspace = NULL;
12001 gint ncomp = 0;
12002 guint32 ncomp_map = 0;
12003 gint32 *comp_map = NULL;
12004 guint32 nchan_def = 0;
12005 gint32 *chan_def = NULL;
12006
12007 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12008 /* some required atoms */
12009 mjp2 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12010 if (!mjp2)
12011 break;
12012 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
12013 if (!jp2h)
12014 break;
12015
12016 /* number of components; redundant with info in codestream, but useful
12017 to a muxer */
12018 ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
12019 if (!ihdr || QT_UINT32 (ihdr->data) != 22)
12020 break;
12021 ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);
12022
12023 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
12024 if (!colr)
12025 break;
12026 GST_DEBUG_OBJECT (qtdemux, "found colr");
12027 /* extract colour space info */
12028 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12029 switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
12030 case 16:
12031 colorspace = "sRGB";
12032 break;
12033 case 17:
12034 colorspace = "GRAY";
12035 break;
12036 case 18:
12037 colorspace = "sYUV";
12038 break;
12039 default:
12040 colorspace = NULL;
12041 break;
12042 }
12043 }
12044 if (!colorspace)
12045 /* colr is required, and only values 16, 17, and 18 are specified,
12046 so error if we have no colorspace */
12047 break;
12048
12049 /* extract component mapping */
12050 cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
12051 if (cmap) {
12052 guint32 cmap_len = 0;
12053 int i;
12054 cmap_len = QT_UINT32 (cmap->data);
12055 if (cmap_len >= 8) {
12056 /* normal box, subtract off header */
12057 cmap_len -= 8;
12058 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12059 if (cmap_len % 4 == 0) {
12060 ncomp_map = (cmap_len / 4);
12061 comp_map = g_new0 (gint32, ncomp_map);
12062 for (i = 0; i < ncomp_map; i++) {
12063 guint16 cmp;
12064 guint8 mtyp, pcol;
12065 cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12066 mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12067 pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
12068 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12069 }
12070 }
12071 }
12072 }
12073 /* extract channel definitions */
12074 cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
12075 if (cdef) {
12076 guint32 cdef_len = 0;
12077 int i;
12078 cdef_len = QT_UINT32 (cdef->data);
12079 if (cdef_len >= 10) {
12080 /* normal box, subtract off header and len */
12081 cdef_len -= 10;
12082 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12083 if (cdef_len % 6 == 0) {
12084 nchan_def = (cdef_len / 6);
12085 chan_def = g_new0 (gint32, nchan_def);
12086 for (i = 0; i < nchan_def; i++)
12087 chan_def[i] = -1;
12088 for (i = 0; i < nchan_def; i++) {
12089 guint16 cn, typ, asoc;
12090 cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12091 typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12092 asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
12093 if (cn < nchan_def) {
12094 switch (typ) {
12095 case 0:
12096 chan_def[cn] = asoc;
12097 break;
12098 case 1:
12099 chan_def[cn] = 0; /* alpha */
12100 break;
12101 default:
12102 chan_def[cn] = -typ;
12103 }
12104 }
12105 }
12106 }
12107 }
12108 }
12109
12110 gst_caps_set_simple (entry->caps,
12111 "num-components", G_TYPE_INT, ncomp, NULL);
12112 gst_caps_set_simple (entry->caps,
12113 "colorspace", G_TYPE_STRING, colorspace, NULL);
12114
12115 if (comp_map) {
12116 GValue arr = { 0, };
12117 GValue elt = { 0, };
12118 int i;
12119 g_value_init (&arr, GST_TYPE_ARRAY);
12120 g_value_init (&elt, G_TYPE_INT);
12121 for (i = 0; i < ncomp_map; i++) {
12122 g_value_set_int (&elt, comp_map[i]);
12123 gst_value_array_append_value (&arr, &elt);
12124 }
12125 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12126 "component-map", &arr);
12127 g_value_unset (&elt);
12128 g_value_unset (&arr);
12129 g_free (comp_map);
12130 }
12131
12132 if (chan_def) {
12133 GValue arr = { 0, };
12134 GValue elt = { 0, };
12135 int i;
12136 g_value_init (&arr, GST_TYPE_ARRAY);
12137 g_value_init (&elt, G_TYPE_INT);
12138 for (i = 0; i < nchan_def; i++) {
12139 g_value_set_int (&elt, chan_def[i]);
12140 gst_value_array_append_value (&arr, &elt);
12141 }
12142 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12143 "channel-definitions", &arr);
12144 g_value_unset (&elt);
12145 g_value_unset (&arr);
12146 g_free (chan_def);
12147 }
12148
12149 /* some optional atoms */
12150 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
12151 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);
12152
12153 /* indicate possible fields in caps */
12154 if (field) {
12155 data = (guint8 *) field->data + 8;
12156 if (*data != 1)
12157 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12158 (gint) * data, NULL);
12159 }
12160 /* add codec_data if provided */
12161 if (prefix) {
12162 GstBuffer *buf;
12163 guint len;
12164
12165 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12166 data = prefix->data;
12167 len = QT_UINT32 (data);
12168 if (len > 0x8) {
12169 len -= 0x8;
12170 buf = gst_buffer_new_and_alloc (len);
12171 gst_buffer_fill (buf, 0, data + 8, len);
12172 gst_caps_set_simple (entry->caps,
12173 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12174 gst_buffer_unref (buf);
12175 }
12176 }
12177 break;
12178 }
12179 case FOURCC_SVQ3:
12180 case FOURCC_VP31:
12181 {
12182 GstBuffer *buf;
12183 GstBuffer *seqh = NULL;
12184 const guint8 *gamma_data = NULL;
12185 guint len = QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
12186
12187 qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
12188 &seqh);
12189 if (gamma_data) {
12190 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
12191 QT_FP32 (gamma_data), NULL);
12192 }
12193 if (seqh) {
12194 /* sorry for the bad name, but we don't know what this is, other
12195 * than its own fourcc */
12196 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12197 NULL);
12198 gst_buffer_unref (seqh);
12199 }
12200
12201 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12202 buf = gst_buffer_new_and_alloc (len);
12203 gst_buffer_fill (buf, 0, stsd_data, len);
12204 gst_caps_set_simple (entry->caps,
12205 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12206 gst_buffer_unref (buf);
12207 break;
12208 }
12209 case FOURCC_jpeg:
12210 {
12211 /* https://developer.apple.com/standards/qtff-2001.pdf,
12212 * page 92, "Video Sample Description", under table 3.1 */
12213 GstByteReader br;
12214
12215 const gint compressor_offset =
12216 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12217 const gint min_size = compressor_offset + 32 + 2 + 2;
12218 GNode *jpeg;
12219 guint32 len;
12220 guint16 color_table_id = 0;
12221 gboolean ok;
12222
12223 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12224
12225 /* recover information on interlaced/progressive */
12226 jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
12227 if (!jpeg)
12228 break;
12229
12230 len = QT_UINT32 (jpeg->data);
12231 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12232 min_size);
12233 if (len >= min_size) {
12234 gst_byte_reader_init (&br, jpeg->data, len);
12235
12236 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12237 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12238 if (color_table_id != 0) {
12239 /* the spec says there can be concatenated chunks in the data, and we want
12240 * to find one called field. Walk through them. */
12241 gint offset = min_size;
12242 while (offset + 8 < len) {
12243 guint32 size = 0, tag;
12244 ok = gst_byte_reader_get_uint32_le (&br, &size);
12245 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12246 if (!ok || size < 8) {
12247 GST_WARNING_OBJECT (qtdemux,
12248 "Failed to walk optional chunk list");
12249 break;
12250 }
12251 GST_DEBUG_OBJECT (qtdemux,
12252 "Found optional %4.4s chunk, size %u",
12253 (const char *) &tag, size);
12254 if (tag == FOURCC_fiel) {
12255 guint8 n_fields = 0, ordering = 0;
12256 gst_byte_reader_get_uint8 (&br, &n_fields);
12257 gst_byte_reader_get_uint8 (&br, &ordering);
12258 if (n_fields == 1 || n_fields == 2) {
12259 GST_DEBUG_OBJECT (qtdemux,
12260 "Found fiel tag with %u fields, ordering %u",
12261 n_fields, ordering);
12262 if (n_fields == 2)
12263 gst_caps_set_simple (CUR_STREAM (stream)->caps,
12264 "interlace-mode", G_TYPE_STRING, "interleaved",
12265 NULL);
12266 } else {
12267 GST_WARNING_OBJECT (qtdemux,
12268 "Found fiel tag with invalid fields (%u)", n_fields);
12269 }
12270 }
12271 offset += size;
12272 }
12273 } else {
12274 GST_DEBUG_OBJECT (qtdemux,
12275 "Color table ID is 0, not trying to get interlacedness");
12276 }
12277 } else {
12278 GST_WARNING_OBJECT (qtdemux,
12279 "Length of jpeg chunk is too small, not trying to get interlacedness");
12280 }
12281
12282 break;
12283 }
12284 case FOURCC_rle_:
12285 case FOURCC_WRLE:
12286 {
12287 gst_caps_set_simple (entry->caps,
12288 "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
12289 NULL);
12290 break;
12291 }
12292 case FOURCC_XiTh:
12293 {
12294 GNode *xith, *xdxt;
12295
12296 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
12297 xith = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12298 if (!xith)
12299 break;
12300
12301 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
12302 if (!xdxt)
12303 break;
12304
12305 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12306 /* collect the headers and store them in a stream list so that we can
12307 * send them out first */
12308 qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
12309 break;
12310 }
12311 case FOURCC_ovc1:
12312 {
12313 GNode *ovc1;
12314 guint8 *ovc1_data;
12315 guint ovc1_len;
12316 GstBuffer *buf;
12317
12318 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
12319 ovc1 = qtdemux_tree_get_child_by_index (stsd, stsd_index);
12320 if (!ovc1)
12321 break;
12322 ovc1_data = ovc1->data;
12323 ovc1_len = QT_UINT32 (ovc1_data);
12324 if (ovc1_len <= 198) {
12325 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12326 break;
12327 }
12328 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12329 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12330 gst_caps_set_simple (entry->caps,
12331 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12332 gst_buffer_unref (buf);
12333 break;
12334 }
12335 case FOURCC_vc_1:
12336 {
12337 guint len = QT_UINT32 (stsd_entry_data);
12338 len = len <= 0x56 ? 0 : len - 0x56;
12339 const guint8 *vc1_data = stsd_entry_data + 0x56;
12340
12341 /* find dvc1 */
12342 while (len >= 8) {
12343 guint size;
12344
12345 if (QT_UINT32 (vc1_data) <= 8)
12346 size = 0;
12347 else if (QT_UINT32 (vc1_data) <= len)
12348 size = QT_UINT32 (vc1_data) - 8;
12349 else
12350 size = len - 8;
12351
12352 if (size < 1)
12353 /* No real data, so break out */
12354 break;
12355
12356 switch (QT_FOURCC (vc1_data + 0x4)) {
12357 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12358 {
12359 GstBuffer *buf;
12360
12361 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12362 buf = gst_buffer_new_and_alloc (size);
12363 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12364 gst_caps_set_simple (entry->caps,
12365 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12366 gst_buffer_unref (buf);
12367 break;
12368 }
12369 default:
12370 break;
12371 }
12372 len -= size + 8;
12373 vc1_data += size + 8;
12374 }
12375 break;
12376 }
12377 case FOURCC_av01:
12378 {
12379 guint len = QT_UINT32 (stsd_entry_data);
12380 len = len <= 0x56 ? 0 : len - 0x56;
12381 const guint8 *av1_data = stsd_entry_data + 0x56;
12382
12383 /* find av1C */
12384 while (len >= 0x8) {
12385 guint size;
12386
12387 if (QT_UINT32 (av1_data) <= 0x8)
12388 size = 0;
12389 else if (QT_UINT32 (av1_data) <= len)
12390 size = QT_UINT32 (av1_data) - 0x8;
12391 else
12392 size = len - 0x8;
12393
12394 if (size < 1)
12395 /* No real data, so break out */
12396 break;
12397
12398 switch (QT_FOURCC (av1_data + 0x4)) {
12399 case FOURCC_av1C:
12400 {
12401 /* parse, if found */
12402 GstBuffer *buf;
12403
12404 GST_DEBUG_OBJECT (qtdemux,
12405 "found av1C codec_data in stsd of size %d", size);
12406
12407 /* not enough data, just ignore and hope for the best */
12408 if (size < 4)
12409 break;
12410
12411 /* Content is:
12412 * 4 bytes: atom length
12413 * 4 bytes: fourcc
12414 *
12415 * version 1 (marker=1):
12416 *
12417 * unsigned int (1) marker = 1;
12418 * unsigned int (7) version = 1;
12419 * unsigned int (3) seq_profile;
12420 * unsigned int (5) seq_level_idx_0;
12421 * unsigned int (1) seq_tier_0;
12422 * unsigned int (1) high_bitdepth;
12423 * unsigned int (1) twelve_bit;
12424 * unsigned int (1) monochrome;
12425 * unsigned int (1) chroma_subsampling_x;
12426 * unsigned int (1) chroma_subsampling_y;
12427 * unsigned int (2) chroma_sample_position;
12428 * unsigned int (3) reserved = 0;
12429 *
12430 * unsigned int (1) initial_presentation_delay_present;
12431 * if (initial_presentation_delay_present) {
12432 * unsigned int (4) initial_presentation_delay_minus_one;
12433 * } else {
12434 * unsigned int (4) reserved = 0;
12435 * }
12436 *
12437 * unsigned int (8) configOBUs[];
12438 *
12439 * rest: OBUs.
12440 */
12441
12442 switch (av1_data[8]) {
12443 case 0x81:{
12444 guint8 pres_delay_field;
12445
12446 /* We let profile and the other parts be figured out by
12447 * av1parse and only include the presentation delay here
12448 * if present */
12449 /* We skip initial_presentation_delay* for now */
12450 pres_delay_field = *(av1_data + 11);
12451 if (pres_delay_field & (1 << 5)) {
12452 gst_caps_set_simple (entry->caps,
12453 "presentation-delay", G_TYPE_INT,
12454 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12455 }
12456
12457 buf = gst_buffer_new_and_alloc (size);
12458 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12459 gst_buffer_fill (buf, 0, av1_data + 8, size);
12460 gst_caps_set_simple (entry->caps,
12461 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12462 gst_buffer_unref (buf);
12463 break;
12464 }
12465 default:
12466 GST_WARNING ("Unknown version 0x%02x of av1C box",
12467 av1_data[8]);
12468 break;
12469 }
12470
12471 break;
12472 }
zengliang.li54713f02024-05-17 02:46:54 +000012473 case FOURCC_dvcC:
12474 case FOURCC_dvvC:
12475 case FOURCC_dvwC:
12476 {
12477 guint32 dv_version_major = QT_UINT8(avc_data+8);
12478 guint32 dv_version_minor = QT_UINT8(avc_data+9);
12479 guint32 tmp = QT_UINT16(avc_data+10);
12480 guint32 dv_profile = (tmp >> 9) & 0x7f; // 7 bits
12481 guint32 dv_level = (tmp >> 3) & 0x3f; // 6 bits
12482 guint32 rpu_present_flag = (tmp >> 2) & 0x01;
12483 guint32 el_present_flag = (tmp >> 1) & 0x01; // 1 bit
12484 guint32 bl_present_flag = tmp & 0x01; // 1 bit
12485
12486 guint32 dv_bl_signal_compatibility_id = 0;// 0 stands for None
12487 if (len >= 24) {
12488 guint32 id = QT_UINT8(avc_data+12);
12489 dv_bl_signal_compatibility_id = (id >> 4) & 0x0f; // 4 bits
12490 }
12491 GST_DEBUG_OBJECT (qtdemux,"DOVI in dvcC/dvvC box, version: %d.%d, profile: %d, level: %d, "
12492 "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n",
12493 dv_version_major, dv_version_minor,
12494 dv_profile, dv_level,
12495 rpu_present_flag,
12496 el_present_flag,
12497 bl_present_flag,
12498 dv_bl_signal_compatibility_id
12499 );
12500 /*
12501 Expected results are:
12502 • If the test vector (MP4) carries an undefined/unknown Base Layer Signal Compatibility ID (dv_bl_signal_
12503 compatibility_id), the device under test rejects the playback.
12504 • If the test vector (MP4) is of an undefined/unknown Dolby Vision profile, but its Base Layer Signal
12505 Compatibility ID (dv_bl_signal_compatibility_id) is valid, the device under test plays the content
12506 properly and TV displays in Dolby Vision picture mode.
12507 Note: Level 11 metadata changes when the embedded label on the left side of the test pattern
12508 changes.
12509 • If the Dolby Vision configuration box contains unknown elements (reserved fields with non-zero value),
12510 the device under test ignores the unknown elements properly and play the MP4 test vector properly and
12511 TV displays in Dolby Vision picture mode.
12512 */
12513 if (dv_bl_signal_compatibility_id != 0 &&
12514 dv_bl_signal_compatibility_id != 1 &&
12515 dv_bl_signal_compatibility_id != 2 &&
12516 dv_bl_signal_compatibility_id != 4 &&
12517 dv_bl_signal_compatibility_id != 6) {
12518 GST_ERROR_OBJECT(qtdemux,"Not support dolby vision config box");
12519 goto corrupt_file;
12520 }
12521
12522 } break;
zengliang.li5f31ef42024-05-16 08:27:38 +000012523 default:
12524 break;
12525 }
12526
12527 len -= size + 8;
12528 av1_data += size + 8;
12529 }
12530
12531 break;
12532 }
12533
12534 /* TODO: Need to parse vpcC for VP8 codec too.
12535 * Note that VPCodecConfigurationBox (vpcC) is defined for
12536 * vp08, vp09, and vp10 fourcc. */
12537 case FOURCC_vp09:
12538 {
12539 guint len = QT_UINT32 (stsd_entry_data);
12540 len = len <= 0x56 ? 0 : len - 0x56;
12541 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12542
12543 /* find vpcC */
12544 while (len >= 0x8) {
12545 guint size;
12546
12547 if (QT_UINT32 (vpcc_data) <= 0x8)
12548 size = 0;
12549 else if (QT_UINT32 (vpcc_data) <= len)
12550 size = QT_UINT32 (vpcc_data) - 0x8;
12551 else
12552 size = len - 0x8;
12553
12554 if (size < 1)
12555 /* No real data, so break out */
12556 break;
12557
12558 switch (QT_FOURCC (vpcc_data + 0x4)) {
12559 case FOURCC_vpcC:
12560 {
12561 const gchar *profile_str = NULL;
12562 const gchar *chroma_format_str = NULL;
12563 guint8 profile;
12564 guint8 bitdepth;
12565 guint8 chroma_format;
12566 GstVideoColorimetry cinfo;
12567
12568 /* parse, if found */
12569 GST_DEBUG_OBJECT (qtdemux,
12570 "found vp codec_data in stsd of size %d", size);
12571
12572 /* the meaning of "size" is length of the atom body, excluding
12573 * atom length and fourcc fields */
12574 if (size < 12)
12575 break;
12576
12577 /* Content is:
12578 * 4 bytes: atom length
12579 * 4 bytes: fourcc
12580 * 1 byte: version
12581 * 3 bytes: flags
12582 * 1 byte: profile
12583 * 1 byte: level
12584 * 4 bits: bitDepth
12585 * 3 bits: chromaSubsampling
12586 * 1 bit: videoFullRangeFlag
12587 * 1 byte: colourPrimaries
12588 * 1 byte: transferCharacteristics
12589 * 1 byte: matrixCoefficients
12590 * 2 bytes: codecInitializationDataSize (should be zero for vp8 and vp9)
12591 * rest: codecInitializationData (not used for vp8 and vp9)
12592 */
12593
12594 if (vpcc_data[8] != 1) {
12595 GST_WARNING_OBJECT (qtdemux,
12596 "unknown vpcC version %d", vpcc_data[8]);
12597 break;
12598 }
12599
12600 profile = vpcc_data[12];
12601 switch (profile) {
12602 case 0:
12603 profile_str = "0";
12604 break;
12605 case 1:
12606 profile_str = "1";
12607 break;
12608 case 2:
12609 profile_str = "2";
12610 break;
12611 case 3:
12612 profile_str = "3";
12613 break;
12614 default:
12615 break;
12616 }
12617
12618 if (profile_str) {
12619 gst_caps_set_simple (entry->caps,
12620 "profile", G_TYPE_STRING, profile_str, NULL);
12621 }
12622
12623 /* skip level, the VP9 spec v0.6 defines only one level atm,
12624 * but webm spec define various ones. Add level to caps
12625 * if we really need it then */
12626
12627 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
12628 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
12629 gst_caps_set_simple (entry->caps,
12630 "bit-depth-luma", G_TYPE_UINT, bitdepth,
12631 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
12632 }
12633
12634 chroma_format = (vpcc_data[14] & 0xe) >> 1;
12635 switch (chroma_format) {
12636 case 0:
12637 case 1:
12638 chroma_format_str = "4:2:0";
12639 break;
12640 case 2:
12641 chroma_format_str = "4:2:2";
12642 break;
12643 case 3:
12644 chroma_format_str = "4:4:4";
12645 break;
12646 default:
12647 break;
12648 }
12649
12650 if (chroma_format_str) {
12651 gst_caps_set_simple (entry->caps,
12652 "chroma-format", G_TYPE_STRING, chroma_format_str,
12653 NULL);
12654 }
12655
12656 if ((vpcc_data[14] & 0x1) != 0)
12657 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
12658 else
12659 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
12660 cinfo.primaries =
12661 gst_video_color_primaries_from_iso (vpcc_data[15]);
12662 cinfo.transfer =
12663 gst_video_transfer_function_from_iso (vpcc_data[16]);
12664 cinfo.matrix =
12665 gst_video_color_matrix_from_iso (vpcc_data[17]);
12666
12667 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
12668 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
12669 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
12670 /* set this only if all values are known, otherwise this
12671 * might overwrite valid ones parsed from other color box */
12672 CUR_STREAM (stream)->colorimetry = cinfo;
12673 }
12674 break;
12675 }
12676 default:
12677 break;
12678 }
12679
12680 len -= size + 8;
12681 vpcc_data += size + 8;
12682 }
12683
12684 break;
12685 }
12686 default:
12687 break;
12688 }
12689 }
12690
12691 GST_INFO_OBJECT (qtdemux,
12692 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
12693 GST_FOURCC_ARGS (fourcc), entry->caps);
12694
12695 } else if (stream->subtype == FOURCC_soun) {
12696 GNode *wave;
12697 guint version, samplesize;
12698 guint16 compression_id;
12699 gboolean amrwb = FALSE;
12700
12701 offset = 16;
12702 /* sample description entry (16) + sound sample description v0 (20) */
12703 if (len < 36)
12704 goto corrupt_file;
12705
12706 version = QT_UINT32 (stsd_entry_data + offset);
12707 entry->n_channels = QT_UINT16 (stsd_entry_data + offset + 8);
12708 samplesize = QT_UINT16 (stsd_entry_data + offset + 10);
12709 compression_id = QT_UINT16 (stsd_entry_data + offset + 12);
12710 entry->rate = QT_FP32 (stsd_entry_data + offset + 16);
12711
12712 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
12713 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
12714 QT_UINT32 (stsd_entry_data + offset + 4));
12715 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12716 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
12717 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
12718 GST_LOG_OBJECT (qtdemux, "packet size: %d",
12719 QT_UINT16 (stsd_entry_data + offset + 14));
12720 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12721
12722 if (compression_id == 0xfffe)
12723 entry->sampled = TRUE;
12724
12725 /* first assume uncompressed audio */
12726 entry->bytes_per_sample = samplesize / 8;
12727 entry->samples_per_frame = entry->n_channels;
12728 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12729 entry->samples_per_packet = entry->samples_per_frame;
12730 entry->bytes_per_packet = entry->bytes_per_sample;
12731
12732 offset = 36;
12733
12734 if (version == 0x00010000) {
12735 /* sample description entry (16) + sound sample description v1 (20+16) */
12736 if (len < 52)
12737 goto corrupt_file;
12738
12739 /* take information from here over the normal sample description */
12740 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset);
12741 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 4);
12742 entry->bytes_per_frame = QT_UINT32 (stsd_entry_data + offset + 8);
12743 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 12);
12744
12745 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
12746 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
12747 entry->samples_per_packet);
12748 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12749 entry->bytes_per_packet);
12750 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
12751 entry->bytes_per_frame);
12752 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
12753 entry->bytes_per_sample);
12754
12755 if (!entry->sampled && entry->bytes_per_packet) {
12756 entry->samples_per_frame = (entry->bytes_per_frame /
12757 entry->bytes_per_packet) * entry->samples_per_packet;
12758 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
12759 entry->samples_per_frame);
12760 }
12761 } else if (version == 0x00020000) {
12762 /* sample description entry (16) + sound sample description v2 (56) */
12763 if (len < 72)
12764 goto corrupt_file;
12765
12766 /* take information from here over the normal sample description */
12767 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
12768 entry->n_channels = QT_UINT32 (stsd_entry_data + offset + 12);
12769 entry->samples_per_frame = entry->n_channels;
12770 entry->bytes_per_sample = QT_UINT32 (stsd_entry_data + offset + 20) / 8;
12771 entry->bytes_per_packet = QT_UINT32 (stsd_entry_data + offset + 28);
12772 entry->samples_per_packet = QT_UINT32 (stsd_entry_data + offset + 32);
12773 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
12774
12775 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
12776 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
12777 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
12778 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
12779 entry->bytes_per_sample * 8);
12780 GST_LOG_OBJECT (qtdemux, "format flags: %X",
12781 QT_UINT32 (stsd_entry_data + offset + 24));
12782 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
12783 entry->bytes_per_packet);
12784 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
12785 entry->samples_per_packet);
12786 } else if (version != 0x00000) {
12787 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
12788 version);
12789 }
12790
12791 switch (fourcc) {
12792 /* Yes, these have to be hard-coded */
12793 case FOURCC_MAC6:
12794 {
12795 entry->samples_per_packet = 6;
12796 entry->bytes_per_packet = 1;
12797 entry->bytes_per_frame = 1 * entry->n_channels;
12798 entry->bytes_per_sample = 1;
12799 entry->samples_per_frame = 6 * entry->n_channels;
12800 break;
12801 }
12802 case FOURCC_MAC3:
12803 {
12804 entry->samples_per_packet = 3;
12805 entry->bytes_per_packet = 1;
12806 entry->bytes_per_frame = 1 * entry->n_channels;
12807 entry->bytes_per_sample = 1;
12808 entry->samples_per_frame = 3 * entry->n_channels;
12809 break;
12810 }
12811 case FOURCC_ima4:
12812 {
12813 entry->samples_per_packet = 64;
12814 entry->bytes_per_packet = 34;
12815 entry->bytes_per_frame = 34 * entry->n_channels;
12816 entry->bytes_per_sample = 2;
12817 entry->samples_per_frame = 64 * entry->n_channels;
12818 break;
12819 }
12820 case FOURCC_ulaw:
12821 case FOURCC_alaw:
12822 {
12823 entry->samples_per_packet = 1;
12824 entry->bytes_per_packet = 1;
12825 entry->bytes_per_frame = 1 * entry->n_channels;
12826 entry->bytes_per_sample = 1;
12827 entry->samples_per_frame = 1 * entry->n_channels;
12828 break;
12829 }
12830 case FOURCC_agsm:
12831 {
12832 entry->samples_per_packet = 160;
12833 entry->bytes_per_packet = 33;
12834 entry->bytes_per_frame = 33 * entry->n_channels;
12835 entry->bytes_per_sample = 2;
12836 entry->samples_per_frame = 160 * entry->n_channels;
12837 break;
12838 }
12839 /* fix up any invalid header information from above */
12840 case FOURCC_twos:
12841 case FOURCC_sowt:
12842 case FOURCC_raw_:
12843 case FOURCC_lpcm:
12844 /* Sometimes these are set to 0 in the sound sample descriptions so
12845 * let's try to infer useful values from the other information we
12846 * have available */
12847 if (entry->bytes_per_sample == 0)
12848 entry->bytes_per_sample =
12849 entry->bytes_per_frame / entry->n_channels;
12850 if (entry->bytes_per_sample == 0)
12851 entry->bytes_per_sample = samplesize / 8;
12852
12853 if (entry->bytes_per_frame == 0)
12854 entry->bytes_per_frame =
12855 entry->bytes_per_sample * entry->n_channels;
12856
12857 if (entry->bytes_per_packet == 0)
12858 entry->bytes_per_packet = entry->bytes_per_sample;
12859
12860 if (entry->samples_per_frame == 0)
12861 entry->samples_per_frame = entry->n_channels;
12862
12863 if (entry->samples_per_packet == 0)
12864 entry->samples_per_packet = entry->samples_per_frame;
12865
12866 break;
12867 case FOURCC_in24:
12868 case FOURCC_in32:
12869 case FOURCC_fl32:
12870 case FOURCC_fl64:
12871 case FOURCC_s16l:{
12872 switch (fourcc) {
12873 case FOURCC_in24:
12874 entry->bytes_per_sample = 3;
12875 break;
12876 case FOURCC_in32:
12877 case FOURCC_fl32:
12878 entry->bytes_per_sample = 4;
12879 break;
12880 case FOURCC_fl64:
12881 entry->bytes_per_sample = 8;
12882 break;
12883 case FOURCC_s16l:
12884 entry->bytes_per_sample = 2;
12885 break;
12886 default:
12887 g_assert_not_reached ();
12888 break;
12889 }
12890 entry->samples_per_frame = entry->n_channels;
12891 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
12892 entry->samples_per_packet = entry->samples_per_frame;
12893 entry->bytes_per_packet = entry->bytes_per_sample;
12894 break;
12895 }
12896 default:
12897 break;
12898 }
12899
12900 if (entry->caps)
12901 gst_caps_unref (entry->caps);
12902
12903 entry->caps = qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
12904 stsd_entry_data + 32, len - 16, &codec);
12905
12906 switch (fourcc) {
12907 case FOURCC_in24:
12908 case FOURCC_in32:
12909 case FOURCC_fl32:
12910 case FOURCC_fl64:
12911 {
12912 GNode *enda;
12913 GNode *fmt;
12914
12915 fmt = qtdemux_tree_get_child_by_type (stsd, fourcc);
12916
12917 enda = qtdemux_tree_get_child_by_type (fmt, FOURCC_enda);
12918 if (!enda) {
12919 wave = qtdemux_tree_get_child_by_type (fmt, FOURCC_wave);
12920 if (wave)
12921 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
12922 }
12923 if (enda) {
12924 int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
12925 const gchar *format_str;
12926
12927 switch (fourcc) {
12928 case FOURCC_in24:
12929 format_str = (enda_value) ? "S24LE" : "S24BE";
12930 break;
12931 case FOURCC_in32:
12932 format_str = (enda_value) ? "S32LE" : "S32BE";
12933 break;
12934 case FOURCC_fl32:
12935 format_str = (enda_value) ? "F32LE" : "F32BE";
12936 break;
12937 case FOURCC_fl64:
12938 format_str = (enda_value) ? "F64LE" : "F64BE";
12939 break;
12940 default:
12941 g_assert_not_reached ();
12942 break;
12943 }
12944 gst_caps_set_simple (entry->caps,
12945 "format", G_TYPE_STRING, format_str, NULL);
12946 }
12947 break;
12948 }
12949 case FOURCC_owma:
12950 {
12951 const guint8 *owma_data;
12952 const gchar *codec_name = NULL;
12953 guint owma_len;
12954 GstBuffer *buf;
12955 gint version = 1;
12956 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
12957 /* FIXME this should also be gst_riff_strf_auds,
12958 * but the latter one is actually missing bits-per-sample :( */
12959 typedef struct
12960 {
12961 gint16 wFormatTag;
12962 gint16 nChannels;
12963 gint32 nSamplesPerSec;
12964 gint32 nAvgBytesPerSec;
12965 gint16 nBlockAlign;
12966 gint16 wBitsPerSample;
12967 gint16 cbSize;
12968 } WAVEFORMATEX;
12969 WAVEFORMATEX *wfex;
12970
12971 GST_DEBUG_OBJECT (qtdemux, "parse owma");
12972 owma_data = stsd_entry_data;
12973 owma_len = QT_UINT32 (owma_data);
12974 if (owma_len <= 54) {
12975 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
12976 break;
12977 }
12978 wfex = (WAVEFORMATEX *) (owma_data + 36);
12979 buf = gst_buffer_new_and_alloc (owma_len - 54);
12980 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
12981 if (wfex->wFormatTag == 0x0161) {
12982 codec_name = "Windows Media Audio";
12983 version = 2;
12984 } else if (wfex->wFormatTag == 0x0162) {
12985 codec_name = "Windows Media Audio 9 Pro";
12986 version = 3;
12987 } else if (wfex->wFormatTag == 0x0163) {
12988 codec_name = "Windows Media Audio 9 Lossless";
12989 /* is that correct? gstffmpegcodecmap.c is missing it, but
12990 * fluendo codec seems to support it */
12991 version = 4;
12992 }
12993
12994 gst_caps_set_simple (entry->caps,
12995 "codec_data", GST_TYPE_BUFFER, buf,
12996 "wmaversion", G_TYPE_INT, version,
12997 "block_align", G_TYPE_INT,
12998 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
12999 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13000 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13001 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13002 gst_buffer_unref (buf);
13003
13004 if (codec_name) {
13005 g_free (codec);
13006 codec = g_strdup (codec_name);
13007 }
13008 break;
13009 }
13010 case FOURCC_wma_:
13011 {
13012 guint len = QT_UINT32 (stsd_entry_data);
13013 len = len <= offset ? 0 : len - offset;
13014 const guint8 *wfex_data = stsd_entry_data + offset;
13015 const gchar *codec_name = NULL;
13016 gint version = 1;
13017 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13018 /* FIXME this should also be gst_riff_strf_auds,
13019 * but the latter one is actually missing bits-per-sample :( */
13020 typedef struct
13021 {
13022 gint16 wFormatTag;
13023 gint16 nChannels;
13024 gint32 nSamplesPerSec;
13025 gint32 nAvgBytesPerSec;
13026 gint16 nBlockAlign;
13027 gint16 wBitsPerSample;
13028 gint16 cbSize;
13029 } WAVEFORMATEX;
13030 WAVEFORMATEX wfex;
13031
13032 /* FIXME: unify with similar wavformatex parsing code above */
13033 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13034
13035 /* find wfex */
13036 while (len >= 8) {
13037 guint size;
13038
13039 if (QT_UINT32 (wfex_data) <= 0x8)
13040 size = 0;
13041 else if (QT_UINT32 (wfex_data) <= len)
13042 size = QT_UINT32 (wfex_data) - 8;
13043 else
13044 size = len - 8;
13045
13046 if (size < 1)
13047 /* No real data, so break out */
13048 break;
13049
13050 switch (QT_FOURCC (wfex_data + 4)) {
13051 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13052 {
13053 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13054
13055 if (size < 8 + 18)
13056 break;
13057
13058 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13059 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13060 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13061 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13062 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13063 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13064 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13065
13066 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13067 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13068 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13069 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13070 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13071 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13072
13073 if (wfex.wFormatTag == 0x0161) {
13074 codec_name = "Windows Media Audio";
13075 version = 2;
13076 } else if (wfex.wFormatTag == 0x0162) {
13077 codec_name = "Windows Media Audio 9 Pro";
13078 version = 3;
13079 } else if (wfex.wFormatTag == 0x0163) {
13080 codec_name = "Windows Media Audio 9 Lossless";
13081 /* is that correct? gstffmpegcodecmap.c is missing it, but
13082 * fluendo codec seems to support it */
13083 version = 4;
13084 }
13085
13086 gst_caps_set_simple (entry->caps,
13087 "wmaversion", G_TYPE_INT, version,
13088 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13089 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13090 "width", G_TYPE_INT, wfex.wBitsPerSample,
13091 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13092
13093 if (size > wfex.cbSize) {
13094 GstBuffer *buf;
13095
13096 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13097 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13098 size - wfex.cbSize);
13099 gst_caps_set_simple (entry->caps,
13100 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13101 gst_buffer_unref (buf);
13102 } else {
13103 GST_WARNING_OBJECT (qtdemux, "no codec data");
13104 }
13105
13106 if (codec_name) {
13107 g_free (codec);
13108 codec = g_strdup (codec_name);
13109 }
13110 break;
13111 }
13112 default:
13113 break;
13114 }
13115 len -= size + 8;
13116 wfex_data += size + 8;
13117 }
13118 break;
13119 }
13120 case FOURCC_opus:
13121 {
13122 const guint8 *dops_data;
13123 guint8 *channel_mapping = NULL;
13124 guint32 rate;
13125 guint8 channels;
13126 guint8 channel_mapping_family;
13127 guint8 stream_count;
13128 guint8 coupled_count;
13129 guint8 i;
13130
13131 version = GST_READ_UINT16_BE (stsd_entry_data + 16);
13132 if (version == 1)
13133 dops_data = stsd_entry_data + 51;
13134 else
13135 dops_data = stsd_entry_data + 35;
13136
13137 channels = GST_READ_UINT8 (dops_data + 10);
13138 rate = GST_READ_UINT32_LE (dops_data + 13);
13139 channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
13140 stream_count = GST_READ_UINT8 (dops_data + 20);
13141 coupled_count = GST_READ_UINT8 (dops_data + 21);
13142
13143 if (channels > 0) {
13144 channel_mapping = g_malloc (channels * sizeof (guint8));
13145 for (i = 0; i < channels; i++)
13146 channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
13147 }
13148
13149 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
13150 channel_mapping_family, stream_count, coupled_count,
13151 channel_mapping);
13152 g_free (channel_mapping);
13153 break;
13154 }
13155 default:
13156 break;
13157 }
13158
13159 if (codec) {
13160 GstStructure *s;
13161 gint bitrate = 0;
13162
13163 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13164 GST_TAG_AUDIO_CODEC, codec, NULL);
13165 g_free (codec);
13166 codec = NULL;
13167
13168 /* some bitrate info may have ended up in caps */
13169 s = gst_caps_get_structure (entry->caps, 0);
13170 gst_structure_get_int (s, "bitrate", &bitrate);
13171 if (bitrate > 0)
13172 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13173 GST_TAG_BITRATE, bitrate, NULL);
13174 }
13175
13176 esds = NULL;
13177 mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
13178 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
13179 if (stream->protected) {
13180 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
13181 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13182 }
13183 if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
13184 mp4a = NULL;
13185 }
13186 } else {
13187 mp4a = NULL;
13188 }
13189 }
13190
13191 wave = NULL;
13192 if (mp4a) {
13193 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
13194 if (wave)
13195 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
13196 if (!esds)
13197 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
13198 }
13199
13200
13201 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13202 16 bits is a byte-swapped wave-style codec identifier,
13203 and we can find a WAVE header internally to a 'wave' atom here.
13204 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13205 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13206 is big-endian).
13207 */
13208 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13209 if (len < offset + 20) {
13210 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13211 } else {
13212 guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
13213 const guint8 *data = stsd_entry_data + offset + 16;
13214 GNode *wavenode;
13215 GNode *waveheadernode;
13216
13217 wavenode = g_node_new ((guint8 *) data);
13218 if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
13219 const guint8 *waveheader;
13220 guint32 headerlen;
13221
13222 waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
13223 if (waveheadernode) {
13224 waveheader = (const guint8 *) waveheadernode->data;
13225 headerlen = QT_UINT32 (waveheader);
13226
13227 if (headerlen > 8) {
13228 gst_riff_strf_auds *header = NULL;
13229 GstBuffer *headerbuf;
13230 GstBuffer *extra;
13231
13232 waveheader += 8;
13233 headerlen -= 8;
13234
13235 headerbuf = gst_buffer_new_and_alloc (headerlen);
13236 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13237
13238 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13239 headerbuf, &header, &extra)) {
13240 gst_caps_unref (entry->caps);
13241 /* FIXME: Need to do something with the channel reorder map */
13242 entry->caps =
13243 gst_riff_create_audio_caps (header->format, NULL, header,
13244 extra, NULL, NULL, NULL);
13245
13246 if (extra)
13247 gst_buffer_unref (extra);
13248 g_free (header);
13249 }
13250 }
13251 } else
13252 GST_DEBUG ("Didn't find waveheadernode for this codec");
13253 }
13254 g_node_destroy (wavenode);
13255 }
13256 } else if (esds) {
13257 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13258 stream->stream_tags);
13259 } else {
13260 switch (fourcc) {
13261#if 0
13262 /* FIXME: what is in the chunk? */
13263 case FOURCC_QDMC:
13264 {
13265 gint len = QT_UINT32 (stsd_data);
13266
13267 /* seems to be always = 116 = 0x74 */
13268 break;
13269 }
13270#endif
13271 case FOURCC_QDM2:
13272 {
13273 gint len = QT_UINT32 (stsd_entry_data);
13274
13275 if (len > 0x3C) {
13276 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13277
13278 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13279 gst_caps_set_simple (entry->caps,
13280 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13281 gst_buffer_unref (buf);
13282 }
13283 gst_caps_set_simple (entry->caps,
13284 "samplesize", G_TYPE_INT, samplesize, NULL);
13285 break;
13286 }
13287 case FOURCC_alac:
13288 {
13289 GNode *alac, *wave = NULL;
13290
13291 /* apparently, m4a has this atom appended directly in the stsd entry,
13292 * while mov has it in a wave atom */
13293 alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
13294 if (alac) {
13295 /* alac now refers to stsd entry atom */
13296 wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
13297 if (wave)
13298 alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
13299 else
13300 alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
13301 }
13302 if (alac) {
13303 const guint8 *alac_data = alac->data;
13304 gint len = QT_UINT32 (alac->data);
13305 GstBuffer *buf;
13306
13307 if (len < 36) {
13308 GST_DEBUG_OBJECT (qtdemux,
13309 "discarding alac atom with unexpected len %d", len);
13310 } else {
13311 /* codec-data contains alac atom size and prefix,
13312 * ffmpeg likes it that way, not quite gst-ish though ...*/
13313 buf = gst_buffer_new_and_alloc (len);
13314 gst_buffer_fill (buf, 0, alac->data, len);
13315 gst_caps_set_simple (entry->caps,
13316 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13317 gst_buffer_unref (buf);
13318
13319 entry->bytes_per_frame = QT_UINT32 (alac_data + 12);
13320 entry->n_channels = QT_UINT8 (alac_data + 21);
13321 entry->rate = QT_UINT32 (alac_data + 32);
13322 samplesize = QT_UINT8 (alac_data + 16 + 1);
13323 }
13324 }
13325 gst_caps_set_simple (entry->caps,
13326 "samplesize", G_TYPE_INT, samplesize, NULL);
13327 break;
13328 }
13329 case FOURCC_fLaC:
13330 {
13331 /* The codingname of the sample entry is 'fLaC' */
13332 GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);
13333
13334 if (flac) {
13335 /* The 'dfLa' box is added to the sample entry to convey
13336 initializing information for the decoder. */
13337 const GNode *dfla =
13338 qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);
13339
13340 if (dfla) {
13341 const guint32 len = QT_UINT32 (dfla->data);
13342
13343 /* Must contain at least dfLa box header (12),
13344 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13345 if (len < 50) {
13346 GST_DEBUG_OBJECT (qtdemux,
13347 "discarding dfla atom with unexpected len %d", len);
13348 } else {
13349 /* skip dfLa header to get the METADATA_BLOCKs */
13350 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13351 const guint32 metadata_blocks_len = len - 12;
13352
13353 gchar *stream_marker = g_strdup ("fLaC");
13354 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13355 strlen (stream_marker));
13356
13357 guint32 index = 0;
13358 guint32 remainder = 0;
13359 guint32 block_size = 0;
13360 gboolean is_last = FALSE;
13361
13362 GValue array = G_VALUE_INIT;
13363 GValue value = G_VALUE_INIT;
13364
13365 g_value_init (&array, GST_TYPE_ARRAY);
13366 g_value_init (&value, GST_TYPE_BUFFER);
13367
13368 gst_value_set_buffer (&value, block);
13369 gst_value_array_append_value (&array, &value);
13370 g_value_reset (&value);
13371
13372 gst_buffer_unref (block);
13373
13374 /* check there's at least one METADATA_BLOCK_HEADER's worth
13375 * of data, and we haven't already finished parsing */
13376 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13377 remainder = metadata_blocks_len - index;
13378
13379 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13380 block_size = 4 +
13381 (metadata_blocks[index + 1] << 16) +
13382 (metadata_blocks[index + 2] << 8) +
13383 metadata_blocks[index + 3];
13384
13385 /* be careful not to read off end of box */
13386 if (block_size > remainder) {
13387 break;
13388 }
13389
13390 is_last = metadata_blocks[index] >> 7;
13391
13392 block = gst_buffer_new_and_alloc (block_size);
13393
13394 gst_buffer_fill (block, 0, &metadata_blocks[index],
13395 block_size);
13396
13397 gst_value_set_buffer (&value, block);
13398 gst_value_array_append_value (&array, &value);
13399 g_value_reset (&value);
13400
13401 gst_buffer_unref (block);
13402
13403 index += block_size;
13404 }
13405
13406 /* only append the metadata if we successfully read all of it */
13407 if (is_last) {
13408 gst_structure_set_value (gst_caps_get_structure (CUR_STREAM
13409 (stream)->caps, 0), "streamheader", &array);
13410 } else {
13411 GST_WARNING_OBJECT (qtdemux,
13412 "discarding all METADATA_BLOCKs due to invalid "
13413 "block_size %d at idx %d, rem %d", block_size, index,
13414 remainder);
13415 }
13416
13417 g_value_unset (&value);
13418 g_value_unset (&array);
13419
13420 /* The sample rate obtained from the stsd may not be accurate
13421 * since it cannot represent rates greater than 65535Hz, so
13422 * override that value with the sample rate from the
13423 * METADATA_BLOCK_STREAMINFO block */
13424 CUR_STREAM (stream)->rate =
13425 (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
13426 }
13427 }
13428 }
13429 break;
13430 }
13431 case FOURCC_sawb:
13432 /* Fallthrough! */
13433 amrwb = TRUE;
13434 case FOURCC_samr:
13435 {
13436 gint len = QT_UINT32 (stsd_entry_data);
13437
13438 if (len > 0x24) {
13439 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13440 guint bitrate;
13441
13442 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13443
13444 /* If we have enough data, let's try to get the 'damr' atom. See
13445 * the 3GPP container spec (26.244) for more details. */
13446 if ((len - 0x34) > 8 &&
13447 (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
13448 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13449 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13450 }
13451
13452 gst_caps_set_simple (entry->caps,
13453 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13454 gst_buffer_unref (buf);
13455 }
13456 break;
13457 }
13458 case FOURCC_mp4a:
13459 {
13460 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
13461 gint len = QT_UINT32 (stsd_entry_data);
13462 guint16 sound_version = 0;
13463 /* FIXME: Can this be determined somehow? There doesn't seem to be
13464 * anything in mp4a atom that specifis compression */
13465 gint profile = 2;
13466 guint16 channels = entry->n_channels;
13467 guint32 time_scale = (guint32) entry->rate;
13468 gint sample_rate_index = -1;
13469
13470 if (len >= 34) {
13471 sound_version = QT_UINT16 (stsd_entry_data + 16);
13472
13473 if (sound_version == 1) {
13474 channels = QT_UINT16 (stsd_entry_data + 24);
13475 time_scale = QT_UINT32 (stsd_entry_data + 30);
13476 } else {
13477 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13478 sound_version);
13479 }
13480 } else {
13481 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13482 len);
13483 }
13484
13485 sample_rate_index =
13486 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13487 if (sample_rate_index >= 0 && channels > 0) {
13488 guint8 codec_data[2];
13489 GstBuffer *buf;
13490
13491 /* build AAC codec data */
13492 codec_data[0] = profile << 3;
13493 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13494 codec_data[1] = (sample_rate_index & 0x01) << 7;
13495 codec_data[1] |= (channels & 0xF) << 3;
13496
13497 buf = gst_buffer_new_and_alloc (2);
13498 gst_buffer_fill (buf, 0, codec_data, 2);
13499 gst_caps_set_simple (entry->caps,
13500 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13501 gst_buffer_unref (buf);
13502 }
13503 break;
13504 }
13505 case FOURCC_lpcm:
13506 case FOURCC_in24:
13507 case FOURCC_in32:
13508 case FOURCC_fl32:
13509 case FOURCC_fl64:
13510 case FOURCC_s16l:
13511 /* Fully handled elsewhere */
13512 break;
13513 default:
13514 GST_INFO_OBJECT (qtdemux,
13515 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13516 break;
13517 }
13518 }
13519 GST_INFO_OBJECT (qtdemux,
13520 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13521 GST_FOURCC_ARGS (fourcc), entry->caps);
13522
13523 } else if (stream->subtype == FOURCC_strm) {
13524 if (fourcc == FOURCC_rtsp) {
13525 stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
13526 } else {
13527 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
13528 GST_FOURCC_ARGS (fourcc));
13529 goto unknown_stream;
13530 }
13531 entry->sampled = TRUE;
13532 } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
13533 || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
13534 || stream->subtype == FOURCC_clcp || stream->subtype == FOURCC_wvtt) {
13535
13536 entry->sampled = TRUE;
13537 entry->sparse = TRUE;
13538
13539 entry->caps =
13540 qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13541 &codec);
13542 if (codec) {
13543 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13544 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13545 g_free (codec);
13546 codec = NULL;
13547 }
13548
13549 /* hunt for sort-of codec data */
13550 switch (fourcc) {
13551 case FOURCC_mp4s:
13552 {
13553 GNode *mp4s = NULL;
13554 GNode *esds = NULL;
13555
13556 /* look for palette in a stsd->mp4s->esds sub-atom */
13557 mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
13558 if (mp4s)
13559 esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
13560 if (esds == NULL) {
13561 /* Invalid STSD */
13562 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13563 break;
13564 }
13565
13566 gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
13567 stream->stream_tags);
13568 break;
13569 }
13570 default:
13571 GST_INFO_OBJECT (qtdemux,
13572 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13573 break;
13574 }
13575 GST_INFO_OBJECT (qtdemux,
13576 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13577 GST_FOURCC_ARGS (fourcc), entry->caps);
13578 } else {
13579 /* everything in 1 sample */
13580 entry->sampled = TRUE;
13581
13582 entry->caps =
13583 qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
13584 &codec);
13585
13586 if (entry->caps == NULL)
13587 goto unknown_stream;
13588
13589 if (codec) {
13590 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13591 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13592 g_free (codec);
13593 codec = NULL;
13594 }
13595 }
13596
13597 /* promote to sampled format */
13598 if (entry->fourcc == FOURCC_samr) {
13599 /* force mono 8000 Hz for AMR */
13600 entry->sampled = TRUE;
13601 entry->n_channels = 1;
13602 entry->rate = 8000;
13603 } else if (entry->fourcc == FOURCC_sawb) {
13604 /* force mono 16000 Hz for AMR-WB */
13605 entry->sampled = TRUE;
13606 entry->n_channels = 1;
13607 entry->rate = 16000;
13608 } else if (entry->fourcc == FOURCC_mp4a) {
13609 entry->sampled = TRUE;
13610 }
13611
13612
13613 stsd_entry_data += len;
13614 remaining_stsd_len -= len;
13615
13616 }
13617
13618 /* collect sample information */
13619 if (!qtdemux_stbl_init (qtdemux, stream, stbl))
13620 goto samples_failed;
13621
13622 if (qtdemux->fragmented) {
13623 guint64 offset;
13624
13625 /* need all moov samples as basis; probably not many if any at all */
13626 /* prevent moof parsing taking of at this time */
13627 offset = qtdemux->moof_offset;
13628 qtdemux->moof_offset = 0;
13629 if (stream->n_samples &&
13630 !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
13631 qtdemux->moof_offset = offset;
13632 goto samples_failed;
13633 }
13634 qtdemux->moof_offset = offset;
13635 /* movie duration more reliable in this case (e.g. mehd) */
13636 if (qtdemux->segment.duration &&
13637 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13638 stream->duration =
13639 GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
13640 }
13641
13642 /* configure segments */
13643 if (!qtdemux_parse_segments (qtdemux, stream, trak))
13644 goto segments_failed;
13645
13646 /* add some language tag, if useful */
13647 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13648 strcmp (stream->lang_id, "und")) {
13649 const gchar *lang_code;
13650
13651 /* convert ISO 639-2 code to ISO 639-1 */
13652 lang_code = gst_tag_get_language_code (stream->lang_id);
13653 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13654 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13655 }
13656
13657 /* Check for UDTA tags */
13658 if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
13659 qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
13660 }
13661
13662 /* Insert and sort new stream in track-id order.
13663 * This will help in comparing old/new streams during stream update check */
13664 g_ptr_array_add (qtdemux->active_streams, stream);
13665 g_ptr_array_sort (qtdemux->active_streams,
13666 (GCompareFunc) qtdemux_track_id_compare_func);
13667 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
13668 QTDEMUX_N_STREAMS (qtdemux));
13669
13670 return TRUE;
13671
13672/* ERRORS */
13673corrupt_file:
13674 {
13675 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
13676 (_("This file is corrupt and cannot be played.")), (NULL));
13677 if (stream)
13678 gst_qtdemux_stream_unref (stream);
13679 return FALSE;
13680 }
13681error_encrypted:
13682 {
13683 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
13684 gst_qtdemux_stream_unref (stream);
13685 return FALSE;
13686 }
13687samples_failed:
13688segments_failed:
13689 {
13690 /* we posted an error already */
13691 /* free stbl sub-atoms */
13692 gst_qtdemux_stbl_free (stream);
13693 gst_qtdemux_stream_unref (stream);
13694 return FALSE;
13695 }
13696existing_stream:
13697 {
13698 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
13699 track_id);
13700 return TRUE;
13701 }
13702unknown_stream:
13703 {
13704 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
13705 GST_FOURCC_ARGS (stream->subtype));
13706 gst_qtdemux_stream_unref (stream);
13707 return TRUE;
13708 }
13709}
13710
13711/* If we can estimate the overall bitrate, and don't have information about the
13712 * stream bitrate for exactly one stream, this guesses the stream bitrate as
13713 * the overall bitrate minus the sum of the bitrates of all other streams. This
13714 * should be useful for the common case where we have one audio and one video
13715 * stream and can estimate the bitrate of one, but not the other. */
13716static void
13717gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
13718{
13719 QtDemuxStream *stream = NULL;
13720 gint64 size, sys_bitrate, sum_bitrate = 0;
13721 GstClockTime duration;
13722 guint bitrate;
13723 gint i;
13724
13725 if (qtdemux->fragmented)
13726 return;
13727
13728 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
13729
13730 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
13731 || size <= 0) {
13732 GST_DEBUG_OBJECT (qtdemux,
13733 "Size in bytes of the stream not known - bailing");
13734 return;
13735 }
13736
13737 /* Subtract the header size */
13738 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
13739 size, qtdemux->header_size);
13740
13741 if (size < qtdemux->header_size)
13742 return;
13743
13744 size = size - qtdemux->header_size;
13745
13746 if (!gst_qtdemux_get_duration (qtdemux, &duration)) {
13747 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
13748 return;
13749 }
13750
13751 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13752 QtDemuxStream *str = QTDEMUX_NTH_STREAM (qtdemux, i);
13753 switch (str->subtype) {
13754 case FOURCC_soun:
13755 case FOURCC_vide:
13756 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
13757 CUR_STREAM (str)->caps);
13758 /* retrieve bitrate, prefer avg then max */
13759 bitrate = 0;
13760 if (str->stream_tags) {
13761 if (gst_tag_list_get_uint (str->stream_tags,
13762 GST_TAG_MAXIMUM_BITRATE, &bitrate))
13763 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
13764 if (gst_tag_list_get_uint (str->stream_tags,
13765 GST_TAG_NOMINAL_BITRATE, &bitrate))
13766 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
13767 if (gst_tag_list_get_uint (str->stream_tags,
13768 GST_TAG_BITRATE, &bitrate))
13769 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
13770 }
13771 if (bitrate)
13772 sum_bitrate += bitrate;
13773 else {
13774 if (stream) {
13775 GST_DEBUG_OBJECT (qtdemux,
13776 ">1 stream with unknown bitrate - bailing");
13777 return;
13778 } else
13779 stream = str;
13780 }
13781
13782 default:
13783 /* For other subtypes, we assume no significant impact on bitrate */
13784 break;
13785 }
13786 }
13787
13788 if (!stream) {
13789 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
13790 return;
13791 }
13792
13793 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
13794
13795 if (sys_bitrate < sum_bitrate) {
13796 /* This can happen, since sum_bitrate might be derived from maximum
13797 * bitrates and not average bitrates */
13798 GST_DEBUG_OBJECT (qtdemux,
13799 "System bitrate less than sum bitrate - bailing");
13800 return;
13801 }
13802
13803 bitrate = sys_bitrate - sum_bitrate;
13804 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
13805 ", Stream bitrate = %u", sys_bitrate, bitrate);
13806
13807 if (!stream->stream_tags)
13808 stream->stream_tags = gst_tag_list_new_empty ();
13809 else
13810 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
13811
13812 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13813 GST_TAG_BITRATE, bitrate, NULL);
13814}
13815
13816static GstFlowReturn
13817qtdemux_prepare_streams (GstQTDemux * qtdemux)
13818{
13819 GstFlowReturn ret = GST_FLOW_OK;
13820 gint i;
13821
13822 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
13823
13824 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13825 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13826 guint32 sample_num = 0;
13827
13828 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13829 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13830
13831 if (qtdemux->fragmented && qtdemux->pullbased) {
13832 /* need all moov samples first */
13833 GST_OBJECT_LOCK (qtdemux);
13834 while (stream->n_samples == 0)
13835 if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
13836 break;
13837 GST_OBJECT_UNLOCK (qtdemux);
13838 } else {
13839 /* discard any stray moof */
13840 qtdemux->moof_offset = 0;
13841 }
13842
13843 /* prepare braking */
13844 if (ret != GST_FLOW_ERROR)
13845 ret = GST_FLOW_OK;
13846
13847 /* in pull mode, we should have parsed some sample info by now;
13848 * and quite some code will not handle no samples.
13849 * in push mode, we'll just have to deal with it */
13850 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
13851 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
13852 g_ptr_array_remove_index (qtdemux->active_streams, i);
13853 i--;
13854 continue;
13855 } else if (stream->track_id == qtdemux->chapters_track_id &&
13856 (stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl)) {
13857 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
13858 so that it doesn't look like a subtitle track */
13859 g_ptr_array_remove_index (qtdemux->active_streams, i);
13860 i--;
13861 continue;
13862 }
13863
13864 /* parse the initial sample for use in setting the frame rate cap */
13865 while (sample_num == 0 && sample_num < stream->n_samples) {
13866 if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
13867 break;
13868 ++sample_num;
13869 }
13870 }
13871
13872 return ret;
13873}
13874
13875static gboolean
13876_stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
13877{
13878 return g_strcmp0 (stream->stream_id, stream_id) == 0;
13879}
13880
13881static gboolean
13882qtdemux_is_streams_update (GstQTDemux * qtdemux)
13883{
13884 gint i;
13885
13886 /* Different length, updated */
13887 if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
13888 return TRUE;
13889
13890 /* streams in list are sorted in track-id order */
13891 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13892 /* Different stream-id, updated */
13893 if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
13894 QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
13895 return TRUE;
13896 }
13897
13898 return FALSE;
13899}
13900
13901static gboolean
13902qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
13903 QtDemuxStream * oldstream, QtDemuxStream * newstream)
13904{
13905 /* Connect old stream's srcpad to new stream */
13906 newstream->pad = oldstream->pad;
13907 oldstream->pad = NULL;
13908
13909 /* unset new_stream to prevent stream-start event, unless we are EOS in which
13910 * case we need to force one through */
13911 newstream->new_stream = newstream->pad != NULL
13912 && GST_PAD_IS_EOS (newstream->pad);
13913
13914 return gst_qtdemux_configure_stream (qtdemux, newstream);
13915}
13916
13917static gboolean
13918qtdemux_update_streams (GstQTDemux * qtdemux)
13919{
13920 gint i;
13921 g_assert (qtdemux->streams_aware);
13922
13923 /* At below, figure out which stream in active_streams has identical stream-id
13924 * with that of in old_streams. If there is matching stream-id,
13925 * corresponding newstream will not be exposed again,
13926 * but demux will reuse srcpad of matched old stream
13927 *
13928 * active_streams : newly created streams from the latest moov
13929 * old_streams : existing streams (belong to previous moov)
13930 */
13931
13932 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13933 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13934 QtDemuxStream *oldstream = NULL;
13935 guint target;
13936
13937 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
13938 stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
13939
13940 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
13941 stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
13942 oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
13943
13944 /* null pad stream cannot be reused */
13945 if (oldstream->pad == NULL)
13946 oldstream = NULL;
13947 }
13948
13949 if (oldstream) {
13950 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
13951
13952 if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
13953 return FALSE;
13954
13955 /* we don't need to preserve order of old streams */
13956 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
13957 } else {
13958 GstTagList *list;
13959
13960 /* now we have all info and can expose */
13961 list = stream->stream_tags;
13962 stream->stream_tags = NULL;
13963 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
13964 return FALSE;
13965 }
13966 }
13967
13968 return TRUE;
13969}
13970
13971/* Must be called with expose lock */
13972static GstFlowReturn
13973qtdemux_expose_streams (GstQTDemux * qtdemux)
13974{
13975 gint i;
13976
13977 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
13978
13979 if (!qtdemux_is_streams_update (qtdemux)) {
13980 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
13981 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13982 QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
13983 QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
13984 if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
13985 return GST_FLOW_ERROR;
13986 }
13987
13988 g_ptr_array_set_size (qtdemux->old_streams, 0);
13989 qtdemux->need_segment = TRUE;
13990
13991 return GST_FLOW_OK;
13992 }
13993
13994 if (qtdemux->streams_aware) {
13995 if (!qtdemux_update_streams (qtdemux))
13996 return GST_FLOW_ERROR;
13997 } else {
13998 for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
13999 QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
14000 GstTagList *list;
14001
14002 /* now we have all info and can expose */
14003 list = stream->stream_tags;
14004 stream->stream_tags = NULL;
14005 if (!gst_qtdemux_add_stream (qtdemux, stream, list))
14006 return GST_FLOW_ERROR;
14007
14008 }
14009 }
14010
14011 gst_qtdemux_guess_bitrate (qtdemux);
14012
14013 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14014
14015 /* If we have still old_streams, it's no more used stream */
14016 for (i = 0; i < qtdemux->old_streams->len; i++) {
14017 QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14018
14019 if (stream->pad) {
14020 GstEvent *event;
14021
14022 event = gst_event_new_eos ();
14023 if (qtdemux->segment_seqnum)
14024 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14025
14026 gst_pad_push_event (stream->pad, event);
14027 }
14028 }
14029
14030 g_ptr_array_set_size (qtdemux->old_streams, 0);
14031
14032 /* check if we should post a redirect in case there is a single trak
14033 * and it is a redirecting trak */
14034 if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14035 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
14036 GstMessage *m;
14037
14038 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14039 "an external content");
14040 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14041 gst_structure_new ("redirect",
14042 "new-location", G_TYPE_STRING,
14043 QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
14044 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14045 g_free (qtdemux->redirect_location);
14046 qtdemux->redirect_location =
14047 g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
14048 }
14049
14050 g_ptr_array_foreach (qtdemux->active_streams,
14051 (GFunc) qtdemux_do_allocation, qtdemux);
14052
14053 qtdemux->need_segment = TRUE;
14054
14055 qtdemux->exposed = TRUE;
14056 return GST_FLOW_OK;
14057}
14058
14059typedef struct
14060{
14061 GstStructure *structure; /* helper for sort function */
14062 gchar *location;
14063 guint min_req_bitrate;
14064 guint min_req_qt_version;
14065} GstQtReference;
14066
14067static gint
14068qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
14069{
14070 GstQtReference *ref_a = (GstQtReference *) a;
14071 GstQtReference *ref_b = (GstQtReference *) b;
14072
14073 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14074 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14075
14076 /* known bitrates go before unknown; higher bitrates go first */
14077 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14078}
14079
14080/* sort the redirects and post a message for the application.
14081 */
14082static void
14083qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
14084{
14085 GstQtReference *best;
14086 GstStructure *s;
14087 GstMessage *msg;
14088 GValue list_val = { 0, };
14089 GList *l;
14090
14091 g_assert (references != NULL);
14092
14093 references = g_list_sort (references, qtdemux_redirects_sort_func);
14094
14095 best = (GstQtReference *) references->data;
14096
14097 g_value_init (&list_val, GST_TYPE_LIST);
14098
14099 for (l = references; l != NULL; l = l->next) {
14100 GstQtReference *ref = (GstQtReference *) l->data;
14101 GValue struct_val = { 0, };
14102
14103 ref->structure = gst_structure_new ("redirect",
14104 "new-location", G_TYPE_STRING, ref->location, NULL);
14105
14106 if (ref->min_req_bitrate > 0) {
14107 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14108 ref->min_req_bitrate, NULL);
14109 }
14110
14111 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14112 g_value_set_boxed (&struct_val, ref->structure);
14113 gst_value_list_append_value (&list_val, &struct_val);
14114 g_value_unset (&struct_val);
14115 /* don't free anything here yet, since we need best->structure below */
14116 }
14117
14118 g_assert (best != NULL);
14119 s = gst_structure_copy (best->structure);
14120
14121 if (g_list_length (references) > 1) {
14122 gst_structure_set_value (s, "locations", &list_val);
14123 }
14124
14125 g_value_unset (&list_val);
14126
14127 for (l = references; l != NULL; l = l->next) {
14128 GstQtReference *ref = (GstQtReference *) l->data;
14129
14130 gst_structure_free (ref->structure);
14131 g_free (ref->location);
14132 g_free (ref);
14133 }
14134 g_list_free (references);
14135
14136 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14137 g_free (qtdemux->redirect_location);
14138 qtdemux->redirect_location =
14139 g_strdup (gst_structure_get_string (s, "new-location"));
14140 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14141 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14142}
14143
14144/* look for redirect nodes, collect all redirect information and
14145 * process it.
14146 */
14147static gboolean
14148qtdemux_parse_redirects (GstQTDemux * qtdemux)
14149{
14150 GNode *rmra, *rmda, *rdrf;
14151
14152 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
14153 if (rmra) {
14154 GList *redirects = NULL;
14155
14156 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
14157 while (rmda) {
14158 GstQtReference ref = { NULL, NULL, 0, 0 };
14159 GNode *rmdr, *rmvc;
14160
14161 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
14162 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
14163 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14164 ref.min_req_bitrate);
14165 }
14166
14167 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
14168 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
14169 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
14170
14171#ifndef GST_DISABLE_GST_DEBUG
14172 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
14173#endif
14174 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
14175
14176 GST_LOG_OBJECT (qtdemux,
14177 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14178 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14179 bitmask, check_type);
14180 if (package == FOURCC_qtim && check_type == 0) {
14181 ref.min_req_qt_version = version;
14182 }
14183 }
14184
14185 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
14186 if (rdrf) {
14187 guint32 ref_type;
14188 guint8 *ref_data;
14189 guint ref_len;
14190
14191 ref_len = QT_UINT32 ((guint8 *) rdrf->data);
14192 if (ref_len > 20) {
14193 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
14194 ref_data = (guint8 *) rdrf->data + 20;
14195 if (ref_type == FOURCC_alis) {
14196 guint record_len, record_version, fn_len;
14197
14198 if (ref_len > 70) {
14199 /* MacOSX alias record, google for alias-layout.txt */
14200 record_len = QT_UINT16 (ref_data + 4);
14201 record_version = QT_UINT16 (ref_data + 4 + 2);
14202 fn_len = QT_UINT8 (ref_data + 50);
14203 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14204 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14205 }
14206 } else {
14207 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14208 ref_len);
14209 }
14210 } else if (ref_type == FOURCC_url_) {
14211 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14212 } else {
14213 GST_DEBUG_OBJECT (qtdemux,
14214 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14215 GST_FOURCC_ARGS (ref_type));
14216 }
14217 if (ref.location != NULL) {
14218 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14219 redirects =
14220 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14221 } else {
14222 GST_WARNING_OBJECT (qtdemux,
14223 "Failed to extract redirect location from rdrf atom");
14224 }
14225 } else {
14226 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14227 }
14228 }
14229
14230 /* look for others */
14231 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
14232 }
14233
14234 if (redirects != NULL) {
14235 qtdemux_process_redirects (qtdemux, redirects);
14236 }
14237 }
14238 return TRUE;
14239}
14240
14241static GstTagList *
14242qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
14243{
14244 const gchar *fmt;
14245
14246 if (tags == NULL) {
14247 tags = gst_tag_list_new_empty ();
14248 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14249 }
14250
14251 if (qtdemux->major_brand == FOURCC_mjp2)
14252 fmt = "Motion JPEG 2000";
14253 else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
14254 fmt = "3GP";
14255 else if (qtdemux->major_brand == FOURCC_qt__)
14256 fmt = "Quicktime";
14257 else if (qtdemux->fragmented)
14258 fmt = "ISO fMP4";
14259 else
14260 fmt = "ISO MP4/M4A";
14261
14262 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14263 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14264
14265 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14266 fmt, NULL);
14267
14268 return tags;
14269}
14270
14271/* we have read the complete moov node now.
14272 * This function parses all of the relevant info, creates the traks and
14273 * prepares all data structures for playback
14274 */
14275static gboolean
14276qtdemux_parse_tree (GstQTDemux * qtdemux)
14277{
14278 GNode *mvhd;
14279 GNode *trak;
14280 GNode *udta;
14281 GNode *mvex;
14282 GNode *pssh;
14283 guint64 creation_time;
14284 GstDateTime *datetime = NULL;
14285 gint version;
14286
14287 /* make sure we have a usable taglist */
14288 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14289
14290 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
14291 if (mvhd == NULL) {
14292 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
14293 return qtdemux_parse_redirects (qtdemux);
14294 }
14295
14296 version = QT_UINT8 ((guint8 *) mvhd->data + 8);
14297 if (version == 1) {
14298 creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
14299 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
14300 qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
14301 } else if (version == 0) {
14302 creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
14303 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
14304 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
14305 } else {
14306 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14307 return FALSE;
14308 }
14309
14310 /* Moving qt creation time (secs since 1904) to unix time */
14311 if (creation_time != 0) {
14312 /* Try to use epoch first as it should be faster and more commonly found */
14313 if (creation_time >= QTDEMUX_SECONDS_FROM_1904_TO_1970) {
14314 gint64 now_s;
14315
14316 creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
14317 /* some data cleansing sanity */
14318 now_s = g_get_real_time () / G_USEC_PER_SEC;
14319 if (now_s + 24 * 3600 < creation_time) {
14320 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14321 } else {
14322 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14323 }
14324 } else {
14325 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14326 GDateTime *dt, *dt_local;
14327
14328 dt = g_date_time_add_seconds (base_dt, creation_time);
14329 dt_local = g_date_time_to_local (dt);
14330 datetime = gst_date_time_new_from_g_date_time (dt_local);
14331
14332 g_date_time_unref (base_dt);
14333 g_date_time_unref (dt);
14334 }
14335 }
14336 if (datetime) {
14337 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14338 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14339 datetime, NULL);
14340 gst_date_time_unref (datetime);
14341 }
14342
14343 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14344 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14345
14346 /* check for fragmented file and get some (default) data */
14347 mvex = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvex);
14348 if (mvex) {
14349 GNode *mehd;
14350 GstByteReader mehd_data;
14351
14352 /* let track parsing or anyone know weird stuff might happen ... */
14353 qtdemux->fragmented = TRUE;
14354
14355 /* compensate for total duration */
14356 mehd = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_mehd, &mehd_data);
14357 if (mehd)
14358 qtdemux_parse_mehd (qtdemux, &mehd_data);
14359 }
14360
14361 /* Update the movie segment duration, unless it was directly given to us
14362 * by upstream. Otherwise let it as is, as we don't want to mangle the
14363 * duration provided by upstream that may come e.g. from a MPD file. */
14364 if (!qtdemux->upstream_format_is_time) {
14365 GstClockTime duration;
14366 /* set duration in the segment info */
14367 gst_qtdemux_get_duration (qtdemux, &duration);
14368 qtdemux->segment.duration = duration;
14369 /* also do not exceed duration; stop is set that way post seek anyway,
14370 * and segment activation falls back to duration,
14371 * whereas loop only checks stop, so let's align this here as well */
14372 qtdemux->segment.stop = duration;
14373 }
14374
14375 /* parse all traks */
14376 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
14377 while (trak) {
14378 qtdemux_parse_trak (qtdemux, trak);
14379 /* iterate all siblings */
14380 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
14381 }
14382
14383 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14384
14385 /* find tags */
14386 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
14387 if (udta) {
14388 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14389 } else {
14390 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14391 }
14392
14393 /* maybe also some tags in meta box */
14394 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta);
14395 if (udta) {
14396 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
14397 qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
14398 } else {
14399 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14400 }
14401
14402 /* parse any protection system info */
14403 pssh = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_pssh);
14404 while (pssh) {
14405 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
14406 qtdemux_parse_pssh (qtdemux, pssh);
14407 pssh = qtdemux_tree_get_sibling_by_type (pssh, FOURCC_pssh);
14408 }
14409
14410 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
14411
14412 return TRUE;
14413}
14414
14415/* taken from ffmpeg */
14416static int
14417read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
14418{
14419 int count = 4;
14420 int len = 0;
14421
14422 while (count--) {
14423 int c;
14424
14425 if (ptr >= end)
14426 return -1;
14427
14428 c = *ptr++;
14429 len = (len << 7) | (c & 0x7f);
14430 if (!(c & 0x80))
14431 break;
14432 }
14433 *end_out = ptr;
14434 return len;
14435}
14436
14437static GList *
14438parse_xiph_stream_headers (GstQTDemux * qtdemux, gpointer codec_data,
14439 gsize codec_data_size)
14440{
14441 GList *list = NULL;
14442 guint8 *p = codec_data;
14443 gint i, offset, num_packets;
14444 guint *length, last;
14445
14446 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14447
14448 if (codec_data == NULL || codec_data_size == 0)
14449 goto error;
14450
14451 /* start of the stream and vorbis audio or theora video, need to
14452 * send the codec_priv data as first three packets */
14453 num_packets = p[0] + 1;
14454 GST_DEBUG_OBJECT (qtdemux,
14455 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14456 (guint) num_packets, codec_data_size);
14457
14458 /* Let's put some limits, Don't think there even is a xiph codec
14459 * with more than 3-4 headers */
14460 if (G_UNLIKELY (num_packets > 16)) {
14461 GST_WARNING_OBJECT (qtdemux,
14462 "Unlikely number of xiph headers, most likely not valid");
14463 goto error;
14464 }
14465
14466 length = g_alloca (num_packets * sizeof (guint));
14467 last = 0;
14468 offset = 1;
14469
14470 /* first packets, read length values */
14471 for (i = 0; i < num_packets - 1; i++) {
14472 length[i] = 0;
14473 while (offset < codec_data_size) {
14474 length[i] += p[offset];
14475 if (p[offset++] != 0xff)
14476 break;
14477 }
14478 last += length[i];
14479 }
14480 if (offset + last > codec_data_size)
14481 goto error;
14482
14483 /* last packet is the remaining size */
14484 length[i] = codec_data_size - offset - last;
14485
14486 for (i = 0; i < num_packets; i++) {
14487 GstBuffer *hdr;
14488
14489 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14490
14491 if (offset + length[i] > codec_data_size)
14492 goto error;
14493
14494 hdr = gst_buffer_new_memdup (p + offset, length[i]);
14495 list = g_list_append (list, hdr);
14496
14497 offset += length[i];
14498 }
14499
14500 return list;
14501
14502 /* ERRORS */
14503error:
14504 {
14505 if (list != NULL)
14506 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14507 return NULL;
14508 }
14509
14510}
14511
14512/* this can change the codec originally present in @list */
14513static void
14514gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
14515 QtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
14516{
14517 int len = QT_UINT32 (esds->data);
14518 guint8 *ptr = esds->data;
14519 guint8 *end = ptr + len;
14520 int tag;
14521 guint8 *data_ptr = NULL;
14522 int data_len = 0;
14523 guint8 object_type_id = 0;
14524 guint8 stream_type = 0;
14525 const char *codec_name = NULL;
14526 GstCaps *caps = NULL;
14527
14528 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14529 ptr += 8;
14530 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
14531 ptr += 4;
14532 while (ptr + 1 < end) {
14533 tag = QT_UINT8 (ptr);
14534 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14535 ptr++;
14536 len = read_descr_size (ptr, end, &ptr);
14537 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14538
14539 /* Check the stated amount of data is available for reading */
14540 if (len < 0 || ptr + len > end)
14541 break;
14542
14543 switch (tag) {
14544 case ES_DESCRIPTOR_TAG:
14545 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", QT_UINT16 (ptr));
14546 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", QT_UINT8 (ptr + 2));
14547 ptr += 3;
14548 break;
14549 case DECODER_CONFIG_DESC_TAG:{
14550 guint max_bitrate, avg_bitrate;
14551
14552 object_type_id = QT_UINT8 (ptr);
14553 stream_type = QT_UINT8 (ptr + 1) >> 2;
14554 max_bitrate = QT_UINT32 (ptr + 5);
14555 avg_bitrate = QT_UINT32 (ptr + 9);
14556 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14557 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
14558 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
14559 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14560 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14561 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14562 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14563 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14564 }
14565 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14566 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14567 avg_bitrate, NULL);
14568 }
14569 ptr += 13;
14570 break;
14571 }
14572 case DECODER_SPECIFIC_INFO_TAG:
14573 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14574 if (object_type_id == 0xe0 && len == 0x40) {
14575 guint8 *data;
14576 GstStructure *s;
14577 guint32 clut[16];
14578 gint i;
14579
14580 GST_DEBUG_OBJECT (qtdemux,
14581 "Have VOBSUB palette. Creating palette event");
14582 /* move to decConfigDescr data and read palette */
14583 data = ptr;
14584 for (i = 0; i < 16; i++) {
14585 clut[i] = QT_UINT32 (data);
14586 data += 4;
14587 }
14588
14589 s = gst_structure_new ("application/x-gst-dvd", "event",
14590 G_TYPE_STRING, "dvd-spu-clut-change",
14591 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14592 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14593 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14594 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14595 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14596 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14597 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14598 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14599 NULL);
14600
14601 /* store event and trigger custom processing */
14602 stream->pending_event =
14603 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14604 } else {
14605 /* Generic codec_data handler puts it on the caps */
14606 data_ptr = ptr;
14607 data_len = len;
14608 }
14609
14610 ptr += len;
14611 break;
14612 case SL_CONFIG_DESC_TAG:
14613 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
14614 ptr += 1;
14615 break;
14616 default:
14617 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14618 tag);
14619 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14620 ptr += len;
14621 break;
14622 }
14623 }
14624
14625 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14626 * in use, and should also be used to override some other parameters for some
14627 * codecs. */
14628 switch (object_type_id) {
14629 case 0x20: /* MPEG-4 */
14630 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14631 * profile_and_level_indication */
14632 if (data_ptr != NULL && data_len >= 5 &&
14633 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14634 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14635 data_ptr + 4, data_len - 4);
14636 }
14637 break; /* Nothing special needed here */
14638 case 0x21: /* H.264 */
14639 codec_name = "H.264 / AVC";
14640 caps = gst_caps_new_simple ("video/x-h264",
14641 "stream-format", G_TYPE_STRING, "avc",
14642 "alignment", G_TYPE_STRING, "au", NULL);
14643 break;
14644 case 0x40: /* AAC (any) */
14645 case 0x66: /* AAC Main */
14646 case 0x67: /* AAC LC */
14647 case 0x68: /* AAC SSR */
14648 /* Override channels and rate based on the codec_data, as it's often
14649 * wrong. */
14650 /* Only do so for basic setup without HE-AAC extension */
14651 if (data_ptr && data_len == 2) {
14652 guint channels, rate;
14653
14654 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14655 if (channels > 0)
14656 entry->n_channels = channels;
14657
14658 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
14659 if (rate > 0)
14660 entry->rate = rate;
14661 }
14662
14663 /* Set level and profile if possible */
14664 if (data_ptr != NULL && data_len >= 2) {
14665 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
14666 data_ptr, data_len);
14667 } else {
14668 const gchar *profile_str = NULL;
14669 GstBuffer *buffer;
14670 GstMapInfo map;
14671 guint8 *codec_data;
14672 gint rate_idx, profile;
14673
14674 /* No codec_data, let's invent something.
14675 * FIXME: This is wrong for SBR! */
14676
14677 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
14678
14679 buffer = gst_buffer_new_and_alloc (2);
14680 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
14681 codec_data = map.data;
14682
14683 rate_idx =
14684 gst_codec_utils_aac_get_index_from_sample_rate (CUR_STREAM
14685 (stream)->rate);
14686
14687 switch (object_type_id) {
14688 case 0x66:
14689 profile_str = "main";
14690 profile = 0;
14691 break;
14692 case 0x67:
14693 profile_str = "lc";
14694 profile = 1;
14695 break;
14696 case 0x68:
14697 profile_str = "ssr";
14698 profile = 2;
14699 break;
14700 default:
14701 profile = 3;
14702 break;
14703 }
14704
14705 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
14706 codec_data[1] =
14707 ((rate_idx & 0x1) << 7) | (CUR_STREAM (stream)->n_channels << 3);
14708
14709 gst_buffer_unmap (buffer, &map);
14710 gst_caps_set_simple (CUR_STREAM (stream)->caps, "codec_data",
14711 GST_TYPE_BUFFER, buffer, NULL);
14712 gst_buffer_unref (buffer);
14713
14714 if (profile_str) {
14715 gst_caps_set_simple (CUR_STREAM (stream)->caps, "profile",
14716 G_TYPE_STRING, profile_str, NULL);
14717 }
14718 }
14719 break;
14720 case 0x60: /* MPEG-2, various profiles */
14721 case 0x61:
14722 case 0x62:
14723 case 0x63:
14724 case 0x64:
14725 case 0x65:
14726 codec_name = "MPEG-2 video";
14727 caps = gst_caps_new_simple ("video/mpeg",
14728 "mpegversion", G_TYPE_INT, 2,
14729 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14730 break;
14731 case 0x69: /* MPEG-2 BC audio */
14732 case 0x6B: /* MPEG-1 audio */
14733 caps = gst_caps_new_simple ("audio/mpeg",
14734 "mpegversion", G_TYPE_INT, 1, NULL);
14735 codec_name = "MPEG-1 audio";
14736 break;
14737 case 0x6A: /* MPEG-1 */
14738 codec_name = "MPEG-1 video";
14739 caps = gst_caps_new_simple ("video/mpeg",
14740 "mpegversion", G_TYPE_INT, 1,
14741 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14742 break;
14743 case 0x6C: /* MJPEG */
14744 caps =
14745 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14746 NULL);
14747 codec_name = "Motion-JPEG";
14748 break;
14749 case 0x6D: /* PNG */
14750 caps =
14751 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
14752 NULL);
14753 codec_name = "PNG still images";
14754 break;
14755 case 0x6E: /* JPEG2000 */
14756 codec_name = "JPEG-2000";
14757 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14758 break;
14759 case 0xA4: /* Dirac */
14760 codec_name = "Dirac";
14761 caps = gst_caps_new_empty_simple ("video/x-dirac");
14762 break;
14763 case 0xA5: /* AC3 */
14764 codec_name = "AC-3 audio";
14765 caps = gst_caps_new_simple ("audio/x-ac3",
14766 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14767 break;
14768 case 0xA9: /* AC3 */
14769 codec_name = "DTS audio";
14770 caps = gst_caps_new_simple ("audio/x-dts",
14771 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
14772 break;
14773 case 0xDD:
14774 if (stream_type == 0x05 && data_ptr) {
14775 GList *headers =
14776 parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
14777 if (headers) {
14778 GList *tmp;
14779 GValue arr_val = G_VALUE_INIT;
14780 GValue buf_val = G_VALUE_INIT;
14781 GstStructure *s;
14782
14783 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
14784 codec_name = "Vorbis";
14785 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
14786 g_value_init (&arr_val, GST_TYPE_ARRAY);
14787 g_value_init (&buf_val, GST_TYPE_BUFFER);
14788 for (tmp = headers; tmp; tmp = tmp->next) {
14789 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
14790 gst_value_array_append_value (&arr_val, &buf_val);
14791 }
14792 s = gst_caps_get_structure (caps, 0);
14793 gst_structure_take_value (s, "streamheader", &arr_val);
14794 g_value_unset (&buf_val);
14795 g_list_free (headers);
14796
14797 data_ptr = NULL;
14798 data_len = 0;
14799 }
14800 }
14801 break;
14802 case 0xE1: /* QCELP */
14803 /* QCELP, the codec_data is a riff tag (little endian) with
14804 * 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). */
14805 caps = gst_caps_new_empty_simple ("audio/qcelp");
14806 codec_name = "QCELP";
14807 break;
14808 default:
14809 break;
14810 }
14811
14812 /* If we have a replacement caps, then change our caps for this stream */
14813 if (caps) {
14814 gst_caps_unref (entry->caps);
14815 entry->caps = caps;
14816 }
14817
14818 if (codec_name && list)
14819 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14820 GST_TAG_AUDIO_CODEC, codec_name, NULL);
14821
14822 /* Add the codec_data attribute to caps, if we have it */
14823 if (data_ptr) {
14824 GstBuffer *buffer;
14825
14826 buffer = gst_buffer_new_and_alloc (data_len);
14827 gst_buffer_fill (buffer, 0, data_ptr, data_len);
14828
14829 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
14830 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
14831
14832 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
14833 buffer, NULL);
14834 gst_buffer_unref (buffer);
14835 }
14836
14837}
14838
14839static inline GstCaps *
14840_get_unknown_codec_name (const gchar * type, guint32 fourcc)
14841{
14842 GstCaps *caps;
14843 guint i;
14844 char *s, fourstr[5];
14845
14846 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
14847 for (i = 0; i < 4; i++) {
14848 if (!g_ascii_isalnum (fourstr[i]))
14849 fourstr[i] = '_';
14850 }
14851 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
14852 caps = gst_caps_new_empty_simple (s);
14853 g_free (s);
14854 return caps;
14855}
14856
14857#define _codec(name) \
14858 do { \
14859 if (codec_name) { \
14860 *codec_name = g_strdup (name); \
14861 } \
14862 } while (0)
14863
14864static GstCaps *
14865qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
14866 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
14867 const guint8 * stsd_entry_data, gchar ** codec_name)
14868{
14869 GstCaps *caps = NULL;
14870 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
14871
14872 switch (fourcc) {
14873 case FOURCC_png:
14874 _codec ("PNG still images");
14875 caps = gst_caps_new_empty_simple ("image/png");
14876 break;
14877 case FOURCC_jpeg:
14878 _codec ("JPEG still images");
14879 caps =
14880 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14881 NULL);
14882 break;
14883 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
14884 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
14885 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
14886 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
14887 _codec ("Motion-JPEG");
14888 caps =
14889 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
14890 NULL);
14891 break;
14892 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
14893 _codec ("Motion-JPEG format B");
14894 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
14895 break;
14896 case FOURCC_mjp2:
14897 _codec ("JPEG-2000");
14898 /* override to what it should be according to spec, avoid palette_data */
14899 entry->bits_per_sample = 24;
14900 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
14901 break;
14902 case FOURCC_SVQ3:
14903 _codec ("Sorensen video v.3");
14904 caps = gst_caps_new_simple ("video/x-svq",
14905 "svqversion", G_TYPE_INT, 3, NULL);
14906 break;
14907 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
14908 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
14909 _codec ("Sorensen video v.1");
14910 caps = gst_caps_new_simple ("video/x-svq",
14911 "svqversion", G_TYPE_INT, 1, NULL);
14912 break;
14913 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
14914 caps = gst_caps_new_empty_simple ("video/x-raw");
14915 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
14916 _codec ("Windows Raw RGB");
14917 stream->alignment = 32;
14918 break;
14919 case FOURCC_raw_:
14920 {
14921 guint16 bps;
14922
14923 bps = QT_UINT16 (stsd_entry_data + 82);
14924 switch (bps) {
14925 case 15:
14926 format = GST_VIDEO_FORMAT_RGB15;
14927 break;
14928 case 16:
14929 format = GST_VIDEO_FORMAT_RGB16;
14930 break;
14931 case 24:
14932 format = GST_VIDEO_FORMAT_RGB;
14933 break;
14934 case 32:
14935 format = GST_VIDEO_FORMAT_ARGB;
14936 break;
14937 default:
14938 /* unknown */
14939 break;
14940 }
14941 break;
14942 }
14943 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
14944 format = GST_VIDEO_FORMAT_I420;
14945 break;
14946 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
14947 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
14948 format = GST_VIDEO_FORMAT_I420;
14949 break;
14950 case FOURCC_2vuy:
14951 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
14952 format = GST_VIDEO_FORMAT_UYVY;
14953 break;
14954 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
14955 format = GST_VIDEO_FORMAT_v308;
14956 break;
14957 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
14958 format = GST_VIDEO_FORMAT_v216;
14959 break;
14960 case FOURCC_v210:
14961 format = GST_VIDEO_FORMAT_v210;
14962 break;
14963 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
14964 format = GST_VIDEO_FORMAT_r210;
14965 break;
14966 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
14967 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
14968 format = GST_VIDEO_FORMAT_v410;
14969 break;
14970 */
14971 /* Packed YUV 4:4:4:4 8 bit in 32 bits
14972 * but different order than AYUV
14973 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
14974 format = GST_VIDEO_FORMAT_v408;
14975 break;
14976 */
14977 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
14978 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
14979 _codec ("MPEG-1 video");
14980 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
14981 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
14982 break;
14983 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
14984 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
14985 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
14986 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
14987 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
14988 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
14989 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
14990 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
14991 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
14992 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
14993 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
14994 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
14995 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
14996 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
14997 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
14998 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
14999 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15000 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15001 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15002 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15003 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15004 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15005 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15006 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15007 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15008 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15009 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15010 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15011 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15012 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15013 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15014 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15015 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15016 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15017 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15018 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15019 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15020 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15021 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15022 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15023 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15024 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15025 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15026 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15027 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15028 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15029 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
15030 _codec ("MPEG-2 video");
15031 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15032 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15033 break;
15034 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
15035 _codec ("GIF still images");
15036 caps = gst_caps_new_empty_simple ("image/gif");
15037 break;
15038 case FOURCC_h263:
15039 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
15040 case FOURCC_s263:
15041 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
15042 _codec ("H.263");
15043 /* ffmpeg uses the height/width props, don't know why */
15044 caps = gst_caps_new_simple ("video/x-h263",
15045 "variant", G_TYPE_STRING, "itu", NULL);
15046 break;
15047 case FOURCC_mp4v:
15048 case FOURCC_MP4V:
15049 _codec ("MPEG-4 video");
15050 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15051 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15052 break;
15053 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15054 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
15055 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
15056 caps = gst_caps_new_simple ("video/x-msmpeg",
15057 "msmpegversion", G_TYPE_INT, 43, NULL);
15058 break;
15059 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
15060 _codec ("DivX 3");
15061 caps = gst_caps_new_simple ("video/x-divx",
15062 "divxversion", G_TYPE_INT, 3, NULL);
15063 break;
15064 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15065 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
15066 _codec ("DivX 4");
15067 caps = gst_caps_new_simple ("video/x-divx",
15068 "divxversion", G_TYPE_INT, 4, NULL);
15069 break;
15070 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
15071 _codec ("DivX 5");
15072 caps = gst_caps_new_simple ("video/x-divx",
15073 "divxversion", G_TYPE_INT, 5, NULL);
15074 break;
15075
15076 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
15077 _codec ("FFV1");
15078 caps = gst_caps_new_simple ("video/x-ffv",
15079 "ffvversion", G_TYPE_INT, 1, NULL);
15080 break;
15081
15082 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15083 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
15084 case FOURCC_XVID:
15085 case FOURCC_xvid:
15086 case FOURCC_FMP4:
15087 case FOURCC_fmp4:
15088 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15089 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15090 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15091 _codec ("MPEG-4");
15092 break;
15093
15094 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
15095 _codec ("Cinepak");
15096 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15097 break;
15098 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
15099 _codec ("Apple QuickDraw");
15100 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15101 break;
15102 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
15103 _codec ("Apple video");
15104 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15105 break;
15106 case FOURCC_H264:
15107 case FOURCC_avc1:
15108 case FOURCC_dva1:
15109 _codec ("H.264 / AVC");
15110 caps = gst_caps_new_simple ("video/x-h264",
15111 "stream-format", G_TYPE_STRING, "avc",
15112 "alignment", G_TYPE_STRING, "au", NULL);
15113 break;
15114 case FOURCC_avc3:
15115 case FOURCC_dvav:
15116 _codec ("H.264 / AVC");
15117 caps = gst_caps_new_simple ("video/x-h264",
15118 "stream-format", G_TYPE_STRING, "avc3",
15119 "alignment", G_TYPE_STRING, "au", NULL);
15120 break;
15121 case FOURCC_H265:
15122 case FOURCC_hvc1:
15123 case FOURCC_dvh1:
15124 _codec ("H.265 / HEVC");
15125 caps = gst_caps_new_simple ("video/x-h265",
15126 "stream-format", G_TYPE_STRING, "hvc1",
15127 "alignment", G_TYPE_STRING, "au", NULL);
15128 break;
15129 case FOURCC_hev1:
15130 case FOURCC_dvhe:
15131 _codec ("H.265 / HEVC");
15132 caps = gst_caps_new_simple ("video/x-h265",
15133 "stream-format", G_TYPE_STRING, "hev1",
15134 "alignment", G_TYPE_STRING, "au", NULL);
15135 break;
15136 case FOURCC_rle_:
15137 _codec ("Run-length encoding");
15138 caps = gst_caps_new_simple ("video/x-rle",
15139 "layout", G_TYPE_STRING, "quicktime", NULL);
15140 break;
15141 case FOURCC_WRLE:
15142 _codec ("Run-length encoding");
15143 caps = gst_caps_new_simple ("video/x-rle",
15144 "layout", G_TYPE_STRING, "microsoft", NULL);
15145 break;
15146 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15147 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
15148 _codec ("Indeo Video 3");
15149 caps = gst_caps_new_simple ("video/x-indeo",
15150 "indeoversion", G_TYPE_INT, 3, NULL);
15151 break;
15152 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15153 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
15154 _codec ("Intel Video 4");
15155 caps = gst_caps_new_simple ("video/x-indeo",
15156 "indeoversion", G_TYPE_INT, 4, NULL);
15157 break;
15158 case FOURCC_dvcp:
15159 case FOURCC_dvc_:
15160 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15161 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15162 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15163 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15164 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15165 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
15166 _codec ("DV Video");
15167 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15168 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15169 break;
15170 case FOURCC_dv5n: /* DVCPRO50 NTSC */
15171 case FOURCC_dv5p: /* DVCPRO50 PAL */
15172 _codec ("DVCPro50 Video");
15173 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15174 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15175 break;
15176 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15177 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
15178 _codec ("DVCProHD Video");
15179 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15180 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15181 break;
15182 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
15183 _codec ("Apple Graphics (SMC)");
15184 caps = gst_caps_new_empty_simple ("video/x-smc");
15185 break;
15186 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
15187 _codec ("VP3");
15188 caps = gst_caps_new_empty_simple ("video/x-vp3");
15189 break;
15190 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
15191 _codec ("VP6 Flash");
15192 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15193 break;
15194 case FOURCC_XiTh:
15195 _codec ("Theora");
15196 caps = gst_caps_new_empty_simple ("video/x-theora");
15197 /* theora uses one byte of padding in the data stream because it does not
15198 * allow 0 sized packets while theora does */
15199 entry->padding = 1;
15200 break;
15201 case FOURCC_drac:
15202 _codec ("Dirac");
15203 caps = gst_caps_new_empty_simple ("video/x-dirac");
15204 break;
15205 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
15206 _codec ("TIFF still images");
15207 caps = gst_caps_new_empty_simple ("image/tiff");
15208 break;
15209 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
15210 _codec ("Apple Intermediate Codec");
15211 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15212 break;
15213 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
15214 _codec ("AVID DNxHD");
15215 caps = gst_caps_from_string ("video/x-dnxhd");
15216 break;
15217 case FOURCC_VP80:
15218 case FOURCC_vp08:
15219 _codec ("On2 VP8");
15220 caps = gst_caps_from_string ("video/x-vp8");
15221 break;
15222 case FOURCC_vp09:
15223 _codec ("Google VP9");
15224 caps = gst_caps_from_string ("video/x-vp9");
15225 break;
15226 case FOURCC_apcs:
15227 _codec ("Apple ProRes LT");
15228 caps =
15229 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15230 NULL);
15231 break;
15232 case FOURCC_apch:
15233 _codec ("Apple ProRes HQ");
15234 caps =
15235 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15236 NULL);
15237 break;
15238 case FOURCC_apcn:
15239 _codec ("Apple ProRes");
15240 caps =
15241 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15242 "standard", NULL);
15243 break;
15244 case FOURCC_apco:
15245 _codec ("Apple ProRes Proxy");
15246 caps =
15247 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15248 "proxy", NULL);
15249 break;
15250 case FOURCC_ap4h:
15251 _codec ("Apple ProRes 4444");
15252 caps =
15253 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15254 "4444", NULL);
15255
15256 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15257 if (entry->bits_per_sample > 0) {
15258 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15259 NULL);
15260 }
15261 break;
15262 case FOURCC_ap4x:
15263 _codec ("Apple ProRes 4444 XQ");
15264 caps =
15265 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15266 "4444xq", NULL);
15267
15268 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15269 if (entry->bits_per_sample > 0) {
15270 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15271 NULL);
15272 }
15273 break;
15274 case FOURCC_cfhd:
15275 _codec ("GoPro CineForm");
15276 caps = gst_caps_from_string ("video/x-cineform");
15277 break;
15278 case FOURCC_vc_1:
15279 case FOURCC_ovc1:
15280 _codec ("VC-1");
15281 caps = gst_caps_new_simple ("video/x-wmv",
15282 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15283 break;
15284 case FOURCC_av01:
15285 _codec ("AV1");
15286 caps = gst_caps_new_simple ("video/x-av1",
15287 "stream-format", G_TYPE_STRING, "obu-stream",
15288 "alignment", G_TYPE_STRING, "tu", NULL);
15289 break;
15290 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15291 default:
15292 {
15293 caps = _get_unknown_codec_name ("video", fourcc);
15294 break;
15295 }
15296 }
15297
15298 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15299 GstVideoInfo info;
15300
15301 gst_video_info_init (&info);
15302 gst_video_info_set_format (&info, format, entry->width, entry->height);
15303
15304 caps = gst_video_info_to_caps (&info);
15305 *codec_name = gst_pb_utils_get_codec_description (caps);
15306
15307 /* enable clipping for raw video streams */
15308 stream->need_clip = TRUE;
15309 stream->alignment = 32;
15310 }
15311
15312 return caps;
15313}
15314
15315static guint
15316round_up_pow2 (guint n)
15317{
15318 n = n - 1;
15319 n = n | (n >> 1);
15320 n = n | (n >> 2);
15321 n = n | (n >> 4);
15322 n = n | (n >> 8);
15323 n = n | (n >> 16);
15324 return n + 1;
15325}
15326
15327static GstCaps *
15328qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15329 QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
15330 int len, gchar ** codec_name)
15331{
15332 GstCaps *caps;
15333 const GstStructure *s;
15334 const gchar *name;
15335 gint endian = 0;
15336 GstAudioFormat format = 0;
15337 gint depth;
15338
15339 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15340
15341 depth = entry->bytes_per_packet * 8;
15342
15343 switch (fourcc) {
15344 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
15345 case FOURCC_raw_:
15346 /* 8-bit audio is unsigned */
15347 if (depth == 8)
15348 format = GST_AUDIO_FORMAT_U8;
15349 /* otherwise it's signed and big-endian just like 'twos' */
15350 case FOURCC_twos:
15351 endian = G_BIG_ENDIAN;
15352 /* fall-through */
15353 case FOURCC_sowt:
15354 {
15355 gchar *str;
15356
15357 if (!endian)
15358 endian = G_LITTLE_ENDIAN;
15359
15360 if (!format)
15361 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15362
15363 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
15364 _codec (str);
15365 g_free (str);
15366
15367 caps = gst_caps_new_simple ("audio/x-raw",
15368 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15369 "layout", G_TYPE_STRING, "interleaved", NULL);
15370 stream->alignment = GST_ROUND_UP_8 (depth);
15371 stream->alignment = round_up_pow2 (stream->alignment);
15372 break;
15373 }
15374 case FOURCC_fl64:
15375 _codec ("Raw 64-bit floating-point audio");
15376 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15377 * endian later */
15378 caps = gst_caps_new_simple ("audio/x-raw",
15379 "format", G_TYPE_STRING, "F64BE",
15380 "layout", G_TYPE_STRING, "interleaved", NULL);
15381 stream->alignment = 8;
15382 break;
15383 case FOURCC_fl32:
15384 _codec ("Raw 32-bit floating-point audio");
15385 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15386 * endian later */
15387 caps = gst_caps_new_simple ("audio/x-raw",
15388 "format", G_TYPE_STRING, "F32BE",
15389 "layout", G_TYPE_STRING, "interleaved", NULL);
15390 stream->alignment = 4;
15391 break;
15392 case FOURCC_in24:
15393 _codec ("Raw 24-bit PCM audio");
15394 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15395 * endian later */
15396 caps = gst_caps_new_simple ("audio/x-raw",
15397 "format", G_TYPE_STRING, "S24BE",
15398 "layout", G_TYPE_STRING, "interleaved", NULL);
15399 stream->alignment = 4;
15400 break;
15401 case FOURCC_in32:
15402 _codec ("Raw 32-bit PCM audio");
15403 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15404 * endian later */
15405 caps = gst_caps_new_simple ("audio/x-raw",
15406 "format", G_TYPE_STRING, "S32BE",
15407 "layout", G_TYPE_STRING, "interleaved", NULL);
15408 stream->alignment = 4;
15409 break;
15410 case FOURCC_s16l:
15411 _codec ("Raw 16-bit PCM audio");
15412 caps = gst_caps_new_simple ("audio/x-raw",
15413 "format", G_TYPE_STRING, "S16LE",
15414 "layout", G_TYPE_STRING, "interleaved", NULL);
15415 stream->alignment = 2;
15416 break;
15417 case FOURCC_ulaw:
15418 _codec ("Mu-law audio");
15419 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15420 break;
15421 case FOURCC_alaw:
15422 _codec ("A-law audio");
15423 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15424 break;
15425 case 0x0200736d:
15426 case 0x6d730002:
15427 _codec ("Microsoft ADPCM");
15428 /* Microsoft ADPCM-ACM code 2 */
15429 caps = gst_caps_new_simple ("audio/x-adpcm",
15430 "layout", G_TYPE_STRING, "microsoft", NULL);
15431 break;
15432 case 0x1100736d:
15433 case 0x6d730011:
15434 _codec ("DVI/IMA ADPCM");
15435 caps = gst_caps_new_simple ("audio/x-adpcm",
15436 "layout", G_TYPE_STRING, "dvi", NULL);
15437 break;
15438 case 0x1700736d:
15439 case 0x6d730017:
15440 _codec ("DVI/Intel IMA ADPCM");
15441 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15442 caps = gst_caps_new_simple ("audio/x-adpcm",
15443 "layout", G_TYPE_STRING, "quicktime", NULL);
15444 break;
15445 case 0x5500736d:
15446 case 0x6d730055:
15447 /* MPEG layer 3, CBR only (pre QT4.1) */
15448 case FOURCC__mp3:
15449 case FOURCC_mp3_:
15450 _codec ("MPEG-1 layer 3");
15451 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15452 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15453 "mpegversion", G_TYPE_INT, 1, NULL);
15454 break;
15455 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
15456 _codec ("MPEG-1 layer 2");
15457 /* MPEG layer 2 */
15458 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15459 "mpegversion", G_TYPE_INT, 1, NULL);
15460 break;
15461 case 0x20736d:
15462 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
15463 _codec ("EAC-3 audio");
15464 caps = gst_caps_new_simple ("audio/x-eac3",
15465 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15466 entry->sampled = TRUE;
15467 break;
15468 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
15469 case FOURCC_ac_3:
15470 _codec ("AC-3 audio");
15471 caps = gst_caps_new_simple ("audio/x-ac3",
15472 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15473 entry->sampled = TRUE;
15474 break;
15475 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15476 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
15477 _codec ("DTS audio");
15478 caps = gst_caps_new_simple ("audio/x-dts",
15479 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15480 entry->sampled = TRUE;
15481 break;
15482 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15483 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
15484 _codec ("DTS-HD audio");
15485 caps = gst_caps_new_simple ("audio/x-dts",
15486 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15487 entry->sampled = TRUE;
15488 break;
zengliang.li469a5ca2024-05-16 12:03:55 +000015489 case GST_MAKE_FOURCC ('d', 't', 's', 'e'): // DTS Low Bit Rate (LBR)
15490 _codec ("DTS LBR audio");
15491 caps = gst_caps_new_simple ("audio/x-dts",
15492 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15493 entry->sampled = TRUE;
15494 break;
zengliang.li5f31ef42024-05-16 08:27:38 +000015495 case FOURCC_MAC3:
15496 _codec ("MACE-3");
15497 caps = gst_caps_new_simple ("audio/x-mace",
15498 "maceversion", G_TYPE_INT, 3, NULL);
15499 break;
15500 case FOURCC_MAC6:
15501 _codec ("MACE-6");
15502 caps = gst_caps_new_simple ("audio/x-mace",
15503 "maceversion", G_TYPE_INT, 6, NULL);
15504 break;
15505 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15506 /* ogg/vorbis */
15507 caps = gst_caps_new_empty_simple ("application/ogg");
15508 break;
15509 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
15510 _codec ("DV audio");
15511 caps = gst_caps_new_empty_simple ("audio/x-dv");
15512 break;
15513 case FOURCC_mp4a:
15514 _codec ("MPEG-4 AAC audio");
15515 caps = gst_caps_new_simple ("audio/mpeg",
15516 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15517 "stream-format", G_TYPE_STRING, "raw", NULL);
15518 break;
15519 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
15520 _codec ("QDesign Music");
15521 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15522 break;
15523 case FOURCC_QDM2:
15524 _codec ("QDesign Music v.2");
15525 /* FIXME: QDesign music version 2 (no constant) */
15526 if (FALSE && data) {
15527 caps = gst_caps_new_simple ("audio/x-qdm2",
15528 "framesize", G_TYPE_INT, QT_UINT32 (data + 52),
15529 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40),
15530 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL);
15531 } else {
15532 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15533 }
15534 break;
15535 case FOURCC_agsm:
15536 _codec ("GSM audio");
15537 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15538 break;
15539 case FOURCC_samr:
15540 _codec ("AMR audio");
15541 caps = gst_caps_new_empty_simple ("audio/AMR");
15542 break;
15543 case FOURCC_sawb:
15544 _codec ("AMR-WB audio");
15545 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15546 break;
15547 case FOURCC_ima4:
15548 _codec ("Quicktime IMA ADPCM");
15549 caps = gst_caps_new_simple ("audio/x-adpcm",
15550 "layout", G_TYPE_STRING, "quicktime", NULL);
15551 break;
15552 case FOURCC_alac:
15553 _codec ("Apple lossless audio");
15554 caps = gst_caps_new_empty_simple ("audio/x-alac");
15555 break;
15556 case FOURCC_fLaC:
15557 _codec ("Free Lossless Audio Codec");
15558 caps = gst_caps_new_simple ("audio/x-flac",
15559 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15560 break;
15561 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
15562 _codec ("QualComm PureVoice");
15563 caps = gst_caps_from_string ("audio/qcelp");
15564 break;
15565 case FOURCC_wma_:
15566 case FOURCC_owma:
15567 _codec ("WMA");
15568 caps = gst_caps_new_empty_simple ("audio/x-wma");
15569 break;
15570 case FOURCC_opus:
15571 _codec ("Opus");
15572 caps = gst_caps_new_empty_simple ("audio/x-opus");
15573 break;
15574 case FOURCC_lpcm:
15575 {
15576 guint32 flags = 0;
15577 guint32 depth = 0;
15578 guint32 width = 0;
15579 GstAudioFormat format;
15580 enum
15581 {
15582 FLAG_IS_FLOAT = 0x1,
15583 FLAG_IS_BIG_ENDIAN = 0x2,
15584 FLAG_IS_SIGNED = 0x4,
15585 FLAG_IS_PACKED = 0x8,
15586 FLAG_IS_ALIGNED_HIGH = 0x10,
15587 FLAG_IS_NON_INTERLEAVED = 0x20
15588 };
15589 _codec ("Raw LPCM audio");
15590
15591 if (data && len >= 36) {
15592 depth = QT_UINT32 (data + 24);
15593 flags = QT_UINT32 (data + 28);
15594 width = QT_UINT32 (data + 32) * 8 / entry->n_channels;
15595 }
15596 if ((flags & FLAG_IS_FLOAT) == 0) {
15597 if (depth == 0)
15598 depth = 16;
15599 if (width == 0)
15600 width = 16;
15601 if ((flags & FLAG_IS_ALIGNED_HIGH))
15602 depth = width;
15603
15604 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15605 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15606 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15607 caps = gst_caps_new_simple ("audio/x-raw",
15608 "format", G_TYPE_STRING,
15609 format !=
15610 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15611 "UNKNOWN", "layout", G_TYPE_STRING,
15612 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15613 "interleaved", NULL);
15614 stream->alignment = GST_ROUND_UP_8 (depth);
15615 stream->alignment = round_up_pow2 (stream->alignment);
15616 } else {
15617 if (width == 0)
15618 width = 32;
15619 if (width == 64) {
15620 if (flags & FLAG_IS_BIG_ENDIAN)
15621 format = GST_AUDIO_FORMAT_F64BE;
15622 else
15623 format = GST_AUDIO_FORMAT_F64LE;
15624 } else {
15625 if (flags & FLAG_IS_BIG_ENDIAN)
15626 format = GST_AUDIO_FORMAT_F32BE;
15627 else
15628 format = GST_AUDIO_FORMAT_F32LE;
15629 }
15630 caps = gst_caps_new_simple ("audio/x-raw",
15631 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15632 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15633 "non-interleaved" : "interleaved", NULL);
15634 stream->alignment = width / 8;
15635 }
15636 break;
15637 }
15638 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
15639 {
15640 _codec ("AC4");
15641 caps = gst_caps_new_empty_simple ("audio/x-ac4");
15642 break;
15643 }
15644 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15645 /* ? */
15646 default:
15647 {
15648 caps = _get_unknown_codec_name ("audio", fourcc);
15649 break;
15650 }
15651 }
15652
15653 if (caps) {
15654 GstCaps *templ_caps =
15655 gst_static_pad_template_get_caps (&gst_qtdemux_audiosrc_template);
15656 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
15657 gst_caps_unref (caps);
15658 gst_caps_unref (templ_caps);
15659 caps = intersection;
15660 }
15661
15662 /* enable clipping for raw audio streams */
15663 s = gst_caps_get_structure (caps, 0);
15664 name = gst_structure_get_name (s);
15665 if (g_str_has_prefix (name, "audio/x-raw")) {
15666 stream->need_clip = TRUE;
15667 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
15668 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
15669 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
15670 stream->max_buffer_size);
15671 }
15672 return caps;
15673}
15674
15675static GstCaps *
15676qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15677 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15678 const guint8 * stsd_entry_data, gchar ** codec_name)
15679{
15680 GstCaps *caps;
15681
15682 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15683
15684 switch (fourcc) {
15685 case FOURCC_mp4s:
15686 _codec ("DVD subtitle");
15687 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
15688 stream->process_func = gst_qtdemux_process_buffer_dvd;
15689 break;
15690 case FOURCC_text:
15691 _codec ("Quicktime timed text");
15692 goto text;
15693 case FOURCC_tx3g:
15694 _codec ("3GPP timed text");
15695 text:
15696 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
15697 "utf8", NULL);
15698 /* actual text piece needs to be extracted */
15699 stream->process_func = gst_qtdemux_process_buffer_text;
15700 break;
15701 case FOURCC_stpp:
15702 _codec ("XML subtitles");
15703 caps = gst_caps_new_empty_simple ("application/ttml+xml");
15704 break;
15705 case FOURCC_wvtt:
15706 {
15707 GstBuffer *buffer;
15708 const gchar *buf = "WEBVTT\n\n";
15709
15710 _codec ("WebVTT subtitles");
15711 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
15712 stream->process_func = gst_qtdemux_process_buffer_wvtt;
15713
15714 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
15715 buffer = gst_buffer_new_and_alloc (8);
15716 gst_buffer_fill (buffer, 0, buf, 8);
15717 stream->buffers = g_slist_append (stream->buffers, buffer);
15718
15719 break;
15720 }
15721 case FOURCC_c608:
15722 _codec ("CEA 608 Closed Caption");
15723 caps =
15724 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
15725 G_TYPE_STRING, "s334-1a", NULL);
15726 stream->process_func = gst_qtdemux_process_buffer_clcp;
15727 stream->need_split = TRUE;
15728 break;
15729 case FOURCC_c708:
15730 _codec ("CEA 708 Closed Caption");
15731 caps =
15732 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
15733 G_TYPE_STRING, "cdp", NULL);
15734 stream->process_func = gst_qtdemux_process_buffer_clcp;
15735 break;
15736
15737 default:
15738 {
15739 caps = _get_unknown_codec_name ("text", fourcc);
15740 break;
15741 }
15742 }
15743 return caps;
15744}
15745
15746static GstCaps *
15747qtdemux_generic_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
15748 QtDemuxStreamStsdEntry * entry, guint32 fourcc,
15749 const guint8 * stsd_entry_data, gchar ** codec_name)
15750{
15751 GstCaps *caps;
15752
15753 switch (fourcc) {
15754 case FOURCC_m1v:
15755 _codec ("MPEG 1 video");
15756 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15757 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15758 break;
15759 default:
15760 caps = NULL;
15761 break;
15762 }
15763 return caps;
15764}
15765
15766static void
15767gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
15768 const gchar * system_id)
15769{
15770 gint i;
15771
15772 if (!qtdemux->protection_system_ids)
15773 qtdemux->protection_system_ids =
15774 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
15775 /* Check whether we already have an entry for this system ID. */
15776 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
15777 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
15778 if (g_ascii_strcasecmp (system_id, id) == 0) {
15779 return;
15780 }
15781 }
15782 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
15783 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,
15784 -1));
15785}