blob: e05caa11ef85e4911785f93d256d8bf23f344ad5 [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
zengliang.lia6c0cdd2024-05-18 07:15:51 +000062#include "gstamlisomp4elements.h"
63#include "aml-qtatomparser.h"
64#include "aml-qtdemux_types.h"
65#include "aml-qtdemux_dump.h"
66#include "aml-fourcc.h"
67#include "aml-descriptors.h"
68#include "aml-qtdemux_lang.h"
69#include "aml-qtdemux.h"
70#include "aml-qtpalette.h"
71#include "aml-qtdemux_tags.h"
72#include "aml-qtdemux_tree.h"
73#include "aml-qtdemux-webvtt.h"
zengliang.li5f31ef42024-05-16 08:27:38 +000074
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 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000086#define AML_QTDEMUX_MAX_ATOM_SIZE (32*1024*1024)
zengliang.li5f31ef42024-05-16 08:27:38 +000087
88/* if the sample index is larger than this, something is likely wrong */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000089#define AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
zengliang.li5f31ef42024-05-16 08:27:38 +000090
91/* For converting qt creation times to unix epoch times */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000092#define AML_QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
93#define AML_QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
94#define AML_QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
95 AML_QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * AML_QTDEMUX_SECONDS_PER_DAY)
zengliang.li5f31ef42024-05-16 08:27:38 +000096
zengliang.lia6c0cdd2024-05-18 07:15:51 +000097#define AML_QTDEMUX_TREE_NODE_FOURCC(n) (AML_QT_FOURCC(((guint8 *) (n)->data) + 4))
zengliang.li5f31ef42024-05-16 08:27:38 +000098
zengliang.lia6c0cdd2024-05-18 07:15:51 +000099#define AML_STREAM_IS_EOS(s) ((s)->time_position == GST_CLOCK_TIME_NONE)
zengliang.li5f31ef42024-05-16 08:27:38 +0000100
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000101#define AML_ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
zengliang.li5f31ef42024-05-16 08:27:38 +0000102
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000103#define AML_QTDEMUX_STREAM(s) ((AmlQtDemuxStream *)(s))
104#define AML_QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
105#define AML_QTDEMUX_NTH_STREAM(demux,idx) \
106 AML_QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
107#define AML_QTDEMUX_NTH_OLD_STREAM(demux,idx) \
108 AML_QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
zengliang.li5f31ef42024-05-16 08:27:38 +0000109
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000110#define AML_CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))
zengliang.li5f31ef42024-05-16 08:27:38 +0000111
zengliang.li0b0fc6d2024-06-14 18:47:50 +0800112GST_DEBUG_CATEGORY (aml_qtdemux_debug);
113#define GST_CAT_DEFAULT aml_qtdemux_debug
zengliang.li5f31ef42024-05-16 08:27:38 +0000114
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000115typedef struct _AmlQtDemuxCencSampleSetInfo AmlQtDemuxCencSampleSetInfo;
116typedef struct _AmlQtDemuxAavdEncryptionInfo AmlQtDemuxAavdEncryptionInfo;
zengliang.li5f31ef42024-05-16 08:27:38 +0000117
118/* Macros for converting to/from timescale */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000119#define AML_QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
120#define AML_GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
zengliang.li5f31ef42024-05-16 08:27:38 +0000121
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000122#define AML_QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
123#define AML_GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))
zengliang.li5f31ef42024-05-16 08:27:38 +0000124
125/* timestamp is the DTS */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000126#define AML_QTSAMPLE_DTS(stream,sample) (AML_QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
zengliang.li5f31ef42024-05-16 08:27:38 +0000127/* timestamp + offset + cslg_shift is the outgoing PTS */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000128#define AML_QTSAMPLE_PTS(stream,sample) (AML_QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
zengliang.li5f31ef42024-05-16 08:27:38 +0000129/* timestamp + offset is the PTS used for internal seek calculations */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000130#define AML_QTSAMPLE_PTS_NO_CSLG(stream,sample) (AML_QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
zengliang.li5f31ef42024-05-16 08:27:38 +0000131/* timestamp + duration - dts is the duration */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000132#define AML_QTSAMPLE_DUR_DTS(stream, sample, dts) (AML_QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
zengliang.li5f31ef42024-05-16 08:27:38 +0000133
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000134#define AML_QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)
zengliang.li5f31ef42024-05-16 08:27:38 +0000135
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000136#define AML_QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
137#define AML_QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
zengliang.li5f31ef42024-05-16 08:27:38 +0000138 GST_TRACE("Locking from thread %p", g_thread_self()); \
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000139 g_mutex_lock (AML_QTDEMUX_EXPOSE_GET_LOCK (demux)); \
zengliang.li5f31ef42024-05-16 08:27:38 +0000140 GST_TRACE("Locked from thread %p", g_thread_self()); \
141 } G_STMT_END
142
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000143#define AML_QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
zengliang.li5f31ef42024-05-16 08:27:38 +0000144 GST_TRACE("Unlocking from thread %p", g_thread_self()); \
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000145 g_mutex_unlock (AML_QTDEMUX_EXPOSE_GET_LOCK (demux)); \
zengliang.li5f31ef42024-05-16 08:27:38 +0000146 } G_STMT_END
147
xuesong.jianga8fbc442024-09-26 20:31:31 +0800148#define AML_QTDEMUX_SWITCH_GAP (200 * GST_MSECOND)
149
zengliang.li5f31ef42024-05-16 08:27:38 +0000150/*
151 * Quicktime has tracks and segments. A track is a continuous piece of
152 * multimedia content. The track is not always played from start to finish but
153 * instead, pieces of the track are 'cut out' and played in sequence. This is
154 * what the segments do.
155 *
156 * Inside the track we have keyframes (K) and delta frames. The track has its
157 * own timing, which starts from 0 and extends to end. The position in the track
158 * is called the media_time.
159 *
160 * The segments now describe the pieces that should be played from this track
161 * and are basically tuples of media_time/duration/rate entries. We can have
162 * multiple segments and they are all played after one another. An example:
163 *
164 * segment 1: media_time: 1 second, duration: 1 second, rate 1
165 * segment 2: media_time: 3 second, duration: 2 second, rate 2
166 *
167 * To correctly play back this track, one must play: 1 second of media starting
168 * from media_time 1 followed by 2 seconds of media starting from media_time 3
169 * at a rate of 2.
170 *
171 * Each of the segments will be played at a specific time, the first segment at
172 * time 0, the second one after the duration of the first one, etc.. Note that
173 * the time in resulting playback is not identical to the media_time of the
174 * track anymore.
175 *
176 * Visually, assuming the track has 4 second of media_time:
177 *
178 * (a) (b) (c) (d)
179 * .-----------------------------------------------------------.
180 * track: | K.....K.........K........K.......K.......K...........K... |
181 * '-----------------------------------------------------------'
182 * 0 1 2 3 4
183 * .------------^ ^ .----------^ ^
184 * / .-------------' / .------------------'
185 * / / .-----' /
186 * .--------------. .--------------.
187 * | segment 1 | | segment 2 |
188 * '--------------' '--------------'
189 *
190 * The challenge here is to cut out the right pieces of the track for each of
191 * the playback segments. This fortunately can easily be done with the SEGMENT
192 * events of GStreamer.
193 *
194 * For playback of segment 1, we need to provide the decoder with the keyframe
195 * (a), in the above figure, but we must instruct it only to output the decoded
196 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
197 * position set to the time of the segment: 0.
198 *
199 * We then proceed to push data from keyframe (a) to frame (b). The decoder
200 * decodes but clips all before media_time 1.
201 *
202 * After finishing a segment, we push out a new SEGMENT event with the clipping
203 * boundaries of the new data.
204 *
205 * This is a good usecase for the GStreamer accumulated SEGMENT events.
206 */
207
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000208struct _AmlQtDemuxSegment
zengliang.li5f31ef42024-05-16 08:27:38 +0000209{
210 /* global time and duration, all gst time */
211 GstClockTime time;
212 GstClockTime stop_time;
213 GstClockTime duration;
214 /* media time of trak, all gst time */
215 GstClockTime media_start;
216 GstClockTime media_stop;
217 gdouble rate;
218 /* Media start time in trak timescale units */
219 guint32 trak_media_start;
220};
221
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000222#define AML_QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
zengliang.li5f31ef42024-05-16 08:27:38 +0000223
224/* Used with fragmented MP4 files (mfra atom) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000225struct _AmlQtDemuxRandomAccessEntry
zengliang.li5f31ef42024-05-16 08:27:38 +0000226{
227 GstClockTime ts;
228 guint64 moof_offset;
229};
230
231
232/* Contains properties and cryptographic info for a set of samples from a
233 * track protected using Common Encryption (cenc) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000234struct _AmlQtDemuxCencSampleSetInfo
zengliang.li5f31ef42024-05-16 08:27:38 +0000235{
236 GstStructure *default_properties;
237
238 /* @crypto_info holds one GstStructure per sample */
239 GPtrArray *crypto_info;
240};
241
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000242struct _AmlQtDemuxAavdEncryptionInfo
zengliang.li5f31ef42024-05-16 08:27:38 +0000243{
244 GstStructure *default_properties;
245};
246
247static const gchar *
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000248aml_qt_demux_state_string (enum AmlQtDemuxState state)
zengliang.li5f31ef42024-05-16 08:27:38 +0000249{
250 switch (state) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000251 case AML_QTDEMUX_STATE_INITIAL:
zengliang.li5f31ef42024-05-16 08:27:38 +0000252 return "<INITIAL>";
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000253 case AML_QTDEMUX_STATE_HEADER:
zengliang.li5f31ef42024-05-16 08:27:38 +0000254 return "<HEADER>";
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000255 case AML_QTDEMUX_STATE_MOVIE:
zengliang.li5f31ef42024-05-16 08:27:38 +0000256 return "<MOVIE>";
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000257 case AML_QTDEMUX_STATE_BUFFER_MDAT:
zengliang.li5f31ef42024-05-16 08:27:38 +0000258 return "<BUFFER_MDAT>";
259 default:
260 return "<UNKNOWN>";
261 }
262}
263
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000264static GstFlowReturn aml_qtdemux_add_fragmented_samples (GstAmlQTDemux * qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +0000265
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000266static void gst_aml_qtdemux_check_send_pending_segment (GstAmlQTDemux * demux);
zengliang.li5f31ef42024-05-16 08:27:38 +0000267
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000268static GstStaticPadTemplate gst_aml_qtdemux_sink_template =
zengliang.li5f31ef42024-05-16 08:27:38 +0000269 GST_STATIC_PAD_TEMPLATE ("sink",
270 GST_PAD_SINK,
271 GST_PAD_ALWAYS,
272 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
273 "application/x-3gp")
274 );
275
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000276static GstStaticPadTemplate gst_aml_qtdemux_videosrc_template =
zengliang.li5f31ef42024-05-16 08:27:38 +0000277GST_STATIC_PAD_TEMPLATE ("video_%u",
278 GST_PAD_SRC,
279 GST_PAD_SOMETIMES,
280 GST_STATIC_CAPS_ANY);
281
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000282static GstStaticPadTemplate gst_aml_qtdemux_audiosrc_template =
zengliang.li5f31ef42024-05-16 08:27:38 +0000283GST_STATIC_PAD_TEMPLATE ("audio_%u",
284 GST_PAD_SRC,
285 GST_PAD_SOMETIMES,
286 GST_STATIC_CAPS_ANY);
287
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000288static GstStaticPadTemplate gst_aml_qtdemux_subsrc_template =
zengliang.li5f31ef42024-05-16 08:27:38 +0000289GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
290 GST_PAD_SRC,
291 GST_PAD_SOMETIMES,
292 GST_STATIC_CAPS_ANY);
293
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000294#define gst_aml_qtdemux_parent_class parent_class
295G_DEFINE_TYPE (GstAmlQTDemux, gst_aml_qtdemux, GST_TYPE_ELEMENT);
296GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (amlqtdemux, "amlqtdemux",
297 GST_RANK_PRIMARY + 100, GST_TYPE_AML_QTDEMUX, aml_isomp4_element_init (plugin));
zengliang.li5f31ef42024-05-16 08:27:38 +0000298
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000299static void gst_aml_qtdemux_dispose (GObject * object);
300static void gst_aml_qtdemux_finalize (GObject * object);
zengliang.li5f31ef42024-05-16 08:27:38 +0000301
302static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000303gst_aml_qtdemux_find_index_linear (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * str,
zengliang.li5f31ef42024-05-16 08:27:38 +0000304 GstClockTime media_time);
305static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000306gst_aml_qtdemux_find_index_for_given_media_offset_linear (GstAmlQTDemux * qtdemux,
307 AmlQtDemuxStream * str, gint64 media_offset);
zengliang.li5f31ef42024-05-16 08:27:38 +0000308
309#if 0
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000310static void gst_aml_qtdemux_set_index (GstElement * element, GstIndex * index);
311static GstIndex *gst_aml_qtdemux_get_index (GstElement * element);
zengliang.li5f31ef42024-05-16 08:27:38 +0000312#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000313static GstStateChangeReturn gst_aml_qtdemux_change_state (GstElement * element,
zengliang.li5f31ef42024-05-16 08:27:38 +0000314 GstStateChange transition);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000315static void gst_aml_qtdemux_set_context (GstElement * element,
zengliang.li5f31ef42024-05-16 08:27:38 +0000316 GstContext * context);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000317static gboolean aml_qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
318static gboolean aml_qtdemux_sink_activate_mode (GstPad * sinkpad,
zengliang.li5f31ef42024-05-16 08:27:38 +0000319 GstObject * parent, GstPadMode mode, gboolean active);
320
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000321static void gst_aml_qtdemux_loop (GstPad * pad);
322static GstFlowReturn gst_aml_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +0000323 GstBuffer * inbuf);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000324static gboolean gst_aml_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +0000325 GstEvent * event);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000326static gboolean gst_aml_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +0000327 GstQuery * query);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000328static gboolean gst_aml_qtdemux_setcaps (GstAmlQTDemux * qtdemux, GstCaps * caps);
329static gboolean gst_aml_qtdemux_configure_stream (GstAmlQTDemux * qtdemux,
330 AmlQtDemuxStream * stream);
331static void gst_aml_qtdemux_stream_check_and_change_stsd_index (GstAmlQTDemux * demux,
332 AmlQtDemuxStream * stream);
333static GstFlowReturn gst_aml_qtdemux_process_adapter (GstAmlQTDemux * demux,
zengliang.li5f31ef42024-05-16 08:27:38 +0000334 gboolean force);
335
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000336static void gst_aml_qtdemux_check_seekability (GstAmlQTDemux * demux);
zengliang.li5f31ef42024-05-16 08:27:38 +0000337
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000338static gboolean aml_qtdemux_parse_moov (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +0000339 const guint8 * buffer, guint length);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000340static gboolean aml_qtdemux_parse_node (GstAmlQTDemux * qtdemux, GNode * node,
zengliang.li5f31ef42024-05-16 08:27:38 +0000341 const guint8 * buffer, guint length);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000342static gboolean aml_qtdemux_parse_tree (GstAmlQTDemux * qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +0000343
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000344static void gst_aml_qtdemux_handle_esds (GstAmlQTDemux * qtdemux,
345 AmlQtDemuxStream * stream, AmlQtDemuxStreamStsdEntry * entry, GNode * esds,
zengliang.li5f31ef42024-05-16 08:27:38 +0000346 GstTagList * list);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000347static GstCaps *aml_qtdemux_video_caps (GstAmlQTDemux * qtdemux,
348 AmlQtDemuxStream * stream, AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +0000349 const guint8 * stsd_entry_data, gchar ** codec_name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000350static GstCaps *aml_qtdemux_audio_caps (GstAmlQTDemux * qtdemux,
351 AmlQtDemuxStream * stream, AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +0000352 const guint8 * data, int len, gchar ** codec_name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000353static GstCaps *aml_qtdemux_sub_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
354 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
zengliang.li5f31ef42024-05-16 08:27:38 +0000355 gchar ** codec_name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000356static GstCaps *aml_qtdemux_generic_caps (GstAmlQTDemux * qtdemux,
357 AmlQtDemuxStream * stream, AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +0000358 const guint8 * stsd_entry_data, gchar ** codec_name);
359
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000360static gboolean aml_qtdemux_parse_samples (GstAmlQTDemux * qtdemux,
361 AmlQtDemuxStream * stream, guint32 n);
362static GstFlowReturn aml_qtdemux_expose_streams (GstAmlQTDemux * qtdemux);
363static AmlQtDemuxStream *gst_aml_qtdemux_stream_ref (AmlQtDemuxStream * stream);
364static void gst_aml_qtdemux_stream_unref (AmlQtDemuxStream * stream);
365static void gst_aml_qtdemux_stream_clear (AmlQtDemuxStream * stream);
366static GstFlowReturn aml_qtdemux_prepare_streams (GstAmlQTDemux * qtdemux);
367static void aml_qtdemux_do_allocation (AmlQtDemuxStream * stream,
368 GstAmlQTDemux * qtdemux);
369static gboolean gst_aml_qtdemux_activate_segment (GstAmlQTDemux * qtdemux,
370 AmlQtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
371static gboolean gst_aml_qtdemux_stream_update_segment (GstAmlQTDemux * qtdemux,
372 AmlQtDemuxStream * stream, gint seg_idx, GstClockTime offset,
zengliang.li5f31ef42024-05-16 08:27:38 +0000373 GstClockTime * _start, GstClockTime * _stop);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000374static void gst_aml_qtdemux_send_gap_for_segment (GstAmlQTDemux * demux,
375 AmlQtDemuxStream * stream, gint segment_index, GstClockTime pos);
zengliang.li5f31ef42024-05-16 08:27:38 +0000376
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000377static gboolean aml_qtdemux_pull_mfro_mfra (GstAmlQTDemux * qtdemux);
378static void aml_check_update_duration (GstAmlQTDemux * qtdemux, GstClockTime duration);
zengliang.li5f31ef42024-05-16 08:27:38 +0000379
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000380static gchar *aml_qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);
zengliang.li5f31ef42024-05-16 08:27:38 +0000381
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000382static GstStructure *aml_qtdemux_get_cenc_sample_properties (GstAmlQTDemux * qtdemux,
383 AmlQtDemuxStream * stream, guint sample_index);
384static void gst_aml_qtdemux_append_protection_system_id (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +0000385 const gchar * id);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000386static void aml_qtdemux_gst_structure_free (GstStructure * gststructure);
387static void gst_aml_qtdemux_reset (GstAmlQTDemux * qtdemux, gboolean hard);
388static GstVideoColorPrimaries gst_aml_video_color_primaries_from_iso_compat (guint value);
389static GstVideoTransferFunction gst_aml_video_transfer_function_from_iso_compat (guint value);
390static GstVideoColorMatrix gst_aml_video_color_matrix_from_iso_compat (guint value);
391
zengliang.li5f31ef42024-05-16 08:27:38 +0000392
393static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000394gst_aml_qtdemux_class_init (GstAmlQTDemuxClass * klass)
zengliang.li5f31ef42024-05-16 08:27:38 +0000395{
396 GObjectClass *gobject_class;
397 GstElementClass *gstelement_class;
398
399 gobject_class = (GObjectClass *) klass;
400 gstelement_class = (GstElementClass *) klass;
401
402 parent_class = g_type_class_peek_parent (klass);
403
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000404 gobject_class->dispose = gst_aml_qtdemux_dispose;
405 gobject_class->finalize = gst_aml_qtdemux_finalize;
zengliang.li5f31ef42024-05-16 08:27:38 +0000406
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000407 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_aml_qtdemux_change_state);
zengliang.li5f31ef42024-05-16 08:27:38 +0000408#if 0
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000409 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_aml_qtdemux_set_index);
410 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_aml_qtdemux_get_index);
zengliang.li5f31ef42024-05-16 08:27:38 +0000411#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000412 gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_aml_qtdemux_set_context);
zengliang.li5f31ef42024-05-16 08:27:38 +0000413
414 gst_tag_register_musicbrainz_tags ();
415
416 gst_element_class_add_static_pad_template (gstelement_class,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000417 &gst_aml_qtdemux_sink_template);
zengliang.li5f31ef42024-05-16 08:27:38 +0000418 gst_element_class_add_static_pad_template (gstelement_class,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000419 &gst_aml_qtdemux_videosrc_template);
zengliang.li5f31ef42024-05-16 08:27:38 +0000420 gst_element_class_add_static_pad_template (gstelement_class,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000421 &gst_aml_qtdemux_audiosrc_template);
zengliang.li5f31ef42024-05-16 08:27:38 +0000422 gst_element_class_add_static_pad_template (gstelement_class,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000423 &gst_aml_qtdemux_subsrc_template);
zengliang.li5f31ef42024-05-16 08:27:38 +0000424 gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
425 "Codec/Demuxer",
426 "Demultiplex a QuickTime file into audio and video streams",
427 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
428
zengliang.li0b0fc6d2024-06-14 18:47:50 +0800429 GST_DEBUG_CATEGORY_INIT (aml_qtdemux_debug, "amlqtdemux", 0, "amlqtdemux plugin");
zengliang.li5f31ef42024-05-16 08:27:38 +0000430 gst_riff_init ();
431}
432
433static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000434gst_aml_qtdemux_init (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +0000435{
436 qtdemux->sinkpad =
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000437 gst_pad_new_from_static_template (&gst_aml_qtdemux_sink_template, "sink");
438 gst_pad_set_activate_function (qtdemux->sinkpad, aml_qtdemux_sink_activate);
zengliang.li5f31ef42024-05-16 08:27:38 +0000439 gst_pad_set_activatemode_function (qtdemux->sinkpad,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000440 aml_qtdemux_sink_activate_mode);
441 gst_pad_set_chain_function (qtdemux->sinkpad, gst_aml_qtdemux_chain);
442 gst_pad_set_event_function (qtdemux->sinkpad, gst_aml_qtdemux_handle_sink_event);
443 gst_pad_set_query_function (qtdemux->sinkpad, gst_aml_qtdemux_handle_sink_query);
zengliang.li5f31ef42024-05-16 08:27:38 +0000444 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
445
446 qtdemux->adapter = gst_adapter_new ();
447 g_queue_init (&qtdemux->protection_event_queue);
448 qtdemux->flowcombiner = gst_flow_combiner_new ();
449 g_mutex_init (&qtdemux->expose_lock);
450
451 qtdemux->active_streams = g_ptr_array_new_with_free_func
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000452 ((GDestroyNotify) gst_aml_qtdemux_stream_unref);
zengliang.li5f31ef42024-05-16 08:27:38 +0000453 qtdemux->old_streams = g_ptr_array_new_with_free_func
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000454 ((GDestroyNotify) gst_aml_qtdemux_stream_unref);
zengliang.li5f31ef42024-05-16 08:27:38 +0000455
456 GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
457
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000458 gst_aml_qtdemux_reset (qtdemux, TRUE);
zengliang.lid0c84c32024-05-17 03:33:20 +0000459 qtdemux->cal_discontinuity_pos = FALSE;
460 qtdemux->discontinuity_base_pos = 0;
xuesong.jianga8fbc442024-09-26 20:31:31 +0800461
462 qtdemux->smooth_switch_enable = TRUE;
463 qtdemux->pre_keyframe_pts = GST_CLOCK_TIME_NONE;
464 qtdemux->pre_keyframe_dts = GST_CLOCK_TIME_NONE;
zengliang.li5f31ef42024-05-16 08:27:38 +0000465}
466
467static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000468gst_aml_qtdemux_finalize (GObject * object)
zengliang.li5f31ef42024-05-16 08:27:38 +0000469{
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000470 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (object);
zengliang.li5f31ef42024-05-16 08:27:38 +0000471
472 g_free (qtdemux->redirect_location);
473
474 G_OBJECT_CLASS (parent_class)->finalize (object);
475}
476
477static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000478gst_aml_qtdemux_dispose (GObject * object)
zengliang.li5f31ef42024-05-16 08:27:38 +0000479{
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000480 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (object);
zengliang.li5f31ef42024-05-16 08:27:38 +0000481
482 if (qtdemux->adapter) {
483 g_object_unref (G_OBJECT (qtdemux->adapter));
484 qtdemux->adapter = NULL;
485 }
486 gst_tag_list_unref (qtdemux->tag_list);
487 gst_flow_combiner_free (qtdemux->flowcombiner);
488 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
489 NULL);
490 g_queue_clear (&qtdemux->protection_event_queue);
491
492 g_free (qtdemux->cenc_aux_info_sizes);
493 qtdemux->cenc_aux_info_sizes = NULL;
494 g_mutex_clear (&qtdemux->expose_lock);
495
496 g_ptr_array_free (qtdemux->active_streams, TRUE);
497 g_ptr_array_free (qtdemux->old_streams, TRUE);
498
499 G_OBJECT_CLASS (parent_class)->dispose (object);
500}
501
502static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000503gst_aml_qtdemux_post_no_playable_stream_error (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +0000504{
505 if (qtdemux->redirect_location) {
506 GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
507 (_("This file contains no playable streams.")),
508 ("no known streams found, a redirect message has been posted"),
509 ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
510 } else {
511 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
512 (_("This file contains no playable streams.")),
513 ("no known streams found"));
514 }
515}
516
517static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000518_gst_aml_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
zengliang.li5f31ef42024-05-16 08:27:38 +0000519{
520 return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
521 mem, size, 0, size, mem, free_func);
522}
523
524static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000525gst_aml_qtdemux_pull_atom (GstAmlQTDemux * qtdemux, guint64 offset, guint64 size,
zengliang.li5f31ef42024-05-16 08:27:38 +0000526 GstBuffer ** buf)
527{
528 GstFlowReturn flow;
529 GstMapInfo map;
530 gsize bsize;
531
532 if (G_UNLIKELY (size == 0)) {
533 GstFlowReturn ret;
534 GstBuffer *tmp = NULL;
535
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000536 ret = gst_aml_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
zengliang.li5f31ef42024-05-16 08:27:38 +0000537 if (ret != GST_FLOW_OK)
538 return ret;
539
540 gst_buffer_map (tmp, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000541 size = AML_QT_UINT32 (map.data);
zengliang.li5f31ef42024-05-16 08:27:38 +0000542 GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
543
544 gst_buffer_unmap (tmp, &map);
545 gst_buffer_unref (tmp);
546 }
547
548 /* Sanity check: catch bogus sizes (fuzzed/broken files) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000549 if (G_UNLIKELY (size > AML_QTDEMUX_MAX_ATOM_SIZE)) {
550 if (qtdemux->state != AML_QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
zengliang.li5f31ef42024-05-16 08:27:38 +0000551 /* we're pulling header but already got most interesting bits,
552 * so never mind the rest (e.g. tags) (that much) */
553 GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
554 size);
555 return GST_FLOW_EOS;
556 } else {
557 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
558 (_("This file is invalid and cannot be played.")),
559 ("atom has bogus size %" G_GUINT64_FORMAT, size));
560 return GST_FLOW_ERROR;
561 }
562 }
563
564 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);
565
566 if (G_UNLIKELY (flow != GST_FLOW_OK))
567 return flow;
568
569 bsize = gst_buffer_get_size (*buf);
570 /* Catch short reads - we don't want any partial atoms */
571 if (G_UNLIKELY (bsize < size)) {
572 GST_WARNING_OBJECT (qtdemux,
573 "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
574 gst_buffer_unref (*buf);
575 *buf = NULL;
576 return GST_FLOW_EOS;
577 }
578
579 return flow;
580}
581
582#if 1
583static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000584gst_aml_qtdemux_src_convert (GstAmlQTDemux * qtdemux, GstPad * pad,
zengliang.li5f31ef42024-05-16 08:27:38 +0000585 GstFormat src_format, gint64 src_value, GstFormat dest_format,
586 gint64 * dest_value)
587{
588 gboolean res = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000589 AmlQtDemuxStream *stream = gst_pad_get_element_private (pad);
zengliang.li5f31ef42024-05-16 08:27:38 +0000590 gint32 index;
591
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000592 if (stream->subtype != AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +0000593 res = FALSE;
594 goto done;
595 }
596
597 switch (src_format) {
598 case GST_FORMAT_TIME:
599 switch (dest_format) {
600 case GST_FORMAT_BYTES:{
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000601 index = gst_aml_qtdemux_find_index_linear (qtdemux, stream, src_value);
zengliang.li5f31ef42024-05-16 08:27:38 +0000602 if (-1 == index) {
603 res = FALSE;
604 goto done;
605 }
606
607 *dest_value = stream->samples[index].offset;
608
609 GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
610 GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
611 GST_TIME_ARGS (src_value), *dest_value);
612 break;
613 }
614 default:
615 res = FALSE;
616 break;
617 }
618 break;
619 case GST_FORMAT_BYTES:
620 switch (dest_format) {
621 case GST_FORMAT_TIME:{
622 index =
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000623 gst_aml_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +0000624 stream, src_value);
625
626 if (-1 == index) {
627 res = FALSE;
628 goto done;
629 }
630
631 *dest_value =
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000632 AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +0000633 stream->samples[index].timestamp);
634 GST_DEBUG_OBJECT (qtdemux,
635 "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
636 GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
637 break;
638 }
639 default:
640 res = FALSE;
641 break;
642 }
643 break;
644 default:
645 res = FALSE;
646 break;
647 }
648
649done:
650 return res;
651}
652#endif
653
654static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000655gst_aml_qtdemux_get_duration (GstAmlQTDemux * qtdemux, GstClockTime * duration)
zengliang.li5f31ef42024-05-16 08:27:38 +0000656{
657 gboolean res = FALSE;
658
659 *duration = GST_CLOCK_TIME_NONE;
660
661 if (qtdemux->duration != 0 &&
662 qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000663 *duration = AML_QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
zengliang.li5f31ef42024-05-16 08:27:38 +0000664 res = TRUE;
665 } else {
666 *duration = GST_CLOCK_TIME_NONE;
667 }
668
669 return res;
670}
671
672static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000673gst_aml_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +0000674 GstQuery * query)
675{
676 gboolean res = FALSE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000677 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +0000678
679 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
680
681 switch (GST_QUERY_TYPE (query)) {
682 case GST_QUERY_POSITION:{
683 GstFormat fmt;
684
685 gst_query_parse_position (query, &fmt, NULL);
686 if (fmt == GST_FORMAT_TIME
687 && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
688 gst_query_set_position (query, GST_FORMAT_TIME,
689 qtdemux->segment.position);
690 res = TRUE;
691 }
692 }
693 break;
694 case GST_QUERY_DURATION:{
695 GstFormat fmt;
696
697 gst_query_parse_duration (query, &fmt, NULL);
698 if (fmt == GST_FORMAT_TIME) {
699 /* First try to query upstream */
700 res = gst_pad_query_default (pad, parent, query);
701 if (!res) {
702 GstClockTime duration;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000703 if (gst_aml_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +0000704 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
705 res = TRUE;
706 }
707 }
708 }
709 break;
710 }
711 case GST_QUERY_CONVERT:{
712 GstFormat src_fmt, dest_fmt;
713 gint64 src_value, dest_value = 0;
714
715 gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
716
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000717 res = gst_aml_qtdemux_src_convert (qtdemux, pad,
zengliang.li5f31ef42024-05-16 08:27:38 +0000718 src_fmt, src_value, dest_fmt, &dest_value);
719 if (res)
720 gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
721
722 break;
723 }
724 case GST_QUERY_FORMATS:
725 gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
726 res = TRUE;
727 break;
728 case GST_QUERY_SEEKING:{
729 GstFormat fmt;
730 gboolean seekable;
731
732 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
733
734 if (fmt == GST_FORMAT_BYTES) {
735 /* We always refuse BYTES seeks from downstream */
736 break;
737 }
738
739 /* try upstream first */
740 res = gst_pad_query_default (pad, parent, query);
741
742 if (!res) {
743 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
744 if (fmt == GST_FORMAT_TIME) {
745 GstClockTime duration;
746
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000747 gst_aml_qtdemux_get_duration (qtdemux, &duration);
zengliang.li5f31ef42024-05-16 08:27:38 +0000748 seekable = TRUE;
749 if (!qtdemux->pullbased) {
750 GstQuery *q;
751
752 /* we might be able with help from upstream */
753 seekable = FALSE;
754 q = gst_query_new_seeking (GST_FORMAT_BYTES);
755 if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
756 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
757 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
758 }
759 gst_query_unref (q);
760 }
761 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
762 res = TRUE;
763 }
764 }
765 break;
766 }
767 case GST_QUERY_SEGMENT:
768 {
769 GstFormat format;
770 gint64 start, stop;
771
772 format = qtdemux->segment.format;
773
774 start =
775 gst_segment_to_stream_time (&qtdemux->segment, format,
776 qtdemux->segment.start);
777 if ((stop = qtdemux->segment.stop) == -1)
778 stop = qtdemux->segment.duration;
779 else
780 stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);
781
782 gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
783 res = TRUE;
784 break;
785 }
786 default:
787 res = gst_pad_query_default (pad, parent, query);
788 break;
789 }
790
791 return res;
792}
793
794static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000795gst_aml_qtdemux_push_tags (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +0000796{
797 if (G_LIKELY (stream->pad)) {
798 GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
799 GST_DEBUG_PAD_NAME (stream->pad));
800
801 if (!gst_tag_list_is_empty (stream->stream_tags)) {
802 GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
803 stream->stream_tags);
804 gst_pad_push_event (stream->pad,
805 gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
806 }
807
808 if (G_UNLIKELY (stream->send_global_tags)) {
809 GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
810 qtdemux->tag_list);
811 gst_pad_push_event (stream->pad,
812 gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
813 stream->send_global_tags = FALSE;
814 }
815 }
816}
817
818/* push event on all source pads; takes ownership of the event */
819static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000820gst_aml_qtdemux_push_event (GstAmlQTDemux * qtdemux, GstEvent * event)
zengliang.li5f31ef42024-05-16 08:27:38 +0000821{
822 gboolean has_valid_stream = FALSE;
823 GstEventType etype = GST_EVENT_TYPE (event);
824 guint i;
825
826 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
827 GST_EVENT_TYPE_NAME (event));
828
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000829 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +0000830 GstPad *pad;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000831 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +0000832 GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
833
834 if ((pad = stream->pad)) {
835 has_valid_stream = TRUE;
836
837 if (etype == GST_EVENT_EOS) {
838 /* let's not send twice */
839 if (stream->sent_eos)
840 continue;
841 stream->sent_eos = TRUE;
842 }
843
844 gst_pad_push_event (pad, gst_event_ref (event));
845 }
846 }
847
848 gst_event_unref (event);
849
850 /* if it is EOS and there are no pads, post an error */
851 if (!has_valid_stream && etype == GST_EVENT_EOS) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000852 gst_aml_qtdemux_post_no_playable_stream_error (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +0000853 }
854}
855
856typedef struct
857{
858 guint64 media_time;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000859} AmlFindData;
zengliang.li5f31ef42024-05-16 08:27:38 +0000860
861static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000862aml_find_func (AmlQtDemuxSample * s1, gint64 * media_time, gpointer user_data)
zengliang.li5f31ef42024-05-16 08:27:38 +0000863{
864 if ((gint64) s1->timestamp > *media_time)
865 return 1;
866 if ((gint64) s1->timestamp == *media_time)
867 return 0;
868
869 return -1;
870}
871
872/* find the index of the sample that includes the data for @media_time using a
873 * binary search. Only to be called in optimized cases of linear search below.
874 *
875 * Returns the index of the sample with the corresponding *DTS*.
876 */
877static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000878gst_aml_qtdemux_find_index (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * str,
zengliang.li5f31ef42024-05-16 08:27:38 +0000879 guint64 media_time)
880{
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000881 AmlQtDemuxSample *result;
zengliang.li5f31ef42024-05-16 08:27:38 +0000882 guint32 index;
883
884 /* convert media_time to mov format */
885 media_time =
886 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
887
888 result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000889 sizeof (AmlQtDemuxSample), (GCompareDataFunc) aml_find_func,
zengliang.li5f31ef42024-05-16 08:27:38 +0000890 GST_SEARCH_MODE_BEFORE, &media_time, NULL);
891
892 if (G_LIKELY (result))
893 index = result - str->samples;
894 else
895 index = 0;
896
897 return index;
898}
899
900
901
902/* find the index of the sample that includes the data for @media_offset using a
903 * linear search
904 *
905 * Returns the index of the sample.
906 */
907static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000908gst_aml_qtdemux_find_index_for_given_media_offset_linear (GstAmlQTDemux * qtdemux,
909 AmlQtDemuxStream * str, gint64 media_offset)
zengliang.li5f31ef42024-05-16 08:27:38 +0000910{
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000911 AmlQtDemuxSample *result = str->samples;
zengliang.li5f31ef42024-05-16 08:27:38 +0000912 guint32 index = 0;
913
914 if (result == NULL || str->n_samples == 0)
915 return -1;
916
917 if (media_offset == result->offset)
918 return index;
919
920 result++;
921 while (index < str->n_samples - 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000922 if (!aml_qtdemux_parse_samples (qtdemux, str, index + 1))
zengliang.li5f31ef42024-05-16 08:27:38 +0000923 goto parse_failed;
924
925 if (media_offset < result->offset)
926 break;
927
928 index++;
929 result++;
930 }
931 return index;
932
933 /* ERRORS */
934parse_failed:
935 {
936 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
937 return -1;
938 }
939}
940
941/* find the index of the sample that includes the data for @media_time using a
942 * linear search, and keeping in mind that not all samples may have been parsed
943 * yet. If possible, it will delegate to binary search.
944 *
945 * Returns the index of the sample.
946 */
947static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000948gst_aml_qtdemux_find_index_linear (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * str,
zengliang.li5f31ef42024-05-16 08:27:38 +0000949 GstClockTime media_time)
950{
951 guint32 index = 0;
952 guint64 mov_time;
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000953 AmlQtDemuxSample *sample;
zengliang.li5f31ef42024-05-16 08:27:38 +0000954
955 /* convert media_time to mov format */
956 mov_time =
957 gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
958
959 sample = str->samples;
960 if (mov_time == sample->timestamp + sample->pts_offset)
961 return index;
962
963 /* use faster search if requested time in already parsed range */
964 sample = str->samples + str->stbl_index;
965 if (str->stbl_index >= 0 && mov_time <= sample->timestamp) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000966 index = gst_aml_qtdemux_find_index (qtdemux, str, media_time);
zengliang.li5f31ef42024-05-16 08:27:38 +0000967 sample = str->samples + index;
968 } else {
969 while (index < str->n_samples - 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +0000970 if (!aml_qtdemux_parse_samples (qtdemux, str, index + 1))
zengliang.li5f31ef42024-05-16 08:27:38 +0000971 goto parse_failed;
972
973 sample = str->samples + index + 1;
974 if (mov_time < sample->timestamp) {
975 sample = str->samples + index;
976 break;
977 }
978
979 index++;
980 }
981 }
982
983 /* sample->timestamp is now <= media_time, need to find the corresponding
984 * PTS now by looking backwards */
985 while (index > 0 && sample->timestamp + sample->pts_offset > mov_time) {
986 index--;
987 sample = str->samples + index;
988 }
989
990 return index;
991
992 /* ERRORS */
993parse_failed:
994 {
995 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
996 return -1;
997 }
998}
999
1000/* find the index of the keyframe needed to decode the sample at @index
1001 * of stream @str, or of a subsequent keyframe (depending on @next)
1002 *
1003 * Returns the index of the keyframe.
1004 */
1005static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001006gst_aml_qtdemux_find_keyframe (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * str,
zengliang.li5f31ef42024-05-16 08:27:38 +00001007 guint32 index, gboolean next)
1008{
1009 guint32 new_index = index;
1010
1011 if (index >= str->n_samples) {
1012 new_index = str->n_samples;
1013 goto beach;
1014 }
1015
1016 /* all keyframes, return index */
1017 if (str->all_keyframe) {
1018 new_index = index;
1019 goto beach;
1020 }
1021
1022 /* else search until we have a keyframe */
1023 while (new_index < str->n_samples) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001024 if (next && !aml_qtdemux_parse_samples (qtdemux, str, new_index))
zengliang.li5f31ef42024-05-16 08:27:38 +00001025 goto parse_failed;
1026
1027 if (str->samples[new_index].keyframe)
1028 break;
1029
1030 if (new_index == 0)
1031 break;
1032
1033 if (next)
1034 new_index++;
1035 else
1036 new_index--;
1037 }
1038
1039 if (new_index == str->n_samples) {
1040 GST_DEBUG_OBJECT (qtdemux, "no next keyframe");
1041 new_index = -1;
1042 }
1043
1044beach:
1045 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index %s index %u "
1046 "gave %u", next ? "after" : "before", index, new_index);
1047
1048 return new_index;
1049
1050 /* ERRORS */
1051parse_failed:
1052 {
1053 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", new_index);
1054 return -1;
1055 }
1056}
1057
1058/* find the segment for @time_position for @stream
1059 *
1060 * Returns the index of the segment containing @time_position.
1061 * Returns the last segment and sets the @eos variable to TRUE
1062 * if the time is beyond the end. @eos may be NULL
1063 */
1064static guint32
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001065gst_aml_qtdemux_find_segment (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00001066 GstClockTime time_position)
1067{
1068 gint i;
1069 guint32 seg_idx;
1070
1071 GST_LOG_OBJECT (stream->pad, "finding segment for %" GST_TIME_FORMAT,
1072 GST_TIME_ARGS (time_position));
1073
1074 seg_idx = -1;
1075 for (i = 0; i < stream->n_segments; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001076 AmlQtDemuxSegment *segment = &stream->segments[i];
zengliang.li5f31ef42024-05-16 08:27:38 +00001077
1078 GST_LOG_OBJECT (stream->pad,
1079 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
1080 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
1081
1082 /* For the last segment we include stop_time in the last segment */
1083 if (i < stream->n_segments - 1) {
1084 if (segment->time <= time_position && time_position < segment->stop_time) {
1085 GST_LOG_OBJECT (stream->pad, "segment %d matches", i);
1086 seg_idx = i;
1087 break;
1088 }
1089 } else {
1090 /* Last segment always matches */
1091 seg_idx = i;
1092 break;
1093 }
1094 }
1095 return seg_idx;
1096}
1097
1098/* move the stream @str to the sample position @index.
1099 *
1100 * Updates @str->sample_index and marks discontinuity if needed.
1101 */
1102static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001103gst_aml_qtdemux_move_stream (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * str,
zengliang.li5f31ef42024-05-16 08:27:38 +00001104 guint32 index)
1105{
1106 /* no change needed */
1107 if (index == str->sample_index)
1108 return;
1109
1110 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
1111 str->n_samples);
1112
1113 /* position changed, we have a discont */
1114 str->sample_index = index;
1115 str->offset_in_sample = 0;
1116 /* Each time we move in the stream we store the position where we are
1117 * starting from */
1118 str->from_sample = index;
1119 str->discont = TRUE;
1120}
1121
1122static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001123gst_aml_qtdemux_adjust_seek (GstAmlQTDemux * qtdemux, gint64 desired_time,
zengliang.li5f31ef42024-05-16 08:27:38 +00001124 gboolean use_sparse, gboolean next, gint64 * key_time, gint64 * key_offset)
1125{
1126 guint64 min_offset;
1127 gint64 min_byte_offset = -1;
1128 guint i;
1129
1130 min_offset = desired_time;
1131
1132 /* for each stream, find the index of the sample in the segment
1133 * and move back to the previous keyframe. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001134 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
1135 AmlQtDemuxStream *str;
zengliang.li5f31ef42024-05-16 08:27:38 +00001136 guint32 index, kindex;
1137 guint32 seg_idx;
1138 GstClockTime media_start;
1139 GstClockTime media_time;
1140 GstClockTime seg_time;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001141 AmlQtDemuxSegment *seg;
zengliang.li5f31ef42024-05-16 08:27:38 +00001142 gboolean empty_segment = FALSE;
1143
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001144 str = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00001145
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001146 if (AML_CUR_STREAM (str)->sparse && !use_sparse)
zengliang.li5f31ef42024-05-16 08:27:38 +00001147 continue;
1148
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001149 seg_idx = gst_aml_qtdemux_find_segment (qtdemux, str, desired_time);
zengliang.li5f31ef42024-05-16 08:27:38 +00001150 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
1151
1152 /* get segment and time in the segment */
1153 seg = &str->segments[seg_idx];
1154 seg_time = (desired_time - seg->time) * seg->rate;
1155
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001156 while (AML_QTSEGMENT_IS_EMPTY (seg)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00001157 seg_time = 0;
1158 empty_segment = TRUE;
1159 GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
1160 seg_idx);
1161 seg_idx++;
1162 if (seg_idx == str->n_segments)
1163 break;
1164 seg = &str->segments[seg_idx];
1165 }
1166
1167 if (seg_idx == str->n_segments) {
1168 /* FIXME track shouldn't have the last segment as empty, but if it
1169 * happens we better handle it */
1170 continue;
1171 }
1172
1173 /* get the media time in the segment */
1174 media_start = seg->media_start + seg_time;
1175
1176 /* get the index of the sample with media time */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001177 index = gst_aml_qtdemux_find_index_linear (qtdemux, str, media_start);
zengliang.li5f31ef42024-05-16 08:27:38 +00001178 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
1179 " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
1180 GST_TIME_ARGS (media_start), index, str->samples[index].offset,
1181 empty_segment);
1182
1183 /* shift to next frame if we are looking for next keyframe */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001184 if (next && AML_QTSAMPLE_PTS_NO_CSLG (str, &str->samples[index]) < media_start
zengliang.li5f31ef42024-05-16 08:27:38 +00001185 && index < str->stbl_index)
1186 index++;
1187
1188 if (!empty_segment) {
1189 /* find previous keyframe */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001190 kindex = gst_aml_qtdemux_find_keyframe (qtdemux, str, index, next);
zengliang.li5f31ef42024-05-16 08:27:38 +00001191
1192 /* we will settle for one before if none found after */
1193 if (next && kindex == -1)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001194 kindex = gst_aml_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00001195
1196 /* Update the requested time whenever a keyframe was found, to make it
1197 * accurate and avoid having the first buffer fall outside of the segment
1198 */
1199 if (kindex != -1) {
1200 index = kindex;
1201
1202 /* get timestamp of keyframe */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001203 media_time = AML_QTSAMPLE_PTS_NO_CSLG (str, &str->samples[kindex]);
zengliang.li5f31ef42024-05-16 08:27:38 +00001204 GST_DEBUG_OBJECT (qtdemux,
1205 "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
1206 G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
1207 str->samples[kindex].offset);
1208
1209 /* keyframes in the segment get a chance to change the
1210 * desired_offset. keyframes out of the segment are
1211 * ignored. */
1212 if (media_time >= seg->media_start) {
1213 GstClockTime seg_time;
1214
1215 /* this keyframe is inside the segment, convert back to
1216 * segment time */
1217 seg_time = (media_time - seg->media_start) + seg->time;
1218 if ((!next && (seg_time < min_offset)) ||
1219 (next && (seg_time > min_offset)))
1220 min_offset = seg_time;
1221 }
1222 }
1223 }
1224
1225 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
1226 min_byte_offset = str->samples[index].offset;
1227 }
1228
1229 if (key_time)
1230 *key_time = min_offset;
1231 if (key_offset)
1232 *key_offset = min_byte_offset;
1233}
1234
1235static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001236gst_aml_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
zengliang.li5f31ef42024-05-16 08:27:38 +00001237 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
1238{
1239 gboolean res;
1240
1241 g_return_val_if_fail (format != NULL, FALSE);
1242 g_return_val_if_fail (cur != NULL, FALSE);
1243 g_return_val_if_fail (stop != NULL, FALSE);
1244
1245 if (*format == GST_FORMAT_TIME)
1246 return TRUE;
1247
1248 res = TRUE;
1249 if (cur_type != GST_SEEK_TYPE_NONE)
1250 res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
1251 if (res && stop_type != GST_SEEK_TYPE_NONE)
1252 res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);
1253
1254 if (res)
1255 *format = GST_FORMAT_TIME;
1256
1257 return res;
1258}
1259
1260/* perform seek in push based mode:
1261 find BYTE position to move to based on time and delegate to upstream
1262*/
1263static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001264gst_aml_qtdemux_do_push_seek (GstAmlQTDemux * qtdemux, GstPad * pad, GstEvent * event)
zengliang.li5f31ef42024-05-16 08:27:38 +00001265{
1266 gdouble rate;
1267 GstFormat format;
1268 GstSeekFlags flags;
1269 GstSeekType cur_type, stop_type;
1270 gint64 cur, stop, key_cur;
1271 gboolean res;
1272 gint64 byte_cur;
1273 gint64 original_stop;
1274 guint32 seqnum;
1275
1276 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");
1277
1278 gst_event_parse_seek (event, &rate, &format, &flags,
1279 &cur_type, &cur, &stop_type, &stop);
1280 seqnum = gst_event_get_seqnum (event);
1281
bo.xiaof8fd4c52024-08-13 11:43:09 +08001282#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
zengliang.li5f31ef42024-05-16 08:27:38 +00001283 /* Directly send the instant-rate-change event here before taking the
1284 * stream-lock so that it can be applied as soon as possible */
bo.xiaof8fd4c52024-08-13 11:43:09 +08001285 if (flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE) {
1286 GstEvent *ev;
zengliang.li5f31ef42024-05-16 08:27:38 +00001287
1288 /* instant rate change only supported if direction does not change. All
1289 * other requirements are already checked before creating the seek event
1290 * but let's double-check here to be sure */
bo.xiaof8fd4c52024-08-13 11:43:09 +08001291 if ((qtdemux->segment.rate > 0 && rate < 0) ||
zengliang.li5f31ef42024-05-16 08:27:38 +00001292 (qtdemux->segment.rate < 0 && rate > 0) ||
1293 cur_type != GST_SEEK_TYPE_NONE ||
1294 stop_type != GST_SEEK_TYPE_NONE || (flags & GST_SEEK_FLAG_FLUSH)) {
1295 GST_ERROR_OBJECT (qtdemux,
1296 "Instant rate change seeks only supported in the "
1297 "same direction, without flushing and position change");
1298 return FALSE;
1299 }
1300
1301 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1302 (GstSegmentFlags) flags);
1303 gst_event_set_seqnum (ev, seqnum);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001304 gst_aml_qtdemux_push_event (qtdemux, ev);
zengliang.li5f31ef42024-05-16 08:27:38 +00001305 return TRUE;
bo.xiaof8fd4c52024-08-13 11:43:09 +08001306 }
1307#endif
zengliang.li5f31ef42024-05-16 08:27:38 +00001308
1309 /* only forward streaming and seeking is possible */
1310 if (rate <= 0)
1311 goto unsupported_seek;
1312
1313 /* convert to TIME if needed and possible */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001314 if (!gst_aml_qtdemux_convert_seek (pad, &format, cur_type, &cur,
zengliang.li5f31ef42024-05-16 08:27:38 +00001315 stop_type, &stop))
1316 goto no_format;
1317
1318 /* Upstream seek in bytes will have undefined stop, but qtdemux stores
1319 * the original stop position to use when upstream pushes the new segment
1320 * for this seek */
1321 original_stop = stop;
1322 stop = -1;
1323
1324 /* find reasonable corresponding BYTE position,
1325 * also try to mind about keyframes, since we can not go back a bit for them
1326 * later on */
1327 /* determining @next here based on SNAP_BEFORE/SNAP_AFTER should
1328 * mostly just work, but let's not yet boldly go there ... */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001329 gst_aml_qtdemux_adjust_seek (qtdemux, cur, FALSE, FALSE, &key_cur, &byte_cur);
zengliang.li5f31ef42024-05-16 08:27:38 +00001330
1331 if (byte_cur == -1)
1332 goto abort_seek;
1333
1334 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
1335 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
1336 stop);
1337
1338 GST_OBJECT_LOCK (qtdemux);
1339 qtdemux->seek_offset = byte_cur;
1340 if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
1341 qtdemux->push_seek_start = cur;
1342 } else {
1343 qtdemux->push_seek_start = key_cur;
1344 }
1345
1346 if (stop_type == GST_SEEK_TYPE_NONE) {
1347 qtdemux->push_seek_stop = qtdemux->segment.stop;
1348 } else {
1349 qtdemux->push_seek_stop = original_stop;
1350 }
1351 GST_OBJECT_UNLOCK (qtdemux);
1352
1353 qtdemux->segment_seqnum = seqnum;
1354 /* BYTE seek event */
1355 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
1356 stop_type, stop);
1357 gst_event_set_seqnum (event, seqnum);
1358 res = gst_pad_push_event (qtdemux->sinkpad, event);
1359
1360 return res;
1361
1362 /* ERRORS */
1363abort_seek:
1364 {
1365 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
1366 "seek aborted.");
1367 return FALSE;
1368 }
1369unsupported_seek:
1370 {
1371 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
1372 return FALSE;
1373 }
1374no_format:
1375 {
1376 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1377 return FALSE;
1378 }
1379}
1380
1381/* perform the seek.
1382 *
1383 * We set all segment_indexes in the streams to unknown and
1384 * adjust the time_position to the desired position. this is enough
1385 * to trigger a segment switch in the streaming thread to start
1386 * streaming from the desired position.
1387 *
1388 * Keyframe seeking is a little more complicated when dealing with
1389 * segments. Ideally we want to move to the previous keyframe in
1390 * the segment but there might not be a keyframe in the segment. In
1391 * fact, none of the segments could contain a keyframe. We take a
1392 * practical approach: seek to the previous keyframe in the segment,
1393 * if there is none, seek to the beginning of the segment.
1394 *
1395 * Called with STREAM_LOCK
1396 */
1397static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001398gst_aml_qtdemux_perform_seek (GstAmlQTDemux * qtdemux, GstSegment * segment,
zengliang.li5f31ef42024-05-16 08:27:38 +00001399 guint32 seqnum, GstSeekFlags flags)
1400{
1401 gint64 desired_offset;
1402 guint i;
1403
1404 desired_offset = segment->position;
1405
1406 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
1407 GST_TIME_ARGS (desired_offset));
1408
1409 /* may not have enough fragmented info to do this adjustment,
1410 * and we can't scan (and probably should not) at this time with
1411 * possibly flushing upstream */
1412 if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
1413 gint64 min_offset;
1414 gboolean next, before, after;
1415
1416 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
1417 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
1418 next = after && !before;
1419 if (segment->rate < 0)
1420 next = !next;
1421
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001422 gst_aml_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, next, &min_offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00001423 NULL);
1424 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1425 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1426 desired_offset = min_offset;
1427 }
1428
1429 /* and set all streams to the final position */
1430 GST_OBJECT_LOCK (qtdemux);
1431 gst_flow_combiner_reset (qtdemux->flowcombiner);
1432 GST_OBJECT_UNLOCK (qtdemux);
1433 qtdemux->segment_seqnum = seqnum;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001434 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
1435 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00001436
1437 stream->time_position = desired_offset;
1438 stream->accumulated_base = 0;
1439 stream->sample_index = -1;
1440 stream->offset_in_sample = 0;
1441 stream->segment_index = -1;
1442 stream->sent_eos = FALSE;
1443 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
1444
1445 if (segment->flags & GST_SEEK_FLAG_FLUSH)
1446 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1447 }
1448 segment->position = desired_offset;
1449 if (segment->rate >= 0) {
1450 segment->start = desired_offset;
1451 /* We need to update time as we update start in that direction */
1452 segment->time = desired_offset;
1453
1454 /* we stop at the end */
1455 if (segment->stop == -1)
1456 segment->stop = segment->duration;
1457 } else {
1458 segment->stop = desired_offset;
1459 }
1460
1461 if (qtdemux->fragmented)
1462 qtdemux->fragmented_seek_pending = TRUE;
1463
1464 return TRUE;
1465}
1466
1467/* do a seek in pull based mode */
1468static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001469gst_aml_qtdemux_do_seek (GstAmlQTDemux * qtdemux, GstPad * pad, GstEvent * event)
zengliang.li5f31ef42024-05-16 08:27:38 +00001470{
1471 gdouble rate = 1.0;
1472 GstFormat format;
1473 GstSeekFlags flags;
1474 GstSeekType cur_type, stop_type;
1475 gint64 cur, stop;
bo.xiaof8fd4c52024-08-13 11:43:09 +08001476 gboolean flush, instant_rate_change;
zengliang.li5f31ef42024-05-16 08:27:38 +00001477 gboolean update;
1478 GstSegment seeksegment;
1479 guint32 seqnum = GST_SEQNUM_INVALID;
1480 GstEvent *flush_event;
1481 gboolean ret;
1482
1483 GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
1484
1485 gst_event_parse_seek (event, &rate, &format, &flags,
1486 &cur_type, &cur, &stop_type, &stop);
1487 seqnum = gst_event_get_seqnum (event);
1488
1489 /* we have to have a format as the segment format. Try to convert
1490 * if not. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001491 if (!gst_aml_qtdemux_convert_seek (pad, &format, cur_type, &cur,
zengliang.li5f31ef42024-05-16 08:27:38 +00001492 stop_type, &stop))
1493 goto no_format;
1494
1495 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
1496
1497 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
bo.xiaof8fd4c52024-08-13 11:43:09 +08001498
1499#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
1500 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
zengliang.li5f31ef42024-05-16 08:27:38 +00001501
1502 /* Directly send the instant-rate-change event here before taking the
1503 * stream-lock so that it can be applied as soon as possible */
bo.xiaof8fd4c52024-08-13 11:43:09 +08001504 if (instant_rate_change) {
1505 GstEvent *ev;
zengliang.li5f31ef42024-05-16 08:27:38 +00001506
1507 /* instant rate change only supported if direction does not change. All
1508 * other requirements are already checked before creating the seek event
1509 * but let's double-check here to be sure */
bo.xiaof8fd4c52024-08-13 11:43:09 +08001510 if ((qtdemux->segment.rate > 0 && rate < 0) ||
zengliang.li5f31ef42024-05-16 08:27:38 +00001511 (qtdemux->segment.rate < 0 && rate > 0) ||
1512 cur_type != GST_SEEK_TYPE_NONE ||
1513 stop_type != GST_SEEK_TYPE_NONE || flush) {
1514 GST_ERROR_OBJECT (qtdemux,
1515 "Instant rate change seeks only supported in the "
1516 "same direction, without flushing and position change");
1517 return FALSE;
1518 }
1519
1520 ev = gst_event_new_instant_rate_change (rate / qtdemux->segment.rate,
1521 (GstSegmentFlags) flags);
1522 gst_event_set_seqnum (ev, seqnum);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001523 gst_aml_qtdemux_push_event (qtdemux, ev);
zengliang.li5f31ef42024-05-16 08:27:38 +00001524 return TRUE;
bo.xiaof8fd4c52024-08-13 11:43:09 +08001525 }
1526#endif
zengliang.li5f31ef42024-05-16 08:27:38 +00001527
1528 /* stop streaming, either by flushing or by pausing the task */
1529 if (flush) {
1530 flush_event = gst_event_new_flush_start ();
1531 if (seqnum != GST_SEQNUM_INVALID)
1532 gst_event_set_seqnum (flush_event, seqnum);
1533 /* unlock upstream pull_range */
1534 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
1535 /* make sure out loop function exits */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001536 gst_aml_qtdemux_push_event (qtdemux, flush_event);
zengliang.li5f31ef42024-05-16 08:27:38 +00001537 } else {
1538 /* non flushing seek, pause the task */
1539 gst_pad_pause_task (qtdemux->sinkpad);
1540 }
1541
1542 /* wait for streaming to finish */
1543 GST_PAD_STREAM_LOCK (qtdemux->sinkpad);
1544
1545 /* copy segment, we need this because we still need the old
1546 * segment when we close the current segment. */
1547 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));
1548
1549 /* configure the segment with the seek variables */
1550 GST_DEBUG_OBJECT (qtdemux, "configuring seek");
1551 if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
1552 cur_type, cur, stop_type, stop, &update)) {
1553 ret = FALSE;
1554 GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
1555 } else {
1556 /* now do the seek */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001557 ret = gst_aml_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
zengliang.li5f31ef42024-05-16 08:27:38 +00001558 }
1559
1560 /* prepare for streaming again */
1561 if (flush) {
1562 flush_event = gst_event_new_flush_stop (TRUE);
1563 if (seqnum != GST_SEQNUM_INVALID)
1564 gst_event_set_seqnum (flush_event, seqnum);
1565
1566 gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001567 gst_aml_qtdemux_push_event (qtdemux, flush_event);
zengliang.li5f31ef42024-05-16 08:27:38 +00001568 }
1569
1570 /* commit the new segment */
1571 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
1572
1573 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1574 GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
1575 qtdemux->segment.format, qtdemux->segment.position);
1576 if (seqnum != GST_SEQNUM_INVALID)
1577 gst_message_set_seqnum (msg, seqnum);
1578 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
1579 }
1580
1581 /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001582 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_aml_qtdemux_loop,
zengliang.li5f31ef42024-05-16 08:27:38 +00001583 qtdemux->sinkpad, NULL);
1584
1585 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
1586
1587 return ret;
1588
1589 /* ERRORS */
1590no_format:
1591 {
1592 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
1593 return FALSE;
1594 }
1595}
1596
1597static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001598aml_qtdemux_ensure_index (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00001599{
1600 guint i;
1601
1602 GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");
1603
1604 /* Build complete index */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001605 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
1606 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00001607
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001608 if (!aml_qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00001609 GST_LOG_OBJECT (qtdemux,
1610 "Building complete index of track-id %u for seeking failed!",
1611 stream->track_id);
1612 return FALSE;
1613 }
1614 }
1615
1616 return TRUE;
1617}
1618
1619static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001620gst_aml_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +00001621 GstEvent * event)
1622{
1623 gboolean res = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001624 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +00001625
1626 switch (GST_EVENT_TYPE (event)) {
1627 case GST_EVENT_RECONFIGURE:
1628 GST_OBJECT_LOCK (qtdemux);
1629 gst_flow_combiner_reset (qtdemux->flowcombiner);
1630 GST_OBJECT_UNLOCK (qtdemux);
1631 res = gst_pad_event_default (pad, parent, event);
1632 break;
1633 case GST_EVENT_SEEK:
1634 {
1635 GstSeekFlags flags = 0;
1636 GstFormat seek_format;
bo.xiaof8fd4c52024-08-13 11:43:09 +08001637#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
1638 gboolean instant_rate_change;
1639#endif
zengliang.li5f31ef42024-05-16 08:27:38 +00001640
1641#ifndef GST_DISABLE_GST_DEBUG
1642 GstClockTime ts = gst_util_get_timestamp ();
1643#endif
1644 guint32 seqnum = gst_event_get_seqnum (event);
1645
1646 qtdemux->received_seek = TRUE;
1647
1648 gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
1649 NULL);
bo.xiaof8fd4c52024-08-13 11:43:09 +08001650#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
1651 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1652#endif
zengliang.li5f31ef42024-05-16 08:27:38 +00001653
1654 if (seqnum == qtdemux->segment_seqnum) {
1655 GST_LOG_OBJECT (pad,
1656 "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum);
1657 gst_event_unref (event);
1658 return TRUE;
1659 }
1660
1661 if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
1662 /* seek should be handled by upstream, we might need to re-download fragments */
1663 GST_DEBUG_OBJECT (qtdemux,
1664 "let upstream handle seek for fragmented playback");
1665 goto upstream;
1666 }
1667
1668 if (seek_format == GST_FORMAT_BYTES) {
1669 GST_DEBUG_OBJECT (pad, "Rejecting seek request in bytes format");
1670 gst_event_unref (event);
1671 return FALSE;
1672 }
1673
1674 gst_event_parse_seek_trickmode_interval (event,
1675 &qtdemux->trickmode_interval);
1676
1677 /* Build complete index for seeking;
1678 * if not a fragmented file at least and we're really doing a seek,
1679 * not just an instant-rate-change */
bo.xiaof8fd4c52024-08-13 11:43:09 +08001680#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
1681 if (!qtdemux->fragmented && !instant_rate_change) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001682 if (!aml_qtdemux_ensure_index (qtdemux))
zengliang.li5f31ef42024-05-16 08:27:38 +00001683 goto index_failed;
bo.xiaof8fd4c52024-08-13 11:43:09 +08001684 }
1685#else
1686 if (!qtdemux->fragmented)
1687 if (!aml_qtdemux_ensure_index (qtdemux))
1688 goto index_failed;
1689#endif
1690
zengliang.li5f31ef42024-05-16 08:27:38 +00001691#ifndef GST_DISABLE_GST_DEBUG
1692 ts = gst_util_get_timestamp () - ts;
1693 GST_INFO_OBJECT (qtdemux,
1694 "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1695#endif
1696 if (qtdemux->pullbased) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001697 res = gst_aml_qtdemux_do_seek (qtdemux, pad, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00001698 } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
1699 GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
1700 res = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001701 } else if (qtdemux->state == AML_QTDEMUX_STATE_MOVIE
1702 && AML_QTDEMUX_N_STREAMS (qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00001703 && !qtdemux->fragmented) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001704 res = gst_aml_qtdemux_do_push_seek (qtdemux, pad, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00001705 } else {
1706 GST_DEBUG_OBJECT (qtdemux,
1707 "ignoring seek in push mode in current state");
1708 res = FALSE;
1709 }
1710 gst_event_unref (event);
1711 }
1712 break;
1713 default:
1714 upstream:
1715 res = gst_pad_event_default (pad, parent, event);
1716 break;
1717 }
1718
1719done:
1720 return res;
1721
1722 /* ERRORS */
1723index_failed:
1724 {
1725 GST_ERROR_OBJECT (qtdemux, "Index failed");
1726 gst_event_unref (event);
1727 res = FALSE;
1728 goto done;
1729 }
1730}
1731
1732/* Find, for each track, the first sample in coding order that has a file offset >= @byte_pos.
1733 *
1734 * If @fw is false, the coding order is explored backwards.
1735 *
1736 * If @set is true, each stream will be moved to its matched sample, or EOS if no matching
1737 * sample is found for that track.
1738 *
1739 * The stream and sample index of the sample with the minimum offset in the direction explored
1740 * (see @fw) is returned in the output parameters @_stream and @_index respectively.
1741 *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001742 * @_time is set to the AML_QTSAMPLE_PTS of the matched sample with the minimum AML_QTSAMPLE_PTS in the
1743 * direction explored, which may not always match the AML_QTSAMPLE_PTS of the sample returned in
zengliang.li5f31ef42024-05-16 08:27:38 +00001744 * @_stream and @_index. */
1745static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001746gst_aml_qtdemux_find_sample (GstAmlQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
1747 gboolean set, AmlQtDemuxStream ** _stream, gint * _index, gint64 * _time)
zengliang.li5f31ef42024-05-16 08:27:38 +00001748{
1749 gint i, index;
1750 gint64 time, min_time;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001751 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00001752 gint iter;
1753
1754 min_time = -1;
1755 stream = NULL;
1756 index = -1;
1757
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001758 for (iter = 0; iter < AML_QTDEMUX_N_STREAMS (qtdemux); iter++) {
1759 AmlQtDemuxStream *str;
zengliang.li5f31ef42024-05-16 08:27:38 +00001760 gint inc;
1761 gboolean set_sample;
1762
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001763 str = AML_QTDEMUX_NTH_STREAM (qtdemux, iter);
zengliang.li5f31ef42024-05-16 08:27:38 +00001764 set_sample = !set;
1765
1766 if (fw) {
1767 i = 0;
1768 inc = 1;
1769 } else {
1770 i = str->n_samples - 1;
1771 inc = -1;
1772 }
1773
1774 for (; (i >= 0) && (i < str->n_samples); i += inc) {
1775 if (str->samples[i].size == 0)
1776 continue;
1777
1778 if (fw && (str->samples[i].offset < byte_pos))
1779 continue;
1780
1781 if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
1782 continue;
1783
1784 /* move stream to first available sample */
1785 if (set) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001786 gst_aml_qtdemux_move_stream (qtdemux, str, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00001787 set_sample = TRUE;
1788 }
1789
1790 /* avoid index from sparse streams since they might be far away */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001791 if (!AML_CUR_STREAM (str)->sparse) {
zengliang.li5f31ef42024-05-16 08:27:38 +00001792 /* determine min/max time */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001793 time = AML_QTSAMPLE_PTS (str, &str->samples[i]);
zengliang.li5f31ef42024-05-16 08:27:38 +00001794 if (min_time == -1 || (!fw && time > min_time) ||
1795 (fw && time < min_time)) {
1796 min_time = time;
1797 }
1798
1799 /* determine stream with leading sample, to get its position */
1800 if (!stream ||
1801 (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
1802 (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
1803 stream = str;
1804 index = i;
1805 }
1806 }
1807 break;
1808 }
1809
1810 /* no sample for this stream, mark eos */
1811 if (!set_sample)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001812 gst_aml_qtdemux_move_stream (qtdemux, str, str->n_samples);
zengliang.li5f31ef42024-05-16 08:27:38 +00001813 }
1814
1815 if (_time)
1816 *_time = min_time;
1817 if (_stream)
1818 *_stream = stream;
1819 if (_index)
1820 *_index = index;
1821}
1822
1823/* Copied from mpegtsbase code */
1824/* FIXME: replace this function when we add new util function for stream-id creation */
1825static gchar *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001826_aml_get_upstream_id (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00001827{
1828 gchar *upstream_id = gst_pad_get_stream_id (demux->sinkpad);
1829
1830 if (!upstream_id) {
1831 /* Try to create one from the upstream URI, else use a randome number */
1832 GstQuery *query;
1833 gchar *uri = NULL;
1834
1835 /* Try to generate one from the URI query and
1836 * if it fails take a random number instead */
1837 query = gst_query_new_uri ();
1838 if (gst_element_query (GST_ELEMENT_CAST (demux), query)) {
1839 gst_query_parse_uri (query, &uri);
1840 }
1841
1842 if (uri) {
1843 GChecksum *cs;
1844
1845 /* And then generate an SHA256 sum of the URI */
1846 cs = g_checksum_new (G_CHECKSUM_SHA256);
1847 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
1848 g_free (uri);
1849 upstream_id = g_strdup (g_checksum_get_string (cs));
1850 g_checksum_free (cs);
1851 } else {
1852 /* Just get some random number if the URI query fails */
1853 GST_FIXME_OBJECT (demux, "Creating random stream-id, consider "
1854 "implementing a deterministic way of creating a stream-id");
1855 upstream_id =
1856 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
1857 g_random_int (), g_random_int ());
1858 }
1859
1860 gst_query_unref (query);
1861 }
1862 return upstream_id;
1863}
1864
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001865static AmlQtDemuxStream *
1866_aml_create_stream (GstAmlQTDemux * demux, guint32 track_id)
zengliang.li5f31ef42024-05-16 08:27:38 +00001867{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001868 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00001869 gchar *upstream_id;
1870
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001871 stream = g_new0 (AmlQtDemuxStream, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +00001872 stream->demux = demux;
1873 stream->track_id = track_id;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001874 upstream_id = _aml_get_upstream_id (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00001875 stream->stream_id = g_strdup_printf ("%s/%03u", upstream_id, track_id);
1876 g_free (upstream_id);
1877 /* new streams always need a discont */
1878 stream->discont = TRUE;
1879 /* we enable clipping for raw audio/video streams */
1880 stream->need_clip = FALSE;
1881 stream->process_func = NULL;
1882 stream->segment_index = -1;
1883 stream->time_position = 0;
1884 stream->sample_index = -1;
1885 stream->offset_in_sample = 0;
1886 stream->new_stream = TRUE;
1887 stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
1888 stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1889 stream->protected = FALSE;
1890 stream->protection_scheme_type = 0;
1891 stream->protection_scheme_version = 0;
1892 stream->protection_scheme_info = NULL;
1893 stream->n_samples_moof = 0;
1894 stream->duration_moof = 0;
1895 stream->duration_last_moof = 0;
1896 stream->alignment = 1;
1897 stream->stream_tags = gst_tag_list_new_empty ();
1898 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
1899 g_queue_init (&stream->protection_scheme_event_queue);
1900 stream->ref_count = 1;
1901 /* consistent default for push based mode */
1902 gst_segment_init (&stream->segment, GST_FORMAT_TIME);
1903 return stream;
1904}
1905
1906static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001907gst_aml_qtdemux_setcaps (GstAmlQTDemux * demux, GstCaps * caps)
zengliang.li5f31ef42024-05-16 08:27:38 +00001908{
1909 GstStructure *structure;
1910 const gchar *variant;
1911 const GstCaps *mediacaps = NULL;
1912
1913 GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);
1914
1915 structure = gst_caps_get_structure (caps, 0);
1916 variant = gst_structure_get_string (structure, "variant");
1917
1918 if (variant && strcmp (variant, "mse-bytestream") == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001919 demux->variant = AML_VARIANT_MSE_BYTESTREAM;
zengliang.li5f31ef42024-05-16 08:27:38 +00001920 }
1921
1922 if (variant && strcmp (variant, "mss-fragmented") == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001923 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00001924 const GValue *value;
1925
1926 demux->fragmented = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001927 demux->variant = AML_VARIANT_MSS_FRAGMENTED;
zengliang.li5f31ef42024-05-16 08:27:38 +00001928
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001929 if (AML_QTDEMUX_N_STREAMS (demux) > 1) {
zengliang.li5f31ef42024-05-16 08:27:38 +00001930 /* can't do this, we can only renegotiate for another mss format */
1931 return FALSE;
1932 }
1933
1934 value = gst_structure_get_value (structure, "media-caps");
1935 /* create stream */
1936 if (value) {
1937 const GValue *timescale_v;
1938
1939 /* TODO update when stream changes during playback */
1940
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001941 if (AML_QTDEMUX_N_STREAMS (demux) == 0) {
1942 stream = _aml_create_stream (demux, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +00001943 g_ptr_array_add (demux->active_streams, stream);
1944 /* mss has no stsd/stsd entry, use id 0 as default */
1945 stream->stsd_entries_length = 1;
1946 stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001947 stream->stsd_entries = g_new0 (AmlQtDemuxStreamStsdEntry, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +00001948 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001949 stream = AML_QTDEMUX_NTH_STREAM (demux, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00001950 }
1951
1952 timescale_v = gst_structure_get_value (structure, "timescale");
1953 if (timescale_v) {
1954 stream->timescale = g_value_get_uint64 (timescale_v);
1955 } else {
1956 /* default mss timescale */
1957 stream->timescale = 10000000;
1958 }
1959 demux->timescale = stream->timescale;
1960
1961 mediacaps = gst_value_get_caps (value);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001962 if (!AML_CUR_STREAM (stream)->caps
1963 || !gst_caps_is_equal_fixed (mediacaps, AML_CUR_STREAM (stream)->caps)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00001964 GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
1965 mediacaps);
1966 stream->new_caps = TRUE;
1967 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001968 gst_caps_replace (&AML_CUR_STREAM (stream)->caps, (GstCaps *) mediacaps);
zengliang.li5f31ef42024-05-16 08:27:38 +00001969 structure = gst_caps_get_structure (mediacaps, 0);
1970 if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001971 stream->subtype = AML_FOURCC_vide;
zengliang.li5f31ef42024-05-16 08:27:38 +00001972
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001973 gst_structure_get_int (structure, "width", &AML_CUR_STREAM (stream)->width);
zengliang.li5f31ef42024-05-16 08:27:38 +00001974 gst_structure_get_int (structure, "height",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001975 &AML_CUR_STREAM (stream)->height);
zengliang.li5f31ef42024-05-16 08:27:38 +00001976 gst_structure_get_fraction (structure, "framerate",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001977 &AML_CUR_STREAM (stream)->fps_n, &AML_CUR_STREAM (stream)->fps_d);
zengliang.li5f31ef42024-05-16 08:27:38 +00001978 } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
1979 gint rate = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001980 stream->subtype = AML_FOURCC_soun;
zengliang.li5f31ef42024-05-16 08:27:38 +00001981 gst_structure_get_int (structure, "channels",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001982 &AML_CUR_STREAM (stream)->n_channels);
zengliang.li5f31ef42024-05-16 08:27:38 +00001983 gst_structure_get_int (structure, "rate", &rate);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001984 AML_CUR_STREAM (stream)->rate = rate;
zengliang.li5f31ef42024-05-16 08:27:38 +00001985 } else if (gst_structure_has_name (structure, "application/x-cenc")) {
1986 if (gst_structure_has_field (structure, "original-media-type")) {
1987 const gchar *media_type =
1988 gst_structure_get_string (structure, "original-media-type");
1989 if (g_str_has_prefix (media_type, "video")) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001990 stream->subtype = AML_FOURCC_vide;
zengliang.li5f31ef42024-05-16 08:27:38 +00001991 } else if (g_str_has_prefix (media_type, "audio")) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00001992 stream->subtype = AML_FOURCC_soun;
zengliang.li5f31ef42024-05-16 08:27:38 +00001993 }
1994 }
1995 }
1996 }
1997 gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
1998 }
1999
2000 return TRUE;
2001}
2002
2003static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002004gst_aml_qtdemux_reset (GstAmlQTDemux * qtdemux, gboolean hard)
zengliang.li5f31ef42024-05-16 08:27:38 +00002005{
2006 gint i;
2007
2008 GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
2009 gst_pad_stop_task (qtdemux->sinkpad);
2010
2011 if (hard || qtdemux->upstream_format_is_time) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002012 qtdemux->state = AML_QTDEMUX_STATE_INITIAL;
zengliang.li5f31ef42024-05-16 08:27:38 +00002013 qtdemux->neededbytes = 16;
2014 qtdemux->todrop = 0;
2015 qtdemux->pullbased = FALSE;
2016 g_clear_pointer (&qtdemux->redirect_location, g_free);
2017 qtdemux->first_mdat = -1;
2018 qtdemux->header_size = 0;
2019 qtdemux->mdatoffset = -1;
2020 qtdemux->restoredata_offset = -1;
2021 if (qtdemux->mdatbuffer)
2022 gst_buffer_unref (qtdemux->mdatbuffer);
2023 if (qtdemux->restoredata_buffer)
2024 gst_buffer_unref (qtdemux->restoredata_buffer);
2025 qtdemux->mdatbuffer = NULL;
2026 qtdemux->restoredata_buffer = NULL;
2027 qtdemux->mdatleft = 0;
2028 qtdemux->mdatsize = 0;
2029 if (qtdemux->comp_brands)
2030 gst_buffer_unref (qtdemux->comp_brands);
2031 qtdemux->comp_brands = NULL;
2032 qtdemux->last_moov_offset = -1;
2033 if (qtdemux->moov_node_compressed) {
2034 g_node_destroy (qtdemux->moov_node_compressed);
2035 if (qtdemux->moov_node)
2036 g_free (qtdemux->moov_node->data);
2037 }
2038 qtdemux->moov_node_compressed = NULL;
2039 if (qtdemux->moov_node)
2040 g_node_destroy (qtdemux->moov_node);
2041 qtdemux->moov_node = NULL;
2042 if (qtdemux->tag_list)
2043 gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
2044 qtdemux->tag_list = gst_tag_list_new_empty ();
2045 gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
2046#if 0
2047 if (qtdemux->element_index)
2048 gst_object_unref (qtdemux->element_index);
2049 qtdemux->element_index = NULL;
2050#endif
2051 qtdemux->major_brand = 0;
2052 qtdemux->upstream_format_is_time = FALSE;
2053 qtdemux->upstream_seekable = FALSE;
2054 qtdemux->upstream_size = 0;
2055
2056 qtdemux->fragment_start = -1;
2057 qtdemux->fragment_start_offset = -1;
2058 qtdemux->duration = 0;
2059 qtdemux->moof_offset = 0;
2060 qtdemux->chapters_track_id = 0;
2061 qtdemux->have_group_id = FALSE;
2062 qtdemux->group_id = G_MAXUINT;
2063
2064 g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
2065 NULL);
2066 g_queue_clear (&qtdemux->protection_event_queue);
2067
2068 qtdemux->received_seek = FALSE;
2069 qtdemux->first_moof_already_parsed = FALSE;
2070 }
2071 qtdemux->offset = 0;
2072 gst_adapter_clear (qtdemux->adapter);
2073 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
2074 qtdemux->need_segment = TRUE;
2075
2076 if (hard) {
2077 qtdemux->segment_seqnum = GST_SEQNUM_INVALID;
2078 qtdemux->trickmode_interval = 0;
2079 g_ptr_array_set_size (qtdemux->active_streams, 0);
2080 g_ptr_array_set_size (qtdemux->old_streams, 0);
2081 qtdemux->n_video_streams = 0;
2082 qtdemux->n_audio_streams = 0;
2083 qtdemux->n_sub_streams = 0;
2084 qtdemux->exposed = FALSE;
2085 qtdemux->fragmented = FALSE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002086 qtdemux->variant = AML_VARIANT_NONE;
zengliang.li5f31ef42024-05-16 08:27:38 +00002087 gst_caps_replace (&qtdemux->media_caps, NULL);
2088 qtdemux->timescale = 0;
2089 qtdemux->got_moov = FALSE;
2090 qtdemux->cenc_aux_info_offset = 0;
2091 qtdemux->cenc_aux_info_sizes = NULL;
2092 qtdemux->cenc_aux_sample_count = 0;
2093 if (qtdemux->protection_system_ids) {
2094 g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
2095 qtdemux->protection_system_ids = NULL;
2096 }
2097 qtdemux->streams_aware = GST_OBJECT_PARENT (qtdemux)
2098 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (qtdemux),
2099 GST_BIN_FLAG_STREAMS_AWARE);
2100
2101 if (qtdemux->preferred_protection_system_id) {
2102 g_free (qtdemux->preferred_protection_system_id);
2103 qtdemux->preferred_protection_system_id = NULL;
2104 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002105 } else if (qtdemux->variant == AML_VARIANT_MSS_FRAGMENTED) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002106 gst_flow_combiner_reset (qtdemux->flowcombiner);
2107 g_ptr_array_foreach (qtdemux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002108 (GFunc) gst_aml_qtdemux_stream_clear, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00002109 } else {
2110 gst_flow_combiner_reset (qtdemux->flowcombiner);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002111 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
2112 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00002113 stream->sent_eos = FALSE;
2114 stream->time_position = 0;
2115 stream->accumulated_base = 0;
2116 stream->last_keyframe_dts = GST_CLOCK_TIME_NONE;
2117 }
2118 }
2119}
2120
2121
2122/* Maps the @segment to the qt edts internal segments and pushes
2123 * the corresponding segment event.
2124 *
2125 * If it ends up being at a empty segment, a gap will be pushed and the next
2126 * edts segment will be activated in sequence.
2127 *
2128 * To be used in push-mode only */
2129static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002130gst_aml_qtdemux_map_and_push_segments (GstAmlQTDemux * qtdemux, GstSegment * segment)
zengliang.li5f31ef42024-05-16 08:27:38 +00002131{
2132 gint i, iter;
2133
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002134 for (iter = 0; iter < AML_QTDEMUX_N_STREAMS (qtdemux); iter++) {
2135 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, iter);
zengliang.li5f31ef42024-05-16 08:27:38 +00002136
2137 stream->time_position = segment->start;
2138
2139 /* in push mode we should be guaranteed that we will have empty segments
2140 * at the beginning and then one segment after, other scenarios are not
2141 * supported and are discarded when parsing the edts */
2142 for (i = 0; i < stream->n_segments; i++) {
2143 if (stream->segments[i].stop_time > segment->start) {
2144 /* push the empty segment and move to the next one */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002145 gst_aml_qtdemux_activate_segment (qtdemux, stream, i,
zengliang.li5f31ef42024-05-16 08:27:38 +00002146 stream->time_position);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002147 if (AML_QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
2148 gst_aml_qtdemux_send_gap_for_segment (qtdemux, stream, i,
zengliang.li5f31ef42024-05-16 08:27:38 +00002149 stream->time_position);
2150
2151 /* accumulate previous segments */
2152 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
2153 stream->accumulated_base +=
2154 (stream->segment.stop -
2155 stream->segment.start) / ABS (stream->segment.rate);
2156 continue;
2157 }
2158
2159 g_assert (i == stream->n_segments - 1);
2160 }
2161 }
2162 }
2163}
2164
2165static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002166gst_aml_qtdemux_stream_concat (GstAmlQTDemux * qtdemux, GPtrArray * dest,
zengliang.li5f31ef42024-05-16 08:27:38 +00002167 GPtrArray * src)
2168{
2169 guint i;
2170 guint len;
2171
2172 len = src->len;
2173
2174 if (len == 0)
2175 return;
2176
2177 for (i = 0; i < len; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002178 AmlQtDemuxStream *stream = g_ptr_array_index (src, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00002179
2180#ifndef GST_DISABLE_GST_DEBUG
2181 GST_DEBUG_OBJECT (qtdemux, "Move stream %p (stream-id %s) to %p",
2182 stream, GST_STR_NULL (stream->stream_id), dest);
2183#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002184 g_ptr_array_add (dest, gst_aml_qtdemux_stream_ref (stream));
zengliang.li5f31ef42024-05-16 08:27:38 +00002185 }
2186
2187 g_ptr_array_set_size (src, 0);
2188}
2189
2190static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002191gst_aml_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +00002192 GstEvent * event)
2193{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002194 GstAmlQTDemux *demux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +00002195 gboolean res = TRUE;
2196
xuesong.jianga8fbc442024-09-26 20:31:31 +08002197 GST_LOG_OBJECT (demux, "handling %s event %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), event);
zengliang.li5f31ef42024-05-16 08:27:38 +00002198
2199 switch (GST_EVENT_TYPE (event)) {
2200 case GST_EVENT_SEGMENT:
2201 {
2202 gint64 offset = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002203 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00002204 gint idx;
2205 GstSegment segment;
2206
2207 /* some debug output */
2208 gst_event_copy_segment (event, &segment);
2209 GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
2210 &segment);
2211
2212 if (segment.format == GST_FORMAT_TIME) {
2213 demux->upstream_format_is_time = TRUE;
2214 demux->segment_seqnum = gst_event_get_seqnum (event);
2215 } else {
2216 GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
2217 "not in time format");
2218
2219 /* chain will send initial newsegment after pads have been added */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002220 if (demux->state != AML_QTDEMUX_STATE_MOVIE || !AML_QTDEMUX_N_STREAMS (demux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002221 GST_DEBUG_OBJECT (demux, "still starting, eating event");
2222 goto exit;
2223 }
2224 }
2225
2226 /* check if this matches a time seek we received previously
2227 * FIXME for backwards compatibility reasons we use the
2228 * seek_offset here to compare. In the future we might want to
2229 * change this to use the seqnum as it uniquely should identify
2230 * the segment that corresponds to the seek. */
2231 GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
2232 ", received segment offset %" G_GINT64_FORMAT,
2233 demux->seek_offset, segment.start);
2234 if (segment.format == GST_FORMAT_BYTES
2235 && demux->seek_offset == segment.start) {
2236 GST_OBJECT_LOCK (demux);
2237 offset = segment.start;
2238
2239 segment.format = GST_FORMAT_TIME;
2240 segment.start = demux->push_seek_start;
2241 segment.stop = demux->push_seek_stop;
2242 GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
2243 "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2244 GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
2245 GST_OBJECT_UNLOCK (demux);
2246 }
2247
2248 /* we only expect a BYTE segment, e.g. following a seek */
2249 if (segment.format == GST_FORMAT_BYTES) {
2250 if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
2251 offset = segment.start;
2252
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002253 gst_aml_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
zengliang.li5f31ef42024-05-16 08:27:38 +00002254 NULL, (gint64 *) & segment.start);
2255 if ((gint64) segment.start < 0)
2256 segment.start = 0;
2257 }
2258 if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002259 gst_aml_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
zengliang.li5f31ef42024-05-16 08:27:38 +00002260 NULL, (gint64 *) & segment.stop);
2261 /* keyframe seeking should already arrange for start >= stop,
2262 * but make sure in other rare cases */
2263 segment.stop = MAX (segment.stop, segment.start);
2264 }
2265 } else if (segment.format == GST_FORMAT_TIME) {
2266 /* push all data on the adapter before starting this
2267 * new segment */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002268 gst_aml_qtdemux_process_adapter (demux, TRUE);
zengliang.li5f31ef42024-05-16 08:27:38 +00002269 } else {
2270 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
2271 goto exit;
2272 }
2273
2274 /* We shouldn't modify upstream driven TIME FORMAT segment */
2275 if (!demux->upstream_format_is_time) {
2276 /* accept upstream's notion of segment and distribute along */
2277 segment.format = GST_FORMAT_TIME;
2278 segment.position = segment.time = segment.start;
2279 segment.duration = demux->segment.duration;
2280 segment.base = gst_segment_to_running_time (&demux->segment,
2281 GST_FORMAT_TIME, demux->segment.position);
2282 }
2283
2284 gst_segment_copy_into (&segment, &demux->segment);
2285 GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
2286
2287 /* map segment to internal qt segments and push on each stream */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002288 if (AML_QTDEMUX_N_STREAMS (demux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002289 demux->need_segment = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002290 gst_aml_qtdemux_check_send_pending_segment (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00002291 }
2292
2293 /* clear leftover in current segment, if any */
2294 gst_adapter_clear (demux->adapter);
2295
2296 /* set up streaming thread */
2297 demux->offset = offset;
2298 if (demux->upstream_format_is_time) {
2299 GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
2300 "set values to restart reading from a new atom");
2301 demux->neededbytes = 16;
2302 demux->todrop = 0;
xuesong.jianga8fbc442024-09-26 20:31:31 +08002303 demux->pre_keyframe_pts = GST_CLOCK_TIME_NONE;
2304 demux->pre_keyframe_dts = GST_CLOCK_TIME_NONE;
zengliang.li5f31ef42024-05-16 08:27:38 +00002305 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002306 gst_aml_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
zengliang.li5f31ef42024-05-16 08:27:38 +00002307 NULL);
2308 if (stream) {
2309 demux->todrop = stream->samples[idx].offset - offset;
2310 demux->neededbytes = demux->todrop + stream->samples[idx].size;
2311 } else {
2312 /* set up for EOS */
2313 demux->neededbytes = -1;
2314 demux->todrop = 0;
2315 }
2316 }
2317 exit:
2318 gst_event_unref (event);
2319 res = TRUE;
2320 goto drop;
2321 }
2322 case GST_EVENT_FLUSH_START:
2323 {
2324 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2325 gst_event_unref (event);
2326 goto drop;
2327 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002328 AML_QTDEMUX_EXPOSE_LOCK (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00002329 res = gst_pad_event_default (demux->sinkpad, parent, event);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002330 AML_QTDEMUX_EXPOSE_UNLOCK (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00002331 goto drop;
2332 }
2333 case GST_EVENT_FLUSH_STOP:
2334 {
2335 guint64 dur;
2336
2337 dur = demux->segment.duration;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002338 gst_aml_qtdemux_reset (demux, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00002339 demux->segment.duration = dur;
2340
2341 if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
2342 gst_event_unref (event);
2343 goto drop;
2344 }
2345 break;
2346 }
2347 case GST_EVENT_EOS:
2348 /* If we are in push mode, and get an EOS before we've seen any streams,
2349 * then error out - we have nowhere to send the EOS */
2350 if (!demux->pullbased) {
2351 gint i;
2352 gboolean has_valid_stream = FALSE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002353 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
2354 if (AML_QTDEMUX_NTH_STREAM (demux, i)->pad != NULL) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002355 has_valid_stream = TRUE;
2356 break;
2357 }
2358 }
2359 if (!has_valid_stream)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002360 gst_aml_qtdemux_post_no_playable_stream_error (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00002361 else {
2362 GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
2363 (guint) gst_adapter_available (demux->adapter));
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002364 if (gst_aml_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002365 res = FALSE;
2366 }
2367 }
2368 }
2369 break;
2370 case GST_EVENT_CAPS:{
2371 GstCaps *caps = NULL;
2372
2373 gst_event_parse_caps (event, &caps);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002374 gst_aml_qtdemux_setcaps (demux, caps);
zengliang.li5f31ef42024-05-16 08:27:38 +00002375 res = TRUE;
2376 gst_event_unref (event);
2377 goto drop;
2378 }
2379 case GST_EVENT_PROTECTION:
2380 {
2381 const gchar *system_id = NULL;
2382
2383 gst_event_parse_protection (event, &system_id, NULL, NULL);
2384 GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
2385 system_id);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002386 gst_aml_qtdemux_append_protection_system_id (demux, system_id);
zengliang.li5f31ef42024-05-16 08:27:38 +00002387 /* save the event for later, for source pads that have not been created */
2388 g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
2389 /* send it to all pads that already exist */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002390 gst_aml_qtdemux_push_event (demux, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00002391 res = TRUE;
2392 goto drop;
2393 }
2394 case GST_EVENT_STREAM_START:
2395 {
2396 res = TRUE;
2397 gst_event_unref (event);
2398
2399 /* Drain all the buffers */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002400 gst_aml_qtdemux_process_adapter (demux, TRUE);
2401 gst_aml_qtdemux_reset (demux, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00002402 /* We expect new moov box after new stream-start event */
2403 if (demux->exposed) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002404 gst_aml_qtdemux_stream_concat (demux,
zengliang.li5f31ef42024-05-16 08:27:38 +00002405 demux->old_streams, demux->active_streams);
2406 }
2407
2408 goto drop;
2409 }
zengliang.lid0c84c32024-05-17 03:33:20 +00002410 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:
2411 {
2412 if (gst_event_has_name(event, "AML-DISCONTINUITY-BASE-POS"))
2413 {
2414 GST_DEBUG_OBJECT (demux, "Handle event AML-DISCONTINUITY-BASE-POS");
2415 demux->cal_discontinuity_pos = TRUE;
2416
2417 res = TRUE;
2418 gst_event_unref(event);
2419 goto drop;
2420 }
2421 break;
2422 }
zengliang.li5f31ef42024-05-16 08:27:38 +00002423 default:
2424 break;
2425 }
2426
2427 res = gst_pad_event_default (demux->sinkpad, parent, event) & res;
2428
2429drop:
2430 return res;
2431}
2432
2433static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002434gst_aml_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +00002435 GstQuery * query)
2436{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002437 GstAmlQTDemux *demux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +00002438 gboolean res = FALSE;
2439
2440 switch (GST_QUERY_TYPE (query)) {
2441 case GST_QUERY_BITRATE:
2442 {
2443 GstClockTime duration;
2444
2445 /* populate demux->upstream_size if not done yet */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002446 gst_aml_qtdemux_check_seekability (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00002447
2448 if (demux->upstream_size != -1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002449 && gst_aml_qtdemux_get_duration (demux, &duration)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002450 guint bitrate =
2451 gst_util_uint64_scale (8 * demux->upstream_size, GST_SECOND,
2452 duration);
2453
2454 GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
2455 " duration %" GST_TIME_FORMAT " resulting a bitrate of %u",
2456 demux->upstream_size, GST_TIME_ARGS (duration), bitrate);
2457
2458 /* TODO: better results based on ranges/index tables */
2459 gst_query_set_bitrate (query, bitrate);
2460 res = TRUE;
2461 }
2462 break;
2463 }
2464 default:
2465 res = gst_pad_query_default (pad, (GstObject *) demux, query);
2466 break;
2467 }
2468
2469 return res;
2470}
2471
2472
2473#if 0
2474static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002475gst_aml_qtdemux_set_index (GstElement * element, GstIndex * index)
zengliang.li5f31ef42024-05-16 08:27:38 +00002476{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002477 GstAmlQTDemux *demux = GST_AML_QTDEMUX (element);
zengliang.li5f31ef42024-05-16 08:27:38 +00002478
2479 GST_OBJECT_LOCK (demux);
2480 if (demux->element_index)
2481 gst_object_unref (demux->element_index);
2482 if (index) {
2483 demux->element_index = gst_object_ref (index);
2484 } else {
2485 demux->element_index = NULL;
2486 }
2487 GST_OBJECT_UNLOCK (demux);
2488 /* object lock might be taken again */
2489 if (index)
2490 gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
2491 GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
2492 demux->element_index, demux->index_id);
2493}
2494
2495static GstIndex *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002496gst_aml_qtdemux_get_index (GstElement * element)
zengliang.li5f31ef42024-05-16 08:27:38 +00002497{
2498 GstIndex *result = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002499 GstAmlQTDemux *demux = GST_AML_QTDEMUX (element);
zengliang.li5f31ef42024-05-16 08:27:38 +00002500
2501 GST_OBJECT_LOCK (demux);
2502 if (demux->element_index)
2503 result = gst_object_ref (demux->element_index);
2504 GST_OBJECT_UNLOCK (demux);
2505
2506 GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);
2507
2508 return result;
2509}
2510#endif
2511
2512static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002513gst_aml_qtdemux_stbl_free (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002514{
2515 g_free ((gpointer) stream->stco.data);
2516 stream->stco.data = NULL;
2517 g_free ((gpointer) stream->stsz.data);
2518 stream->stsz.data = NULL;
2519 g_free ((gpointer) stream->stsc.data);
2520 stream->stsc.data = NULL;
2521 g_free ((gpointer) stream->stts.data);
2522 stream->stts.data = NULL;
2523 g_free ((gpointer) stream->stss.data);
2524 stream->stss.data = NULL;
2525 g_free ((gpointer) stream->stps.data);
2526 stream->stps.data = NULL;
2527 g_free ((gpointer) stream->ctts.data);
2528 stream->ctts.data = NULL;
2529}
2530
2531static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002532gst_aml_qtdemux_stream_flush_segments_data (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002533{
2534 g_free (stream->segments);
2535 stream->segments = NULL;
2536 stream->segment_index = -1;
2537 stream->accumulated_base = 0;
2538}
2539
2540static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002541gst_aml_qtdemux_stream_flush_samples_data (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002542{
2543 g_free (stream->samples);
2544 stream->samples = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002545 gst_aml_qtdemux_stbl_free (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00002546
2547 /* fragments */
2548 g_free (stream->ra_entries);
2549 stream->ra_entries = NULL;
2550 stream->n_ra_entries = 0;
2551
2552 stream->sample_index = -1;
2553 stream->stbl_index = -1;
2554 stream->n_samples = 0;
2555 stream->time_position = 0;
2556
2557 stream->n_samples_moof = 0;
2558 stream->duration_moof = 0;
2559 stream->duration_last_moof = 0;
2560}
2561
2562static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002563gst_aml_qtdemux_stream_clear (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002564{
2565 gint i;
2566 if (stream->allocator)
2567 gst_object_unref (stream->allocator);
2568 while (stream->buffers) {
2569 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
2570 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
2571 }
2572 for (i = 0; i < stream->stsd_entries_length; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002573 AmlQtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
zengliang.li5f31ef42024-05-16 08:27:38 +00002574 if (entry->rgb8_palette) {
2575 gst_memory_unref (entry->rgb8_palette);
2576 entry->rgb8_palette = NULL;
2577 }
2578 entry->sparse = FALSE;
2579 }
2580
2581 if (stream->stream_tags)
2582 gst_tag_list_unref (stream->stream_tags);
2583
2584 stream->stream_tags = gst_tag_list_new_empty ();
2585 gst_tag_list_set_scope (stream->stream_tags, GST_TAG_SCOPE_STREAM);
2586 g_free (stream->redirect_uri);
2587 stream->redirect_uri = NULL;
2588 stream->sent_eos = FALSE;
2589 stream->protected = FALSE;
2590 if (stream->protection_scheme_info) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002591 if (stream->protection_scheme_type == AML_FOURCC_cenc
2592 || stream->protection_scheme_type == AML_FOURCC_cbcs
xuesong.jiangec2fff52024-10-21 16:16:19 +08002593 || stream->protection_scheme_type == AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002594 || stream->protection_scheme_type == AML_FOURCC_cens) {
2595 AmlQtDemuxCencSampleSetInfo *info =
2596 (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00002597 if (info->default_properties)
2598 gst_structure_free (info->default_properties);
2599 if (info->crypto_info)
2600 g_ptr_array_free (info->crypto_info, TRUE);
2601 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002602 if (stream->protection_scheme_type == AML_FOURCC_aavd) {
2603 AmlQtDemuxAavdEncryptionInfo *info =
2604 (AmlQtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00002605 if (info->default_properties)
2606 gst_structure_free (info->default_properties);
2607 }
2608 g_free (stream->protection_scheme_info);
2609 stream->protection_scheme_info = NULL;
2610 }
2611 stream->protection_scheme_type = 0;
2612 stream->protection_scheme_version = 0;
2613 g_queue_foreach (&stream->protection_scheme_event_queue,
2614 (GFunc) gst_event_unref, NULL);
2615 g_queue_clear (&stream->protection_scheme_event_queue);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002616 gst_aml_qtdemux_stream_flush_segments_data (stream);
2617 gst_aml_qtdemux_stream_flush_samples_data (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00002618}
2619
2620static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002621gst_aml_qtdemux_stream_reset (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002622{
2623 gint i;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002624 gst_aml_qtdemux_stream_clear (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00002625 for (i = 0; i < stream->stsd_entries_length; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002626 AmlQtDemuxStreamStsdEntry *entry = &stream->stsd_entries[i];
zengliang.li5f31ef42024-05-16 08:27:38 +00002627 if (entry->caps) {
2628 gst_caps_unref (entry->caps);
2629 entry->caps = NULL;
2630 }
2631 }
2632 g_free (stream->stsd_entries);
2633 stream->stsd_entries = NULL;
2634 stream->stsd_entries_length = 0;
2635}
2636
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002637static AmlQtDemuxStream *
2638gst_aml_qtdemux_stream_ref (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002639{
2640 g_atomic_int_add (&stream->ref_count, 1);
2641
2642 return stream;
2643}
2644
2645static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002646gst_aml_qtdemux_stream_unref (AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00002647{
2648 if (g_atomic_int_dec_and_test (&stream->ref_count)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002649 gst_aml_qtdemux_stream_reset (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00002650 gst_tag_list_unref (stream->stream_tags);
2651 if (stream->pad) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002652 GstAmlQTDemux *demux = stream->demux;
zengliang.li5f31ef42024-05-16 08:27:38 +00002653 gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
2654 GST_OBJECT_LOCK (demux);
2655 gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
2656 GST_OBJECT_UNLOCK (demux);
2657 }
2658 g_free (stream->stream_id);
2659 g_free (stream);
2660 }
2661}
2662
2663static GstStateChangeReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002664gst_aml_qtdemux_change_state (GstElement * element, GstStateChange transition)
zengliang.li5f31ef42024-05-16 08:27:38 +00002665{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002666 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (element);
zengliang.li5f31ef42024-05-16 08:27:38 +00002667 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
2668
2669 switch (transition) {
2670 case GST_STATE_CHANGE_READY_TO_PAUSED:
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002671 gst_aml_qtdemux_reset (qtdemux, TRUE);
zengliang.li5f31ef42024-05-16 08:27:38 +00002672 break;
2673 default:
2674 break;
2675 }
2676
2677 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2678
2679 switch (transition) {
2680 case GST_STATE_CHANGE_PAUSED_TO_READY:{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002681 gst_aml_qtdemux_reset (qtdemux, TRUE);
zengliang.li5f31ef42024-05-16 08:27:38 +00002682 break;
2683 }
2684 default:
2685 break;
2686 }
2687
2688 return result;
2689}
2690
2691static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002692gst_aml_qtdemux_set_context (GstElement * element, GstContext * context)
zengliang.li5f31ef42024-05-16 08:27:38 +00002693{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002694 GstAmlQTDemux *qtdemux = GST_AML_QTDEMUX (element);
zengliang.li5f31ef42024-05-16 08:27:38 +00002695
2696 g_return_if_fail (GST_IS_CONTEXT (context));
2697
2698 if (gst_context_has_context_type (context,
2699 "drm-preferred-decryption-system-id")) {
2700 const GstStructure *s;
2701
2702 s = gst_context_get_structure (context);
2703 g_free (qtdemux->preferred_protection_system_id);
2704 qtdemux->preferred_protection_system_id =
2705 g_strdup (gst_structure_get_string (s, "decryption-system-id"));
2706 GST_DEBUG_OBJECT (element, "set preferred decryption system to %s",
2707 qtdemux->preferred_protection_system_id);
2708 }
2709
2710 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
2711}
2712
2713static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002714aml_qtdemux_parse_ftyp (GstAmlQTDemux * qtdemux, const guint8 * buffer, gint length)
zengliang.li5f31ef42024-05-16 08:27:38 +00002715{
2716 /* counts as header data */
2717 qtdemux->header_size += length;
2718
2719 /* only consider at least a sufficiently complete ftyp atom */
2720 if (length >= 20) {
2721 GstBuffer *buf;
2722
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002723 qtdemux->major_brand = AML_QT_FOURCC (buffer + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +00002724 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
2725 GST_FOURCC_ARGS (qtdemux->major_brand));
2726 if (qtdemux->comp_brands)
2727 gst_buffer_unref (qtdemux->comp_brands);
2728 buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
2729 gst_buffer_fill (buf, 0, buffer + 16, length - 16);
2730 }
2731}
2732
2733static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002734aml_qtdemux_update_default_sample_cenc_settings (GstAmlQTDemux * qtdemux,
2735 AmlQtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
zengliang.li5f31ef42024-05-16 08:27:38 +00002736 guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
2737 guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
2738 const guint8 * constant_iv)
2739{
2740 GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
2741 gst_buffer_fill (kid_buf, 0, kid, 16);
2742 if (info->default_properties)
2743 gst_structure_free (info->default_properties);
2744 info->default_properties =
2745 gst_structure_new ("application/x-cenc",
2746 "iv_size", G_TYPE_UINT, iv_size,
2747 "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
2748 "kid", GST_TYPE_BUFFER, kid_buf, NULL);
2749 GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
shipeng sunc5d54622025-01-06 14:32:59 +08002750 "is_encrypted=%u, iv_size=%u constant_iv_size=%u crypt_block %u:%u",
2751 is_encrypted, iv_size, constant_iv_size, crypt_byte_block, skip_byte_block);
zengliang.li5f31ef42024-05-16 08:27:38 +00002752 gst_buffer_unref (kid_buf);
xuesong.jiang45374612024-10-10 20:10:13 +08002753 if (protection_scheme_type == AML_FOURCC_cbcs || protection_scheme_type == AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00002754 if (crypt_byte_block != 0 || skip_byte_block != 0) {
2755 gst_structure_set (info->default_properties, "crypt_byte_block",
2756 G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
2757 skip_byte_block, NULL);
2758 }
2759 if (constant_iv != NULL) {
2760 GstBuffer *constant_iv_buf =
2761 gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
2762 gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
2763 gst_structure_set (info->default_properties, "constant_iv_size",
2764 G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
2765 NULL);
2766 gst_buffer_unref (constant_iv_buf);
2767 }
zengliang.li5f31ef42024-05-16 08:27:38 +00002768 }
xuesong.jiangec2fff52024-10-21 16:16:19 +08002769
2770 if (protection_scheme_type == AML_FOURCC_cens)
2771 gst_structure_set (info->default_properties, "cipher-mode", G_TYPE_STRING, "cens", NULL);
2772 else if (protection_scheme_type == AML_FOURCC_cbc1)
2773 gst_structure_set (info->default_properties, "cipher-mode", G_TYPE_STRING, "cbc1", NULL);
2774 else if (protection_scheme_type == AML_FOURCC_cbcs)
2775 gst_structure_set (info->default_properties, "cipher-mode", G_TYPE_STRING, "cbcs", NULL);
2776 else
2777 gst_structure_set (info->default_properties, "cipher-mode", G_TYPE_STRING, "cenc", NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00002778}
2779
2780static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002781aml_qtdemux_update_default_piff_encryption_settings (GstAmlQTDemux * qtdemux,
2782 AmlQtDemuxCencSampleSetInfo * info, GstByteReader * br)
zengliang.li5f31ef42024-05-16 08:27:38 +00002783{
2784 guint32 algorithm_id = 0;
2785 const guint8 *kid;
2786 gboolean is_encrypted = TRUE;
2787 guint8 iv_size = 8;
2788
2789 if (!gst_byte_reader_get_uint24_le (br, &algorithm_id)) {
2790 GST_ERROR_OBJECT (qtdemux, "Error getting box's algorithm ID field");
2791 return FALSE;
2792 }
2793
2794 algorithm_id >>= 8;
2795 if (algorithm_id == 0) {
2796 is_encrypted = FALSE;
2797 } else if (algorithm_id == 1) {
2798 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CTR encrypted stream");
2799 } else if (algorithm_id == 2) {
2800 GST_DEBUG_OBJECT (qtdemux, "AES 128-bits CBC encrypted stream");
2801 }
2802
2803 if (!gst_byte_reader_get_uint8 (br, &iv_size))
2804 return FALSE;
2805
2806 if (!gst_byte_reader_get_data (br, 16, &kid))
2807 return FALSE;
2808
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002809 aml_qtdemux_update_default_sample_cenc_settings (qtdemux, info,
2810 is_encrypted, AML_FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00002811 gst_structure_set (info->default_properties, "piff_algorithm_id",
2812 G_TYPE_UINT, algorithm_id, NULL);
2813 return TRUE;
2814}
2815
2816
2817static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002818aml_qtdemux_parse_piff (GstAmlQTDemux * qtdemux, const guint8 * buffer, gint length,
zengliang.li5f31ef42024-05-16 08:27:38 +00002819 guint offset)
2820{
2821 GstByteReader br;
2822 guint8 version;
2823 guint32 flags = 0;
2824 guint i;
2825 guint iv_size = 8;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002826 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00002827 GstStructure *structure;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002828 AmlQtDemuxCencSampleSetInfo *ss_info = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00002829 const gchar *system_id;
2830 gboolean uses_sub_sample_encryption = FALSE;
2831 guint32 sample_count;
2832
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002833 if (AML_QTDEMUX_N_STREAMS (qtdemux) == 0)
zengliang.li5f31ef42024-05-16 08:27:38 +00002834 return;
2835
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002836 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00002837
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002838 structure = gst_caps_get_structure (AML_CUR_STREAM (stream)->caps, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00002839 if (!gst_structure_has_name (structure, "application/x-cenc")) {
2840 GST_WARNING_OBJECT (qtdemux,
2841 "Attempting PIFF box parsing on an unencrypted stream.");
2842 return;
2843 }
2844
2845 if (!gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
2846 G_TYPE_STRING, &system_id, NULL)) {
2847 GST_WARNING_OBJECT (qtdemux, "%s field not present in caps",
2848 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD);
2849 return;
2850 }
2851
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002852 gst_aml_qtdemux_append_protection_system_id (qtdemux, system_id);
zengliang.li5f31ef42024-05-16 08:27:38 +00002853
2854 stream->protected = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002855 stream->protection_scheme_type = AML_FOURCC_cenc;
zengliang.li5f31ef42024-05-16 08:27:38 +00002856
2857 if (!stream->protection_scheme_info)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002858 stream->protection_scheme_info = g_new0 (AmlQtDemuxCencSampleSetInfo, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +00002859
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002860 ss_info = (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00002861 if (!ss_info->default_properties) {
2862 ss_info->default_properties =
2863 gst_structure_new ("application/x-cenc",
2864 "iv_size", G_TYPE_UINT, iv_size, "encrypted", G_TYPE_BOOLEAN, TRUE,
2865 NULL);
2866
2867 }
2868
2869 if (ss_info->crypto_info) {
2870 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
2871 g_ptr_array_free (ss_info->crypto_info, TRUE);
2872 ss_info->crypto_info = NULL;
2873 }
2874
2875 /* skip UUID */
2876 gst_byte_reader_init (&br, buffer + offset + 16, length - offset - 16);
2877
2878 if (!gst_byte_reader_get_uint8 (&br, &version)) {
2879 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
2880 return;
2881 }
2882
2883 if (!gst_byte_reader_get_uint24_be (&br, &flags)) {
2884 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
2885 return;
2886 }
2887
2888 if ((flags & 0x000001)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002889 if (!aml_qtdemux_update_default_piff_encryption_settings (qtdemux, ss_info,
zengliang.li5f31ef42024-05-16 08:27:38 +00002890 &br))
2891 return;
2892 } else if ((flags & 0x000002)) {
2893 uses_sub_sample_encryption = TRUE;
2894 }
2895
2896 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size",
2897 &iv_size)) {
2898 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
2899 return;
2900 }
2901
2902 if (!gst_byte_reader_get_uint32_be (&br, &sample_count)) {
2903 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
2904 return;
2905 }
2906
2907 ss_info->crypto_info =
2908 g_ptr_array_new_full (sample_count,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002909 (GDestroyNotify) aml_qtdemux_gst_structure_free);
zengliang.li5f31ef42024-05-16 08:27:38 +00002910
2911 for (i = 0; i < sample_count; ++i) {
2912 GstStructure *properties;
2913 guint8 *data;
2914 GstBuffer *buf;
2915
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002916 properties = aml_qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00002917 if (properties == NULL) {
2918 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
2919 qtdemux->cenc_aux_sample_count = i;
2920 return;
2921 }
2922
2923 if (!gst_byte_reader_dup_data (&br, iv_size, &data)) {
2924 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
2925 gst_structure_free (properties);
2926 qtdemux->cenc_aux_sample_count = i;
2927 return;
2928 }
shipeng sunc5d54622025-01-06 14:32:59 +08002929
zengliang.li5f31ef42024-05-16 08:27:38 +00002930 buf = gst_buffer_new_wrapped (data, iv_size);
2931 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
2932 gst_buffer_unref (buf);
2933
2934 if (uses_sub_sample_encryption) {
2935 guint16 n_subsamples;
2936 const GValue *kid_buf_value;
2937
2938 if (!gst_byte_reader_get_uint16_be (&br, &n_subsamples)
2939 || n_subsamples == 0) {
2940 GST_ERROR_OBJECT (qtdemux,
2941 "failed to get subsample count for sample %u", i);
2942 gst_structure_free (properties);
2943 qtdemux->cenc_aux_sample_count = i;
2944 return;
2945 }
2946 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
2947 if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
2948 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
2949 i);
2950 gst_structure_free (properties);
2951 qtdemux->cenc_aux_sample_count = i;
2952 return;
2953 }
2954 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
2955
2956 kid_buf_value =
2957 gst_structure_get_value (ss_info->default_properties, "kid");
2958
2959 gst_structure_set (properties,
2960 "subsample_count", G_TYPE_UINT, n_subsamples,
2961 "subsamples", GST_TYPE_BUFFER, buf, NULL);
2962 gst_structure_set_value (properties, "kid", kid_buf_value);
2963 gst_buffer_unref (buf);
2964 } else {
2965 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
2966 }
2967
2968 g_ptr_array_add (ss_info->crypto_info, properties);
2969 }
2970
2971 qtdemux->cenc_aux_sample_count = sample_count;
2972}
2973
2974static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002975aml_qtdemux_parse_uuid (GstAmlQTDemux * qtdemux, const guint8 * buffer, gint length)
zengliang.li5f31ef42024-05-16 08:27:38 +00002976{
2977 static const guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
2978 0x97, 0xA9, 0x42, 0xE8,
2979 0x9C, 0x71, 0x99, 0x94,
2980 0x91, 0xE3, 0xAF, 0xAC
2981 };
2982 static const guint8 playready_uuid[] = {
2983 0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
2984 0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
2985 };
2986
2987 static const guint8 piff_sample_encryption_uuid[] = {
2988 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
2989 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
2990 };
2991
2992 guint offset;
2993
2994 /* counts as header data */
2995 qtdemux->header_size += length;
2996
zengliang.lia6c0cdd2024-05-18 07:15:51 +00002997 offset = (AML_QT_UINT32 (buffer) == 0) ? 16 : 8;
zengliang.li5f31ef42024-05-16 08:27:38 +00002998
2999 if (length <= offset + 16) {
3000 GST_DEBUG_OBJECT (qtdemux, "uuid atom is too short, skipping");
3001 return;
3002 }
3003
3004 if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
3005 GstBuffer *buf;
3006 GstTagList *taglist;
3007
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003008 buf = _gst_aml_buffer_new_wrapped ((guint8 *) buffer + offset + 16,
zengliang.li5f31ef42024-05-16 08:27:38 +00003009 length - offset - 16, NULL);
3010 taglist = gst_tag_list_from_xmp_buffer (buf);
3011 gst_buffer_unref (buf);
3012
3013 /* make sure we have a usable taglist */
3014 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
3015
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003016 aml_qtdemux_handle_xmp_taglist (qtdemux, qtdemux->tag_list, taglist);
zengliang.li5f31ef42024-05-16 08:27:38 +00003017
3018 } else if (memcmp (buffer + offset, playready_uuid, 16) == 0) {
3019 int len;
3020 const gunichar2 *s_utf16;
3021 char *contents;
3022
3023 len = GST_READ_UINT16_LE (buffer + offset + 0x30);
3024 s_utf16 = (const gunichar2 *) (buffer + offset + 0x32);
3025 contents = g_utf16_to_utf8 (s_utf16, len / 2, NULL, NULL, NULL);
3026 GST_ERROR_OBJECT (qtdemux, "contents: %s", contents);
3027
3028 g_free (contents);
3029
3030 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT,
3031 (_("Cannot play stream because it is encrypted with PlayReady DRM.")),
3032 (NULL));
3033 } else if (memcmp (buffer + offset, piff_sample_encryption_uuid, 16) == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003034 aml_qtdemux_parse_piff (qtdemux, buffer, length, offset);
zengliang.li5f31ef42024-05-16 08:27:38 +00003035 } else {
3036 GST_DEBUG_OBJECT (qtdemux, "Ignoring unknown uuid: %08x-%08x-%08x-%08x",
3037 GST_READ_UINT32_LE (buffer + offset),
3038 GST_READ_UINT32_LE (buffer + offset + 4),
3039 GST_READ_UINT32_LE (buffer + offset + 8),
3040 GST_READ_UINT32_LE (buffer + offset + 12));
3041 }
3042}
3043
3044static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003045aml_qtdemux_parse_sidx (GstAmlQTDemux * qtdemux, const guint8 * buffer, gint length)
zengliang.li5f31ef42024-05-16 08:27:38 +00003046{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003047 GstAmlSidxParser sidx_parser;
3048 GstAmlIsoffParserResult res;
zengliang.li5f31ef42024-05-16 08:27:38 +00003049 guint consumed;
3050
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003051 gst_aml_isoff_qt_sidx_parser_init (&sidx_parser);
zengliang.li5f31ef42024-05-16 08:27:38 +00003052
3053 res =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003054 gst_aml_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
zengliang.li5f31ef42024-05-16 08:27:38 +00003055 &consumed);
3056 GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003057 if (res == GST_AML_ISOFF_QT_PARSER_DONE) {
3058 aml_check_update_duration (qtdemux, sidx_parser.cumulative_pts);
zengliang.li5f31ef42024-05-16 08:27:38 +00003059 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003060 gst_aml_isoff_qt_sidx_parser_clear (&sidx_parser);
zengliang.li5f31ef42024-05-16 08:27:38 +00003061}
3062
3063/* caller verifies at least 8 bytes in buf */
3064static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003065aml_extract_initial_length_and_fourcc (const guint8 * data, guint size,
zengliang.li5f31ef42024-05-16 08:27:38 +00003066 guint64 * plength, guint32 * pfourcc)
3067{
3068 guint64 length;
3069 guint32 fourcc;
3070
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003071 length = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +00003072 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003073 fourcc = AML_QT_FOURCC (data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00003074 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
3075
3076 if (length == 0) {
3077 length = G_MAXUINT64;
3078 } else if (length == 1 && size >= 16) {
3079 /* this means we have an extended size, which is the 64 bit value of
3080 * the next 8 bytes */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003081 length = AML_QT_UINT64 (data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +00003082 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
3083 }
3084
3085 if (plength)
3086 *plength = length;
3087 if (pfourcc)
3088 *pfourcc = fourcc;
3089}
3090
3091static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003092aml_qtdemux_parse_mehd (GstAmlQTDemux * qtdemux, GstByteReader * br)
zengliang.li5f31ef42024-05-16 08:27:38 +00003093{
3094 guint32 version = 0;
3095 GstClockTime duration = 0;
3096
3097 if (!gst_byte_reader_get_uint32_be (br, &version))
3098 goto failed;
3099
3100 version >>= 24;
3101 if (version == 1) {
3102 if (!gst_byte_reader_get_uint64_be (br, &duration))
3103 goto failed;
3104 } else {
3105 guint32 dur = 0;
3106
3107 if (!gst_byte_reader_get_uint32_be (br, &dur))
3108 goto failed;
3109 duration = dur;
3110 }
3111
3112 GST_INFO_OBJECT (qtdemux, "mehd duration: %" G_GUINT64_FORMAT, duration);
3113 qtdemux->duration = duration;
3114
3115 return TRUE;
3116
3117failed:
3118 {
3119 GST_DEBUG_OBJECT (qtdemux, "parsing mehd failed");
3120 return FALSE;
3121 }
3122}
3123
3124static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003125aml_qtdemux_parse_trex (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003126 guint32 * ds_duration, guint32 * ds_size, guint32 * ds_flags)
3127{
3128 if (!stream->parsed_trex && qtdemux->moov_node) {
3129 GNode *mvex, *trex;
3130 GstByteReader trex_data;
3131
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003132 mvex = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_mvex);
zengliang.li5f31ef42024-05-16 08:27:38 +00003133 if (mvex) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003134 trex = aml_qtdemux_tree_get_child_by_type_full (mvex, AML_FOURCC_trex,
zengliang.li5f31ef42024-05-16 08:27:38 +00003135 &trex_data);
3136 while (trex) {
3137 guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
3138
3139 /* skip version/flags */
3140 if (!gst_byte_reader_skip (&trex_data, 4))
3141 goto next;
3142 if (!gst_byte_reader_get_uint32_be (&trex_data, &id))
3143 goto next;
3144 if (id != stream->track_id)
3145 goto next;
3146 if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
3147 goto next;
3148 if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
3149 goto next;
3150 if (!gst_byte_reader_get_uint32_be (&trex_data, &size))
3151 goto next;
3152 if (!gst_byte_reader_get_uint32_be (&trex_data, &flags))
3153 goto next;
3154
3155 GST_DEBUG_OBJECT (qtdemux, "fragment defaults for stream %d; "
3156 "duration %d, size %d, flags 0x%x", stream->track_id,
3157 dur, size, flags);
3158
3159 stream->parsed_trex = TRUE;
3160 stream->def_sample_description_index = sdi;
3161 stream->def_sample_duration = dur;
3162 stream->def_sample_size = size;
3163 stream->def_sample_flags = flags;
3164
3165 next:
3166 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003167 trex = aml_qtdemux_tree_get_sibling_by_type_full (trex, AML_FOURCC_trex,
zengliang.li5f31ef42024-05-16 08:27:38 +00003168 &trex_data);
3169 }
3170 }
3171 }
3172
3173 *ds_duration = stream->def_sample_duration;
3174 *ds_size = stream->def_sample_size;
3175 *ds_flags = stream->def_sample_flags;
3176
3177 /* even then, above values are better than random ... */
3178 if (G_UNLIKELY (!stream->parsed_trex)) {
3179 GST_WARNING_OBJECT (qtdemux,
3180 "failed to find fragment defaults for stream %d", stream->track_id);
3181 return FALSE;
3182 }
3183
3184 return TRUE;
3185}
3186
3187/* This method should be called whenever a more accurate duration might
3188 * have been found. It will update all relevant variables if/where needed
3189 */
3190static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003191aml_check_update_duration (GstAmlQTDemux * qtdemux, GstClockTime duration)
zengliang.li5f31ef42024-05-16 08:27:38 +00003192{
3193 guint i;
3194 guint64 movdur;
3195 GstClockTime prevdur;
3196
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003197 movdur = AML_GSTTIME_TO_QTTIME (qtdemux, duration);
zengliang.li5f31ef42024-05-16 08:27:38 +00003198
3199 if (movdur > qtdemux->duration) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003200 prevdur = AML_QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
zengliang.li5f31ef42024-05-16 08:27:38 +00003201 GST_DEBUG_OBJECT (qtdemux,
3202 "Updating total duration to %" GST_TIME_FORMAT " was %" GST_TIME_FORMAT,
3203 GST_TIME_ARGS (duration), GST_TIME_ARGS (prevdur));
3204 qtdemux->duration = movdur;
3205 GST_DEBUG_OBJECT (qtdemux,
3206 "qtdemux->segment.duration: %" GST_TIME_FORMAT " .stop: %"
3207 GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.duration),
3208 GST_TIME_ARGS (qtdemux->segment.stop));
3209 if (qtdemux->segment.duration == prevdur) {
3210 /* If the current segment has duration/stop identical to previous duration
3211 * update them also (because they were set at that point in time with
3212 * the wrong duration */
3213 /* We convert the value *from* the timescale version to avoid rounding errors */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003214 GstClockTime fixeddur = AML_QTTIME_TO_GSTTIME (qtdemux, movdur);
zengliang.li5f31ef42024-05-16 08:27:38 +00003215 GST_DEBUG_OBJECT (qtdemux, "Updated segment.duration and segment.stop");
3216 qtdemux->segment.duration = fixeddur;
3217 qtdemux->segment.stop = fixeddur;
3218 }
3219 }
3220
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003221 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
3222 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00003223
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003224 movdur = AML_GSTTIME_TO_QTSTREAMTIME (stream, duration);
zengliang.li5f31ef42024-05-16 08:27:38 +00003225 if (movdur > stream->duration) {
3226 GST_DEBUG_OBJECT (qtdemux,
3227 "Updating stream #%d duration to %" GST_TIME_FORMAT, i,
3228 GST_TIME_ARGS (duration));
3229 stream->duration = movdur;
3230 /* internal duration tracking state has been updated above, so */
3231 /* preserve an open-ended dummy segment rather than repeatedly updating
3232 * it and spamming downstream accordingly with segment events */
3233 /* also mangle the edit list end time when fragmented with a single edit
3234 * list that may only cover any non-fragmented data */
3235 if ((stream->dummy_segment ||
3236 (qtdemux->fragmented && stream->n_segments == 1)) &&
3237 GST_CLOCK_TIME_IS_VALID (stream->segments[0].duration)) {
3238 /* Update all dummy values to new duration */
3239 stream->segments[0].stop_time = duration;
3240 stream->segments[0].duration = duration;
3241 stream->segments[0].media_stop = duration;
3242
3243 /* let downstream know we possibly have a new stop time */
3244 if (stream->segment_index != -1) {
3245 GstClockTime pos;
3246
3247 if (qtdemux->segment.rate >= 0) {
3248 pos = stream->segment.start;
3249 } else {
3250 pos = stream->segment.stop;
3251 }
3252
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003253 gst_aml_qtdemux_stream_update_segment (qtdemux, stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003254 stream->segment_index, pos, NULL, NULL);
3255 }
3256 }
3257 }
3258 }
3259}
3260
3261static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003262aml_qtdemux_parse_trun (GstAmlQTDemux * qtdemux, GstByteReader * trun,
3263 AmlQtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
zengliang.li5f31ef42024-05-16 08:27:38 +00003264 guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
3265 gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
3266 gboolean has_tfdt)
3267{
3268 GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
3269 guint64 timestamp;
3270 gint32 data_offset = 0;
3271 guint8 version;
3272 guint32 flags = 0, first_flags = 0, samples_count = 0;
3273 gint i;
3274 guint8 *data;
3275 guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003276 AmlQtDemuxSample *sample;
zengliang.li5f31ef42024-05-16 08:27:38 +00003277 gboolean ismv = FALSE;
3278 gint64 initial_offset;
3279 gint32 min_ct = 0;
3280
3281 GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
3282 "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
3283 "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
3284 d_sample_size, d_sample_flags, *base_offset, decode_ts);
3285
3286 if (stream->pending_seek && moof_offset < stream->pending_seek->moof_offset) {
3287 GST_INFO_OBJECT (stream->pad, "skipping trun before seek target fragment");
3288 return TRUE;
3289 }
3290
3291 /* presence of stss or not can't really tell us much,
3292 * and flags and so on tend to be marginally reliable in these files */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003293 if (stream->subtype == AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003294 GST_DEBUG_OBJECT (qtdemux,
3295 "sound track in fragmented file; marking all keyframes");
3296 stream->all_keyframe = TRUE;
3297 }
3298
3299 if (!gst_byte_reader_get_uint8 (trun, &version) ||
3300 !gst_byte_reader_get_uint24_be (trun, &flags))
3301 goto fail;
3302
3303 if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
3304 goto fail;
3305
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003306 if (flags & AML_TR_DATA_OFFSET) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003307 /* note this is really signed */
3308 if (!gst_byte_reader_get_int32_be (trun, &data_offset))
3309 goto fail;
3310 GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
3311 /* default base offset = first byte of moof */
3312 if (*base_offset == -1) {
3313 GST_LOG_OBJECT (qtdemux, "base_offset at moof");
3314 *base_offset = moof_offset;
3315 }
3316 *running_offset = *base_offset + data_offset;
3317 } else {
3318 /* if no offset at all, that would mean data starts at moof start,
3319 * which is a bit wrong and is ismv crappy way, so compensate
3320 * assuming data is in mdat following moof */
3321 if (*base_offset == -1) {
3322 *base_offset = moof_offset + moof_length + 8;
3323 GST_LOG_OBJECT (qtdemux, "base_offset assumed in mdat after moof");
3324 ismv = TRUE;
3325 }
3326 if (*running_offset == -1)
3327 *running_offset = *base_offset;
3328 }
3329
3330 GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
3331 *running_offset);
3332 GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
3333 data_offset, flags, samples_count);
3334
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003335 if (flags & AML_TR_FIRST_SAMPLE_FLAGS) {
3336 if (G_UNLIKELY (flags & AML_TR_SAMPLE_FLAGS)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003337 GST_DEBUG_OBJECT (qtdemux,
3338 "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003339 flags ^= AML_TR_FIRST_SAMPLE_FLAGS;
zengliang.li5f31ef42024-05-16 08:27:38 +00003340 }
zengliang.li34751a82024-05-17 02:52:12 +00003341
3342 if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
3343 goto fail;
3344 GST_LOG_OBJECT (qtdemux, "first flags: 0x%x", first_flags);
zengliang.li5f31ef42024-05-16 08:27:38 +00003345 }
3346
3347 /* FIXME ? spec says other bits should also be checked to determine
3348 * entry size (and prefix size for that matter) */
3349 entry_size = 0;
3350 dur_offset = size_offset = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003351 if (flags & AML_TR_SAMPLE_DURATION) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003352 GST_LOG_OBJECT (qtdemux, "entry duration present");
3353 dur_offset = entry_size;
3354 entry_size += 4;
3355 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003356 if (flags & AML_TR_SAMPLE_SIZE) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003357 GST_LOG_OBJECT (qtdemux, "entry size present");
3358 size_offset = entry_size;
3359 entry_size += 4;
3360 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003361 if (flags & AML_TR_SAMPLE_FLAGS) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003362 GST_LOG_OBJECT (qtdemux, "entry flags present");
3363 flags_offset = entry_size;
3364 entry_size += 4;
3365 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003366 if (flags & AML_TR_COMPOSITION_TIME_OFFSETS) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003367 GST_LOG_OBJECT (qtdemux, "entry ct offset present");
3368 ct_offset = entry_size;
3369 entry_size += 4;
3370 }
3371
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003372 if (!aml_qt_atom_parser_has_chunks (trun, samples_count, entry_size))
zengliang.li5f31ef42024-05-16 08:27:38 +00003373 goto fail;
3374 data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
3375
3376 if (stream->n_samples + samples_count >=
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003377 AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (AmlQtDemuxSample))
zengliang.li5f31ef42024-05-16 08:27:38 +00003378 goto index_too_big;
3379
3380 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003381 stream->n_samples + samples_count, (guint) sizeof (AmlQtDemuxSample),
zengliang.li5f31ef42024-05-16 08:27:38 +00003382 (stream->n_samples + samples_count) *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003383 sizeof (AmlQtDemuxSample) / (1024.0 * 1024.0));
zengliang.li5f31ef42024-05-16 08:27:38 +00003384
3385 /* create a new array of samples if it's the first sample parsed */
3386 if (stream->n_samples == 0) {
3387 g_assert (stream->samples == NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003388 stream->samples = g_try_new0 (AmlQtDemuxSample, samples_count);
zengliang.li5f31ef42024-05-16 08:27:38 +00003389 /* or try to reallocate it with space enough to insert the new samples */
3390 } else
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003391 stream->samples = g_try_renew (AmlQtDemuxSample, stream->samples,
zengliang.li5f31ef42024-05-16 08:27:38 +00003392 stream->n_samples + samples_count);
3393 if (stream->samples == NULL)
3394 goto out_of_memory;
3395
3396 if (qtdemux->fragment_start != -1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003397 timestamp = AML_GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->fragment_start);
zengliang.li5f31ef42024-05-16 08:27:38 +00003398 qtdemux->fragment_start = -1;
3399 } else {
3400 if (stream->n_samples == 0) {
3401 if (decode_ts > 0) {
3402 timestamp = decode_ts;
3403 } else if (stream->pending_seek != NULL) {
3404 /* if we don't have a timestamp from a tfdt box, we'll use the one
3405 * from the mfra seek table */
3406 GST_INFO_OBJECT (stream->pad, "pending seek ts = %" GST_TIME_FORMAT,
3407 GST_TIME_ARGS (stream->pending_seek->ts));
3408
3409 /* FIXME: this is not fully correct, the timestamp refers to the random
3410 * access sample refered to in the tfra entry, which may not necessarily
3411 * be the first sample in the tfrag/trun (but hopefully/usually is) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003412 timestamp = AML_GSTTIME_TO_QTSTREAMTIME (stream, stream->pending_seek->ts);
zengliang.li5f31ef42024-05-16 08:27:38 +00003413 } else {
3414 timestamp = 0;
3415 }
3416
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003417 gst_ts = AML_QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
zengliang.li5f31ef42024-05-16 08:27:38 +00003418 GST_INFO_OBJECT (stream->pad, "first sample ts %" GST_TIME_FORMAT,
3419 GST_TIME_ARGS (gst_ts));
3420 } else {
3421 /* subsequent fragments extend stream */
3422 timestamp =
3423 stream->samples[stream->n_samples - 1].timestamp +
3424 stream->samples[stream->n_samples - 1].duration;
3425
3426 /* If this is a GST_FORMAT_BYTES stream and there's a significant
3427 * difference (1 sec.) between decode_ts and timestamp, prefer the
3428 * former */
3429 if (has_tfdt && !qtdemux->upstream_format_is_time
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003430 && AML_ABSDIFF (decode_ts, timestamp) >
zengliang.li5f31ef42024-05-16 08:27:38 +00003431 MAX (stream->duration_last_moof / 2,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003432 AML_GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003433 GST_INFO_OBJECT (qtdemux,
3434 "decode_ts (%" GST_TIME_FORMAT ") and timestamp (%" GST_TIME_FORMAT
3435 ") are significantly different (more than %" GST_TIME_FORMAT
3436 "), using decode_ts",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003437 GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream, decode_ts)),
3438 GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream, timestamp)),
3439 GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003440 MAX (stream->duration_last_moof / 2,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003441 AML_GSTTIME_TO_QTSTREAMTIME (stream, GST_SECOND)))));
zengliang.li5f31ef42024-05-16 08:27:38 +00003442 timestamp = decode_ts;
3443 }
3444
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003445 gst_ts = AML_QTSTREAMTIME_TO_GSTTIME (stream, timestamp);
zengliang.li5f31ef42024-05-16 08:27:38 +00003446 GST_INFO_OBJECT (qtdemux, "first sample ts %" GST_TIME_FORMAT
3447 " (extends previous samples)", GST_TIME_ARGS (gst_ts));
3448 }
3449 }
3450
3451 initial_offset = *running_offset;
3452
3453 sample = stream->samples + stream->n_samples;
3454 for (i = 0; i < samples_count; i++) {
3455 guint32 dur, size, sflags;
3456 gint32 ct;
3457
3458 /* first read sample data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003459 if (flags & AML_TR_SAMPLE_DURATION) {
3460 dur = AML_QT_UINT32 (data + dur_offset);
zengliang.li5f31ef42024-05-16 08:27:38 +00003461 } else {
3462 dur = d_sample_duration;
3463 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003464 if (flags & AML_TR_SAMPLE_SIZE) {
3465 size = AML_QT_UINT32 (data + size_offset);
zengliang.li5f31ef42024-05-16 08:27:38 +00003466 } else {
3467 size = d_sample_size;
3468 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003469 if (flags & AML_TR_FIRST_SAMPLE_FLAGS) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003470 if (i == 0) {
3471 sflags = first_flags;
3472 } else {
3473 sflags = d_sample_flags;
3474 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003475 } else if (flags & AML_TR_SAMPLE_FLAGS) {
3476 sflags = AML_QT_UINT32 (data + flags_offset);
zengliang.li5f31ef42024-05-16 08:27:38 +00003477 } else {
3478 sflags = d_sample_flags;
3479 }
3480
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003481 if (flags & AML_TR_COMPOSITION_TIME_OFFSETS) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003482 /* Read offsets as signed numbers regardless of trun version as very
3483 * high offsets are unlikely and there are files out there that use
3484 * version=0 truns with negative offsets */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003485 ct = AML_QT_UINT32 (data + ct_offset);
zengliang.li5f31ef42024-05-16 08:27:38 +00003486
3487 /* FIXME: Set offset to 0 for "no decode samples". This needs
3488 * to be handled in a codec specific manner ideally. */
3489 if (ct == G_MININT32)
3490 ct = 0;
3491 } else {
3492 ct = 0;
3493 }
3494 data += entry_size;
3495
3496 /* fill the sample information */
3497 sample->offset = *running_offset;
3498 sample->pts_offset = ct;
3499 sample->size = size;
3500 sample->timestamp = timestamp;
3501 sample->duration = dur;
3502 /* sample-is-difference-sample */
3503 /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
3504 * now idea how it relates to bitfield other than massive LE/BE confusion */
3505 sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
3506 *running_offset += size;
3507 timestamp += dur;
3508 stream->duration_moof += dur;
3509 sample++;
3510
3511 if (ct < min_ct)
3512 min_ct = ct;
3513 }
3514
3515 /* Shift PTS/DTS to allow for negative composition offsets while keeping
3516 * A/V sync in place. This is similar to the code handling ctts/cslg in the
3517 * non-fragmented case.
3518 */
3519 if (min_ct < 0)
3520 stream->cslg_shift = -min_ct;
3521 else
3522 stream->cslg_shift = 0;
3523
3524 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
3525 stream->cslg_shift);
3526
3527 /* Update total duration if needed */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003528 aml_check_update_duration (qtdemux, AML_QTSTREAMTIME_TO_GSTTIME (stream, timestamp));
zengliang.li5f31ef42024-05-16 08:27:38 +00003529
3530 /* Pre-emptively figure out size of mdat based on trun information.
3531 * If the [mdat] atom is effectively read, it will be replaced by the actual
3532 * size, else we will still be able to use this when dealing with gap'ed
3533 * input */
3534 qtdemux->mdatleft = *running_offset - initial_offset;
3535 qtdemux->mdatoffset = initial_offset;
3536 qtdemux->mdatsize = qtdemux->mdatleft;
3537
3538 stream->n_samples += samples_count;
3539 stream->n_samples_moof += samples_count;
3540
3541 if (stream->pending_seek != NULL)
3542 stream->pending_seek = NULL;
3543
3544 return TRUE;
3545
3546fail:
3547 {
3548 GST_WARNING_OBJECT (qtdemux, "failed to parse trun");
3549 return FALSE;
3550 }
3551out_of_memory:
3552 {
3553 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
3554 stream->n_samples);
3555 return FALSE;
3556 }
3557index_too_big:
3558 {
3559 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
3560 "be larger than %uMB (broken file?)", stream->n_samples,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003561 AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
zengliang.li5f31ef42024-05-16 08:27:38 +00003562 return FALSE;
3563 }
3564}
3565
3566/* find stream with @id */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003567static inline AmlQtDemuxStream *
3568aml_qtdemux_find_stream (GstAmlQTDemux * qtdemux, guint32 id)
zengliang.li5f31ef42024-05-16 08:27:38 +00003569{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003570 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00003571 gint i;
3572
3573 /* check */
3574 if (G_UNLIKELY (!id)) {
3575 GST_DEBUG_OBJECT (qtdemux, "invalid track id 0");
3576 return NULL;
3577 }
3578
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003579 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
3580 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00003581 if (stream->track_id == id)
3582 return stream;
3583 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003584 if (qtdemux->variant == AML_VARIANT_MSS_FRAGMENTED) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003585 /* mss should have only 1 stream anyway */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003586 return AML_QTDEMUX_NTH_STREAM (qtdemux, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00003587 }
3588
3589 return NULL;
3590}
3591
3592static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003593aml_qtdemux_parse_mfhd (GstAmlQTDemux * qtdemux, GstByteReader * mfhd,
zengliang.li5f31ef42024-05-16 08:27:38 +00003594 guint32 * fragment_number)
3595{
3596 if (!gst_byte_reader_skip (mfhd, 4))
3597 goto fail;
3598 if (!gst_byte_reader_get_uint32_be (mfhd, fragment_number))
3599 goto fail;
3600 return TRUE;
3601fail:
3602 {
3603 GST_WARNING_OBJECT (qtdemux, "Failed to parse mfhd atom");
3604 return FALSE;
3605 }
3606}
3607
3608static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003609aml_qtdemux_parse_tfhd (GstAmlQTDemux * qtdemux, GstByteReader * tfhd,
3610 AmlQtDemuxStream ** stream, guint32 * default_sample_duration,
zengliang.li5f31ef42024-05-16 08:27:38 +00003611 guint32 * default_sample_size, guint32 * default_sample_flags,
3612 gint64 * base_offset)
3613{
3614 guint32 flags = 0;
3615 guint32 track_id = 0;
3616
3617 if (!gst_byte_reader_skip (tfhd, 1) ||
3618 !gst_byte_reader_get_uint24_be (tfhd, &flags))
3619 goto invalid_track;
3620
3621 if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
3622 goto invalid_track;
3623
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003624 *stream = aml_qtdemux_find_stream (qtdemux, track_id);
zengliang.li5f31ef42024-05-16 08:27:38 +00003625 if (G_UNLIKELY (!*stream))
3626 goto unknown_stream;
3627
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003628 if (flags & AML_TF_DEFAULT_BASE_IS_MOOF)
zengliang.li5f31ef42024-05-16 08:27:38 +00003629 *base_offset = qtdemux->moof_offset;
3630
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003631 if (flags & AML_TF_BASE_DATA_OFFSET)
zengliang.li5f31ef42024-05-16 08:27:38 +00003632 if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
3633 goto invalid_track;
3634
3635 /* obtain stream defaults */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003636 if (aml_qtdemux_parse_trex (qtdemux, *stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003637 default_sample_duration, default_sample_size, default_sample_flags)) {
3638
3639 /* Default sample description index is only valid if trex parsing succeeded */
3640 (*stream)->stsd_sample_description_id =
3641 (*stream)->def_sample_description_index - 1;
3642 }
3643
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003644 if (flags & AML_TF_SAMPLE_DESCRIPTION_INDEX) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003645 guint32 sample_description_index;
3646 if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
3647 goto invalid_track;
3648 (*stream)->stsd_sample_description_id = sample_description_index - 1;
3649 }
3650
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003651 if (qtdemux->variant == AML_VARIANT_MSS_FRAGMENTED) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003652 /* mss has no stsd entry */
3653 (*stream)->stsd_sample_description_id = 0;
3654 }
3655
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003656 if (flags & AML_TF_DEFAULT_SAMPLE_DURATION)
zengliang.li5f31ef42024-05-16 08:27:38 +00003657 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
3658 goto invalid_track;
3659
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003660 if (flags & AML_TF_DEFAULT_SAMPLE_SIZE)
zengliang.li5f31ef42024-05-16 08:27:38 +00003661 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
3662 goto invalid_track;
3663
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003664 if (flags & AML_TF_DEFAULT_SAMPLE_FLAGS)
zengliang.li5f31ef42024-05-16 08:27:38 +00003665 if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
3666 goto invalid_track;
3667
3668 return TRUE;
3669
3670invalid_track:
3671 {
3672 GST_WARNING_OBJECT (qtdemux, "invalid track fragment header");
3673 return FALSE;
3674 }
3675unknown_stream:
3676 {
3677 GST_DEBUG_OBJECT (qtdemux, "unknown stream (%u) in tfhd", track_id);
3678 return TRUE;
3679 }
3680}
3681
3682static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003683aml_qtdemux_parse_tfdt (GstAmlQTDemux * qtdemux, GstByteReader * br,
zengliang.li5f31ef42024-05-16 08:27:38 +00003684 guint64 * decode_time)
3685{
3686 guint32 version = 0;
3687
3688 if (!gst_byte_reader_get_uint32_be (br, &version))
3689 return FALSE;
3690
3691 version >>= 24;
3692 if (version == 1) {
3693 if (!gst_byte_reader_get_uint64_be (br, decode_time))
3694 goto failed;
3695 } else {
3696 guint32 dec_time = 0;
3697 if (!gst_byte_reader_get_uint32_be (br, &dec_time))
3698 goto failed;
3699 *decode_time = dec_time;
3700 }
3701
3702 GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
3703 *decode_time);
3704
3705 return TRUE;
3706
3707failed:
3708 {
3709 GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
3710 return FALSE;
3711 }
3712}
3713
3714/* Returns a pointer to a GstStructure containing the properties of
3715 * the stream sample identified by @sample_index. The caller must unref
3716 * the returned object after use. Returns NULL if unsuccessful. */
3717static GstStructure *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003718aml_qtdemux_get_cenc_sample_properties (GstAmlQTDemux * qtdemux,
3719 AmlQtDemuxStream * stream, guint sample_index)
zengliang.li5f31ef42024-05-16 08:27:38 +00003720{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003721 AmlQtDemuxCencSampleSetInfo *info = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00003722
3723 g_return_val_if_fail (stream != NULL, NULL);
3724 g_return_val_if_fail (stream->protected, NULL);
3725 g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);
3726
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003727 info = (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00003728
3729 /* Currently, cenc properties for groups of samples are not supported, so
3730 * simply return a copy of the default sample properties */
3731 return gst_structure_copy (info->default_properties);
3732}
3733
3734/* Parses the sizes of sample auxiliary information contained within a stream,
3735 * as given in a saiz box. Returns array of sample_count guint8 size values,
3736 * or NULL on failure */
3737static guint8 *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003738aml_qtdemux_parse_saiz (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003739 GstByteReader * br, guint32 * sample_count)
3740{
3741 guint32 flags = 0;
3742 guint8 *info_sizes;
3743 guint8 default_info_size;
3744
3745 g_return_val_if_fail (qtdemux != NULL, NULL);
3746 g_return_val_if_fail (stream != NULL, NULL);
3747 g_return_val_if_fail (br != NULL, NULL);
3748 g_return_val_if_fail (sample_count != NULL, NULL);
3749
3750 if (!gst_byte_reader_get_uint32_be (br, &flags))
3751 return NULL;
3752
3753 if (flags & 0x1) {
3754 /* aux_info_type and aux_info_type_parameter are ignored */
3755 if (!gst_byte_reader_skip (br, 8))
3756 return NULL;
3757 }
3758
3759 if (!gst_byte_reader_get_uint8 (br, &default_info_size))
3760 return NULL;
3761 GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);
3762
3763 if (!gst_byte_reader_get_uint32_be (br, sample_count))
3764 return NULL;
3765 GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);
3766
3767
3768 if (default_info_size == 0) {
3769 if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
3770 return NULL;
3771 }
3772 } else {
3773 info_sizes = g_new (guint8, *sample_count);
3774 memset (info_sizes, default_info_size, *sample_count);
3775 }
3776
3777 return info_sizes;
3778}
3779
3780/* Parses the offset of sample auxiliary information contained within a stream,
3781 * as given in a saio box. Returns TRUE if successful; FALSE otherwise. */
3782static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003783aml_qtdemux_parse_saio (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003784 GstByteReader * br, guint32 * info_type, guint32 * info_type_parameter,
3785 guint64 * offset)
3786{
3787 guint8 version = 0;
3788 guint32 flags = 0;
3789 guint32 aux_info_type = 0;
3790 guint32 aux_info_type_parameter = 0;
3791 guint32 entry_count;
3792 guint32 off_32;
3793 guint64 off_64;
3794 const guint8 *aux_info_type_data = NULL;
3795
3796 g_return_val_if_fail (qtdemux != NULL, FALSE);
3797 g_return_val_if_fail (stream != NULL, FALSE);
3798 g_return_val_if_fail (br != NULL, FALSE);
3799 g_return_val_if_fail (offset != NULL, FALSE);
3800
3801 if (!gst_byte_reader_get_uint8 (br, &version))
3802 return FALSE;
3803
3804 if (!gst_byte_reader_get_uint24_be (br, &flags))
3805 return FALSE;
3806
3807 if (flags & 0x1) {
3808
3809 if (!gst_byte_reader_get_data (br, 4, &aux_info_type_data))
3810 return FALSE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003811 aux_info_type = AML_QT_FOURCC (aux_info_type_data);
zengliang.li5f31ef42024-05-16 08:27:38 +00003812
3813 if (!gst_byte_reader_get_uint32_be (br, &aux_info_type_parameter))
3814 return FALSE;
3815 } else if (stream->protected) {
3816 aux_info_type = stream->protection_scheme_type;
3817 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003818 aux_info_type = AML_CUR_STREAM (stream)->fourcc;
zengliang.li5f31ef42024-05-16 08:27:38 +00003819 }
3820
3821 if (info_type)
3822 *info_type = aux_info_type;
3823 if (info_type_parameter)
3824 *info_type_parameter = aux_info_type_parameter;
3825
3826 GST_DEBUG_OBJECT (qtdemux, "aux_info_type: '%" GST_FOURCC_FORMAT "', "
3827 "aux_info_type_parameter: %#06x",
3828 GST_FOURCC_ARGS (aux_info_type), aux_info_type_parameter);
3829
3830 if (!gst_byte_reader_get_uint32_be (br, &entry_count))
3831 return FALSE;
3832
3833 if (entry_count != 1) {
3834 GST_ERROR_OBJECT (qtdemux, "multiple offsets are not supported");
3835 return FALSE;
3836 }
3837
3838 if (version == 0) {
3839 if (!gst_byte_reader_get_uint32_be (br, &off_32))
3840 return FALSE;
3841 *offset = (guint64) off_32;
3842 } else {
3843 if (!gst_byte_reader_get_uint64_be (br, &off_64))
3844 return FALSE;
3845 *offset = off_64;
3846 }
3847
3848 GST_DEBUG_OBJECT (qtdemux, "offset: %" G_GUINT64_FORMAT, *offset);
3849 return TRUE;
3850}
3851
3852static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003853aml_qtdemux_gst_structure_free (GstStructure * gststructure)
zengliang.li5f31ef42024-05-16 08:27:38 +00003854{
3855 if (gststructure) {
3856 gst_structure_free (gststructure);
3857 }
3858}
3859
3860/* Parses auxiliary information relating to samples protected using
3861 * Common Encryption (cenc); the format of this information
3862 * is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
3863 * otherwise. */
3864static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003865aml_qtdemux_parse_cenc_aux_info (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00003866 GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
3867{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003868 AmlQtDemuxCencSampleSetInfo *ss_info = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00003869 guint8 size;
3870 gint i;
3871 GPtrArray *old_crypto_info = NULL;
3872 guint old_entries = 0;
3873
3874 g_return_val_if_fail (qtdemux != NULL, FALSE);
3875 g_return_val_if_fail (stream != NULL, FALSE);
3876 g_return_val_if_fail (br != NULL, FALSE);
3877 g_return_val_if_fail (stream->protected, FALSE);
3878 g_return_val_if_fail (stream->protection_scheme_info != NULL, FALSE);
3879
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003880 ss_info = (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00003881
3882 if (ss_info->crypto_info) {
3883 old_crypto_info = ss_info->crypto_info;
3884 /* Count number of non-null entries remaining at the tail end */
3885 for (i = old_crypto_info->len - 1; i >= 0; i--) {
3886 if (g_ptr_array_index (old_crypto_info, i) == NULL)
3887 break;
3888 old_entries++;
3889 }
3890 }
3891
3892 ss_info->crypto_info =
3893 g_ptr_array_new_full (sample_count + old_entries,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003894 (GDestroyNotify) aml_qtdemux_gst_structure_free);
zengliang.li5f31ef42024-05-16 08:27:38 +00003895
3896 /* We preserve old entries because we parse the next moof in advance
3897 * of consuming all samples from the previous moof, and otherwise
3898 * we'd discard the corresponding crypto info for the samples
3899 * from the previous fragment. */
3900 if (old_entries) {
3901 GST_DEBUG_OBJECT (qtdemux, "Preserving %d old crypto info entries",
3902 old_entries);
3903 for (i = old_crypto_info->len - old_entries; i < old_crypto_info->len; i++) {
3904 g_ptr_array_add (ss_info->crypto_info, g_ptr_array_index (old_crypto_info,
3905 i));
3906 g_ptr_array_index (old_crypto_info, i) = NULL;
3907 }
3908 }
3909
3910 if (old_crypto_info) {
3911 /* Everything now belongs to the new array */
3912 g_ptr_array_free (old_crypto_info, TRUE);
3913 }
3914
3915 for (i = 0; i < sample_count; ++i) {
3916 GstStructure *properties;
3917 guint16 n_subsamples = 0;
3918 guint8 *data;
3919 guint iv_size;
3920 GstBuffer *buf;
3921 gboolean could_read_iv;
3922
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003923 properties = aml_qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00003924 if (properties == NULL) {
3925 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
3926 return FALSE;
3927 }
3928 if (!gst_structure_get_uint (properties, "iv_size", &iv_size)) {
3929 GST_ERROR_OBJECT (qtdemux, "failed to get iv_size for sample %u", i);
3930 gst_structure_free (properties);
3931 return FALSE;
3932 }
3933 could_read_iv =
3934 iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
3935 if (could_read_iv) {
3936 buf = gst_buffer_new_wrapped (data, iv_size);
3937 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
3938 gst_buffer_unref (buf);
xuesong.jiang45374612024-10-10 20:10:13 +08003939 } else if (stream->protection_scheme_type == AML_FOURCC_cbcs || stream->protection_scheme_type == AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003940 const GValue *constant_iv_size_value =
3941 gst_structure_get_value (properties, "constant_iv_size");
3942 const GValue *constant_iv_value =
3943 gst_structure_get_value (properties, "iv");
3944 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
3945 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
3946 gst_structure_free (properties);
3947 return FALSE;
3948 }
3949 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
3950 gst_structure_remove_field (properties, "constant_iv_size");
xuesong.jiangec2fff52024-10-21 16:16:19 +08003951 } else if (stream->protection_scheme_type == AML_FOURCC_cenc || stream->protection_scheme_type == AML_FOURCC_cbc1) {
zengliang.li5f31ef42024-05-16 08:27:38 +00003952 GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
3953 gst_structure_free (properties);
3954 return FALSE;
3955 }
3956 size = info_sizes[i];
zengliang.li125c3642024-05-17 06:06:08 +00003957 GST_DEBUG_OBJECT (qtdemux, "size %u iv_size %u", size, iv_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00003958 if (size > iv_size) {
3959 if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
3960 || !(n_subsamples > 0)) {
3961 gst_structure_free (properties);
3962 GST_ERROR_OBJECT (qtdemux,
3963 "failed to get subsample count for sample %u", i);
3964 return FALSE;
3965 }
3966 GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
3967 if (!gst_byte_reader_dup_data (br, n_subsamples * 6, &data)) {
3968 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
3969 i);
3970 gst_structure_free (properties);
3971 return FALSE;
3972 }
3973 buf = gst_buffer_new_wrapped (data, n_subsamples * 6);
3974 if (!buf) {
3975 gst_structure_free (properties);
3976 return FALSE;
3977 }
3978 gst_structure_set (properties,
3979 "subsample_count", G_TYPE_UINT, n_subsamples,
3980 "subsamples", GST_TYPE_BUFFER, buf, NULL);
3981 gst_buffer_unref (buf);
3982 } else {
3983 gst_structure_set (properties, "subsample_count", G_TYPE_UINT, 0, NULL);
3984 }
3985 g_ptr_array_add (ss_info->crypto_info, properties);
3986 }
3987 return TRUE;
3988}
3989
3990/* Converts a UUID in raw byte form to a string representation, as defined in
3991 * RFC 4122. The caller takes ownership of the returned string and is
3992 * responsible for freeing it after use. */
3993static gchar *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00003994aml_qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
zengliang.li5f31ef42024-05-16 08:27:38 +00003995{
3996 const guint8 *uuid = (const guint8 *) uuid_bytes;
3997
3998 return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
3999 "%02x%02x-%02x%02x%02x%02x%02x%02x",
4000 uuid[0], uuid[1], uuid[2], uuid[3],
4001 uuid[4], uuid[5], uuid[6], uuid[7],
4002 uuid[8], uuid[9], uuid[10], uuid[11],
4003 uuid[12], uuid[13], uuid[14], uuid[15]);
4004}
4005
4006/* Parses a Protection System Specific Header box (pssh), as defined in the
4007 * Common Encryption (cenc) standard (ISO/IEC 23001-7), which contains
4008 * information needed by a specific content protection system in order to
4009 * decrypt cenc-protected tracks. Returns TRUE if successful; FALSE
4010 * otherwise. */
4011static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004012aml_qtdemux_parse_pssh (GstAmlQTDemux * qtdemux, GNode * node)
zengliang.li5f31ef42024-05-16 08:27:38 +00004013{
4014 gchar *sysid_string;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004015 guint32 pssh_size = AML_QT_UINT32 (node->data);
zengliang.li5f31ef42024-05-16 08:27:38 +00004016 GstBuffer *pssh = NULL;
4017 GstEvent *event = NULL;
4018 guint32 parent_box_type;
4019 gint i;
4020
4021 if (G_UNLIKELY (pssh_size < 32U)) {
4022 GST_ERROR_OBJECT (qtdemux, "invalid box size");
4023 return FALSE;
4024 }
4025
4026 sysid_string =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004027 aml_qtdemux_uuid_bytes_to_string ((const guint8 *) node->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +00004028
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004029 gst_aml_qtdemux_append_protection_system_id (qtdemux, sysid_string);
zengliang.li5f31ef42024-05-16 08:27:38 +00004030
bo.xiao86472382024-07-23 10:24:15 +08004031#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
4032 pssh = gst_buffer_new_memdup (node->data, pssh_size);
4033#else
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004034 pssh = gst_buffer_new_wrapped (g_memdup (node->data, pssh_size), pssh_size);
bo.xiao86472382024-07-23 10:24:15 +08004035#endif
4036
zengliang.li5f31ef42024-05-16 08:27:38 +00004037 GST_LOG_OBJECT (qtdemux, "cenc pssh size: %" G_GSIZE_FORMAT,
4038 gst_buffer_get_size (pssh));
4039
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004040 parent_box_type = AML_QT_FOURCC ((const guint8 *) node->parent->data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00004041
4042 /* Push an event containing the pssh box onto the queues of all streams. */
4043 event = gst_event_new_protection (sysid_string, pssh,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004044 (parent_box_type == AML_FOURCC_moov) ? "isobmff/moov" : "isobmff/moof");
4045 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
4046 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00004047 GST_TRACE_OBJECT (qtdemux,
4048 "adding protection event for stream %s and system %s",
4049 stream->stream_id, sysid_string);
4050 g_queue_push_tail (&stream->protection_scheme_event_queue,
4051 gst_event_ref (event));
4052 }
4053 g_free (sysid_string);
4054 gst_event_unref (event);
4055 gst_buffer_unref (pssh);
4056 return TRUE;
4057}
4058
shipeng sunc5d54622025-01-06 14:32:59 +08004059static void
4060aml_qtdemux_parse_senc (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream, GstByteReader * br)
4061{
4062 guint8 version = 0;
4063 guint32 flags = 0;
4064 gboolean use_subsample_encryption = FALSE;
4065 guint16 subsample_count = 0;
4066 guint32 byte_of_clear_data = 0;
4067 guint32 byte_of_protected_data = 0;
4068 guint32 sample_count = 0;
4069 gint i, j;
4070 guint iv_size = 8;
4071 AmlQtDemuxCencSampleSetInfo *ss_info = NULL;
4072
4073 if (!stream->protection_scheme_info)
4074 stream->protection_scheme_info = g_new0 (AmlQtDemuxCencSampleSetInfo, 1);
4075
4076 ss_info = (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
4077 if (!ss_info->default_properties) {
4078 ss_info->default_properties =
4079 gst_structure_new ("application/x-cenc",
4080 "encrypted", G_TYPE_BOOLEAN, TRUE,
4081 NULL);
4082 }
4083
4084 if (ss_info->crypto_info) {
4085 GST_LOG_OBJECT (qtdemux, "unreffing existing crypto_info");
4086 g_ptr_array_free (ss_info->crypto_info, TRUE);
4087 ss_info->crypto_info = NULL;
4088 }
4089
4090 if (!gst_byte_reader_get_uint8 (br, &version)) {
4091 GST_ERROR_OBJECT (qtdemux, "Error getting box's version field");
4092 return;
4093 }
4094
4095 GST_DEBUG_OBJECT (qtdemux, "version %d", version);
4096
4097 if (version > 0) {
4098 return;
4099 }
4100
4101 if (!gst_byte_reader_get_uint24_be (br, &flags)) {
4102 GST_ERROR_OBJECT (qtdemux, "Error getting box's flags field");
4103 return;
4104 }
4105
4106 if (flags & 0x000002) {
4107 use_subsample_encryption = TRUE;
4108 }
4109
4110 if (!gst_structure_get_uint (ss_info->default_properties, "iv_size", &iv_size)) {
4111 GST_ERROR_OBJECT (qtdemux, "Error getting encryption IV size field");
4112 return;
4113 }
4114 GST_DEBUG_OBJECT (qtdemux, "iv_size %d", iv_size);
4115
4116 if (!gst_byte_reader_get_uint32_be (br, &sample_count)) {
4117 GST_ERROR_OBJECT (qtdemux, "Error getting box's sample count field");
4118 return;
4119 }
4120
4121 ss_info->crypto_info =
4122 g_ptr_array_new_full (sample_count,
4123 (GDestroyNotify) aml_qtdemux_gst_structure_free);
4124
4125 for (i = 0; i < sample_count; i++) {
4126 GstStructure *properties;
4127 guint8 *data;
4128 GstBuffer *buf;
4129 GstBuffer *subsamples = NULL;
4130
4131 properties = aml_qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
4132 if (properties == NULL) {
4133 GST_ERROR_OBJECT (qtdemux, "failed to get properties for sample %u", i);
4134 qtdemux->cenc_aux_sample_count = i;
4135 return;
4136 }
4137
4138 if (iv_size > 0) {
4139 if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
4140 GST_ERROR_OBJECT (qtdemux, "IV data not present for sample %u", i);
4141 gst_structure_free (properties);
4142 qtdemux->cenc_aux_sample_count = i;
4143 return;
4144 }
4145 buf = gst_buffer_new_wrapped (data, iv_size);
4146 gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
4147 gst_buffer_unref (buf);
4148 } else {
4149 const GValue *constant_iv_size_value =
4150 gst_structure_get_value (properties, "constant_iv_size");
4151 const GValue *constant_iv_value =
4152 gst_structure_get_value (properties, "iv");
4153 if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
4154 GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
4155 gst_structure_free (properties);
4156 return;
4157 }
4158 gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
4159 }
4160
4161 if (use_subsample_encryption) {
4162 const GValue *kid_buf_value;
4163 if (!gst_byte_reader_get_uint16_be (br, &subsample_count)
4164 || subsample_count == 0) {
4165 GST_ERROR_OBJECT (qtdemux,
4166 "failed to get subsample count for sample %u", i);
4167 gst_structure_free (properties);
4168 qtdemux->cenc_aux_sample_count = i;
4169 return;
4170 }
4171 GST_DEBUG_OBJECT (qtdemux, "subsample_count %d", subsample_count);
4172
4173 if (!gst_byte_reader_dup_data (br, subsample_count * 6, &data)) {
4174 GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
4175 i);
4176 gst_structure_free (properties);
4177 qtdemux->cenc_aux_sample_count = i;
4178 return;
4179 }
4180 buf = gst_buffer_new_wrapped (data, subsample_count * 6);// guint16 clr + guint32 enc
4181 kid_buf_value = gst_structure_get_value (ss_info->default_properties, "kid");
4182
4183 gst_structure_set (properties,
4184 "subsample_count", G_TYPE_UINT, subsample_count,
4185 "subsamples", GST_TYPE_BUFFER, buf, NULL);
4186 gst_structure_set_value (properties, "kid", kid_buf_value);
4187 gst_buffer_unref (buf);
4188 } else {
4189 gst_structure_set (properties,
4190 "subsample_count", G_TYPE_UINT, 0,
4191 "subsamples", GST_TYPE_BUFFER, NULL, NULL);
4192 }
4193
4194 g_ptr_array_add (ss_info->crypto_info, properties);
4195 }
4196
4197 qtdemux->cenc_aux_sample_count = sample_count;
4198}
4199
zengliang.li5f31ef42024-05-16 08:27:38 +00004200static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004201aml_qtdemux_parse_moof (GstAmlQTDemux * qtdemux, const guint8 * buffer, guint length,
4202 guint64 moof_offset, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00004203{
4204 GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *mfhd_node;
4205 GNode *uuid_node;
4206 GstByteReader mfhd_data, trun_data, tfhd_data, tfdt_data;
4207 GNode *saiz_node, *saio_node, *pssh_node;
4208 GstByteReader saiz_data, saio_data;
4209 guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
4210 gint64 base_offset, running_offset;
4211 guint32 frag_num;
4212 GstClockTime min_dts = GST_CLOCK_TIME_NONE;
shipeng sunc5d54622025-01-06 14:32:59 +08004213 GNode *senc_node;
4214 GstByteReader senc_data;
zengliang.li5f31ef42024-05-16 08:27:38 +00004215 /* NOTE @stream ignored */
4216
4217 moof_node = g_node_new ((guint8 *) buffer);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004218 aml_qtdemux_parse_node (qtdemux, moof_node, buffer, length);
4219 aml_qtdemux_node_dump (qtdemux, moof_node);
zengliang.li5f31ef42024-05-16 08:27:38 +00004220
4221 /* Get fragment number from mfhd and check it's valid */
4222 mfhd_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004223 aml_qtdemux_tree_get_child_by_type_full (moof_node, AML_FOURCC_mfhd, &mfhd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +00004224 if (mfhd_node == NULL)
4225 goto missing_mfhd;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004226 if (!aml_qtdemux_parse_mfhd (qtdemux, &mfhd_data, &frag_num))
zengliang.li5f31ef42024-05-16 08:27:38 +00004227 goto fail;
4228 GST_DEBUG_OBJECT (qtdemux, "Fragment #%d", frag_num);
4229
4230 /* unknown base_offset to start with */
4231 base_offset = running_offset = -1;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004232 traf_node = aml_qtdemux_tree_get_child_by_type (moof_node, AML_FOURCC_traf);
zengliang.li5f31ef42024-05-16 08:27:38 +00004233 while (traf_node) {
4234 guint64 decode_time = 0;
4235
4236 /* Fragment Header node */
4237 tfhd_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004238 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_tfhd,
zengliang.li5f31ef42024-05-16 08:27:38 +00004239 &tfhd_data);
4240 if (!tfhd_node)
4241 goto missing_tfhd;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004242 if (!aml_qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
zengliang.li5f31ef42024-05-16 08:27:38 +00004243 &ds_size, &ds_flags, &base_offset))
4244 goto missing_tfhd;
4245
4246 /* The following code assumes at most a single set of sample auxiliary
4247 * data in the fragment (consisting of a saiz box and a corresponding saio
4248 * box); in theory, however, there could be multiple sets of sample
4249 * auxiliary data in a fragment. */
4250 saiz_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004251 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_saiz,
zengliang.li5f31ef42024-05-16 08:27:38 +00004252 &saiz_data);
4253 if (saiz_node) {
4254 guint32 info_type = 0;
4255 guint64 offset = 0;
4256 guint32 info_type_parameter = 0;
4257
4258 g_free (qtdemux->cenc_aux_info_sizes);
zengliang.li125c3642024-05-17 06:06:08 +00004259 GST_DEBUG_OBJECT (qtdemux, "parse saiz_node");
zengliang.li5f31ef42024-05-16 08:27:38 +00004260 qtdemux->cenc_aux_info_sizes =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004261 aml_qtdemux_parse_saiz (qtdemux, stream, &saiz_data,
zengliang.li5f31ef42024-05-16 08:27:38 +00004262 &qtdemux->cenc_aux_sample_count);
4263 if (qtdemux->cenc_aux_info_sizes == NULL) {
4264 GST_ERROR_OBJECT (qtdemux, "failed to parse saiz box");
4265 goto fail;
4266 }
4267 saio_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004268 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_saio,
zengliang.li5f31ef42024-05-16 08:27:38 +00004269 &saio_data);
4270 if (!saio_node) {
4271 GST_ERROR_OBJECT (qtdemux, "saiz box without a corresponding saio box");
4272 g_free (qtdemux->cenc_aux_info_sizes);
4273 qtdemux->cenc_aux_info_sizes = NULL;
4274 goto fail;
4275 }
4276
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004277 if (G_UNLIKELY (!aml_qtdemux_parse_saio (qtdemux, stream, &saio_data,
zengliang.li5f31ef42024-05-16 08:27:38 +00004278 &info_type, &info_type_parameter, &offset))) {
4279 GST_ERROR_OBJECT (qtdemux, "failed to parse saio box");
4280 g_free (qtdemux->cenc_aux_info_sizes);
4281 qtdemux->cenc_aux_info_sizes = NULL;
4282 goto fail;
4283 }
4284 if (base_offset > -1 && base_offset > qtdemux->moof_offset)
4285 offset += (guint64) (base_offset - qtdemux->moof_offset);
xuesong.jiangec2fff52024-10-21 16:16:19 +08004286 if ((info_type == AML_FOURCC_cenc || info_type == AML_FOURCC_cbcs || info_type == AML_FOURCC_cens || info_type == AML_FOURCC_cbc1)
zengliang.li5f31ef42024-05-16 08:27:38 +00004287 && info_type_parameter == 0U) {
4288 GstByteReader br;
4289 if (offset > length) {
4290 GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
4291 qtdemux->cenc_aux_info_offset = offset;
4292 } else {
zengliang.li125c3642024-05-17 06:06:08 +00004293 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
zengliang.li5f31ef42024-05-16 08:27:38 +00004294 gst_byte_reader_init (&br, buffer + offset, length - offset);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004295 if (!aml_qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
zengliang.li5f31ef42024-05-16 08:27:38 +00004296 qtdemux->cenc_aux_info_sizes,
4297 qtdemux->cenc_aux_sample_count)) {
4298 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
4299 g_free (qtdemux->cenc_aux_info_sizes);
4300 qtdemux->cenc_aux_info_sizes = NULL;
4301 goto fail;
4302 }
4303 }
4304 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004305 } else {
shipeng sunc5d54622025-01-06 14:32:59 +08004306 GST_WARNING_OBJECT (qtdemux, "no saiz_node, may cbc1 audio, try parse senc");
4307 senc_node =
4308 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_senc,
4309 &senc_data);
4310 if (senc_node) {
4311 GST_DEBUG_OBJECT (qtdemux, "parsing senc box");
4312 aml_qtdemux_parse_senc (qtdemux, stream, &senc_data);
4313 } else {
4314 GST_WARNING_OBJECT (qtdemux, "no senc_node");
4315 }
zengliang.li5f31ef42024-05-16 08:27:38 +00004316 }
4317
4318 tfdt_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004319 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_tfdt,
zengliang.li5f31ef42024-05-16 08:27:38 +00004320 &tfdt_data);
4321 if (tfdt_node) {
4322 /* We'll use decode_time to interpolate timestamps
4323 * in case the input timestamps are missing */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004324 aml_qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
zengliang.li5f31ef42024-05-16 08:27:38 +00004325
4326 GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
4327 " (%" GST_TIME_FORMAT ")", decode_time,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004328 GST_TIME_ARGS (stream ? AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00004329 decode_time) : GST_CLOCK_TIME_NONE));
4330
4331 /* Discard the fragment buffer timestamp info to avoid using it.
4332 * Rely on tfdt instead as it is more accurate than the timestamp
4333 * that is fetched from a manifest/playlist and is usually
4334 * less accurate. */
4335 qtdemux->fragment_start = -1;
4336 }
4337
4338 if (G_UNLIKELY (!stream)) {
4339 /* we lost track of offset, we'll need to regain it,
4340 * but can delay complaining until later or avoid doing so altogether */
4341 base_offset = -2;
4342 goto next;
4343 }
4344 if (G_UNLIKELY (base_offset < -1))
4345 goto lost_offset;
4346
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004347 min_dts = MIN (min_dts, AML_QTSTREAMTIME_TO_GSTTIME (stream, decode_time));
zengliang.li5f31ef42024-05-16 08:27:38 +00004348
4349 if (!qtdemux->pullbased) {
4350 /* Sample tables can grow enough to be problematic if the system memory
4351 * is very low (e.g. embedded devices) and the videos very long
4352 * (~8 MiB/hour for 25-30 fps video + typical AAC audio frames).
4353 * Fortunately, we can easily discard them for each new fragment when
4354 * we know qtdemux will not receive seeks outside of the current fragment.
4355 * adaptivedemux honors this assumption.
4356 * This optimization is also useful for applications that use qtdemux as
4357 * a push-based simple demuxer, like Media Source Extensions. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004358 gst_aml_qtdemux_stream_flush_samples_data (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00004359 }
4360
4361 /* initialise moof sample data */
4362 stream->n_samples_moof = 0;
4363 stream->duration_last_moof = stream->duration_moof;
4364 stream->duration_moof = 0;
4365
4366 /* Track Run node */
4367 trun_node =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004368 aml_qtdemux_tree_get_child_by_type_full (traf_node, AML_FOURCC_trun,
zengliang.li5f31ef42024-05-16 08:27:38 +00004369 &trun_data);
4370 while (trun_node) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004371 aml_qtdemux_parse_trun (qtdemux, &trun_data, stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00004372 ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
4373 &running_offset, decode_time, (tfdt_node != NULL));
4374 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004375 trun_node = aml_qtdemux_tree_get_sibling_by_type_full (trun_node, AML_FOURCC_trun,
zengliang.li5f31ef42024-05-16 08:27:38 +00004376 &trun_data);
4377 /* don't use tfdt for subsequent trun as it only refers to the first */
4378 tfdt_node = NULL;
4379 }
4380
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004381 uuid_node = aml_qtdemux_tree_get_child_by_type (traf_node, AML_FOURCC_uuid);
zengliang.li5f31ef42024-05-16 08:27:38 +00004382 if (uuid_node) {
4383 guint8 *uuid_buffer = (guint8 *) uuid_node->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004384 guint32 box_length = AML_QT_UINT32 (uuid_buffer);
zengliang.li5f31ef42024-05-16 08:27:38 +00004385
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004386 aml_qtdemux_parse_uuid (qtdemux, uuid_buffer, box_length);
zengliang.li5f31ef42024-05-16 08:27:38 +00004387 }
4388
4389 /* if no new base_offset provided for next traf,
4390 * base is end of current traf */
4391 base_offset = running_offset;
4392 running_offset = -1;
4393
4394 if (stream->n_samples_moof && stream->duration_moof)
4395 stream->new_caps = TRUE;
4396
4397 next:
4398 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004399 traf_node = aml_qtdemux_tree_get_sibling_by_type (traf_node, AML_FOURCC_traf);
zengliang.li5f31ef42024-05-16 08:27:38 +00004400 }
4401
4402 /* parse any protection system info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004403 pssh_node = aml_qtdemux_tree_get_child_by_type (moof_node, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +00004404 while (pssh_node) {
4405 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004406 aml_qtdemux_parse_pssh (qtdemux, pssh_node);
4407 pssh_node = aml_qtdemux_tree_get_sibling_by_type (pssh_node, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +00004408 }
4409
4410 if (!qtdemux->upstream_format_is_time
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004411 && qtdemux->variant != AML_VARIANT_MSE_BYTESTREAM
zengliang.li5f31ef42024-05-16 08:27:38 +00004412 && !qtdemux->first_moof_already_parsed
4413 && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
4414 && min_dts != 0) {
4415 /* Unless the user has explicitly requested another seek, perform an
4416 * internal seek to the time specified in the tfdt.
4417 *
4418 * This way if the user opens a file where the first tfdt is 1 hour
4419 * into the presentation, they will not have to wait 1 hour for run
4420 * time to catch up and actual playback to start. */
4421 gint i;
4422
4423 GST_DEBUG_OBJECT (qtdemux, "First fragment has a non-zero tfdt, "
4424 "performing an internal seek to %" GST_TIME_FORMAT,
4425 GST_TIME_ARGS (min_dts));
4426
4427 qtdemux->segment.start = min_dts;
4428 qtdemux->segment.time = qtdemux->segment.position = min_dts;
4429
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004430 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
4431 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00004432 stream->time_position = min_dts;
4433 }
4434
4435 /* Before this code was run a segment was already sent when the moov was
4436 * parsed... which is OK -- some apps (mostly tests) expect a segment to
4437 * be emitted after a moov, and we can emit a second segment anyway for
4438 * special cases like this. */
4439 qtdemux->need_segment = TRUE;
4440 }
4441
4442 qtdemux->first_moof_already_parsed = TRUE;
4443
4444 g_node_destroy (moof_node);
4445 return TRUE;
4446
4447missing_tfhd:
4448 {
4449 GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
4450 goto fail;
4451 }
4452missing_mfhd:
4453 {
4454 GST_DEBUG_OBJECT (qtdemux, "Missing mfhd box");
4455 goto fail;
4456 }
4457lost_offset:
4458 {
4459 GST_DEBUG_OBJECT (qtdemux, "lost offset");
4460 goto fail;
4461 }
4462fail:
4463 {
4464 g_node_destroy (moof_node);
4465 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4466 (_("This file is corrupt and cannot be played.")), (NULL));
4467 return FALSE;
4468 }
4469}
4470
4471#if 0
4472/* might be used if some day we actually use mfra & co
4473 * for random access to fragments,
4474 * but that will require quite some modifications and much less relying
4475 * on a sample array */
4476#endif
4477
4478static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004479aml_qtdemux_parse_tfra (GstAmlQTDemux * qtdemux, GNode * tfra_node)
zengliang.li5f31ef42024-05-16 08:27:38 +00004480{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004481 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00004482 guint32 ver_flags, track_id, len, num_entries, i;
4483 guint value_size, traf_size, trun_size, sample_size;
4484 guint64 time = 0, moof_offset = 0;
4485#if 0
4486 GstBuffer *buf = NULL;
4487 GstFlowReturn ret;
4488#endif
4489 GstByteReader tfra;
4490
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004491 gst_byte_reader_init (&tfra, tfra_node->data, AML_QT_UINT32 (tfra_node->data));
zengliang.li5f31ef42024-05-16 08:27:38 +00004492
4493 if (!gst_byte_reader_skip (&tfra, 8))
4494 return FALSE;
4495
4496 if (!gst_byte_reader_get_uint32_be (&tfra, &ver_flags))
4497 return FALSE;
4498
4499 if (!gst_byte_reader_get_uint32_be (&tfra, &track_id)
4500 || !gst_byte_reader_get_uint32_be (&tfra, &len)
4501 || !gst_byte_reader_get_uint32_be (&tfra, &num_entries))
4502 return FALSE;
4503
4504 GST_DEBUG_OBJECT (qtdemux, "parsing tfra box for track id %u", track_id);
4505
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004506 stream = aml_qtdemux_find_stream (qtdemux, track_id);
zengliang.li5f31ef42024-05-16 08:27:38 +00004507 if (stream == NULL)
4508 goto unknown_trackid;
4509
4510 value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
4511 sample_size = (len & 3) + 1;
4512 trun_size = ((len & 12) >> 2) + 1;
4513 traf_size = ((len & 48) >> 4) + 1;
4514
4515 GST_DEBUG_OBJECT (qtdemux, "%u entries, sizes: value %u, traf %u, trun %u, "
4516 "sample %u", num_entries, value_size, traf_size, trun_size, sample_size);
4517
4518 if (num_entries == 0)
4519 goto no_samples;
4520
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004521 if (!aml_qt_atom_parser_has_chunks (&tfra, num_entries,
zengliang.li5f31ef42024-05-16 08:27:38 +00004522 value_size + value_size + traf_size + trun_size + sample_size))
4523 goto corrupt_file;
4524
4525 g_free (stream->ra_entries);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004526 stream->ra_entries = g_new (AmlQtDemuxRandomAccessEntry, num_entries);
zengliang.li5f31ef42024-05-16 08:27:38 +00004527 stream->n_ra_entries = num_entries;
4528
4529 for (i = 0; i < num_entries; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004530 aml_qt_atom_parser_get_offset (&tfra, value_size, &time);
4531 aml_qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
4532 aml_qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
4533 aml_qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
4534 aml_qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00004535
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004536 time = AML_QTSTREAMTIME_TO_GSTTIME (stream, time);
zengliang.li5f31ef42024-05-16 08:27:38 +00004537
4538 GST_LOG_OBJECT (qtdemux, "fragment time: %" GST_TIME_FORMAT ", "
4539 " moof_offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (time), moof_offset);
4540
4541 stream->ra_entries[i].ts = time;
4542 stream->ra_entries[i].moof_offset = moof_offset;
4543
4544 /* don't want to go through the entire file and read all moofs at startup */
4545#if 0
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004546 ret = gst_aml_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00004547 if (ret != GST_FLOW_OK)
4548 goto corrupt_file;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004549 aml_qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
zengliang.li5f31ef42024-05-16 08:27:38 +00004550 moof_offset, stream);
4551 gst_buffer_unref (buf);
4552#endif
4553 }
4554
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004555 aml_check_update_duration (qtdemux, time);
zengliang.li5f31ef42024-05-16 08:27:38 +00004556
4557 return TRUE;
4558
4559/* ERRORS */
4560unknown_trackid:
4561 {
4562 GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
4563 return FALSE;
4564 }
4565corrupt_file:
4566 {
4567 GST_WARNING_OBJECT (qtdemux, "broken traf box, ignoring");
4568 return FALSE;
4569 }
4570no_samples:
4571 {
4572 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
4573 return FALSE;
4574 }
4575}
4576
4577static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004578aml_qtdemux_pull_mfro_mfra (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00004579{
4580 GstMapInfo mfro_map = GST_MAP_INFO_INIT;
4581 GstMapInfo mfra_map = GST_MAP_INFO_INIT;
4582 GstBuffer *mfro = NULL, *mfra = NULL;
4583 GstFlowReturn flow;
4584 gboolean ret = FALSE;
4585 GNode *mfra_node, *tfra_node;
4586 guint64 mfra_offset = 0;
4587 guint32 fourcc, mfra_size;
4588 gint64 len;
4589
4590 /* query upstream size in bytes */
4591 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &len))
4592 goto size_query_failed;
4593
4594 /* mfro box should be at the very end of the file */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004595 flow = gst_aml_qtdemux_pull_atom (qtdemux, len - 16, 16, &mfro);
zengliang.li5f31ef42024-05-16 08:27:38 +00004596 if (flow != GST_FLOW_OK)
4597 goto exit;
4598
4599 gst_buffer_map (mfro, &mfro_map, GST_MAP_READ);
4600
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004601 fourcc = AML_QT_FOURCC (mfro_map.data + 4);
4602 if (fourcc != AML_FOURCC_mfro)
zengliang.li5f31ef42024-05-16 08:27:38 +00004603 goto exit;
4604
4605 GST_INFO_OBJECT (qtdemux, "Found mfro box");
4606 if (mfro_map.size < 16)
4607 goto invalid_mfro_size;
4608
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004609 mfra_size = AML_QT_UINT32 (mfro_map.data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +00004610 if (mfra_size >= len)
4611 goto invalid_mfra_size;
4612
4613 mfra_offset = len - mfra_size;
4614
4615 GST_INFO_OBJECT (qtdemux, "mfra offset: %" G_GUINT64_FORMAT ", size %u",
4616 mfra_offset, mfra_size);
4617
4618 /* now get and parse mfra box */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004619 flow = gst_aml_qtdemux_pull_atom (qtdemux, mfra_offset, mfra_size, &mfra);
zengliang.li5f31ef42024-05-16 08:27:38 +00004620 if (flow != GST_FLOW_OK)
4621 goto broken_file;
4622
4623 gst_buffer_map (mfra, &mfra_map, GST_MAP_READ);
4624
4625 mfra_node = g_node_new ((guint8 *) mfra_map.data);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004626 aml_qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
zengliang.li5f31ef42024-05-16 08:27:38 +00004627
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004628 tfra_node = aml_qtdemux_tree_get_child_by_type (mfra_node, AML_FOURCC_tfra);
zengliang.li5f31ef42024-05-16 08:27:38 +00004629
4630 while (tfra_node) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004631 aml_qtdemux_parse_tfra (qtdemux, tfra_node);
zengliang.li5f31ef42024-05-16 08:27:38 +00004632 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004633 tfra_node = aml_qtdemux_tree_get_sibling_by_type (tfra_node, AML_FOURCC_tfra);
zengliang.li5f31ef42024-05-16 08:27:38 +00004634 }
4635 g_node_destroy (mfra_node);
4636
4637 GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
4638 ret = TRUE;
4639
4640exit:
4641
4642 if (mfro) {
4643 if (mfro_map.memory != NULL)
4644 gst_buffer_unmap (mfro, &mfro_map);
4645 gst_buffer_unref (mfro);
4646 }
4647 if (mfra) {
4648 if (mfra_map.memory != NULL)
4649 gst_buffer_unmap (mfra, &mfra_map);
4650 gst_buffer_unref (mfra);
4651 }
4652 return ret;
4653
4654/* ERRORS */
4655size_query_failed:
4656 {
4657 GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
4658 goto exit;
4659 }
4660invalid_mfro_size:
4661 {
4662 GST_WARNING_OBJECT (qtdemux, "mfro size is too small");
4663 goto exit;
4664 }
4665invalid_mfra_size:
4666 {
4667 GST_WARNING_OBJECT (qtdemux, "mfra_size in mfro box is invalid");
4668 goto exit;
4669 }
4670broken_file:
4671 {
4672 GST_WARNING_OBJECT (qtdemux, "bogus mfra offset or size, broken file");
4673 goto exit;
4674 }
4675}
4676
4677static guint64
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004678aml_add_offset (guint64 offset, guint64 advance)
zengliang.li5f31ef42024-05-16 08:27:38 +00004679{
4680 /* Avoid 64-bit overflow by clamping */
4681 if (offset > G_MAXUINT64 - advance)
4682 return G_MAXUINT64;
4683 return offset + advance;
4684}
4685
4686static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004687gst_aml_qtdemux_loop_state_header (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00004688{
4689 guint64 length = 0;
4690 guint32 fourcc = 0;
4691 GstBuffer *buf = NULL;
4692 GstFlowReturn ret = GST_FLOW_OK;
4693 guint64 cur_offset = qtdemux->offset;
4694 GstMapInfo map;
4695
4696 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
4697 if (G_UNLIKELY (ret != GST_FLOW_OK))
4698 goto beach;
4699 gst_buffer_map (buf, &map, GST_MAP_READ);
4700 if (G_LIKELY (map.size >= 8))
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004701 aml_extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +00004702 gst_buffer_unmap (buf, &map);
4703 gst_buffer_unref (buf);
4704
4705 /* maybe we already got most we needed, so only consider this eof */
4706 if (G_UNLIKELY (length == 0)) {
4707 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
4708 (_("Invalid atom size.")),
4709 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
4710 GST_FOURCC_ARGS (fourcc)));
4711 ret = GST_FLOW_EOS;
4712 goto beach;
4713 }
4714
4715 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004716 case AML_FOURCC_moof:
zengliang.li5f31ef42024-05-16 08:27:38 +00004717 /* record for later parsing when needed */
4718 if (!qtdemux->moof_offset) {
4719 qtdemux->moof_offset = qtdemux->offset;
4720 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004721 if (aml_qtdemux_pull_mfro_mfra (qtdemux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00004722 /* FIXME */
4723 } else {
4724 qtdemux->offset += length; /* skip moof and keep going */
4725 }
4726 if (qtdemux->got_moov) {
4727 GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
4728 ret = GST_FLOW_EOS;
4729 goto beach;
4730 }
4731 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004732 case AML_FOURCC_mdat:
4733 case AML_FOURCC_free:
4734 case AML_FOURCC_skip:
4735 case AML_FOURCC_wide:
4736 case AML_FOURCC_PICT:
4737 case AML_FOURCC_pnot:
zengliang.li5f31ef42024-05-16 08:27:38 +00004738 {
4739 GST_LOG_OBJECT (qtdemux,
4740 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
4741 GST_FOURCC_ARGS (fourcc), cur_offset);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004742 qtdemux->offset = aml_add_offset (qtdemux->offset, length);
zengliang.li5f31ef42024-05-16 08:27:38 +00004743 break;
4744 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004745 case AML_FOURCC_moov:
zengliang.li5f31ef42024-05-16 08:27:38 +00004746 {
4747 GstBuffer *moov = NULL;
4748
4749 if (qtdemux->got_moov) {
4750 GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004751 qtdemux->offset = aml_add_offset (qtdemux->offset, length);
zengliang.li5f31ef42024-05-16 08:27:38 +00004752 goto beach;
4753 }
4754
4755 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
4756 if (ret != GST_FLOW_OK)
4757 goto beach;
4758 gst_buffer_map (moov, &map, GST_MAP_READ);
4759
4760 if (length != map.size) {
4761 /* Some files have a 'moov' atom at the end of the file which contains
4762 * a terminal 'free' atom where the body of the atom is missing.
4763 * Check for, and permit, this special case.
4764 */
4765 if (map.size >= 8) {
4766 guint8 *final_data = map.data + (map.size - 8);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004767 guint32 final_length = AML_QT_UINT32 (final_data);
4768 guint32 final_fourcc = AML_QT_FOURCC (final_data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00004769
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004770 if (final_fourcc == AML_FOURCC_free
zengliang.li5f31ef42024-05-16 08:27:38 +00004771 && map.size + final_length - 8 == length) {
4772 /* Ok, we've found that special case. Allocate a new buffer with
4773 * that free atom actually present. */
4774 GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
4775 gst_buffer_fill (newmoov, 0, map.data, map.size);
4776 gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
4777 gst_buffer_unmap (moov, &map);
4778 gst_buffer_unref (moov);
4779 moov = newmoov;
4780 gst_buffer_map (moov, &map, GST_MAP_READ);
4781 }
4782 }
4783 }
4784
4785 if (length != map.size) {
4786 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
4787 (_("This file is incomplete and cannot be played.")),
4788 ("We got less than expected (received %" G_GSIZE_FORMAT
4789 ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
4790 (guint) length, cur_offset));
4791 gst_buffer_unmap (moov, &map);
4792 gst_buffer_unref (moov);
4793 ret = GST_FLOW_ERROR;
4794 goto beach;
4795 }
4796 qtdemux->offset += length;
4797
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004798 aml_qtdemux_parse_moov (qtdemux, map.data, length);
4799 aml_qtdemux_node_dump (qtdemux, qtdemux->moov_node);
zengliang.li5f31ef42024-05-16 08:27:38 +00004800
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004801 aml_qtdemux_parse_tree (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00004802 if (qtdemux->moov_node_compressed) {
4803 g_node_destroy (qtdemux->moov_node_compressed);
4804 g_free (qtdemux->moov_node->data);
4805 }
4806 qtdemux->moov_node_compressed = NULL;
4807 g_node_destroy (qtdemux->moov_node);
4808 qtdemux->moov_node = NULL;
4809 gst_buffer_unmap (moov, &map);
4810 gst_buffer_unref (moov);
4811 qtdemux->got_moov = TRUE;
4812
4813 break;
4814 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004815 case AML_FOURCC_ftyp:
zengliang.li5f31ef42024-05-16 08:27:38 +00004816 {
4817 GstBuffer *ftyp = NULL;
4818
4819 /* extract major brand; might come in handy for ISO vs QT issues */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004820 ret = gst_aml_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
zengliang.li5f31ef42024-05-16 08:27:38 +00004821 if (ret != GST_FLOW_OK)
4822 goto beach;
4823 qtdemux->offset += length;
4824 gst_buffer_map (ftyp, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004825 aml_qtdemux_parse_ftyp (qtdemux, map.data, map.size);
zengliang.li5f31ef42024-05-16 08:27:38 +00004826 gst_buffer_unmap (ftyp, &map);
4827 gst_buffer_unref (ftyp);
4828 break;
4829 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004830 case AML_FOURCC_uuid:
zengliang.li5f31ef42024-05-16 08:27:38 +00004831 {
4832 GstBuffer *uuid = NULL;
4833
4834 /* uuid are extension atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004835 ret = gst_aml_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
zengliang.li5f31ef42024-05-16 08:27:38 +00004836 if (ret != GST_FLOW_OK)
4837 goto beach;
4838 qtdemux->offset += length;
4839 gst_buffer_map (uuid, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004840 aml_qtdemux_parse_uuid (qtdemux, map.data, map.size);
zengliang.li5f31ef42024-05-16 08:27:38 +00004841 gst_buffer_unmap (uuid, &map);
4842 gst_buffer_unref (uuid);
4843 break;
4844 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004845 case AML_FOURCC_sidx:
zengliang.li5f31ef42024-05-16 08:27:38 +00004846 {
4847 GstBuffer *sidx = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004848 ret = gst_aml_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
zengliang.li5f31ef42024-05-16 08:27:38 +00004849 if (ret != GST_FLOW_OK)
4850 goto beach;
4851 qtdemux->offset += length;
4852 gst_buffer_map (sidx, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004853 aml_qtdemux_parse_sidx (qtdemux, map.data, map.size);
zengliang.li5f31ef42024-05-16 08:27:38 +00004854 gst_buffer_unmap (sidx, &map);
4855 gst_buffer_unref (sidx);
4856 break;
4857 }
4858 default:
4859 {
4860 GstBuffer *unknown = NULL;
4861
4862 GST_LOG_OBJECT (qtdemux,
4863 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
4864 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
4865 cur_offset);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004866 ret = gst_aml_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
zengliang.li5f31ef42024-05-16 08:27:38 +00004867 if (ret != GST_FLOW_OK)
4868 goto beach;
4869 gst_buffer_map (unknown, &map, GST_MAP_READ);
4870 GST_MEMDUMP ("Unknown tag", map.data, map.size);
4871 gst_buffer_unmap (unknown, &map);
4872 gst_buffer_unref (unknown);
4873 qtdemux->offset += length;
4874 break;
4875 }
4876 }
4877
4878beach:
4879 if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
4880 /* digested all data, show what we have */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004881 aml_qtdemux_prepare_streams (qtdemux);
4882 AML_QTDEMUX_EXPOSE_LOCK (qtdemux);
4883 ret = aml_qtdemux_expose_streams (qtdemux);
4884 AML_QTDEMUX_EXPOSE_UNLOCK (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00004885
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004886 qtdemux->state = AML_QTDEMUX_STATE_MOVIE;
zengliang.li5f31ef42024-05-16 08:27:38 +00004887 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
4888 qtdemux->state);
4889 return ret;
4890 }
4891 return ret;
4892}
4893
4894/* Seeks to the previous keyframe of the indexed stream and
4895 * aligns other streams with respect to the keyframe timestamp
4896 * of indexed stream. Only called in case of Reverse Playback
4897 */
4898static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004899gst_aml_qtdemux_seek_to_previous_keyframe (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00004900{
4901 guint32 seg_idx = 0, k_index = 0;
4902 guint32 ref_seg_idx, ref_k_index;
4903 GstClockTime k_pos = 0, last_stop = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004904 AmlQtDemuxSegment *seg = NULL;
4905 AmlQtDemuxStream *ref_str = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00004906 guint64 seg_media_start_mov; /* segment media start time in mov format */
4907 guint64 target_ts;
4908 gint i;
4909
4910 /* Now we choose an arbitrary stream, get the previous keyframe timestamp
4911 * and finally align all the other streams on that timestamp with their
4912 * respective keyframes */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004913 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
4914 AmlQtDemuxStream *str = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00004915
4916 /* No candidate yet, take the first stream */
4917 if (!ref_str) {
4918 ref_str = str;
4919 continue;
4920 }
4921
4922 /* So that stream has a segment, we prefer video streams */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004923 if (str->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +00004924 ref_str = str;
4925 break;
4926 }
4927 }
4928
4929 if (G_UNLIKELY (!ref_str)) {
4930 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream");
4931 goto eos;
4932 }
4933
4934 if (G_UNLIKELY (!ref_str->from_sample)) {
4935 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file");
4936 goto eos;
4937 }
4938
4939 /* So that stream has been playing from from_sample to to_sample. We will
4940 * get the timestamp of the previous sample and search for a keyframe before
4941 * that. For audio streams we do an arbitrary jump in the past (10 samples) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004942 if (ref_str->subtype == AML_FOURCC_vide) {
4943 k_index = gst_aml_qtdemux_find_keyframe (qtdemux, ref_str,
zengliang.li5f31ef42024-05-16 08:27:38 +00004944 ref_str->from_sample - 1, FALSE);
4945 } else {
4946 if (ref_str->from_sample >= 10)
4947 k_index = ref_str->from_sample - 10;
4948 else
4949 k_index = 0;
4950 }
4951
4952 target_ts =
4953 ref_str->samples[k_index].timestamp +
4954 ref_str->samples[k_index].pts_offset;
4955
4956 /* get current segment for that stream */
4957 seg = &ref_str->segments[ref_str->segment_index];
4958 /* Use segment start in original timescale for comparisons */
4959 seg_media_start_mov = seg->trak_media_start;
4960
4961 GST_LOG_OBJECT (qtdemux, "keyframe index %u ts %" G_GUINT64_FORMAT
4962 " seg start %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
4963 k_index, target_ts, seg_media_start_mov,
4964 GST_TIME_ARGS (seg->media_start));
4965
4966 /* Crawl back through segments to find the one containing this I frame */
4967 while (target_ts < seg_media_start_mov) {
4968 GST_DEBUG_OBJECT (qtdemux,
4969 "keyframe position (sample %u) is out of segment %u " " target %"
4970 G_GUINT64_FORMAT " seg start %" G_GUINT64_FORMAT, k_index,
4971 ref_str->segment_index, target_ts, seg_media_start_mov);
4972
4973 if (G_UNLIKELY (!ref_str->segment_index)) {
4974 /* Reached first segment, let's consider it's EOS */
4975 goto eos;
4976 }
4977 ref_str->segment_index--;
4978 seg = &ref_str->segments[ref_str->segment_index];
4979 /* Use segment start in original timescale for comparisons */
4980 seg_media_start_mov = seg->trak_media_start;
4981 }
4982 /* Calculate time position of the keyframe and where we should stop */
4983 k_pos =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004984 AML_QTSTREAMTIME_TO_GSTTIME (ref_str,
zengliang.li5f31ef42024-05-16 08:27:38 +00004985 target_ts - seg->trak_media_start) + seg->time;
4986 last_stop =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00004987 AML_QTSTREAMTIME_TO_GSTTIME (ref_str,
zengliang.li5f31ef42024-05-16 08:27:38 +00004988 ref_str->samples[ref_str->from_sample].timestamp -
4989 seg->trak_media_start) + seg->time;
4990
4991 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, "
4992 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample,
4993 k_index, GST_TIME_ARGS (k_pos));
4994
4995 /* Set last_stop with the keyframe timestamp we pushed of that stream */
4996 qtdemux->segment.position = last_stop;
4997 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT,
4998 GST_TIME_ARGS (last_stop));
4999
5000 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) {
5001 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment");
5002 goto eos;
5003 }
5004
5005 ref_seg_idx = ref_str->segment_index;
5006 ref_k_index = k_index;
5007
5008 /* Align them all on this */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005009 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005010 guint32 index = 0;
5011 GstClockTime seg_time = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005012 AmlQtDemuxStream *str = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00005013
5014 /* aligning reference stream again might lead to backing up to yet another
5015 * keyframe (due to timestamp rounding issues),
5016 * potentially putting more load on downstream; so let's try to avoid */
5017 if (str == ref_str) {
5018 seg_idx = ref_seg_idx;
5019 seg = &str->segments[seg_idx];
5020 k_index = ref_k_index;
5021 GST_DEBUG_OBJECT (qtdemux, "reference track-id %u segment %d, "
5022 "sample at index %d", str->track_id, ref_str->segment_index, k_index);
5023 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005024 seg_idx = gst_aml_qtdemux_find_segment (qtdemux, str, k_pos);
zengliang.li5f31ef42024-05-16 08:27:38 +00005025 GST_DEBUG_OBJECT (qtdemux,
5026 "track-id %u align segment %d for keyframe pos %" GST_TIME_FORMAT,
5027 str->track_id, seg_idx, GST_TIME_ARGS (k_pos));
5028
5029 /* get segment and time in the segment */
5030 seg = &str->segments[seg_idx];
5031 seg_time = k_pos - seg->time;
5032
5033 /* get the media time in the segment.
5034 * No adjustment for empty "filler" segments */
5035 if (seg->media_start != GST_CLOCK_TIME_NONE)
5036 seg_time += seg->media_start;
5037
5038 /* get the index of the sample with media time */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005039 index = gst_aml_qtdemux_find_index_linear (qtdemux, str, seg_time);
zengliang.li5f31ef42024-05-16 08:27:38 +00005040 GST_DEBUG_OBJECT (qtdemux,
5041 "track-id %u sample for %" GST_TIME_FORMAT " at %u", str->track_id,
5042 GST_TIME_ARGS (seg_time), index);
5043
5044 /* find previous keyframe */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005045 k_index = gst_aml_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00005046 }
5047
5048 /* Remember until where we want to go */
5049 str->to_sample = str->from_sample - 1;
5050 /* Define our time position */
5051 target_ts =
5052 str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005053 str->time_position = AML_QTSTREAMTIME_TO_GSTTIME (str, target_ts) + seg->time;
zengliang.li5f31ef42024-05-16 08:27:38 +00005054 if (seg->media_start != GST_CLOCK_TIME_NONE)
5055 str->time_position -= seg->media_start;
5056
5057 /* Now seek back in time */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005058 gst_aml_qtdemux_move_stream (qtdemux, str, k_index);
zengliang.li5f31ef42024-05-16 08:27:38 +00005059 GST_DEBUG_OBJECT (qtdemux, "track-id %u keyframe at %u, time position %"
5060 GST_TIME_FORMAT " playing from sample %u to %u", str->track_id, k_index,
5061 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample);
5062 }
5063
5064 return GST_FLOW_OK;
5065
5066eos:
5067 return GST_FLOW_EOS;
5068}
5069
5070/*
5071 * Gets the current qt segment start, stop and position for the
5072 * given time offset. This is used in update_segment()
5073 */
5074static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005075gst_aml_qtdemux_stream_segment_get_boundaries (GstAmlQTDemux * qtdemux,
5076 AmlQtDemuxStream * stream, GstClockTime offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00005077 GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
5078{
5079 GstClockTime seg_time;
5080 GstClockTime start, stop, time;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005081 AmlQtDemuxSegment *segment;
zengliang.li5f31ef42024-05-16 08:27:38 +00005082
5083 segment = &stream->segments[stream->segment_index];
5084
5085 /* get time in this segment */
5086 seg_time = (offset - segment->time) * segment->rate;
5087
5088 GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
5089 GST_TIME_ARGS (seg_time));
5090
5091 if (G_UNLIKELY (seg_time > segment->duration)) {
5092 GST_LOG_OBJECT (stream->pad,
5093 "seg_time > segment->duration %" GST_TIME_FORMAT,
5094 GST_TIME_ARGS (segment->duration));
5095 seg_time = segment->duration;
5096 }
5097
5098 /* qtdemux->segment.stop is in outside-time-realm, whereas
5099 * segment->media_stop is in track-time-realm.
5100 *
5101 * In order to compare the two, we need to bring segment.stop
5102 * into the track-time-realm
5103 *
5104 * FIXME - does this comment still hold? Don't see any conversion here */
5105
5106 stop = qtdemux->segment.stop;
5107 if (stop == GST_CLOCK_TIME_NONE)
5108 stop = qtdemux->segment.duration;
5109 if (stop == GST_CLOCK_TIME_NONE)
5110 stop = segment->media_stop;
5111 else
5112 stop =
5113 MIN (segment->media_stop, stop - segment->time + segment->media_start);
5114
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005115 if (G_UNLIKELY (AML_QTSEGMENT_IS_EMPTY (segment))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005116 start = segment->time + seg_time;
5117 time = offset;
5118 stop = start - seg_time + segment->duration;
5119 } else if (qtdemux->segment.rate >= 0) {
5120 start = MIN (segment->media_start + seg_time, stop);
5121 time = offset;
5122 } else {
5123 if (segment->media_start >= qtdemux->segment.start) {
5124 time = segment->time;
5125 } else {
5126 time = segment->time + (qtdemux->segment.start - segment->media_start);
5127 }
5128
5129 start = MAX (segment->media_start, qtdemux->segment.start);
5130 stop = MIN (segment->media_start + seg_time, stop);
5131 }
5132
5133 *_start = start;
5134 *_stop = stop;
5135 *_time = time;
5136}
5137
5138/*
5139 * Updates the qt segment used for the stream and pushes a new segment event
5140 * downstream on this stream's pad.
5141 */
5142static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005143gst_aml_qtdemux_stream_update_segment (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005144 gint seg_idx, GstClockTime offset, GstClockTime * _start,
5145 GstClockTime * _stop)
5146{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005147 AmlQtDemuxSegment *segment;
zengliang.li5f31ef42024-05-16 08:27:38 +00005148 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE, time = 0;
5149 gdouble rate;
5150 GstEvent *event;
5151
5152 /* update the current segment */
5153 stream->segment_index = seg_idx;
5154
5155 /* get the segment */
5156 segment = &stream->segments[seg_idx];
5157
5158 if (G_UNLIKELY (offset < segment->time)) {
5159 GST_WARNING_OBJECT (stream->pad, "offset < segment->time %" GST_TIME_FORMAT,
5160 GST_TIME_ARGS (segment->time));
5161 return FALSE;
5162 }
5163
5164 /* segment lies beyond total indicated duration */
5165 if (G_UNLIKELY (qtdemux->segment.duration != GST_CLOCK_TIME_NONE &&
5166 segment->time > qtdemux->segment.duration)) {
5167 GST_WARNING_OBJECT (stream->pad, "file duration %" GST_TIME_FORMAT
5168 " < segment->time %" GST_TIME_FORMAT,
5169 GST_TIME_ARGS (qtdemux->segment.duration),
5170 GST_TIME_ARGS (segment->time));
5171 return FALSE;
5172 }
5173
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005174 gst_aml_qtdemux_stream_segment_get_boundaries (qtdemux, stream, offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00005175 &start, &stop, &time);
5176
5177 GST_DEBUG_OBJECT (stream->pad, "new segment %d from %" GST_TIME_FORMAT
5178 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
5179 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
5180
5181 /* combine global rate with that of the segment */
5182 rate = segment->rate * qtdemux->segment.rate;
5183
5184 /* Copy flags from main segment */
5185 stream->segment.flags = qtdemux->segment.flags;
5186
5187 /* update the segment values used for clipping */
5188 stream->segment.offset = qtdemux->segment.offset;
5189 stream->segment.base = qtdemux->segment.base + stream->accumulated_base;
5190 stream->segment.applied_rate = qtdemux->segment.applied_rate;
5191 stream->segment.rate = rate;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005192 stream->segment.start = start + AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005193 stream->cslg_shift);
5194 if (stop != -1)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005195 stream->segment.stop = stop + AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005196 stream->cslg_shift);
5197 else
5198 stream->segment.stop = stop;
5199 stream->segment.time = time;
5200 stream->segment.position = stream->segment.start;
5201
5202 GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
5203 &stream->segment);
5204
5205 /* now prepare and send the segment */
5206 if (stream->pad) {
5207 event = gst_event_new_segment (&stream->segment);
5208 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
5209 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
5210 }
5211 gst_pad_push_event (stream->pad, event);
5212 /* assume we can send more data now */
5213 GST_PAD_LAST_FLOW_RETURN (stream->pad) = GST_FLOW_OK;
5214 /* clear to send tags on this pad now */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005215 gst_aml_qtdemux_push_tags (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00005216 }
5217
5218 if (_start)
5219 *_start = start;
5220 if (_stop)
5221 *_stop = stop;
5222
5223 return TRUE;
5224}
5225
5226/* activate the given segment number @seg_idx of @stream at time @offset.
5227 * @offset is an absolute global position over all the segments.
5228 *
5229 * This will push out a NEWSEGMENT event with the right values and
5230 * position the stream index to the first decodable sample before
5231 * @offset.
5232 */
5233static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005234gst_aml_qtdemux_activate_segment (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005235 guint32 seg_idx, GstClockTime offset)
5236{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005237 AmlQtDemuxSegment *segment;
zengliang.li5f31ef42024-05-16 08:27:38 +00005238 guint32 index, kf_index;
5239 GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;
5240
5241 GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
5242 seg_idx, GST_TIME_ARGS (offset));
5243
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005244 if (!gst_aml_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00005245 &start, &stop))
5246 return FALSE;
5247
5248 segment = &stream->segments[stream->segment_index];
5249
5250 /* in the fragmented case, we pick a fragment that starts before our
5251 * desired position and rely on downstream to wait for a keyframe
5252 * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
5253 * tfra entries tells us which trun/sample the key unit is in, but we don't
5254 * make use of this additional information at the moment) */
5255 if (qtdemux->fragmented && !qtdemux->fragmented_seek_pending) {
5256 stream->to_sample = G_MAXUINT32;
5257 return TRUE;
5258 } else {
5259 /* well, it will be taken care of below */
5260 qtdemux->fragmented_seek_pending = FALSE;
5261 /* FIXME ideally the do_fragmented_seek can be done right here,
5262 * rather than at loop level
5263 * (which might even allow handling edit lists in a fragmented file) */
5264 }
5265
5266 /* We don't need to look for a sample in push-based */
5267 if (!qtdemux->pullbased)
5268 return TRUE;
5269
5270 /* and move to the keyframe before the indicated media time of the
5271 * segment */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005272 if (G_LIKELY (!AML_QTSEGMENT_IS_EMPTY (segment))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005273 if (qtdemux->segment.rate >= 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005274 index = gst_aml_qtdemux_find_index_linear (qtdemux, stream, start);
zengliang.li5f31ef42024-05-16 08:27:38 +00005275 stream->to_sample = G_MAXUINT32;
5276 GST_DEBUG_OBJECT (stream->pad,
5277 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5278 GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005279 GST_TIME_ARGS (AML_QTSAMPLE_PTS (stream, &stream->samples[index])));
zengliang.li5f31ef42024-05-16 08:27:38 +00005280 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005281 index = gst_aml_qtdemux_find_index_linear (qtdemux, stream, stop);
zengliang.li5f31ef42024-05-16 08:27:38 +00005282 stream->to_sample = index;
5283 GST_DEBUG_OBJECT (stream->pad,
5284 "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
5285 GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005286 GST_TIME_ARGS (AML_QTSAMPLE_PTS (stream, &stream->samples[index])));
zengliang.li5f31ef42024-05-16 08:27:38 +00005287 }
5288 } else {
5289 GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
5290 "this is an empty segment");
5291 return TRUE;
5292 }
5293
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005294 /* gst_aml_qtdemux_parse_sample () called from gst_aml_qtdemux_find_index_linear ()
zengliang.li5f31ef42024-05-16 08:27:38 +00005295 * encountered an error and printed a message so we return appropriately */
5296 if (index == -1)
5297 return FALSE;
5298
5299 /* we're at the right spot */
5300 if (index == stream->sample_index) {
5301 GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
5302 return TRUE;
5303 }
5304
5305 /* find keyframe of the target index */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005306 kf_index = gst_aml_qtdemux_find_keyframe (qtdemux, stream, index, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00005307
5308 /* go back two frames to provide lead-in for non-raw audio decoders */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005309 if (stream->subtype == AML_FOURCC_soun && !stream->need_clip) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005310 guint32 lead_in = 2;
5311 guint32 old_index = kf_index;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005312 GstStructure *s = gst_caps_get_structure (AML_CUR_STREAM (stream)->caps, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00005313
5314 if (gst_structure_has_name (s, "audio/mpeg")) {
5315 gint mpegversion;
5316 if (gst_structure_get_int (s, "mpegversion", &mpegversion)
5317 && mpegversion == 1) {
5318 /* mp3 could need up to 30 frames of lead-in per mpegaudioparse */
5319 lead_in = 30;
5320 }
5321 }
5322
5323 kf_index = MAX (kf_index, lead_in) - lead_in;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005324 if (aml_qtdemux_parse_samples (qtdemux, stream, kf_index)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005325 GST_DEBUG_OBJECT (stream->pad,
5326 "Moving backwards %u frames to ensure sufficient sound lead-in",
5327 old_index - kf_index);
5328 } else {
5329 kf_index = old_index;
5330 }
5331 }
5332
5333 /* if we move forwards, we don't have to go back to the previous
5334 * keyframe since we already sent that. We can also just jump to
5335 * the keyframe right before the target index if there is one. */
5336 if (index > stream->sample_index) {
5337 /* moving forwards check if we move past a keyframe */
5338 if (kf_index > stream->sample_index) {
5339 GST_DEBUG_OBJECT (stream->pad,
5340 "moving forwards to keyframe at %u "
5341 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
5342 kf_index,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005343 GST_TIME_ARGS (AML_QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5344 GST_TIME_ARGS (AML_QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5345 gst_aml_qtdemux_move_stream (qtdemux, stream, kf_index);
zengliang.li5f31ef42024-05-16 08:27:38 +00005346 } else {
5347 GST_DEBUG_OBJECT (stream->pad,
5348 "moving forwards, keyframe at %u "
5349 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " ) already sent",
5350 kf_index,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005351 GST_TIME_ARGS (AML_QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5352 GST_TIME_ARGS (AML_QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
zengliang.li5f31ef42024-05-16 08:27:38 +00005353 }
5354 } else {
5355 GST_DEBUG_OBJECT (stream->pad,
5356 "moving backwards to %sframe at %u "
5357 "(pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " )",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005358 (stream->subtype == AML_FOURCC_soun) ? "audio " : "key", kf_index,
5359 GST_TIME_ARGS (AML_QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
5360 GST_TIME_ARGS (AML_QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
5361 gst_aml_qtdemux_move_stream (qtdemux, stream, kf_index);
zengliang.li5f31ef42024-05-16 08:27:38 +00005362 }
5363
5364 return TRUE;
5365}
5366
xuesong.jianga8fbc442024-09-26 20:31:31 +08005367static gint32
5368gst_aml_qtdemux_find_keyframe_index (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream, GstClockTime start)
5369{
5370 gint32 index = -1;
5371 gint32 kf_index = -1;
5372 GstClockTime ts = GST_CLOCK_TIME_NONE;
5373 GstClockTime kf_ts = GST_CLOCK_TIME_NONE;
5374 AmlQtDemuxSample* sample = NULL;
5375 AmlQtDemuxSample* kf_sample = NULL;
5376 guint32 ret = -1;
5377 AmlQtDemuxSegment *segment = NULL;
5378
5379 GST_LOG_OBJECT (stream->pad, "find pre key frame in time %" GST_TIME_FORMAT " n_segments:%d", GST_TIME_ARGS (start), stream->n_segments);
5380
5381 //TODO: how to deal with multi-qt-segments
5382 for (gint i = 0; i < stream->n_segments; i++)
5383 {
5384 if (stream->segments[i].stop_time > start)
5385 {
5386 segment = &stream->segments[i];
5387 break;
5388 }
5389 }
5390
5391 if (G_UNLIKELY (AML_QTSEGMENT_IS_EMPTY (segment)))
5392 {
5393 GST_DEBUG_OBJECT (stream->pad, "empty segment");
5394 goto done;
5395 }
5396
5397 if ((index = gst_aml_qtdemux_find_index_linear (qtdemux, stream, start)) == -1)
5398 {
5399 GST_DEBUG_OBJECT (stream->pad, "invalid index");
5400 goto done;
5401 }
5402
5403 sample = &stream->samples[index];
5404 ts = AML_QTSAMPLE_DTS (stream, sample);
5405 GST_DEBUG_OBJECT (stream->pad, "found frame index %d in %d with dts %" GST_TIME_FORMAT " with pts %" GST_TIME_FORMAT,
5406 index, stream->n_samples, GST_TIME_ARGS(ts), GST_TIME_ARGS(AML_QTSAMPLE_PTS (stream, sample)));
5407
5408 if (qtdemux->smooth_switch_enable)
5409 {
5410 gint32 next_kf_index = -1;
5411 AmlQtDemuxSample* next_kf_sample = NULL;
5412 GstClockTime next_kf_pts = -1;
5413 GstClockTime next_kf_dts = -1;
5414
5415 if ((next_kf_index = gst_aml_qtdemux_find_keyframe (qtdemux, stream, index, TRUE)) != -1)
5416 {
5417 next_kf_sample = &stream->samples[next_kf_index];
5418 next_kf_dts = AML_QTSAMPLE_DTS (stream, next_kf_sample);
5419 next_kf_pts = AML_QTSAMPLE_PTS (stream, next_kf_sample);
5420 GST_DEBUG_OBJECT (stream->pad, "found next keyframe %d in %d with dts %" GST_TIME_FORMAT " with pts %" GST_TIME_FORMAT,
5421 next_kf_index, stream->n_samples, GST_TIME_ARGS(next_kf_dts), GST_TIME_ARGS(next_kf_pts));
5422 if (next_kf_dts < start || (next_kf_dts - start) < AML_QTDEMUX_SWITCH_GAP)
5423 {
5424 ret = next_kf_index;
5425 goto done;
5426 }
5427 }
5428 }
5429
5430 if ((kf_index = gst_aml_qtdemux_find_keyframe (qtdemux, stream, index, FALSE)) == -1)
5431 {
5432 GST_DEBUG_OBJECT (stream->pad, "invalid key index");
5433 goto done;
5434 }
5435 kf_sample = &stream->samples[kf_index];
5436 kf_ts = AML_QTSAMPLE_DTS (stream, kf_sample);
5437 GST_DEBUG_OBJECT (stream->pad, "found key frame index %d in %d with dts %" GST_TIME_FORMAT, kf_index, stream->n_samples, GST_TIME_ARGS(kf_ts));
5438
5439 ret = kf_index;
5440
5441done:
5442 return ret;
5443}
5444
zengliang.li5f31ef42024-05-16 08:27:38 +00005445/* prepare to get the current sample of @stream, getting essential values.
5446 *
5447 * This function will also prepare and send the segment when needed.
5448 *
5449 * Return FALSE if the stream is EOS.
5450 *
5451 * PULL-BASED
5452 */
5453static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005454gst_aml_qtdemux_prepare_current_sample (GstAmlQTDemux * qtdemux,
5455 AmlQtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
zengliang.li5f31ef42024-05-16 08:27:38 +00005456 GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
5457 gboolean * keyframe)
5458{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005459 AmlQtDemuxSample *sample;
zengliang.li5f31ef42024-05-16 08:27:38 +00005460 GstClockTime time_position;
5461 guint32 seg_idx;
5462
5463 g_return_val_if_fail (stream != NULL, FALSE);
5464
5465 time_position = stream->time_position;
5466 if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
5467 goto eos;
5468
5469 seg_idx = stream->segment_index;
5470 if (G_UNLIKELY (seg_idx == -1)) {
5471 /* find segment corresponding to time_position if we are looking
5472 * for a segment. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005473 seg_idx = gst_aml_qtdemux_find_segment (qtdemux, stream, time_position);
zengliang.li5f31ef42024-05-16 08:27:38 +00005474 }
5475
5476 /* different segment, activate it, sample_index will be set. */
5477 if (G_UNLIKELY (stream->segment_index != seg_idx))
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005478 gst_aml_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
zengliang.li5f31ef42024-05-16 08:27:38 +00005479
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005480 if (G_UNLIKELY (AML_QTSEGMENT_IS_EMPTY (&stream->
zengliang.li5f31ef42024-05-16 08:27:38 +00005481 segments[stream->segment_index]))) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005482 AmlQtDemuxSegment *seg = &stream->segments[stream->segment_index];
zengliang.li5f31ef42024-05-16 08:27:38 +00005483
5484 GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
5485 " prepare empty sample");
5486
5487 *empty = TRUE;
5488 *pts = *dts = time_position;
5489 *duration = seg->duration - (time_position - seg->time);
5490
5491 return TRUE;
5492 }
5493
5494 *empty = FALSE;
5495
5496 if (stream->sample_index == -1)
5497 stream->sample_index = 0;
5498
5499 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
5500 stream->sample_index, stream->n_samples);
5501
5502 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
5503 if (!qtdemux->fragmented)
5504 goto eos;
5505
5506 GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
5507 do {
5508 GstFlowReturn flow;
5509
5510 GST_OBJECT_LOCK (qtdemux);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005511 flow = aml_qtdemux_add_fragmented_samples (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00005512 GST_OBJECT_UNLOCK (qtdemux);
5513
5514 if (flow != GST_FLOW_OK)
5515 goto eos;
5516 }
5517 while (stream->sample_index >= stream->n_samples);
5518 }
5519
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005520 if (!aml_qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005521 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5522 stream->sample_index);
5523 return FALSE;
5524 }
5525
5526 /* now get the info for the sample we're at */
5527 sample = &stream->samples[stream->sample_index];
5528
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005529 *dts = AML_QTSAMPLE_DTS (stream, sample);
5530 *pts = AML_QTSAMPLE_PTS (stream, sample);
zengliang.li5f31ef42024-05-16 08:27:38 +00005531 *offset = sample->offset;
5532 *size = sample->size;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005533 *duration = AML_QTSAMPLE_DUR_DTS (stream, sample, *dts);
5534 *keyframe = AML_QTSAMPLE_KEYFRAME (stream, sample);
zengliang.li5f31ef42024-05-16 08:27:38 +00005535
5536 return TRUE;
5537
5538 /* special cases */
5539eos:
5540 {
5541 stream->time_position = GST_CLOCK_TIME_NONE;
5542 return FALSE;
5543 }
5544}
5545
5546/* move to the next sample in @stream.
5547 *
5548 * Moves to the next segment when needed.
5549 */
5550static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005551gst_aml_qtdemux_advance_sample (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00005552{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005553 AmlQtDemuxSample *sample;
5554 AmlQtDemuxSegment *segment;
zengliang.li5f31ef42024-05-16 08:27:38 +00005555
5556 /* get current segment */
5557 segment = &stream->segments[stream->segment_index];
5558
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005559 if (G_UNLIKELY (AML_QTSEGMENT_IS_EMPTY (segment))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005560 GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
5561 goto next_segment;
5562 }
5563
5564 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
5565 /* Mark the stream as EOS */
5566 GST_DEBUG_OBJECT (qtdemux,
5567 "reached max allowed sample %u, mark EOS", stream->to_sample);
5568 stream->time_position = GST_CLOCK_TIME_NONE;
5569 return;
5570 }
5571
5572 /* move to next sample */
5573 stream->sample_index++;
5574 stream->offset_in_sample = 0;
5575
5576 GST_TRACE_OBJECT (qtdemux, "advance to sample %u/%u", stream->sample_index,
5577 stream->n_samples);
5578
5579 /* reached the last sample, we need the next segment */
5580 if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
5581 goto next_segment;
5582
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005583 if (!aml_qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005584 GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
5585 stream->sample_index);
5586 return;
5587 }
5588
5589 /* get next sample */
5590 sample = &stream->samples[stream->sample_index];
5591
5592 GST_TRACE_OBJECT (qtdemux, "sample dts %" GST_TIME_FORMAT " media_stop: %"
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005593 GST_TIME_FORMAT, GST_TIME_ARGS (AML_QTSAMPLE_DTS (stream, sample)),
zengliang.li5f31ef42024-05-16 08:27:38 +00005594 GST_TIME_ARGS (segment->media_stop));
5595
5596 /* see if we are past the segment */
bo.xiao19868222024-06-06 16:12:40 +08005597 if (GST_CLOCK_TIME_NONE != AML_QTSAMPLE_PTS (stream, sample) && G_UNLIKELY (AML_QTSAMPLE_PTS (stream, sample) >= segment->media_stop))
zengliang.li5f31ef42024-05-16 08:27:38 +00005598 goto next_segment;
5599
bo.xiao19868222024-06-06 16:12:40 +08005600 if (GST_CLOCK_TIME_NONE != AML_QTSAMPLE_PTS (stream, sample) && AML_QTSAMPLE_PTS (stream, sample) >= segment->media_start) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005601 /* inside the segment, update time_position, looks very familiar to
5602 * GStreamer segments, doesn't it? */
5603 stream->time_position =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005604 AML_QTSAMPLE_PTS (stream, sample) - segment->media_start + segment->time;
zengliang.li5f31ef42024-05-16 08:27:38 +00005605 } else {
5606 /* not yet in segment, time does not yet increment. This means
5607 * that we are still prerolling keyframes to the decoder so it can
5608 * decode the first sample of the segment. */
5609 stream->time_position = segment->time;
5610 }
5611 return;
5612
5613 /* move to the next segment */
5614next_segment:
5615 {
5616 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);
5617
5618 if (stream->segment_index == stream->n_segments - 1) {
5619 /* are we at the end of the last segment, we're EOS */
5620 stream->time_position = GST_CLOCK_TIME_NONE;
5621 } else {
5622 /* else we're only at the end of the current segment */
5623 stream->time_position = segment->stop_time;
5624 }
5625 /* make sure we select a new segment */
5626
5627 /* accumulate previous segments */
5628 if (GST_CLOCK_TIME_IS_VALID (stream->segment.stop))
5629 stream->accumulated_base +=
5630 (stream->segment.stop -
5631 stream->segment.start) / ABS (stream->segment.rate);
5632
5633 stream->segment_index = -1;
5634 }
5635}
5636
5637static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005638gst_aml_qtdemux_sync_streams (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00005639{
5640 gint i;
5641
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005642 if (AML_QTDEMUX_N_STREAMS (demux) <= 1)
zengliang.li5f31ef42024-05-16 08:27:38 +00005643 return;
5644
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005645 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
5646 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00005647 GstClockTime end_time;
5648
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005649 stream = AML_QTDEMUX_NTH_STREAM (demux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00005650
5651 if (!stream->pad)
5652 continue;
5653
5654 /* TODO advance time on subtitle streams here, if any some day */
5655
5656 /* some clips/trailers may have unbalanced streams at the end,
5657 * so send EOS on shorter stream to prevent stalling others */
5658
5659 /* do not mess with EOS if SEGMENT seeking */
5660 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
5661 continue;
5662
5663 if (demux->pullbased) {
5664 /* loop mode is sample time based */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005665 if (!AML_STREAM_IS_EOS (stream))
zengliang.li5f31ef42024-05-16 08:27:38 +00005666 continue;
5667 } else {
5668 /* push mode is byte position based */
5669 if (stream->n_samples &&
5670 stream->samples[stream->n_samples - 1].offset >= demux->offset)
5671 continue;
5672 }
5673
5674 if (stream->sent_eos)
5675 continue;
5676
5677 /* only act if some gap */
5678 end_time = stream->segments[stream->n_segments - 1].stop_time;
5679 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
5680 ", stream end: %" GST_TIME_FORMAT,
5681 GST_TIME_ARGS (demux->segment.position), GST_TIME_ARGS (end_time));
5682 if (GST_CLOCK_TIME_IS_VALID (end_time)
5683 && (end_time + 2 * GST_SECOND < demux->segment.position)) {
5684 GstEvent *event;
5685
5686 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
5687 GST_PAD_NAME (stream->pad));
5688 stream->sent_eos = TRUE;
5689 event = gst_event_new_eos ();
5690 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
5691 gst_event_set_seqnum (event, demux->segment_seqnum);
5692 gst_pad_push_event (stream->pad, event);
5693 }
5694 }
5695}
5696
5697/* EOS and NOT_LINKED need to be combined. This means that we return:
5698 *
5699 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
5700 * GST_FLOW_EOS: when all pads EOS or NOT_LINKED.
5701 */
5702static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005703gst_aml_qtdemux_combine_flows (GstAmlQTDemux * demux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005704 GstFlowReturn ret)
5705{
5706 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret));
5707
5708 if (stream->pad)
5709 ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
5710 ret);
5711 else
5712 ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
5713
5714 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
5715 return ret;
5716}
5717
5718/* the input buffer metadata must be writable. Returns NULL when the buffer is
5719 * completely clipped
5720 *
5721 * Should be used only with raw buffers */
5722static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005723gst_aml_qtdemux_clip_buffer (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00005724 GstBuffer * buf)
5725{
5726 guint64 start, stop, cstart, cstop, diff;
5727 GstClockTime pts, duration;
5728 gsize size, osize;
5729 gint num_rate, denom_rate;
5730 gint frame_size;
5731 gboolean clip_data;
5732 guint offset;
5733
5734 osize = size = gst_buffer_get_size (buf);
5735 offset = 0;
5736
5737 /* depending on the type, setup the clip parameters */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005738 if (stream->subtype == AML_FOURCC_soun) {
5739 frame_size = AML_CUR_STREAM (stream)->bytes_per_frame;
zengliang.li5f31ef42024-05-16 08:27:38 +00005740 num_rate = GST_SECOND;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005741 denom_rate = (gint) AML_CUR_STREAM (stream)->rate;
zengliang.li5f31ef42024-05-16 08:27:38 +00005742 clip_data = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005743 } else if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005744 frame_size = size;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005745 num_rate = AML_CUR_STREAM (stream)->fps_n;
5746 denom_rate = AML_CUR_STREAM (stream)->fps_d;
zengliang.li5f31ef42024-05-16 08:27:38 +00005747 clip_data = FALSE;
5748 } else
5749 goto wrong_type;
5750
5751 if (frame_size <= 0)
5752 goto bad_frame_size;
5753
5754 /* we can only clip if we have a valid pts */
5755 pts = GST_BUFFER_PTS (buf);
5756 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
5757 goto no_pts;
5758
5759 duration = GST_BUFFER_DURATION (buf);
5760
5761 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
5762 duration =
5763 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
5764 }
5765
5766 start = pts;
5767 stop = start + duration;
5768
5769 if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
5770 GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
5771 goto clipped;
5772
5773 /* see if some clipping happened */
5774 diff = cstart - start;
5775 if (diff > 0) {
5776 pts += diff;
5777 duration -= diff;
5778
5779 if (clip_data) {
5780 /* bring clipped time to samples and to bytes */
5781 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5782 diff *= frame_size;
5783
5784 GST_DEBUG_OBJECT (qtdemux,
5785 "clipping start to %" GST_TIME_FORMAT " %"
5786 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
5787
5788 offset = diff;
5789 size -= diff;
5790 }
5791 }
5792 diff = stop - cstop;
5793 if (diff > 0) {
5794 duration -= diff;
5795
5796 if (clip_data) {
5797 /* bring clipped time to samples and then to bytes */
5798 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
5799 diff *= frame_size;
5800 GST_DEBUG_OBJECT (qtdemux,
5801 "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
5802 " bytes", GST_TIME_ARGS (cstop), diff);
5803 size -= diff;
5804 }
5805 }
5806
5807 if (offset != 0 || size != osize)
5808 gst_buffer_resize (buf, offset, size);
5809
5810 GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
5811 GST_BUFFER_PTS (buf) = pts;
5812 GST_BUFFER_DURATION (buf) = duration;
5813
5814 return buf;
5815
5816 /* dropped buffer */
5817wrong_type:
5818 {
5819 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
5820 return buf;
5821 }
5822bad_frame_size:
5823 {
5824 GST_DEBUG_OBJECT (qtdemux, "bad frame size");
5825 return buf;
5826 }
5827no_pts:
5828 {
5829 GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
5830 return buf;
5831 }
5832clipped:
5833 {
5834 GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
5835 gst_buffer_unref (buf);
5836 return NULL;
5837 }
5838}
5839
5840static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005841gst_aml_qtdemux_align_buffer (GstAmlQTDemux * demux,
zengliang.li5f31ef42024-05-16 08:27:38 +00005842 GstBuffer * buffer, gsize alignment)
5843{
5844 GstMapInfo map;
5845
5846 gst_buffer_map (buffer, &map, GST_MAP_READ);
5847
5848 if (map.size < sizeof (guintptr)) {
5849 gst_buffer_unmap (buffer, &map);
5850 return buffer;
5851 }
5852
5853 if (((guintptr) map.data) & (alignment - 1)) {
5854 GstBuffer *new_buffer;
5855 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5856
5857 new_buffer = gst_buffer_new_allocate (NULL,
5858 gst_buffer_get_size (buffer), &params);
5859
5860 /* Copy data "by hand", so ensure alignment is kept: */
5861 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5862
5863 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5864 GST_DEBUG_OBJECT (demux,
5865 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5866 alignment);
5867
5868 gst_buffer_unmap (buffer, &map);
5869 gst_buffer_unref (buffer);
5870
5871 return new_buffer;
5872 }
5873
5874 gst_buffer_unmap (buffer, &map);
5875 return buffer;
5876}
5877
5878static guint8 *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005879aml_convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
zengliang.li5f31ef42024-05-16 08:27:38 +00005880 gsize * res)
5881{
5882 guint8 *storage;
5883 gsize i;
5884
5885 /* We are converting from pairs to triplets */
5886 *res = ccpair_size / 2 * 3;
5887 storage = g_malloc (*res);
5888 for (i = 0; i * 2 < ccpair_size; i += 1) {
5889 /* FIXME: Use line offset 0 as we simply can't know here */
5890 if (field == 1)
5891 storage[i * 3] = 0x80 | 0x00;
5892 else
5893 storage[i * 3] = 0x00 | 0x00;
5894 storage[i * 3 + 1] = ccpair[i * 2];
5895 storage[i * 3 + 2] = ccpair[i * 2 + 1];
5896 }
5897
5898 return storage;
5899}
5900
5901static guint8 *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005902aml_extract_cc_from_data (AmlQtDemuxStream * stream, const guint8 * data, gsize size,
zengliang.li5f31ef42024-05-16 08:27:38 +00005903 gsize * cclen)
5904{
5905 guint8 *res = NULL;
5906 guint32 atom_length, fourcc;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005907 AmlQtDemuxStreamStsdEntry *stsd_entry;
zengliang.li5f31ef42024-05-16 08:27:38 +00005908
5909 GST_MEMDUMP ("caption atom", data, size);
5910
5911 /* There might be multiple atoms */
5912
5913 *cclen = 0;
5914 if (size < 8)
5915 goto invalid_cdat;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005916 atom_length = AML_QT_UINT32 (data);
5917 fourcc = AML_QT_FOURCC (data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00005918 if (G_UNLIKELY (atom_length > size || atom_length == 8))
5919 goto invalid_cdat;
5920
5921 GST_DEBUG_OBJECT (stream->pad, "here");
5922
5923 /* Check if we have something compatible */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005924 stsd_entry = AML_CUR_STREAM (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00005925 switch (stsd_entry->fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005926 case AML_FOURCC_c608:{
zengliang.li5f31ef42024-05-16 08:27:38 +00005927 guint8 *cdat = NULL, *cdt2 = NULL;
5928 gsize cdat_size = 0, cdt2_size = 0;
5929 /* Should be cdat or cdt2 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005930 if (fourcc != AML_FOURCC_cdat && fourcc != AML_FOURCC_cdt2) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005931 GST_WARNING_OBJECT (stream->pad,
5932 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
5933 GST_FOURCC_ARGS (fourcc));
5934 goto invalid_cdat;
5935 }
5936
5937 /* Convert to S334-1 Annex A byte triplet */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005938 if (fourcc == AML_FOURCC_cdat)
5939 cdat = aml_convert_to_s334_1a (data + 8, atom_length - 8, 1, &cdat_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00005940 else
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005941 cdt2 = aml_convert_to_s334_1a (data + 8, atom_length - 8, 2, &cdt2_size);
zengliang.li5f31ef42024-05-16 08:27:38 +00005942 GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
5943 size, atom_length);
5944
5945 /* Check for another atom ? */
5946 if (size > atom_length + 8) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005947 guint32 new_atom_length = AML_QT_UINT32 (data + atom_length);
zengliang.li5f31ef42024-05-16 08:27:38 +00005948 if (size >= atom_length + new_atom_length) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005949 fourcc = AML_QT_FOURCC (data + atom_length + 4);
5950 if (fourcc == AML_FOURCC_cdat) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005951 if (cdat == NULL)
5952 cdat =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005953 aml_convert_to_s334_1a (data + atom_length + 8,
zengliang.li5f31ef42024-05-16 08:27:38 +00005954 new_atom_length - 8, 1, &cdat_size);
5955 else
5956 GST_WARNING_OBJECT (stream->pad,
5957 "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5958 } else {
5959 if (cdt2 == NULL)
5960 cdt2 =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005961 aml_convert_to_s334_1a (data + atom_length + 8,
zengliang.li5f31ef42024-05-16 08:27:38 +00005962 new_atom_length - 8, 2, &cdt2_size);
5963 else
5964 GST_WARNING_OBJECT (stream->pad,
5965 "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
5966 }
5967 }
5968 }
5969
5970 *cclen = cdat_size + cdt2_size;
5971 res = g_malloc (*cclen);
5972 if (cdat_size)
5973 memcpy (res, cdat, cdat_size);
5974 if (cdt2_size)
5975 memcpy (res + cdat_size, cdt2, cdt2_size);
5976 g_free (cdat);
5977 g_free (cdt2);
5978 }
5979 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00005980 case AML_FOURCC_c708:
5981 if (fourcc != AML_FOURCC_ccdp) {
zengliang.li5f31ef42024-05-16 08:27:38 +00005982 GST_WARNING_OBJECT (stream->pad,
5983 "Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
5984 GST_FOURCC_ARGS (fourcc));
5985 goto invalid_cdat;
5986 }
5987 *cclen = atom_length - 8;
5988 res = g_memdup2 (data + 8, *cclen);
5989 break;
5990 default:
5991 /* Keep this here in case other closed caption formats are added */
5992 g_assert_not_reached ();
5993 break;
5994 }
5995
5996 GST_MEMDUMP ("Output", res, *cclen);
5997 return res;
5998
5999 /* Errors */
6000invalid_cdat:
6001 GST_WARNING ("[cdat] atom is too small or invalid");
6002 return NULL;
6003}
6004
6005/* Handle Closed Caption sample buffers.
6006 * The input buffer metadata must be writable,
6007 * but time/duration etc not yet set and need not be preserved */
6008static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006009gst_aml_qtdemux_process_buffer_clcp (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006010 GstBuffer * buf)
6011{
6012 GstBuffer *outbuf = NULL;
6013 GstMapInfo map;
6014 guint8 *cc;
6015 gsize cclen = 0;
6016
6017 gst_buffer_map (buf, &map, GST_MAP_READ);
6018
6019 /* empty buffer is sent to terminate previous subtitle */
6020 if (map.size <= 2) {
6021 gst_buffer_unmap (buf, &map);
6022 gst_buffer_unref (buf);
6023 return NULL;
6024 }
6025
6026 /* For closed caption, we need to extract the information from the
6027 * [cdat],[cdt2] or [ccdp] atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006028 cc = aml_extract_cc_from_data (stream, map.data, map.size, &cclen);
zengliang.li5f31ef42024-05-16 08:27:38 +00006029 gst_buffer_unmap (buf, &map);
6030 if (cc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006031 outbuf = _gst_aml_buffer_new_wrapped (cc, cclen, g_free);
zengliang.li5f31ef42024-05-16 08:27:38 +00006032 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6033 } else {
6034 /* Conversion failed or there's nothing */
6035 }
6036 gst_buffer_unref (buf);
6037
6038 return outbuf;
6039}
6040
6041/* DVD subpicture specific sample handling.
6042 * the input buffer metadata must be writable,
6043 * but time/duration etc not yet set and need not be preserved */
6044static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006045gst_aml_qtdemux_process_buffer_dvd (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006046 GstBuffer * buf)
6047{
6048 /* send a one time dvd clut event */
6049 if (stream->pending_event && stream->pad)
6050 gst_pad_push_event (stream->pad, stream->pending_event);
6051 stream->pending_event = NULL;
6052
6053 /* empty buffer is sent to terminate previous subtitle */
6054 if (gst_buffer_get_size (buf) <= 2) {
6055 gst_buffer_unref (buf);
6056 return NULL;
6057 }
6058
6059 /* That's all the processing needed for subpictures */
6060 return buf;
6061}
6062
6063/* Timed text formats
6064 * the input buffer metadata must be writable,
6065 * but time/duration etc not yet set and need not be preserved */
6066static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006067gst_aml_qtdemux_process_buffer_text (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006068 GstBuffer * buf)
6069{
6070 GstBuffer *outbuf = NULL;
6071 GstMapInfo map;
6072 guint nsize = 0;
6073 gchar *str;
6074
6075 /* not many cases for now */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006076 if (G_UNLIKELY (stream->subtype != AML_FOURCC_text &&
6077 stream->subtype != AML_FOURCC_sbtl)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006078 return buf;
6079 }
6080
6081 gst_buffer_map (buf, &map, GST_MAP_READ);
6082
6083 /* empty buffer is sent to terminate previous subtitle */
6084 if (map.size <= 2) {
6085 gst_buffer_unmap (buf, &map);
6086 gst_buffer_unref (buf);
6087 return NULL;
6088 }
6089
6090 nsize = GST_READ_UINT16_BE (map.data);
6091 nsize = MIN (nsize, map.size - 2);
6092
6093 GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
6094 nsize, map.size);
6095
6096 /* takes care of UTF-8 validation or UTF-16 recognition,
6097 * no other encoding expected */
6098 str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
6099 gst_buffer_unmap (buf, &map);
6100
6101 if (str) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006102 outbuf = _gst_aml_buffer_new_wrapped (str, strlen (str), g_free);
zengliang.li5f31ef42024-05-16 08:27:38 +00006103 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6104 } else {
6105 /* this should not really happen unless the subtitle is corrupted */
6106 }
6107 gst_buffer_unref (buf);
6108
6109 /* FIXME ? convert optional subsequent style info to markup */
6110
6111 return outbuf;
6112}
6113
6114/* WebVTT sample handling according to 14496-30 */
6115static GstBuffer *
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006116gst_aml_qtdemux_process_buffer_wvtt (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006117 GstBuffer * buf)
6118{
6119 GstBuffer *outbuf = NULL;
6120 GstMapInfo map;
6121
6122 if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
6123 g_assert_not_reached (); /* The buffer must be mappable */
6124 }
6125
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006126 if (aml_qtdemux_webvtt_is_empty (qtdemux, map.data, map.size)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006127 GstEvent *gap = NULL;
6128 /* Push a gap event */
6129 stream->segment.position = GST_BUFFER_PTS (buf);
6130 gap =
6131 gst_event_new_gap (stream->segment.position, GST_BUFFER_DURATION (buf));
6132 gst_pad_push_event (stream->pad, gap);
6133
6134 if (GST_BUFFER_DURATION_IS_VALID (buf))
6135 stream->segment.position += GST_BUFFER_DURATION (buf);
6136 } else {
6137 outbuf =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006138 aml_qtdemux_webvtt_decode (qtdemux, GST_BUFFER_PTS (buf),
zengliang.li5f31ef42024-05-16 08:27:38 +00006139 GST_BUFFER_DURATION (buf), map.data, map.size);
6140 gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
6141 }
6142
6143 gst_buffer_unmap (buf, &map);
6144 gst_buffer_unref (buf);
6145
6146 return outbuf;
6147}
6148
6149static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006150gst_aml_qtdemux_push_buffer (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006151 GstBuffer * buf)
6152{
6153 GstFlowReturn ret = GST_FLOW_OK;
6154 GstClockTime pts, duration;
6155
xuesong.jianga8fbc442024-09-26 20:31:31 +08006156 if (G_UNLIKELY (GST_CLOCK_TIME_NONE == qtdemux->pre_keyframe_pts || GST_CLOCK_TIME_NONE == qtdemux->pre_keyframe_dts))
6157 {
6158 gint32 kf_index = -1;
6159
6160 kf_index = gst_aml_qtdemux_find_keyframe_index(qtdemux, stream, qtdemux->segment.start);
6161 if (kf_index != -1)
6162 {
6163 qtdemux->pre_keyframe_pts = AML_QTSAMPLE_PTS (stream, &stream->samples[kf_index]);
6164 qtdemux->pre_keyframe_dts = AML_QTSAMPLE_DTS (stream, &stream->samples[kf_index]);
6165 GST_DEBUG_OBJECT (stream->pad, "updated prev keyframe dts %" GST_TIME_FORMAT " pts %" GST_TIME_FORMAT,
6166 GST_TIME_ARGS(qtdemux->pre_keyframe_dts), GST_TIME_ARGS(qtdemux->pre_keyframe_pts));
6167 }
6168 }
6169
zengliang.li5f31ef42024-05-16 08:27:38 +00006170 if (stream->need_clip)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006171 buf = gst_aml_qtdemux_clip_buffer (qtdemux, stream, buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00006172
6173 if (G_UNLIKELY (buf == NULL))
6174 goto exit;
6175
6176 if (G_UNLIKELY (stream->discont)) {
6177 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6178 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
6179 stream->discont = FALSE;
6180 } else {
6181 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6182 }
6183
zengliang.lid0c84c32024-05-17 03:33:20 +00006184 if (qtdemux->discontinuity_base_pos != 0 && GST_BUFFER_PTS (buf) < qtdemux->discontinuity_base_pos)
6185 {
6186 GST_BUFFER_PTS (buf) = GST_BUFFER_PTS (buf) + qtdemux->discontinuity_base_pos;
6187 GST_BUFFER_DTS (buf) = GST_BUFFER_DTS (buf) + qtdemux->discontinuity_base_pos;
6188 }
6189
zengliang.li5f31ef42024-05-16 08:27:38 +00006190 GST_LOG_OBJECT (qtdemux,
6191 "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
zengliang.lid0c84c32024-05-17 03:33:20 +00006192 ", duration %" GST_TIME_FORMAT " on pad %s" ", discontinuity_base_pos %" GST_TIME_FORMAT,
zengliang.li5f31ef42024-05-16 08:27:38 +00006193 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
6194 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
zengliang.lid0c84c32024-05-17 03:33:20 +00006195 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 +00006196
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006197 if (stream->protected && stream->protection_scheme_type == AML_FOURCC_aavd) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006198 GstStructure *crypto_info;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006199 AmlQtDemuxAavdEncryptionInfo *info =
6200 (AmlQtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00006201
6202 crypto_info = gst_structure_copy (info->default_properties);
6203 if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
6204 GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
6205 }
6206
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006207 if (stream->protected && (stream->protection_scheme_type == AML_FOURCC_cenc
xuesong.jiangec2fff52024-10-21 16:16:19 +08006208 || stream->protection_scheme_type == AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006209 || stream->protection_scheme_type == AML_FOURCC_cbcs
6210 || stream->protection_scheme_type == AML_FOURCC_cens)) {
shipeng sunc5d54622025-01-06 14:32:59 +08006211 GstStructure *crypto_info = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006212 AmlQtDemuxCencSampleSetInfo *info =
6213 (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00006214 gint index;
6215 GstEvent *event;
zengliang.li125c3642024-05-17 06:06:08 +00006216 GstProtectionMeta *meta = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00006217
6218 while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
6219 GST_TRACE_OBJECT (stream->pad, "pushing protection event: %"
6220 GST_PTR_FORMAT, event);
6221 gst_pad_push_event (stream->pad, event);
6222 }
6223
6224 if (info->crypto_info == NULL) {
shipeng sunc5d54622025-01-06 14:32:59 +08006225 GST_DEBUG_OBJECT (qtdemux,
6226 "cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
zengliang.li5f31ef42024-05-16 08:27:38 +00006227 } else {
6228 /* The end of the crypto_info array matches our n_samples position,
6229 * so count backward from there */
6230 index = stream->sample_index - stream->n_samples + info->crypto_info->len;
6231 if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
6232 /* steal structure from array */
6233 crypto_info = g_ptr_array_index (info->crypto_info, index);
6234 g_ptr_array_index (info->crypto_info, index) = NULL;
6235 GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
6236 info->crypto_info->len);
zengliang.li125c3642024-05-17 06:06:08 +00006237
xuesong.jiang45374612024-10-10 20:10:13 +08006238 if (stream->protection_scheme_type == AML_FOURCC_cbcs || stream->protection_scheme_type == AML_FOURCC_cens) {
zengliang.li125c3642024-05-17 06:06:08 +00006239 guint subsample_count = 0;
6240 GstBuffer *subsamples = NULL;
6241 guint crypt_byte_block = 0;
6242 guint skip_byte_block = 0;
6243
6244 gst_structure_get (crypto_info,
6245 "subsample_count", G_TYPE_UINT, &subsample_count,
6246 "subsamples", GST_TYPE_BUFFER, &subsamples,
6247 "crypt_byte_block", G_TYPE_UINT, &crypt_byte_block,
6248 "skip_byte_block", G_TYPE_UINT, &skip_byte_block,
6249 NULL);
shipeng sunc5d54622025-01-06 14:32:59 +08006250 if (crypt_byte_block == 0 && skip_byte_block == 0
6251 && (0 == strncmp(GST_PAD_NAME (stream->pad), "audio", 5))) {
6252 crypt_byte_block = 1;
6253 gst_structure_set (crypto_info,
6254 "crypt_byte_block", G_TYPE_UINT, crypt_byte_block,
6255 NULL);
6256 }
zengliang.li125c3642024-05-17 06:06:08 +00006257 meta = gst_buffer_get_protection_meta(buf);
6258 if (meta) {
6259 GST_DEBUG_OBJECT (qtdemux, "protection metadata name %s", gst_structure_get_name(meta->info));
6260 gst_structure_set (meta->info,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006261 "subsample_count", G_TYPE_UINT, subsample_count,
6262 "subsamples", GST_TYPE_BUFFER, subsamples,
6263 "crypt_byte_block", G_TYPE_UINT, crypt_byte_block,
6264 "skip_byte_block", G_TYPE_UINT, skip_byte_block,
6265 NULL);
zengliang.li125c3642024-05-17 06:06:08 +00006266 } else {
6267 GST_INFO_OBJECT (qtdemux, "no origin cbcs protection metadata");
6268 }
6269 }
6270
6271 if (!meta && (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)))
zengliang.li5f31ef42024-05-16 08:27:38 +00006272 GST_ERROR_OBJECT (qtdemux,
6273 "failed to attach cenc metadata to buffer");
6274 } else {
6275 GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
6276 index, stream->sample_index);
6277 }
6278 }
6279 }
6280
6281 if (stream->alignment > 1)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006282 buf = gst_aml_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +00006283
6284 pts = GST_BUFFER_PTS (buf);
6285 duration = GST_BUFFER_DURATION (buf);
6286
xuesong.jianga8fbc442024-09-26 20:31:31 +08006287 GST_LOG_OBJECT (stream->pad,
6288 "buf dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
6289 " pre key dts %" GST_TIME_FORMAT " pre key pts %" GST_TIME_FORMAT,
6290 GST_TIME_ARGS (GST_BUFFER_DTS (buf)), GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
6291 GST_TIME_ARGS (qtdemux->pre_keyframe_dts), GST_TIME_ARGS (qtdemux->pre_keyframe_pts));
6292
6293 if (qtdemux->pre_keyframe_dts != GST_CLOCK_TIME_NONE && GST_BUFFER_DTS (buf) < qtdemux->pre_keyframe_dts)
6294 {
6295 GST_LOG_OBJECT (stream->pad, "drop this buf");
6296 }
6297 else
6298 {
6299 ret = gst_pad_push (stream->pad, buf);
6300 }
zengliang.li5f31ef42024-05-16 08:27:38 +00006301
6302 if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
6303 /* mark position in stream, we'll need this to know when to send GAP event */
6304 stream->segment.position = pts + duration;
6305 }
6306
6307exit:
6308
6309 return ret;
6310}
6311
6312static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006313gst_aml_qtdemux_split_and_push_buffer (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006314 GstBuffer * buf)
6315{
6316 GstFlowReturn ret = GST_FLOW_OK;
6317
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006318 if (stream->subtype == AML_FOURCC_clcp
6319 && AML_CUR_STREAM (stream)->fourcc == AML_FOURCC_c608 && stream->need_split) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006320 GstMapInfo map;
6321 guint n_output_buffers, n_field1 = 0, n_field2 = 0;
6322 guint n_triplets, i;
6323 guint field1_off = 0, field2_off = 0;
6324
6325 /* We have to split CEA608 buffers so that each outgoing buffer contains
6326 * one byte pair per field according to the framerate of the video track.
6327 *
6328 * If there is only a single byte pair per field we don't have to do
6329 * anything
6330 */
6331
6332 gst_buffer_map (buf, &map, GST_MAP_READ);
6333
6334 n_triplets = map.size / 3;
6335 for (i = 0; i < n_triplets; i++) {
6336 if (map.data[3 * i] & 0x80)
6337 n_field1++;
6338 else
6339 n_field2++;
6340 }
6341
6342 g_assert (n_field1 || n_field2);
6343
6344 /* If there's more than 1 frame we have to split, otherwise we can just
6345 * pass through */
6346 if (n_field1 > 1 || n_field2 > 1) {
6347 n_output_buffers =
6348 gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006349 AML_CUR_STREAM (stream)->fps_n, GST_SECOND * AML_CUR_STREAM (stream)->fps_d);
zengliang.li5f31ef42024-05-16 08:27:38 +00006350
6351 for (i = 0; i < n_output_buffers; i++) {
6352 GstBuffer *outbuf =
6353 gst_buffer_new_and_alloc ((n_field1 ? 3 : 0) + (n_field2 ? 3 : 0));
6354 GstMapInfo outmap;
6355 guint8 *outptr;
6356
6357 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
6358 outptr = outmap.data;
6359
6360 if (n_field1) {
6361 gboolean found = FALSE;
6362
6363 while (map.data + field1_off < map.data + map.size) {
6364 if (map.data[field1_off] & 0x80) {
6365 memcpy (outptr, &map.data[field1_off], 3);
6366 field1_off += 3;
6367 found = TRUE;
6368 break;
6369 }
6370 field1_off += 3;
6371 }
6372
6373 if (!found) {
6374 const guint8 empty[] = { 0x80, 0x80, 0x80 };
6375
6376 memcpy (outptr, empty, 3);
6377 }
6378
6379 outptr += 3;
6380 }
6381
6382 if (n_field2) {
6383 gboolean found = FALSE;
6384
6385 while (map.data + field2_off < map.data + map.size) {
6386 if ((map.data[field2_off] & 0x80) == 0) {
6387 memcpy (outptr, &map.data[field2_off], 3);
6388 field2_off += 3;
6389 found = TRUE;
6390 break;
6391 }
6392 field2_off += 3;
6393 }
6394
6395 if (!found) {
6396 const guint8 empty[] = { 0x00, 0x80, 0x80 };
6397
6398 memcpy (outptr, empty, 3);
6399 }
6400
6401 outptr += 3;
6402 }
6403
6404 gst_buffer_unmap (outbuf, &outmap);
6405
6406 GST_BUFFER_PTS (outbuf) =
6407 GST_BUFFER_PTS (buf) + gst_util_uint64_scale (i,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006408 GST_SECOND * AML_CUR_STREAM (stream)->fps_d,
6409 AML_CUR_STREAM (stream)->fps_n);
zengliang.li5f31ef42024-05-16 08:27:38 +00006410 GST_BUFFER_DURATION (outbuf) =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006411 gst_util_uint64_scale (GST_SECOND, AML_CUR_STREAM (stream)->fps_d,
6412 AML_CUR_STREAM (stream)->fps_n);
zengliang.li5f31ef42024-05-16 08:27:38 +00006413 GST_BUFFER_OFFSET (outbuf) = -1;
6414 GST_BUFFER_OFFSET_END (outbuf) = -1;
6415
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006416 ret = gst_aml_qtdemux_push_buffer (qtdemux, stream, outbuf);
zengliang.li5f31ef42024-05-16 08:27:38 +00006417
6418 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
6419 break;
6420 }
6421 gst_buffer_unmap (buf, &map);
6422 gst_buffer_unref (buf);
6423 } else {
6424 gst_buffer_unmap (buf, &map);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006425 ret = gst_aml_qtdemux_push_buffer (qtdemux, stream, buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00006426 }
6427 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006428 ret = gst_aml_qtdemux_push_buffer (qtdemux, stream, buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00006429 }
6430
6431 return ret;
6432}
6433
6434/* Sets a buffer's attributes properly and pushes it downstream.
6435 * Also checks for additional actions and custom processing that may
6436 * need to be done first.
6437 */
6438static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006439gst_aml_qtdemux_decorate_and_push_buffer (GstAmlQTDemux * qtdemux,
6440 AmlQtDemuxStream * stream, GstBuffer * buf,
zengliang.li5f31ef42024-05-16 08:27:38 +00006441 GstClockTime dts, GstClockTime pts, GstClockTime duration,
6442 gboolean keyframe, GstClockTime position, guint64 byte_position)
6443{
6444 GstFlowReturn ret = GST_FLOW_OK;
6445
6446 /* offset the timestamps according to the edit list */
6447
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006448 if (G_UNLIKELY (AML_CUR_STREAM (stream)->fourcc == AML_FOURCC_rtsp)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006449 gchar *url;
6450 GstMapInfo map;
6451
6452 gst_buffer_map (buf, &map, GST_MAP_READ);
6453 url = g_strndup ((gchar *) map.data, map.size);
6454 gst_buffer_unmap (buf, &map);
6455 if (url != NULL && strlen (url) != 0) {
6456 /* we have RTSP redirect now */
6457 g_free (qtdemux->redirect_location);
6458 qtdemux->redirect_location = g_strdup (url);
6459 gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
6460 gst_message_new_element (GST_OBJECT_CAST (qtdemux),
6461 gst_structure_new ("redirect",
6462 "new-location", G_TYPE_STRING, url, NULL)));
6463 } else {
6464 GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
6465 "posting");
6466 }
6467 g_free (url);
6468 }
6469
6470 /* position reporting */
zengliang.li4681ee42024-05-16 12:25:30 +00006471 if (qtdemux->segment.rate >= 0 && GST_CLOCK_TIME_IS_VALID(position)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006472 qtdemux->segment.position = position;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006473 gst_aml_qtdemux_sync_streams (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00006474 }
6475
6476 if (G_UNLIKELY (!stream->pad)) {
6477 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
6478 gst_buffer_unref (buf);
6479 goto exit;
6480 }
6481
6482 /* send out pending buffers */
6483 while (stream->buffers) {
6484 GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
6485
6486 if (G_UNLIKELY (stream->discont)) {
6487 GST_LOG_OBJECT (qtdemux, "marking discont buffer");
6488 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
6489 stream->discont = FALSE;
6490 } else {
6491 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
6492 }
6493
6494 if (stream->alignment > 1)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006495 buffer = gst_aml_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +00006496 gst_pad_push (stream->pad, buffer);
6497
6498 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
6499 }
6500
6501 /* we're going to modify the metadata */
6502 buf = gst_buffer_make_writable (buf);
6503
6504 GST_BUFFER_DTS (buf) = dts;
6505 GST_BUFFER_PTS (buf) = pts;
6506 GST_BUFFER_DURATION (buf) = duration;
6507 GST_BUFFER_OFFSET (buf) = -1;
6508 GST_BUFFER_OFFSET_END (buf) = -1;
6509
6510 if (G_UNLIKELY (stream->process_func))
6511 buf = stream->process_func (qtdemux, stream, buf);
6512
6513 if (!buf) {
6514 goto exit;
6515 }
6516
6517 if (!keyframe) {
6518 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
6519 stream->on_keyframe = FALSE;
6520 } else {
6521 stream->on_keyframe = TRUE;
6522 }
6523
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006524 if (G_UNLIKELY (AML_CUR_STREAM (stream)->rgb8_palette))
zengliang.li5f31ef42024-05-16 08:27:38 +00006525 gst_buffer_append_memory (buf,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006526 gst_memory_ref (AML_CUR_STREAM (stream)->rgb8_palette));
zengliang.li5f31ef42024-05-16 08:27:38 +00006527
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006528 if (G_UNLIKELY (AML_CUR_STREAM (stream)->padding)) {
6529 gst_buffer_resize (buf, AML_CUR_STREAM (stream)->padding, -1);
zengliang.li5f31ef42024-05-16 08:27:38 +00006530 }
6531#if 0
6532 if (G_UNLIKELY (qtdemux->element_index)) {
6533 GstClockTime stream_time;
6534
6535 stream_time =
6536 gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
6537 timestamp);
6538 if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
6539 GST_LOG_OBJECT (qtdemux,
6540 "adding association %" GST_TIME_FORMAT "-> %"
6541 G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
6542 gst_index_add_association (qtdemux->element_index,
6543 qtdemux->index_id,
6544 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
6545 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
6546 GST_FORMAT_BYTES, byte_position, NULL);
6547 }
6548 }
6549#endif
6550
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006551 ret = gst_aml_qtdemux_split_and_push_buffer (qtdemux, stream, buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00006552
6553exit:
6554 return ret;
6555}
6556
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006557static const AmlQtDemuxRandomAccessEntry *
6558gst_aml_qtdemux_stream_seek_fragment (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006559 GstClockTime pos, gboolean after)
6560{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006561 AmlQtDemuxRandomAccessEntry *entries = stream->ra_entries;
zengliang.li5f31ef42024-05-16 08:27:38 +00006562 guint n_entries = stream->n_ra_entries;
6563 guint i;
6564
6565 /* we assume the table is sorted */
6566 for (i = 0; i < n_entries; ++i) {
6567 if (entries[i].ts > pos)
6568 break;
6569 }
6570
6571 /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
6572 * probably okay to assume that the index lists the very first fragment */
6573 if (i == 0)
6574 return &entries[0];
6575
6576 if (after)
6577 return &entries[i];
6578 else
6579 return &entries[i - 1];
6580}
6581
6582static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006583gst_aml_qtdemux_do_fragmented_seek (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00006584{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006585 const AmlQtDemuxRandomAccessEntry *best_entry = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00006586 gint i;
6587
6588 GST_OBJECT_LOCK (qtdemux);
6589
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006590 g_assert (AML_QTDEMUX_N_STREAMS (qtdemux) > 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00006591
6592 /* first see if we can determine where to go to using mfra,
6593 * before we start clearing things */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006594 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
6595 const AmlQtDemuxRandomAccessEntry *entry;
6596 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00006597 gboolean is_audio_or_video;
6598
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006599 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00006600
6601 if (stream->ra_entries == NULL)
6602 continue;
6603
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006604 if (stream->subtype == AML_FOURCC_vide || stream->subtype == AML_FOURCC_soun)
zengliang.li5f31ef42024-05-16 08:27:38 +00006605 is_audio_or_video = TRUE;
6606 else
6607 is_audio_or_video = FALSE;
6608
6609 entry =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006610 gst_aml_qtdemux_stream_seek_fragment (qtdemux, stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006611 stream->time_position, !is_audio_or_video);
6612
6613 GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
6614 "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
6615
6616 stream->pending_seek = entry;
6617
6618 /* decide position to jump to just based on audio/video tracks, not subs */
6619 if (!is_audio_or_video)
6620 continue;
6621
6622 if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
6623 best_entry = entry;
6624 }
6625
6626 /* no luck, will handle seek otherwise */
6627 if (best_entry == NULL) {
6628 GST_OBJECT_UNLOCK (qtdemux);
6629 return FALSE;
6630 }
6631
6632 /* ok, now we can prepare for processing as of located moof */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006633 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
6634 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00006635
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006636 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00006637
6638 g_free (stream->samples);
6639 stream->samples = NULL;
6640 stream->n_samples = 0;
6641 stream->stbl_index = -1; /* no samples have yet been parsed */
6642 stream->sample_index = -1;
6643
6644 if (stream->protection_scheme_info) {
6645 /* Clear out any old cenc crypto info entries as we'll move to a new moof */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006646 if (stream->protection_scheme_type == AML_FOURCC_cenc
xuesong.jiangec2fff52024-10-21 16:16:19 +08006647 || stream->protection_scheme_type == AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006648 || stream->protection_scheme_type == AML_FOURCC_cbcs
6649 || stream->protection_scheme_type == AML_FOURCC_cens) {
6650 AmlQtDemuxCencSampleSetInfo *info =
6651 (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +00006652 if (info->crypto_info) {
6653 g_ptr_array_free (info->crypto_info, TRUE);
6654 info->crypto_info = NULL;
6655 }
6656 }
6657 }
6658 }
6659
6660 GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
6661 "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006662 GST_TIME_ARGS (AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->time_position),
zengliang.li5f31ef42024-05-16 08:27:38 +00006663 best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
6664
6665 qtdemux->moof_offset = best_entry->moof_offset;
6666
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006667 aml_qtdemux_add_fragmented_samples (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00006668
6669 GST_OBJECT_UNLOCK (qtdemux);
6670 return TRUE;
6671}
6672
6673static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006674gst_aml_qtdemux_loop_state_movie (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00006675{
6676 GstFlowReturn ret = GST_FLOW_OK;
6677 GstBuffer *buf = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006678 AmlQtDemuxStream *stream, *target_stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00006679 GstClockTime min_time;
6680 guint64 offset = 0;
6681 GstClockTime dts = GST_CLOCK_TIME_NONE;
6682 GstClockTime pts = GST_CLOCK_TIME_NONE;
6683 GstClockTime duration = 0;
6684 gboolean keyframe = FALSE;
6685 guint sample_size = 0;
6686 guint num_samples = 1;
6687 gboolean empty = 0;
6688 guint size;
6689 gint i;
6690
6691 if (qtdemux->fragmented_seek_pending) {
6692 GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006693 if (gst_aml_qtdemux_do_fragmented_seek (qtdemux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006694 GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
6695 qtdemux->fragmented_seek_pending = FALSE;
6696 } else {
6697 GST_INFO_OBJECT (qtdemux, "fragmented seek still pending");
6698 }
6699 }
6700
6701 /* Figure out the next stream sample to output, min_time is expressed in
6702 * global time and runs over the edit list segments. */
6703 min_time = G_MAXUINT64;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006704 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006705 GstClockTime position;
6706
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006707 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00006708 position = stream->time_position;
6709
6710 /* position of -1 is EOS */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006711 if ((position != GST_CLOCK_TIME_NONE && position < min_time)
zengliang.li8c9c4092024-05-16 11:35:51 +00006712 || (stream->discont)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006713 min_time = position;
6714 target_stream = stream;
6715 }
6716 }
6717 /* all are EOS */
6718 if (G_UNLIKELY (target_stream == NULL)) {
6719 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS");
6720 goto eos;
6721 }
6722
6723 /* check for segment end */
6724 if (G_UNLIKELY (qtdemux->segment.stop != -1
6725 && qtdemux->segment.rate >= 0
6726 && qtdemux->segment.stop <= min_time && target_stream->on_keyframe)) {
6727 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment.");
6728 target_stream->time_position = GST_CLOCK_TIME_NONE;
6729 goto eos_stream;
6730 }
6731
6732 /* gap events for subtitle streams */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006733 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
6734 stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00006735 if (stream->pad) {
6736 GstClockTime gap_threshold;
6737
6738 /* Only send gap events on non-subtitle streams if lagging way behind. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006739 if (stream->subtype == AML_FOURCC_subp
6740 || stream->subtype == AML_FOURCC_text || stream->subtype == AML_FOURCC_sbtl ||
6741 stream->subtype == AML_FOURCC_wvtt)
zengliang.li5f31ef42024-05-16 08:27:38 +00006742 gap_threshold = 1 * GST_SECOND;
6743 else
6744 gap_threshold = 3 * GST_SECOND;
6745
6746 /* send gap events until the stream catches up */
6747 /* gaps can only be sent after segment is activated (segment.stop is no longer -1) */
6748 while (GST_CLOCK_TIME_IS_VALID (stream->segment.stop) &&
6749 GST_CLOCK_TIME_IS_VALID (stream->segment.position) &&
6750 stream->segment.position < (G_MAXUINT64 - gap_threshold) &&
6751 stream->segment.position + gap_threshold < min_time) {
6752 GstEvent *gap =
6753 gst_event_new_gap (stream->segment.position, gap_threshold);
6754 gst_pad_push_event (stream->pad, gap);
6755 stream->segment.position += gap_threshold;
6756 }
6757 }
6758 }
6759
6760 stream = target_stream;
6761 /* fetch info for the current sample of this stream */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006762 if (G_UNLIKELY (!gst_aml_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
zengliang.li5f31ef42024-05-16 08:27:38 +00006763 &offset, &sample_size, &dts, &pts, &duration, &keyframe)))
6764 goto eos_stream;
6765
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006766 gst_aml_qtdemux_stream_check_and_change_stsd_index (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00006767 if (stream->new_caps) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006768 gst_aml_qtdemux_configure_stream (qtdemux, stream);
6769 aml_qtdemux_do_allocation (stream, qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00006770 }
6771
6772 /* If we're doing a keyframe-only trickmode, only push keyframes on video streams */
6773 if (G_UNLIKELY (qtdemux->segment.
6774 flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006775 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006776 if (!keyframe) {
6777 GST_LOG_OBJECT (qtdemux, "Skipping non-keyframe on track-id %u",
6778 stream->track_id);
6779 goto next;
6780 } else if (qtdemux->trickmode_interval > 0) {
6781 GstClockTimeDiff interval;
6782
6783 if (qtdemux->segment.rate > 0)
6784 interval = stream->time_position - stream->last_keyframe_dts;
6785 else
6786 interval = stream->last_keyframe_dts - stream->time_position;
6787
6788 if (GST_CLOCK_TIME_IS_VALID (stream->last_keyframe_dts)
6789 && interval < qtdemux->trickmode_interval) {
6790 GST_LOG_OBJECT (qtdemux,
6791 "Skipping keyframe within interval on track-id %u",
6792 stream->track_id);
6793 goto next;
6794 } else {
6795 stream->last_keyframe_dts = stream->time_position;
6796 }
6797 }
6798 }
6799 }
6800
6801 GST_DEBUG_OBJECT (qtdemux,
6802 "pushing from track-id %u, empty %d offset %" G_GUINT64_FORMAT
6803 ", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
6804 ", duration %" GST_TIME_FORMAT, stream->track_id, empty, offset,
6805 sample_size, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts),
6806 GST_TIME_ARGS (duration));
6807
6808 if (G_UNLIKELY (empty)) {
6809 /* empty segment, push a gap if there's a second or more
6810 * difference and move to the next one */
6811 if ((pts + duration - stream->segment.position) >= GST_SECOND)
6812 gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
6813 stream->segment.position = pts + duration;
6814 goto next;
6815 }
6816
6817 /* hmm, empty sample, skip and move to next sample */
6818 if (G_UNLIKELY (sample_size <= 0))
6819 goto next;
6820
6821 /* last pushed sample was out of boundary, goto next sample */
6822 if (G_UNLIKELY (GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS))
6823 goto next;
6824
6825 if (stream->max_buffer_size != 0 && sample_size > stream->max_buffer_size) {
6826 GST_DEBUG_OBJECT (qtdemux,
6827 "size %d larger than stream max_buffer_size %d, trimming",
6828 sample_size, stream->max_buffer_size);
6829 size =
6830 MIN (sample_size - stream->offset_in_sample, stream->max_buffer_size);
6831 } else if (stream->min_buffer_size != 0 && stream->offset_in_sample == 0
6832 && sample_size < stream->min_buffer_size) {
6833 guint start_sample_index = stream->sample_index;
6834 guint accumulated_size = sample_size;
6835 guint64 expected_next_offset = offset + sample_size;
6836
6837 GST_DEBUG_OBJECT (qtdemux,
6838 "size %d smaller than stream min_buffer_size %d, combining with the next",
6839 sample_size, stream->min_buffer_size);
6840
6841 while (stream->sample_index < stream->to_sample
6842 && stream->sample_index + 1 < stream->n_samples) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006843 const AmlQtDemuxSample *next_sample;
zengliang.li5f31ef42024-05-16 08:27:38 +00006844
6845 /* Increment temporarily */
6846 stream->sample_index++;
6847
6848 /* Failed to parse sample so let's go back to the previous one that was
6849 * still successful */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006850 if (!aml_qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00006851 stream->sample_index--;
6852 break;
6853 }
6854
6855 next_sample = &stream->samples[stream->sample_index];
6856
6857 /* Not contiguous with the previous sample so let's go back to the
6858 * previous one that was still successful */
6859 if (next_sample->offset != expected_next_offset) {
6860 stream->sample_index--;
6861 break;
6862 }
6863
6864 accumulated_size += next_sample->size;
6865 expected_next_offset += next_sample->size;
6866 if (accumulated_size >= stream->min_buffer_size)
6867 break;
6868 }
6869
6870 num_samples = stream->sample_index + 1 - start_sample_index;
6871 stream->sample_index = start_sample_index;
6872 GST_DEBUG_OBJECT (qtdemux, "Pulling %u samples of size %u at once",
6873 num_samples, accumulated_size);
6874 size = accumulated_size;
6875 } else {
6876 size = sample_size;
6877 }
6878
6879 if (qtdemux->cenc_aux_info_offset > 0) {
6880 GstMapInfo map;
6881 GstByteReader br;
6882 GstBuffer *aux_info = NULL;
6883
6884 /* pull the data stored before the sample */
6885 ret =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006886 gst_aml_qtdemux_pull_atom (qtdemux, qtdemux->offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00006887 offset + stream->offset_in_sample - qtdemux->offset, &aux_info);
6888 if (G_UNLIKELY (ret != GST_FLOW_OK))
6889 goto beach;
6890 gst_buffer_map (aux_info, &map, GST_MAP_READ);
6891 GST_DEBUG_OBJECT (qtdemux, "parsing cenc auxiliary info");
6892 gst_byte_reader_init (&br, map.data + 8, map.size);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006893 if (!aml_qtdemux_parse_cenc_aux_info (qtdemux, stream, &br,
zengliang.li5f31ef42024-05-16 08:27:38 +00006894 qtdemux->cenc_aux_info_sizes, qtdemux->cenc_aux_sample_count)) {
6895 GST_ERROR_OBJECT (qtdemux, "failed to parse cenc auxiliary info");
6896 gst_buffer_unmap (aux_info, &map);
6897 gst_buffer_unref (aux_info);
6898 ret = GST_FLOW_ERROR;
6899 goto beach;
6900 }
6901 gst_buffer_unmap (aux_info, &map);
6902 gst_buffer_unref (aux_info);
6903 }
6904
6905 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
6906 offset);
6907
6908 if (stream->use_allocator) {
6909 /* if we have a per-stream allocator, use it */
6910 buf = gst_buffer_new_allocate (stream->allocator, size, &stream->params);
6911 }
6912
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006913 ret = gst_aml_qtdemux_pull_atom (qtdemux, offset + stream->offset_in_sample,
zengliang.li5f31ef42024-05-16 08:27:38 +00006914 size, &buf);
6915 if (G_UNLIKELY (ret != GST_FLOW_OK))
6916 goto beach;
6917
6918 /* Update for both splitting and combining of samples */
6919 if (size != sample_size) {
6920 pts += gst_util_uint64_scale_int (GST_SECOND,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006921 stream->offset_in_sample / AML_CUR_STREAM (stream)->bytes_per_frame,
zengliang.li5f31ef42024-05-16 08:27:38 +00006922 stream->timescale);
6923 dts +=
6924 gst_util_uint64_scale_int (GST_SECOND,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006925 stream->offset_in_sample / AML_CUR_STREAM (stream)->bytes_per_frame,
zengliang.li5f31ef42024-05-16 08:27:38 +00006926 stream->timescale);
6927 duration =
6928 gst_util_uint64_scale_int (GST_SECOND,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006929 size / AML_CUR_STREAM (stream)->bytes_per_frame, stream->timescale);
zengliang.li5f31ef42024-05-16 08:27:38 +00006930 }
6931
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006932 ret = gst_aml_qtdemux_decorate_and_push_buffer (qtdemux, stream, buf,
zengliang.li5f31ef42024-05-16 08:27:38 +00006933 dts, pts, duration, keyframe, min_time, offset);
6934
6935 if (size < sample_size) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006936 AmlQtDemuxSample *sample = &stream->samples[stream->sample_index];
6937 AmlQtDemuxSegment *segment = &stream->segments[stream->segment_index];
zengliang.li5f31ef42024-05-16 08:27:38 +00006938
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006939 GstClockTime time_position = AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00006940 sample->timestamp +
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006941 stream->offset_in_sample / AML_CUR_STREAM (stream)->bytes_per_frame);
zengliang.li5f31ef42024-05-16 08:27:38 +00006942 if (time_position >= segment->media_start) {
6943 /* inside the segment, update time_position, looks very familiar to
6944 * GStreamer segments, doesn't it? */
6945 stream->time_position = (time_position - segment->media_start) +
6946 segment->time;
6947 } else {
6948 /* not yet in segment, time does not yet increment. This means
6949 * that we are still prerolling keyframes to the decoder so it can
6950 * decode the first sample of the segment. */
6951 stream->time_position = segment->time;
6952 }
6953 } else if (size > sample_size) {
6954 /* Increase to the last sample we already pulled so that advancing
6955 * below brings us to the next sample we need to pull */
6956 stream->sample_index += num_samples - 1;
6957 }
6958
6959 /* combine flows */
6960 GST_OBJECT_LOCK (qtdemux);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006961 ret = gst_aml_qtdemux_combine_flows (qtdemux, stream, ret);
zengliang.li5f31ef42024-05-16 08:27:38 +00006962 GST_OBJECT_UNLOCK (qtdemux);
6963 /* ignore unlinked, we will not push on the pad anymore and we will EOS when
6964 * we have no more data for the pad to push */
6965 if (ret == GST_FLOW_EOS)
6966 ret = GST_FLOW_OK;
6967
6968 stream->offset_in_sample += size;
6969 if (stream->offset_in_sample >= sample_size) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006970 gst_aml_qtdemux_advance_sample (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00006971 }
6972 goto beach;
6973
6974next:
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006975 gst_aml_qtdemux_advance_sample (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00006976
6977beach:
6978 return ret;
6979
6980 /* special cases */
6981eos:
6982 {
6983 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
6984 ret = GST_FLOW_EOS;
6985 goto beach;
6986 }
6987eos_stream:
6988 {
6989 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
6990 /* EOS will be raised if all are EOS */
6991 ret = GST_FLOW_OK;
6992 goto beach;
6993 }
6994}
6995
6996static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006997gst_aml_qtdemux_loop (GstPad * pad)
zengliang.li5f31ef42024-05-16 08:27:38 +00006998{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00006999 GstAmlQTDemux *qtdemux;
zengliang.li5f31ef42024-05-16 08:27:38 +00007000 guint64 cur_offset;
7001 GstFlowReturn ret;
7002
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007003 qtdemux = GST_AML_QTDEMUX (gst_pad_get_parent (pad));
zengliang.li5f31ef42024-05-16 08:27:38 +00007004
7005 cur_offset = qtdemux->offset;
7006 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %s",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007007 cur_offset, aml_qt_demux_state_string (qtdemux->state));
zengliang.li5f31ef42024-05-16 08:27:38 +00007008
7009 switch (qtdemux->state) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007010 case AML_QTDEMUX_STATE_INITIAL:
7011 case AML_QTDEMUX_STATE_HEADER:
7012 ret = gst_aml_qtdemux_loop_state_header (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007013 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007014 case AML_QTDEMUX_STATE_MOVIE:
7015 ret = gst_aml_qtdemux_loop_state_movie (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007016 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_EOS) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007017 ret = gst_aml_qtdemux_seek_to_previous_keyframe (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007018 }
7019 break;
7020 default:
7021 /* ouch */
7022 goto invalid_state;
7023 }
7024
7025 /* if something went wrong, pause */
7026 if (ret != GST_FLOW_OK)
7027 goto pause;
7028
7029done:
7030 gst_object_unref (qtdemux);
7031 return;
7032
7033 /* ERRORS */
7034invalid_state:
7035 {
7036 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
7037 (NULL), ("streaming stopped, invalid state"));
7038 gst_pad_pause_task (pad);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007039 gst_aml_qtdemux_push_event (qtdemux, gst_event_new_eos ());
zengliang.li5f31ef42024-05-16 08:27:38 +00007040 goto done;
7041 }
7042pause:
7043 {
7044 const gchar *reason = gst_flow_get_name (ret);
7045
7046 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason);
7047
7048 gst_pad_pause_task (pad);
7049
7050 /* fatal errors need special actions */
7051 /* check EOS */
7052 if (ret == GST_FLOW_EOS) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007053 if (AML_QTDEMUX_N_STREAMS (qtdemux) == 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007054 /* we have no streams, post an error */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007055 gst_aml_qtdemux_post_no_playable_stream_error (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007056 }
7057 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
7058 gint64 stop;
7059
7060 if ((stop = qtdemux->segment.stop) == -1)
7061 stop = qtdemux->segment.duration;
7062
7063 if (qtdemux->segment.rate >= 0) {
7064 GstMessage *message;
7065 GstEvent *event;
7066
7067 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
7068 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7069 GST_FORMAT_TIME, stop);
7070 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
7071 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7072 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7073 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7074 }
7075 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007076 gst_aml_qtdemux_push_event (qtdemux, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00007077 } else {
7078 GstMessage *message;
7079 GstEvent *event;
7080
7081 /* For Reverse Playback */
7082 GST_LOG_OBJECT (qtdemux, "Sending segment done, at start of segment");
7083 message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
7084 GST_FORMAT_TIME, qtdemux->segment.start);
7085 event = gst_event_new_segment_done (GST_FORMAT_TIME,
7086 qtdemux->segment.start);
7087 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID) {
7088 gst_message_set_seqnum (message, qtdemux->segment_seqnum);
7089 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
7090 }
7091 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), message);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007092 gst_aml_qtdemux_push_event (qtdemux, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00007093 }
7094 } else {
7095 GstEvent *event;
7096
7097 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
7098 event = gst_event_new_eos ();
7099 if (qtdemux->segment_seqnum != GST_SEQNUM_INVALID)
7100 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007101 gst_aml_qtdemux_push_event (qtdemux, event);
zengliang.li5f31ef42024-05-16 08:27:38 +00007102 }
7103 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
7104 GST_ELEMENT_FLOW_ERROR (qtdemux, ret);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007105 gst_aml_qtdemux_push_event (qtdemux, gst_event_new_eos ());
zengliang.li5f31ef42024-05-16 08:27:38 +00007106 }
7107 goto done;
7108 }
7109}
7110
7111/*
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007112 * aml_has_next_entry
zengliang.li5f31ef42024-05-16 08:27:38 +00007113 *
7114 * Returns if there are samples to be played.
7115 */
7116static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007117aml_has_next_entry (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00007118{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007119 AmlQtDemuxStream *stream;
zengliang.li5f31ef42024-05-16 08:27:38 +00007120 gint i;
7121
7122 GST_DEBUG_OBJECT (demux, "Checking if there are samples not played yet");
7123
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007124 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7125 stream = AML_QTDEMUX_NTH_STREAM (demux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00007126
7127 if (stream->sample_index == -1) {
7128 stream->sample_index = 0;
7129 stream->offset_in_sample = 0;
7130 }
7131
7132 if (stream->sample_index >= stream->n_samples) {
7133 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7134 continue;
7135 }
7136 GST_DEBUG_OBJECT (demux, "Found a sample");
7137 return TRUE;
7138 }
7139
7140 GST_DEBUG_OBJECT (demux, "There wasn't any next sample");
7141 return FALSE;
7142}
7143
7144/*
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007145 * aml_next_entry_size
zengliang.li5f31ef42024-05-16 08:27:38 +00007146 *
7147 * Returns the size of the first entry at the current offset.
7148 * If -1, there are none (which means EOS or empty file).
7149 */
7150static guint64
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007151aml_next_entry_size (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00007152{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007153 AmlQtDemuxStream *stream, *target_stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00007154 guint64 smalloffs = (guint64) - 1;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007155 AmlQtDemuxSample *sample;
zengliang.li5f31ef42024-05-16 08:27:38 +00007156 gint i;
7157
7158 GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
7159 demux->offset);
7160
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007161 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7162 stream = AML_QTDEMUX_NTH_STREAM (demux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00007163
7164 if (stream->sample_index == -1) {
7165 stream->sample_index = 0;
7166 stream->offset_in_sample = 0;
7167 }
7168
7169 if (stream->sample_index >= stream->n_samples) {
7170 GST_LOG_OBJECT (demux, "track-id %u samples exhausted", stream->track_id);
7171 continue;
7172 }
7173
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007174 if (!aml_qtdemux_parse_samples (demux, stream, stream->sample_index)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007175 GST_LOG_OBJECT (demux, "Parsing of index %u from stbl atom failed!",
7176 stream->sample_index);
7177 return -1;
7178 }
7179
7180 sample = &stream->samples[stream->sample_index];
7181
7182 GST_LOG_OBJECT (demux,
7183 "Checking track-id %u (sample_index:%d / offset:%" G_GUINT64_FORMAT
7184 " / size:%" G_GUINT32_FORMAT ")", stream->track_id,
7185 stream->sample_index, sample->offset, sample->size);
7186
7187 if (((smalloffs == -1)
7188 || (sample->offset < smalloffs)) && (sample->size)) {
7189 smalloffs = sample->offset;
7190 target_stream = stream;
7191 }
7192 }
7193
7194 if (!target_stream)
7195 return -1;
7196
7197 GST_LOG_OBJECT (demux,
7198 "track-id %u offset %" G_GUINT64_FORMAT " demux->offset :%"
7199 G_GUINT64_FORMAT, target_stream->track_id, smalloffs, demux->offset);
7200
7201 stream = target_stream;
7202 sample = &stream->samples[stream->sample_index];
7203
7204 if (sample->offset >= demux->offset) {
7205 demux->todrop = sample->offset - demux->offset;
7206 return sample->size + demux->todrop;
7207 }
7208
7209 GST_DEBUG_OBJECT (demux,
7210 "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
7211 return -1;
7212}
7213
7214static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007215gst_aml_qtdemux_post_progress (GstAmlQTDemux * demux, gint num, gint denom)
zengliang.li5f31ef42024-05-16 08:27:38 +00007216{
7217 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
7218
7219 gst_element_post_message (GST_ELEMENT_CAST (demux),
7220 gst_message_new_element (GST_OBJECT_CAST (demux),
7221 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
7222}
7223
7224static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007225aml_qtdemux_seek_offset (GstAmlQTDemux * demux, guint64 offset)
zengliang.li5f31ef42024-05-16 08:27:38 +00007226{
7227 GstEvent *event;
7228 gboolean res = 0;
7229
7230 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
7231
7232 event =
7233 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
7234 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
7235 GST_SEEK_TYPE_NONE, -1);
7236
7237 /* store seqnum to drop flush events, they don't need to reach downstream */
7238 demux->offset_seek_seqnum = gst_event_get_seqnum (event);
7239 res = gst_pad_push_event (demux->sinkpad, event);
7240 demux->offset_seek_seqnum = GST_SEQNUM_INVALID;
7241
7242 return res;
7243}
7244
7245/* check for seekable upstream, above and beyond a mere query */
7246static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007247gst_aml_qtdemux_check_seekability (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00007248{
7249 GstQuery *query;
7250 gboolean seekable = FALSE;
7251 gint64 start = -1, stop = -1;
7252
7253 if (demux->upstream_size)
7254 return;
7255
7256 if (demux->upstream_format_is_time)
7257 return;
7258
7259 query = gst_query_new_seeking (GST_FORMAT_BYTES);
7260 if (!gst_pad_peer_query (demux->sinkpad, query)) {
7261 GST_DEBUG_OBJECT (demux, "seeking query failed");
7262 goto done;
7263 }
7264
7265 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
7266
7267 /* try harder to query upstream size if we didn't get it the first time */
7268 if (seekable && stop == -1) {
7269 GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
7270 gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
7271 }
7272
7273 /* if upstream doesn't know the size, it's likely that it's not seekable in
7274 * practice even if it technically may be seekable */
7275 if (seekable && (start != 0 || stop <= start)) {
7276 GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
7277 seekable = FALSE;
7278 }
7279
7280done:
7281 gst_query_unref (query);
7282
7283 GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
7284 G_GUINT64_FORMAT ")", seekable, start, stop);
7285 demux->upstream_seekable = seekable;
7286 demux->upstream_size = seekable ? stop : -1;
7287}
7288
7289static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007290gst_aml_qtdemux_drop_data (GstAmlQTDemux * demux, gint bytes)
zengliang.li5f31ef42024-05-16 08:27:38 +00007291{
7292 g_return_if_fail (bytes <= demux->todrop);
7293
7294 GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
7295 gst_adapter_flush (demux->adapter, bytes);
7296 demux->neededbytes -= bytes;
7297 demux->offset += bytes;
7298 demux->todrop -= bytes;
7299}
7300
7301/* PUSH-MODE only: Send a segment, if not done already. */
7302static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007303gst_aml_qtdemux_check_send_pending_segment (GstAmlQTDemux * demux)
zengliang.li5f31ef42024-05-16 08:27:38 +00007304{
7305 if (G_UNLIKELY (demux->need_segment)) {
7306 gint i;
7307
7308 if (!demux->upstream_format_is_time) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007309 gst_aml_qtdemux_map_and_push_segments (demux, &demux->segment);
zengliang.li5f31ef42024-05-16 08:27:38 +00007310 } else {
7311 GstEvent *segment_event;
7312 segment_event = gst_event_new_segment (&demux->segment);
7313 if (demux->segment_seqnum != GST_SEQNUM_INVALID)
7314 gst_event_set_seqnum (segment_event, demux->segment_seqnum);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007315 gst_aml_qtdemux_push_event (demux, segment_event);
zengliang.li5f31ef42024-05-16 08:27:38 +00007316 }
7317
7318 demux->need_segment = FALSE;
7319
7320 /* clear to send tags on all streams */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007321 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7322 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (demux, i);
7323 gst_aml_qtdemux_push_tags (demux, stream);
7324 if (AML_CUR_STREAM (stream)->sparse) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007325 GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
7326 gst_pad_push_event (stream->pad,
7327 gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
7328 }
7329 }
7330 }
7331}
7332
7333/* Used for push mode only. */
7334static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007335gst_aml_qtdemux_send_gap_for_segment (GstAmlQTDemux * demux,
7336 AmlQtDemuxStream * stream, gint segment_index, GstClockTime pos)
zengliang.li5f31ef42024-05-16 08:27:38 +00007337{
7338 GstClockTime ts, dur;
7339
7340 ts = pos;
7341 dur =
7342 stream->segments[segment_index].duration - (pos -
7343 stream->segments[segment_index].time);
7344 stream->time_position += dur;
7345
7346 /* Only gaps with a duration of at least one second are propagated.
7347 * Same workaround as in pull mode.
7348 * (See 2e45926a96ec5298c6ef29bf912e5e6a06dc3e0e) */
7349 if (dur >= GST_SECOND) {
7350 GstEvent *gap;
7351 gap = gst_event_new_gap (ts, dur);
7352
7353 GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
7354 "segment: %" GST_PTR_FORMAT, gap);
7355 gst_pad_push_event (stream->pad, gap);
7356 }
7357}
7358
7359static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007360gst_aml_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
zengliang.li5f31ef42024-05-16 08:27:38 +00007361{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007362 GstAmlQTDemux *demux;
zengliang.li5f31ef42024-05-16 08:27:38 +00007363
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007364 demux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +00007365
7366 GST_DEBUG_OBJECT (demux,
7367 "Received buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
7368 " offset:%" G_GUINT64_FORMAT " size:%" G_GSIZE_FORMAT " demux offset:%"
7369 G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)),
7370 GST_TIME_ARGS (GST_BUFFER_DTS (inbuf)), GST_BUFFER_OFFSET (inbuf),
7371 gst_buffer_get_size (inbuf), demux->offset);
7372
7373 if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
7374 gboolean is_gap_input = FALSE;
7375 gint i;
7376
7377 GST_DEBUG_OBJECT (demux, "Got DISCONT, marking all streams as DISCONT");
7378
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007379 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7380 AML_QTDEMUX_NTH_STREAM (demux, i)->discont = TRUE;
zengliang.li5f31ef42024-05-16 08:27:38 +00007381 }
7382
7383 /* Check if we can land back on our feet in the case where upstream is
7384 * handling the seeking/pushing of samples with gaps in between (like
7385 * in the case of trick-mode DASH for example) */
7386 if (demux->upstream_format_is_time
7387 && GST_BUFFER_OFFSET (inbuf) != GST_BUFFER_OFFSET_NONE) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007388 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007389 guint32 res;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007390 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (demux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00007391 GST_LOG_OBJECT (demux,
7392 "track-id #%u , checking if offset %" G_GUINT64_FORMAT
7393 " is a sample start", stream->track_id, GST_BUFFER_OFFSET (inbuf));
7394 res =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007395 gst_aml_qtdemux_find_index_for_given_media_offset_linear (demux,
zengliang.li5f31ef42024-05-16 08:27:38 +00007396 stream, GST_BUFFER_OFFSET (inbuf));
7397 if (res != -1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007398 AmlQtDemuxSample *sample = &stream->samples[res];
zengliang.li5f31ef42024-05-16 08:27:38 +00007399 GST_LOG_OBJECT (demux,
7400 "Checking if sample %d from track-id %u is valid (offset:%"
7401 G_GUINT64_FORMAT " size:%" G_GUINT32_FORMAT ")", res,
7402 stream->track_id, sample->offset, sample->size);
7403 if (sample->offset == GST_BUFFER_OFFSET (inbuf)) {
7404 GST_LOG_OBJECT (demux,
7405 "new buffer corresponds to a valid sample : %" G_GUINT32_FORMAT,
7406 res);
7407 is_gap_input = TRUE;
7408 /* We can go back to standard playback mode */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007409 demux->state = AML_QTDEMUX_STATE_MOVIE;
zengliang.li5f31ef42024-05-16 08:27:38 +00007410 /* Remember which sample this stream is at */
7411 stream->sample_index = res;
7412 /* Finally update all push-based values to the expected values */
7413 demux->neededbytes = stream->samples[res].size;
7414 demux->offset = GST_BUFFER_OFFSET (inbuf);
7415 demux->mdatleft =
7416 demux->mdatsize - demux->offset + demux->mdatoffset;
7417 demux->todrop = 0;
7418 }
7419 }
7420 }
7421 if (!is_gap_input) {
7422 GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
7423 /* Reset state if it's a real discont */
7424 demux->neededbytes = 16;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007425 demux->state = AML_QTDEMUX_STATE_INITIAL;
zengliang.li5f31ef42024-05-16 08:27:38 +00007426 demux->offset = GST_BUFFER_OFFSET (inbuf);
7427 gst_adapter_clear (demux->adapter);
7428 }
7429 }
7430 /* Reverse fragmented playback, need to flush all we have before
7431 * consuming a new fragment.
7432 * The samples array have the timestamps calculated by accumulating the
7433 * durations but this won't work for reverse playback of fragments as
7434 * the timestamps of a subsequent fragment should be smaller than the
7435 * previously received one. */
7436 if (!is_gap_input && demux->fragmented && demux->segment.rate < 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007437 gst_aml_qtdemux_process_adapter (demux, TRUE);
zengliang.li5f31ef42024-05-16 08:27:38 +00007438 g_ptr_array_foreach (demux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007439 (GFunc) gst_aml_qtdemux_stream_flush_samples_data, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00007440 }
7441 }
7442
7443 gst_adapter_push (demux->adapter, inbuf);
7444
7445 GST_DEBUG_OBJECT (demux,
7446 "pushing in inbuf %p, neededbytes:%u, available:%" G_GSIZE_FORMAT, inbuf,
7447 demux->neededbytes, gst_adapter_available (demux->adapter));
7448
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007449 return gst_aml_qtdemux_process_adapter (demux, FALSE);
zengliang.li5f31ef42024-05-16 08:27:38 +00007450}
7451
7452static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007453gst_aml_qtdemux_process_adapter (GstAmlQTDemux * demux, gboolean force)
zengliang.li5f31ef42024-05-16 08:27:38 +00007454{
7455 GstFlowReturn ret = GST_FLOW_OK;
7456
7457 /* we never really mean to buffer that much */
7458 if (demux->neededbytes == -1) {
7459 goto eos;
7460 }
7461
7462 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
7463 (ret == GST_FLOW_OK || (ret == GST_FLOW_NOT_LINKED && force))) {
7464
7465#ifndef GST_DISABLE_GST_DEBUG
7466 {
7467 guint64 discont_offset, distance_from_discont;
7468
7469 discont_offset = gst_adapter_offset_at_discont (demux->adapter);
7470 distance_from_discont =
7471 gst_adapter_distance_from_discont (demux->adapter);
7472
7473 GST_DEBUG_OBJECT (demux,
7474 "state:%s , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT
7475 " adapter offset :%" G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007476 " bytes)", aml_qt_demux_state_string (demux->state), demux->neededbytes,
zengliang.li5f31ef42024-05-16 08:27:38 +00007477 demux->offset, discont_offset, distance_from_discont);
7478 }
7479#endif
7480
7481 switch (demux->state) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007482 case AML_QTDEMUX_STATE_INITIAL:{
zengliang.li5f31ef42024-05-16 08:27:38 +00007483 const guint8 *data;
7484 guint32 fourcc;
7485 guint64 size;
7486
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007487 gst_aml_qtdemux_check_seekability (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007488
7489 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7490
7491 /* get fourcc/length, set neededbytes */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007492 aml_extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
zengliang.li5f31ef42024-05-16 08:27:38 +00007493 &size, &fourcc);
7494 gst_adapter_unmap (demux->adapter);
7495 data = NULL;
7496 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
7497 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
7498 if (size == 0) {
7499 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7500 (_("This file is invalid and cannot be played.")),
7501 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
7502 GST_FOURCC_ARGS (fourcc)));
7503 ret = GST_FLOW_ERROR;
7504 break;
7505 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007506 if (fourcc == AML_FOURCC_mdat) {
7507 gint next_entry = aml_next_entry_size (demux);
7508 if (AML_QTDEMUX_N_STREAMS (demux) > 0 && (next_entry != -1
zengliang.li5f31ef42024-05-16 08:27:38 +00007509 || !demux->fragmented)) {
7510 /* we have the headers, start playback */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007511 demux->state = AML_QTDEMUX_STATE_MOVIE;
zengliang.li5f31ef42024-05-16 08:27:38 +00007512 demux->neededbytes = next_entry;
7513 demux->mdatleft = size;
7514 demux->mdatsize = demux->mdatleft;
7515 } else {
7516 /* no headers yet, try to get them */
7517 guint bs;
7518 gboolean res;
7519 guint64 old, target;
7520
7521 buffer_data:
7522 old = demux->offset;
7523 target = old + size;
7524
7525 /* try to jump over the atom with a seek */
7526 /* only bother if it seems worth doing so,
7527 * and avoids possible upstream/server problems */
7528 if (demux->upstream_seekable &&
7529 demux->upstream_size > 4 * (1 << 20)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007530 res = aml_qtdemux_seek_offset (demux, target);
zengliang.li5f31ef42024-05-16 08:27:38 +00007531 } else {
7532 GST_DEBUG_OBJECT (demux, "skipping seek");
7533 res = FALSE;
7534 }
7535
7536 if (res) {
7537 GST_DEBUG_OBJECT (demux, "seek success");
7538 /* remember the offset fo the first mdat so we can seek back to it
7539 * after we have the headers */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007540 if (fourcc == AML_FOURCC_mdat && demux->first_mdat == -1) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007541 demux->first_mdat = old;
7542 GST_DEBUG_OBJECT (demux, "first mdat at %" G_GUINT64_FORMAT,
7543 demux->first_mdat);
7544 }
7545 /* seek worked, continue reading */
7546 demux->offset = target;
7547 demux->neededbytes = 16;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007548 demux->state = AML_QTDEMUX_STATE_INITIAL;
zengliang.li5f31ef42024-05-16 08:27:38 +00007549 } else {
7550 /* seek failed, need to buffer */
7551 demux->offset = old;
7552 GST_DEBUG_OBJECT (demux, "seek failed/skipped");
7553 /* there may be multiple mdat (or alike) buffers */
7554 /* sanity check */
7555 if (demux->mdatbuffer)
7556 bs = gst_buffer_get_size (demux->mdatbuffer);
7557 else
7558 bs = 0;
7559 if (size + bs > 10 * (1 << 20))
7560 goto no_moov;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007561 demux->state = AML_QTDEMUX_STATE_BUFFER_MDAT;
zengliang.li5f31ef42024-05-16 08:27:38 +00007562 demux->neededbytes = size;
7563 if (!demux->mdatbuffer)
7564 demux->mdatoffset = demux->offset;
7565 }
7566 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007567 } else if (G_UNLIKELY (size > AML_QTDEMUX_MAX_ATOM_SIZE)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007568 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7569 (_("This file is invalid and cannot be played.")),
7570 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
7571 GST_FOURCC_ARGS (fourcc), size));
7572 ret = GST_FLOW_ERROR;
7573 break;
7574 } else {
7575 /* this means we already started buffering and still no moov header,
7576 * let's continue buffering everything till we get moov */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007577 if (demux->mdatbuffer && !(fourcc == AML_FOURCC_moov
7578 || fourcc == AML_FOURCC_moof))
zengliang.li5f31ef42024-05-16 08:27:38 +00007579 goto buffer_data;
7580 demux->neededbytes = size;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007581 demux->state = AML_QTDEMUX_STATE_HEADER;
zengliang.li5f31ef42024-05-16 08:27:38 +00007582 }
7583 break;
7584 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007585 case AML_QTDEMUX_STATE_HEADER:{
zengliang.li5f31ef42024-05-16 08:27:38 +00007586 const guint8 *data;
7587 guint32 fourcc;
7588
7589 GST_DEBUG_OBJECT (demux, "In header");
7590
7591 data = gst_adapter_map (demux->adapter, demux->neededbytes);
7592
7593 /* parse the header */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007594 aml_extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
zengliang.li5f31ef42024-05-16 08:27:38 +00007595 &fourcc);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007596 if (fourcc == AML_FOURCC_moov) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007597 /* in usual fragmented setup we could try to scan for more
7598 * and end up at the the moov (after mdat) again */
xuesong.jianga8fbc442024-09-26 20:31:31 +08007599 if (demux->got_moov && AML_QTDEMUX_N_STREAMS (demux) > 0 && !demux->fragmented) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007600 GST_DEBUG_OBJECT (demux,
7601 "Skipping moov atom as we have (this) one already");
7602 } else {
7603 GST_DEBUG_OBJECT (demux, "Parsing [moov]");
7604
7605 if (demux->got_moov && demux->fragmented) {
7606 GST_DEBUG_OBJECT (demux,
7607 "Got a second moov, clean up data from old one");
7608 if (demux->moov_node_compressed) {
7609 g_node_destroy (demux->moov_node_compressed);
7610 if (demux->moov_node)
7611 g_free (demux->moov_node->data);
7612 }
7613 demux->moov_node_compressed = NULL;
7614 if (demux->moov_node)
7615 g_node_destroy (demux->moov_node);
7616 demux->moov_node = NULL;
7617 }
7618
7619 demux->last_moov_offset = demux->offset;
7620
7621 /* Update streams with new moov */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007622 gst_aml_qtdemux_stream_concat (demux,
zengliang.li5f31ef42024-05-16 08:27:38 +00007623 demux->old_streams, demux->active_streams);
7624
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007625 aml_qtdemux_parse_moov (demux, data, demux->neededbytes);
7626 aml_qtdemux_node_dump (demux, demux->moov_node);
7627 aml_qtdemux_parse_tree (demux);
7628 aml_qtdemux_prepare_streams (demux);
7629 AML_QTDEMUX_EXPOSE_LOCK (demux);
7630 aml_qtdemux_expose_streams (demux);
7631 AML_QTDEMUX_EXPOSE_UNLOCK (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007632
7633 demux->got_moov = TRUE;
7634
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007635 gst_aml_qtdemux_check_send_pending_segment (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007636
zengliang.lif09b73f2024-05-17 06:22:11 +00007637 /* check sync and set multiqueue */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007638 if (2 == AML_QTDEMUX_N_STREAMS (demux))
zengliang.lif09b73f2024-05-17 06:22:11 +00007639 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007640 AmlQtDemuxStream *stream_1 = AML_QTDEMUX_NTH_STREAM (demux, 0);
7641 AmlQtDemuxStream *stream_2 = AML_QTDEMUX_NTH_STREAM (demux, 1);
zengliang.lif09b73f2024-05-17 06:22:11 +00007642 if ((0 == strncmp(GST_PAD_NAME (stream_1->pad), "video", 5) && 0 == strncmp(GST_PAD_NAME (stream_2->pad), "audio", 5))
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007643 || (0 == strncmp(GST_PAD_NAME (stream_1->pad), "audio", 5) && 0 == strncmp(GST_PAD_NAME (stream_2->pad), "video", 5)))
zengliang.lif09b73f2024-05-17 06:22:11 +00007644 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007645 GstStructure *s;
7646 GstEvent *event;
7647 s = gst_structure_new_empty ("AML-SET-MAX-BYTE-SIZE");
7648 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, s);
7649 GST_DEBUG("Send SET-MAX-BYTE-SIZE Event");
7650 gst_pad_push_event (stream_1->pad, event);
zengliang.lif09b73f2024-05-17 06:22:11 +00007651 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007652 }
zengliang.lif09b73f2024-05-17 06:22:11 +00007653
zengliang.li5f31ef42024-05-16 08:27:38 +00007654 if (demux->moov_node_compressed) {
7655 g_node_destroy (demux->moov_node_compressed);
7656 g_free (demux->moov_node->data);
7657 }
7658 demux->moov_node_compressed = NULL;
7659 g_node_destroy (demux->moov_node);
7660 demux->moov_node = NULL;
7661 GST_DEBUG_OBJECT (demux, "Finished parsing the header");
7662 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007663 } else if (fourcc == AML_FOURCC_moof) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007664 if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
7665 guint64 dist = 0;
7666 GstClockTime prev_pts;
7667 guint64 prev_offset;
7668 guint64 adapter_discont_offset, adapter_discont_dist;
7669
7670 GST_DEBUG_OBJECT (demux, "Parsing [moof]");
7671
7672 /*
7673 * The timestamp of the moof buffer is relevant as some scenarios
7674 * won't have the initial timestamp in the atoms. Whenever a new
7675 * buffer has started, we get that buffer's PTS and use it as a base
7676 * timestamp for the trun entries.
7677 *
7678 * To keep track of the current buffer timestamp and starting point
7679 * we use gst_adapter_prev_pts that gives us the PTS and the distance
7680 * from the beginning of the buffer, with the distance and demux->offset
7681 * we know if it is still the same buffer or not.
7682 */
7683 prev_pts = gst_adapter_prev_pts (demux->adapter, &dist);
7684 prev_offset = demux->offset - dist;
7685 if (demux->fragment_start_offset == -1
7686 || prev_offset > demux->fragment_start_offset) {
7687 demux->fragment_start_offset = prev_offset;
7688 demux->fragment_start = prev_pts;
zengliang.lid0c84c32024-05-17 03:33:20 +00007689 if (demux->cal_discontinuity_pos)
7690 {
7691 demux->discontinuity_base_pos = demux->fragment_start;
7692 }
7693
zengliang.li5f31ef42024-05-16 08:27:38 +00007694 GST_DEBUG_OBJECT (demux,
7695 "New fragment start found at: %" G_GUINT64_FORMAT " : %"
7696 GST_TIME_FORMAT, demux->fragment_start_offset,
7697 GST_TIME_ARGS (demux->fragment_start));
7698 }
7699
7700 /* We can't use prev_offset() here because this would require
7701 * upstream to set consistent and correct offsets on all buffers
7702 * since the discont. Nothing ever did that in the past and we
7703 * would break backwards compatibility here then.
7704 * Instead take the offset we had at the last discont and count
7705 * the bytes from there. This works with old code as there would
7706 * be no discont between moov and moof, and also works with
7707 * adaptivedemux which correctly sets offset and will set the
7708 * DISCONT flag accordingly when needed.
7709 *
7710 * We also only do this for upstream TIME segments as otherwise
7711 * there are potential backwards compatibility problems with
7712 * seeking in PUSH mode and upstream providing inconsistent
7713 * timestamps. */
7714 adapter_discont_offset =
7715 gst_adapter_offset_at_discont (demux->adapter);
7716 adapter_discont_dist =
7717 gst_adapter_distance_from_discont (demux->adapter);
7718
7719 GST_DEBUG_OBJECT (demux,
7720 "demux offset %" G_GUINT64_FORMAT " adapter offset %"
7721 G_GUINT64_FORMAT " (+ %" G_GUINT64_FORMAT " bytes)",
7722 demux->offset, adapter_discont_offset, adapter_discont_dist);
7723
7724 if (demux->upstream_format_is_time) {
7725 demux->moof_offset = adapter_discont_offset;
7726 if (demux->moof_offset != GST_BUFFER_OFFSET_NONE)
7727 demux->moof_offset += adapter_discont_dist;
7728 if (demux->moof_offset == GST_BUFFER_OFFSET_NONE)
7729 demux->moof_offset = demux->offset;
7730 } else {
7731 demux->moof_offset = demux->offset;
7732 }
7733
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007734 if (!aml_qtdemux_parse_moof (demux, data, demux->neededbytes,
zengliang.li5f31ef42024-05-16 08:27:38 +00007735 demux->moof_offset, NULL)) {
7736 gst_adapter_unmap (demux->adapter);
7737 ret = GST_FLOW_ERROR;
7738 goto done;
7739 }
7740
7741 /* in MSS we need to expose the pads after the first moof as we won't get a moov */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007742 if (demux->variant == AML_VARIANT_MSS_FRAGMENTED && !demux->exposed) {
7743 AML_QTDEMUX_EXPOSE_LOCK (demux);
7744 aml_qtdemux_expose_streams (demux);
7745 AML_QTDEMUX_EXPOSE_UNLOCK (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007746 }
7747
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007748 gst_aml_qtdemux_check_send_pending_segment (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007749 } else {
7750 GST_DEBUG_OBJECT (demux, "Discarding [moof]");
7751 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007752 } else if (fourcc == AML_FOURCC_ftyp) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007753 GST_DEBUG_OBJECT (demux, "Parsing [ftyp]");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007754 aml_qtdemux_parse_ftyp (demux, data, demux->neededbytes);
7755 } else if (fourcc == AML_FOURCC_uuid) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007756 GST_DEBUG_OBJECT (demux, "Parsing [uuid]");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007757 aml_qtdemux_parse_uuid (demux, data, demux->neededbytes);
7758 } else if (fourcc == AML_FOURCC_sidx) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007759 GST_DEBUG_OBJECT (demux, "Parsing [sidx]");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007760 aml_qtdemux_parse_sidx (demux, data, demux->neededbytes);
zengliang.li5f31ef42024-05-16 08:27:38 +00007761 } else {
7762 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007763 case AML_FOURCC_styp:
zengliang.li5f31ef42024-05-16 08:27:38 +00007764 /* [styp] is like a [ftyp], but in fragment header. We ignore it for now
7765 * FALLTHROUGH */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007766 case AML_FOURCC_skip:
7767 case AML_FOURCC_free:
zengliang.li5f31ef42024-05-16 08:27:38 +00007768 /* [free] and [skip] are padding atoms */
7769 GST_DEBUG_OBJECT (demux,
7770 "Skipping fourcc while parsing header : %" GST_FOURCC_FORMAT,
7771 GST_FOURCC_ARGS (fourcc));
7772 break;
7773 default:
7774 GST_WARNING_OBJECT (demux,
7775 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
7776 GST_FOURCC_ARGS (fourcc));
7777 /* Let's jump that one and go back to initial state */
7778 break;
7779 }
7780 }
7781 gst_adapter_unmap (demux->adapter);
7782 data = NULL;
7783
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007784 if (demux->mdatbuffer && AML_QTDEMUX_N_STREAMS (demux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007785 gsize remaining_data_size = 0;
7786
7787 /* the mdat was before the header */
7788 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007789 AML_QTDEMUX_N_STREAMS (demux), demux->mdatbuffer);
zengliang.li5f31ef42024-05-16 08:27:38 +00007790 /* restore our adapter/offset view of things with upstream;
7791 * put preceding buffered data ahead of current moov data.
7792 * This should also handle evil mdat, moov, mdat cases and alike */
7793 gst_adapter_flush (demux->adapter, demux->neededbytes);
7794
7795 /* Store any remaining data after the mdat for later usage */
7796 remaining_data_size = gst_adapter_available (demux->adapter);
7797 if (remaining_data_size > 0) {
7798 g_assert (demux->restoredata_buffer == NULL);
7799 demux->restoredata_buffer =
7800 gst_adapter_take_buffer (demux->adapter, remaining_data_size);
7801 demux->restoredata_offset = demux->offset + demux->neededbytes;
7802 GST_DEBUG_OBJECT (demux,
7803 "Stored %" G_GSIZE_FORMAT " post mdat bytes at offset %"
7804 G_GUINT64_FORMAT, remaining_data_size,
7805 demux->restoredata_offset);
7806 }
7807
7808 gst_adapter_push (demux->adapter, demux->mdatbuffer);
7809 demux->mdatbuffer = NULL;
7810 demux->offset = demux->mdatoffset;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007811 demux->neededbytes = aml_next_entry_size (demux);
7812 demux->state = AML_QTDEMUX_STATE_MOVIE;
zengliang.li5f31ef42024-05-16 08:27:38 +00007813 demux->mdatleft = gst_adapter_available (demux->adapter);
7814 demux->mdatsize = demux->mdatleft;
7815 } else {
7816 GST_DEBUG_OBJECT (demux, "Carrying on normally");
7817 gst_adapter_flush (demux->adapter, demux->neededbytes);
7818
7819 /* only go back to the mdat if there are samples to play */
7820 if (demux->got_moov && demux->first_mdat != -1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007821 && aml_has_next_entry (demux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007822 gboolean res;
7823
7824 /* we need to seek back */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007825 res = aml_qtdemux_seek_offset (demux, demux->first_mdat);
zengliang.li5f31ef42024-05-16 08:27:38 +00007826 if (res) {
7827 demux->offset = demux->first_mdat;
7828 } else {
7829 GST_DEBUG_OBJECT (demux, "Seek back failed");
7830 }
7831 } else {
7832 demux->offset += demux->neededbytes;
7833 }
7834 demux->neededbytes = 16;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007835 demux->state = AML_QTDEMUX_STATE_INITIAL;
zengliang.li5f31ef42024-05-16 08:27:38 +00007836 }
7837
7838 break;
7839 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007840 case AML_QTDEMUX_STATE_BUFFER_MDAT:{
zengliang.li5f31ef42024-05-16 08:27:38 +00007841 GstBuffer *buf;
7842 guint8 fourcc[4];
7843
7844 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
7845 demux->offset);
7846 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
7847 gst_buffer_extract (buf, 0, fourcc, 4);
7848 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007849 GST_FOURCC_ARGS (AML_QT_FOURCC (fourcc)));
zengliang.li5f31ef42024-05-16 08:27:38 +00007850 if (demux->mdatbuffer)
7851 demux->mdatbuffer = gst_buffer_append (demux->mdatbuffer, buf);
7852 else
7853 demux->mdatbuffer = buf;
7854 demux->offset += demux->neededbytes;
7855 demux->neededbytes = 16;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007856 demux->state = AML_QTDEMUX_STATE_INITIAL;
7857 gst_aml_qtdemux_post_progress (demux, 1, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +00007858
7859 break;
7860 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007861 case AML_QTDEMUX_STATE_MOVIE:{
7862 AmlQtDemuxStream *stream = NULL;
7863 AmlQtDemuxSample *sample;
zengliang.li5f31ef42024-05-16 08:27:38 +00007864 GstClockTime dts, pts, duration;
7865 gboolean keyframe;
7866 gint i;
7867
7868 GST_DEBUG_OBJECT (demux,
7869 "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
7870
7871 if (demux->fragmented) {
7872 GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
7873 demux->mdatleft);
7874 if (G_LIKELY (demux->todrop < demux->mdatleft)) {
7875 /* if needed data starts within this atom,
7876 * then it should not exceed this atom */
7877 if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
7878 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
7879 (_("This file is invalid and cannot be played.")),
7880 ("sample data crosses atom boundary"));
7881 ret = GST_FLOW_ERROR;
7882 break;
7883 }
7884 demux->mdatleft -= demux->neededbytes;
7885 } else {
7886 GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
7887 /* so we are dropping more than left in this atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007888 gst_aml_qtdemux_drop_data (demux, demux->mdatleft);
zengliang.li5f31ef42024-05-16 08:27:38 +00007889 demux->mdatleft = 0;
7890
7891 /* need to resume atom parsing so we do not miss any other pieces */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007892 demux->state = AML_QTDEMUX_STATE_INITIAL;
zengliang.li5f31ef42024-05-16 08:27:38 +00007893 demux->neededbytes = 16;
7894
7895 /* check if there was any stored post mdat data from previous buffers */
7896 if (demux->restoredata_buffer) {
7897 g_assert (gst_adapter_available (demux->adapter) == 0);
7898
7899 gst_adapter_push (demux->adapter, demux->restoredata_buffer);
7900 demux->restoredata_buffer = NULL;
7901 demux->offset = demux->restoredata_offset;
7902 }
7903
7904 break;
7905 }
7906 }
7907
7908 if (demux->todrop) {
7909 if (demux->cenc_aux_info_offset > 0) {
7910 GstByteReader br;
7911 const guint8 *data;
7912
7913 GST_DEBUG_OBJECT (demux, "parsing cenc auxiliary info");
7914 data = gst_adapter_map (demux->adapter, demux->todrop);
7915 gst_byte_reader_init (&br, data + 8, demux->todrop);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007916 if (!aml_qtdemux_parse_cenc_aux_info (demux,
7917 AML_QTDEMUX_NTH_STREAM (demux, 0), &br,
zengliang.li5f31ef42024-05-16 08:27:38 +00007918 demux->cenc_aux_info_sizes, demux->cenc_aux_sample_count)) {
7919 GST_ERROR_OBJECT (demux, "failed to parse cenc auxiliary info");
7920 ret = GST_FLOW_ERROR;
7921 gst_adapter_unmap (demux->adapter);
7922 g_free (demux->cenc_aux_info_sizes);
7923 demux->cenc_aux_info_sizes = NULL;
7924 goto done;
7925 }
7926 demux->cenc_aux_info_offset = 0;
7927 g_free (demux->cenc_aux_info_sizes);
7928 demux->cenc_aux_info_sizes = NULL;
7929 gst_adapter_unmap (demux->adapter);
7930 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007931 gst_aml_qtdemux_drop_data (demux, demux->todrop);
zengliang.li5f31ef42024-05-16 08:27:38 +00007932 }
7933
7934 /* first buffer? */
7935 /* initial newsegment sent here after having added pads,
7936 * possible others in sink_event */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007937 gst_aml_qtdemux_check_send_pending_segment (demux);
zengliang.li5f31ef42024-05-16 08:27:38 +00007938
7939 /* Figure out which stream this packet belongs to */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007940 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7941 stream = AML_QTDEMUX_NTH_STREAM (demux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00007942 if (stream->sample_index >= stream->n_samples) {
7943 /* reset to be checked below G_UNLIKELY (stream == NULL) */
7944 stream = NULL;
7945 continue;
7946 }
7947 GST_LOG_OBJECT (demux,
7948 "Checking track-id %u (sample_index:%d / offset:%"
7949 G_GUINT64_FORMAT " / size:%d)", stream->track_id,
7950 stream->sample_index,
7951 stream->samples[stream->sample_index].offset,
7952 stream->samples[stream->sample_index].size);
7953
7954 if (stream->samples[stream->sample_index].offset == demux->offset)
7955 break;
7956 }
7957
7958 if (G_UNLIKELY (stream == NULL))
7959 goto unknown_stream;
7960
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007961 gst_aml_qtdemux_stream_check_and_change_stsd_index (demux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00007962
7963 if (stream->new_caps) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007964 gst_aml_qtdemux_configure_stream (demux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00007965 }
7966
7967 /* Put data in a buffer, set timestamps, caps, ... */
7968 sample = &stream->samples[stream->sample_index];
7969
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007970 if (G_LIKELY (!(AML_STREAM_IS_EOS (stream)))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007971 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007972 GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc));
zengliang.li5f31ef42024-05-16 08:27:38 +00007973
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007974 dts = AML_QTSAMPLE_DTS (stream, sample);
7975 pts = AML_QTSAMPLE_PTS (stream, sample);
7976 duration = AML_QTSAMPLE_DUR_DTS (stream, sample, dts);
7977 keyframe = AML_QTSAMPLE_KEYFRAME (stream, sample);
zengliang.li5f31ef42024-05-16 08:27:38 +00007978
7979 /* check for segment end */
zengliang.li4681ee42024-05-16 12:25:30 +00007980 if (G_UNLIKELY (demux->segment.stop != -1 && GST_CLOCK_TIME_IS_VALID(dts)
zengliang.li5f31ef42024-05-16 08:27:38 +00007981 && demux->segment.stop <= pts && stream->on_keyframe)
7982 && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
7983 GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
7984 stream->time_position = GST_CLOCK_TIME_NONE; /* this means EOS */
7985
7986 /* skip this data, stream is EOS */
7987 gst_adapter_flush (demux->adapter, demux->neededbytes);
7988 demux->offset += demux->neededbytes;
7989
7990 /* check if all streams are eos */
7991 ret = GST_FLOW_EOS;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00007992 for (i = 0; i < AML_QTDEMUX_N_STREAMS (demux); i++) {
7993 if (!AML_STREAM_IS_EOS (AML_QTDEMUX_NTH_STREAM (demux, i))) {
zengliang.li5f31ef42024-05-16 08:27:38 +00007994 ret = GST_FLOW_OK;
7995 break;
7996 }
7997 }
7998 } else {
7999 GstBuffer *outbuf;
8000
8001 outbuf =
8002 gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
8003
8004 /* FIXME: should either be an assert or a plain check */
8005 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
8006
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008007 ret = gst_aml_qtdemux_decorate_and_push_buffer (demux, stream, outbuf,
zengliang.li5f31ef42024-05-16 08:27:38 +00008008 dts, pts, duration, keyframe, dts, demux->offset);
8009 }
8010
8011 /* combine flows */
8012 GST_OBJECT_LOCK (demux);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008013 ret = gst_aml_qtdemux_combine_flows (demux, stream, ret);
zengliang.li5f31ef42024-05-16 08:27:38 +00008014 GST_OBJECT_UNLOCK (demux);
8015 } else {
8016 /* skip this data, stream is EOS */
8017 gst_adapter_flush (demux->adapter, demux->neededbytes);
8018 }
8019
8020 stream->sample_index++;
8021 stream->offset_in_sample = 0;
8022
8023 /* update current offset and figure out size of next buffer */
8024 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
8025 demux->offset, demux->neededbytes);
8026 demux->offset += demux->neededbytes;
8027 GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
8028 demux->offset);
8029
8030
8031 if (ret == GST_FLOW_EOS) {
8032 GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
8033 demux->neededbytes = -1;
8034 goto eos;
8035 }
8036
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008037 if ((demux->neededbytes = aml_next_entry_size (demux)) == -1) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008038 if (demux->fragmented) {
8039 GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
8040 /* there may be more to follow, only finish this atom */
8041 demux->todrop = demux->mdatleft;
8042 demux->neededbytes = demux->todrop;
8043 break;
8044 }
8045 goto eos;
8046 }
8047 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) {
8048 goto non_ok_unlinked_flow;
8049 }
8050 break;
8051 }
8052 default:
8053 goto invalid_state;
8054 }
8055 }
8056
8057 /* when buffering movie data, at least show user something is happening */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008058 if (ret == GST_FLOW_OK && demux->state == AML_QTDEMUX_STATE_BUFFER_MDAT &&
zengliang.li5f31ef42024-05-16 08:27:38 +00008059 gst_adapter_available (demux->adapter) <= demux->neededbytes) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008060 gst_aml_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter),
zengliang.li5f31ef42024-05-16 08:27:38 +00008061 demux->neededbytes);
8062 }
8063done:
8064
8065 return ret;
8066
8067 /* ERRORS */
8068non_ok_unlinked_flow:
8069 {
8070 GST_DEBUG_OBJECT (demux, "Stopping, combined return flow %s",
8071 gst_flow_get_name (ret));
8072 return ret;
8073 }
8074unknown_stream:
8075 {
8076 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
8077 ret = GST_FLOW_ERROR;
8078 goto done;
8079 }
8080eos:
8081 {
8082 GST_DEBUG_OBJECT (demux, "no next entry, EOS");
8083 ret = GST_FLOW_EOS;
8084 goto done;
8085 }
8086invalid_state:
8087 {
8088 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8089 (NULL), ("qtdemuxer invalid state %d", demux->state));
8090 ret = GST_FLOW_ERROR;
8091 goto done;
8092 }
8093no_moov:
8094 {
8095 GST_ELEMENT_ERROR (demux, STREAM, FAILED,
8096 (NULL), ("no 'moov' atom within the first 10 MB"));
8097 ret = GST_FLOW_ERROR;
8098 goto done;
8099 }
8100}
8101
8102static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008103aml_qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent)
zengliang.li5f31ef42024-05-16 08:27:38 +00008104{
8105 GstQuery *query;
8106 gboolean pull_mode;
8107
8108 query = gst_query_new_scheduling ();
8109
8110 if (!gst_pad_peer_query (sinkpad, query)) {
8111 gst_query_unref (query);
8112 goto activate_push;
8113 }
8114
8115 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
8116 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
8117 gst_query_unref (query);
8118
8119 if (!pull_mode)
8120 goto activate_push;
8121
8122 GST_DEBUG_OBJECT (sinkpad, "activating pull");
8123 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
8124
8125activate_push:
8126 {
8127 GST_DEBUG_OBJECT (sinkpad, "activating push");
8128 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
8129 }
8130}
8131
8132static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008133aml_qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
zengliang.li5f31ef42024-05-16 08:27:38 +00008134 GstPadMode mode, gboolean active)
8135{
8136 gboolean res;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008137 GstAmlQTDemux *demux = GST_AML_QTDEMUX (parent);
zengliang.li5f31ef42024-05-16 08:27:38 +00008138
8139 switch (mode) {
8140 case GST_PAD_MODE_PUSH:
8141 demux->pullbased = FALSE;
8142 res = TRUE;
8143 break;
8144 case GST_PAD_MODE_PULL:
8145 if (active) {
8146 demux->pullbased = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008147 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_aml_qtdemux_loop,
zengliang.li5f31ef42024-05-16 08:27:38 +00008148 sinkpad, NULL);
8149 } else {
8150 res = gst_pad_stop_task (sinkpad);
8151 }
8152 break;
8153 default:
8154 res = FALSE;
8155 break;
8156 }
8157 return res;
8158}
8159
8160#ifdef HAVE_ZLIB
8161static void *
8162qtdemux_inflate (void *z_buffer, guint z_length, guint * length)
8163{
8164 guint8 *buffer;
8165 z_stream z;
8166 int ret;
8167
8168 memset (&z, 0, sizeof (z));
8169 z.zalloc = NULL;
8170 z.zfree = NULL;
8171 z.opaque = NULL;
8172
8173 if ((ret = inflateInit (&z)) != Z_OK) {
8174 GST_ERROR ("inflateInit() returned %d", ret);
8175 return NULL;
8176 }
8177
8178 z.next_in = z_buffer;
8179 z.avail_in = z_length;
8180
8181 buffer = (guint8 *) g_malloc (*length);
8182 z.avail_out = *length;
8183 z.next_out = (Bytef *) buffer;
8184 do {
8185 ret = inflate (&z, Z_NO_FLUSH);
8186 if (ret == Z_STREAM_END) {
8187 break;
8188 } else if (ret != Z_OK) {
8189 GST_WARNING ("inflate() returned %d", ret);
8190 break;
8191 }
8192
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008193 if (*length > G_MAXUINT - 4096 || *length > AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008194 GST_WARNING ("too big decompressed data");
8195 ret = Z_MEM_ERROR;
8196 break;
8197 }
8198
8199 *length += 4096;
8200 buffer = (guint8 *) g_realloc (buffer, *length);
8201 z.next_out = (Bytef *) (buffer + z.total_out);
8202 z.avail_out += *length - z.total_out;
8203 } while (z.avail_in > 0);
8204
8205 if (ret != Z_STREAM_END) {
8206 g_free (buffer);
8207 buffer = NULL;
8208 *length = 0;
8209 } else {
8210 *length = z.total_out;
8211 }
8212
8213 inflateEnd (&z);
8214
8215 return buffer;
8216}
8217#endif /* HAVE_ZLIB */
8218
8219static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008220aml_qtdemux_parse_moov (GstAmlQTDemux * qtdemux, const guint8 * buffer, guint length)
zengliang.li5f31ef42024-05-16 08:27:38 +00008221{
8222 GNode *cmov;
8223
8224 qtdemux->moov_node = g_node_new ((guint8 *) buffer);
8225
8226 /* counts as header data */
8227 qtdemux->header_size += length;
8228
8229 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008230 aml_qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
zengliang.li5f31ef42024-05-16 08:27:38 +00008231
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008232 cmov = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_cmov);
zengliang.li5f31ef42024-05-16 08:27:38 +00008233 if (cmov) {
8234 guint32 method;
8235 GNode *dcom;
8236 GNode *cmvd;
8237 guint32 dcom_len;
8238
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008239 dcom = aml_qtdemux_tree_get_child_by_type (cmov, AML_FOURCC_dcom);
8240 cmvd = aml_qtdemux_tree_get_child_by_type (cmov, AML_FOURCC_cmvd);
zengliang.li5f31ef42024-05-16 08:27:38 +00008241 if (dcom == NULL || cmvd == NULL)
8242 goto invalid_compression;
8243
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008244 dcom_len = AML_QT_UINT32 (dcom->data);
zengliang.li5f31ef42024-05-16 08:27:38 +00008245 if (dcom_len < 12)
8246 goto invalid_compression;
8247
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008248 method = AML_QT_FOURCC ((guint8 *) dcom->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +00008249 switch (method) {
8250#ifdef HAVE_ZLIB
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008251 case AML_FOURCC_zlib:{
zengliang.li5f31ef42024-05-16 08:27:38 +00008252 guint uncompressed_length;
8253 guint compressed_length;
8254 guint8 *buf;
8255 guint32 cmvd_len;
8256
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008257 cmvd_len = AML_QT_UINT32 ((guint8 *) cmvd->data);
zengliang.li5f31ef42024-05-16 08:27:38 +00008258 if (cmvd_len < 12)
8259 goto invalid_compression;
8260
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008261 uncompressed_length = AML_QT_UINT32 ((guint8 *) cmvd->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +00008262 compressed_length = cmvd_len - 12;
8263 GST_LOG ("length = %u", uncompressed_length);
8264
8265 buf =
8266 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
8267 compressed_length, &uncompressed_length);
8268
8269 if (buf) {
8270 qtdemux->moov_node_compressed = qtdemux->moov_node;
8271 qtdemux->moov_node = g_node_new (buf);
8272
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008273 aml_qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
zengliang.li5f31ef42024-05-16 08:27:38 +00008274 uncompressed_length);
8275 }
8276 break;
8277 }
8278#endif /* HAVE_ZLIB */
8279 default:
8280 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
8281 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
8282 break;
8283 }
8284 }
8285 return TRUE;
8286
8287 /* ERRORS */
8288invalid_compression:
8289 {
8290 GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
8291 return FALSE;
8292 }
8293}
8294
8295static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008296aml_qtdemux_parse_container (GstAmlQTDemux * qtdemux, GNode * node, const guint8 * buf,
zengliang.li5f31ef42024-05-16 08:27:38 +00008297 const guint8 * end)
8298{
8299 while (G_UNLIKELY (buf < end)) {
8300 GNode *child;
8301 guint32 len;
8302
8303 if (G_UNLIKELY (buf + 4 > end)) {
8304 GST_LOG_OBJECT (qtdemux, "buffer overrun");
8305 break;
8306 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008307 len = AML_QT_UINT32 (buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00008308 if (G_UNLIKELY (len == 0)) {
8309 GST_LOG_OBJECT (qtdemux, "empty container");
8310 break;
8311 }
8312 if (G_UNLIKELY (len < 8)) {
8313 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
8314 break;
8315 }
8316 if (G_UNLIKELY (len > (end - buf))) {
8317 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
8318 (gint) (end - buf));
8319 break;
8320 }
8321
8322 child = g_node_new ((guint8 *) buf);
8323 g_node_append (node, child);
8324 GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008325 aml_qtdemux_parse_node (qtdemux, child, buf, len);
zengliang.li5f31ef42024-05-16 08:27:38 +00008326
8327 buf += len;
8328 }
8329 return TRUE;
8330}
8331
8332static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008333aml_qtdemux_parse_theora_extension (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +00008334 GNode * xdxt)
8335{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008336 int len = AML_QT_UINT32 (xdxt->data);
zengliang.li5f31ef42024-05-16 08:27:38 +00008337 guint8 *buf = xdxt->data;
8338 guint8 *end = buf + len;
8339 GstBuffer *buffer;
8340
8341 /* skip size and type */
8342 buf += 8;
8343 end -= 8;
8344
8345 while (buf < end) {
8346 gint size;
8347 guint32 type;
8348
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008349 size = AML_QT_UINT32 (buf);
8350 type = AML_QT_FOURCC (buf + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00008351
8352 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
8353
8354 if (buf + size > end || size <= 0)
8355 break;
8356
8357 buf += 8;
8358 size -= 8;
8359
8360 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT,
8361 GST_FOURCC_ARGS (type));
8362
8363 switch (type) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008364 case AML_FOURCC_tCtH:
zengliang.li5f31ef42024-05-16 08:27:38 +00008365 buffer = gst_buffer_new_and_alloc (size);
8366 gst_buffer_fill (buffer, 0, buf, size);
8367 stream->buffers = g_slist_append (stream->buffers, buffer);
8368 GST_LOG_OBJECT (qtdemux, "parsing theora header");
8369 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008370 case AML_FOURCC_tCt_:
zengliang.li5f31ef42024-05-16 08:27:38 +00008371 buffer = gst_buffer_new_and_alloc (size);
8372 gst_buffer_fill (buffer, 0, buf, size);
8373 stream->buffers = g_slist_append (stream->buffers, buffer);
8374 GST_LOG_OBJECT (qtdemux, "parsing theora comment");
8375 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008376 case AML_FOURCC_tCtC:
zengliang.li5f31ef42024-05-16 08:27:38 +00008377 buffer = gst_buffer_new_and_alloc (size);
8378 gst_buffer_fill (buffer, 0, buf, size);
8379 stream->buffers = g_slist_append (stream->buffers, buffer);
8380 GST_LOG_OBJECT (qtdemux, "parsing theora codebook");
8381 break;
8382 default:
8383 GST_WARNING_OBJECT (qtdemux,
8384 "unknown theora cookie %" GST_FOURCC_FORMAT,
8385 GST_FOURCC_ARGS (type));
8386 break;
8387 }
8388 buf += size;
8389 }
8390 return TRUE;
8391}
8392
8393static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008394aml_qtdemux_parse_node (GstAmlQTDemux * qtdemux, GNode * node, const guint8 * buffer,
zengliang.li5f31ef42024-05-16 08:27:38 +00008395 guint length)
8396{
8397 guint32 fourcc = 0;
8398 guint32 node_length = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008399 const AmlQtNodeType *type;
zengliang.li5f31ef42024-05-16 08:27:38 +00008400 const guint8 *end;
8401
8402 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
8403
8404 if (G_UNLIKELY (length < 8))
8405 goto not_enough_data;
8406
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008407 node_length = AML_QT_UINT32 (buffer);
8408 fourcc = AML_QT_FOURCC (buffer + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +00008409
8410 /* ignore empty nodes */
8411 if (G_UNLIKELY (fourcc == 0 || node_length == 8))
8412 return TRUE;
8413
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008414 type = aml_qtdemux_type_get (fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +00008415
8416 end = buffer + length;
8417
8418 GST_LOG_OBJECT (qtdemux,
8419 "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
8420 GST_FOURCC_ARGS (fourcc), node_length, type->name);
8421
8422 if (node_length > length)
8423 goto broken_atom_size;
8424
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008425 if (type->flags & AML_QT_FLAG_CONTAINER) {
8426 aml_qtdemux_parse_container (qtdemux, node, buffer + 8, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008427 } else {
8428 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008429 case AML_FOURCC_stsd:
zengliang.li5f31ef42024-05-16 08:27:38 +00008430 {
8431 if (node_length < 20) {
8432 GST_LOG_OBJECT (qtdemux, "skipping small stsd box");
8433 break;
8434 }
8435 GST_DEBUG_OBJECT (qtdemux,
8436 "parsing stsd (sample table, sample description) atom");
8437 /* Skip over 8 byte atom hdr + 1 byte version, 3 bytes flags, 4 byte num_entries */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008438 aml_qtdemux_parse_container (qtdemux, node, buffer + 16, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008439 break;
8440 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008441 case AML_FOURCC_mp4a:
8442 case AML_FOURCC_alac:
8443 case AML_FOURCC_fLaC:
8444 case AML_FOURCC_aavd:
zengliang.li5f31ef42024-05-16 08:27:38 +00008445 {
8446 guint32 version;
8447 guint32 offset;
8448 guint min_size;
8449
8450 /* also read alac (or whatever) in stead of mp4a in the following,
8451 * since a similar layout is used in other cases as well */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008452 if (fourcc == AML_FOURCC_mp4a)
zengliang.li5f31ef42024-05-16 08:27:38 +00008453 min_size = 20;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008454 else if (fourcc == AML_FOURCC_fLaC)
zengliang.li5f31ef42024-05-16 08:27:38 +00008455 min_size = 86;
8456 else
8457 min_size = 40;
8458
8459 /* There are two things we might encounter here: a true mp4a atom, and
8460 an mp4a entry in an stsd atom. The latter is what we're interested
8461 in, and it looks like an atom, but isn't really one. The true mp4a
8462 atom is short, so we detect it based on length here. */
8463 if (length < min_size) {
8464 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8465 GST_FOURCC_ARGS (fourcc));
8466 break;
8467 }
8468
8469 /* 'version' here is the sound sample description version. Types 0 and
8470 1 are documented in the QTFF reference, but type 2 is not: it's
8471 described in Apple header files instead (struct SoundDescriptionV2
8472 in Movies.h) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008473 version = AML_QT_UINT16 (buffer + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +00008474
8475 GST_DEBUG_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT " version 0x%08x",
8476 GST_FOURCC_ARGS (fourcc), version);
8477
8478 /* parse any esds descriptors */
8479 switch (version) {
8480 case 0:
8481 offset = 0x24;
8482 break;
8483 case 1:
8484 offset = 0x34;
8485 break;
8486 case 2:
8487 offset = 0x48;
8488 break;
8489 default:
8490 GST_WARNING_OBJECT (qtdemux,
8491 "unhandled %" GST_FOURCC_FORMAT " version 0x%08x",
8492 GST_FOURCC_ARGS (fourcc), version);
8493 offset = 0;
8494 break;
8495 }
8496 if (offset)
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008497 aml_qtdemux_parse_container (qtdemux, node, buffer + offset, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008498 break;
8499 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008500 case AML_FOURCC_mp4v:
8501 case AML_FOURCC_MP4V:
8502 case AML_FOURCC_fmp4:
8503 case AML_FOURCC_FMP4:
8504 case AML_FOURCC_apcs:
8505 case AML_FOURCC_apch:
8506 case AML_FOURCC_apcn:
8507 case AML_FOURCC_apco:
8508 case AML_FOURCC_ap4h:
8509 case AML_FOURCC_xvid:
8510 case AML_FOURCC_XVID:
8511 case AML_FOURCC_H264:
8512 case AML_FOURCC_avc1:
8513 case AML_FOURCC_avc3:
8514 case AML_FOURCC_H265:
8515 case AML_FOURCC_hvc1:
8516 case AML_FOURCC_hev1:
8517 case AML_FOURCC_dvh1:
8518 case AML_FOURCC_dvhe:
8519 case AML_FOURCC_mjp2:
8520 case AML_FOURCC_encv:
zengliang.li5f31ef42024-05-16 08:27:38 +00008521 {
8522 guint32 version;
8523 guint32 str_len;
8524
8525 /* codec_data is contained inside these atoms, which all have
8526 * the same format. */
8527 /* video sample description size is 86 bytes without extension.
8528 * node_length have to be bigger than 86 bytes because video sample
8529 * description can include extensions such as esds, fiel, glbl, etc. */
8530 if (node_length < 86) {
8531 GST_WARNING_OBJECT (qtdemux, "%" GST_FOURCC_FORMAT
8532 " sample description length too short (%u < 86)",
8533 GST_FOURCC_ARGS (fourcc), node_length);
8534 break;
8535 }
8536
8537 GST_DEBUG_OBJECT (qtdemux, "parsing in %" GST_FOURCC_FORMAT,
8538 GST_FOURCC_ARGS (fourcc));
8539
8540 /* version (2 bytes) : this is set to 0, unless a compressor has changed
8541 * its data format.
8542 * revision level (2 bytes) : must be set to 0. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008543 version = AML_QT_UINT32 (buffer + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +00008544 GST_DEBUG_OBJECT (qtdemux, "version %08x", version);
8545
8546 /* compressor name : PASCAL string and informative purposes
8547 * first byte : the number of bytes to be displayed.
8548 * it has to be less than 32 because it is reserved
8549 * space of 32 bytes total including itself. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008550 str_len = AML_QT_UINT8 (buffer + 50);
zengliang.li5f31ef42024-05-16 08:27:38 +00008551 if (str_len < 32)
8552 GST_DEBUG_OBJECT (qtdemux, "compressorname = %.*s", str_len,
8553 (char *) buffer + 51);
8554 else
8555 GST_WARNING_OBJECT (qtdemux,
8556 "compressorname length too big (%u > 31)", str_len);
8557
8558 GST_MEMDUMP_OBJECT (qtdemux, "video sample description", buffer,
8559 end - buffer);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008560 aml_qtdemux_parse_container (qtdemux, node, buffer + 86, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008561 break;
8562 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008563 case AML_FOURCC_meta:
zengliang.li5f31ef42024-05-16 08:27:38 +00008564 {
8565 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom");
8566
8567 /* You are reading this correctly. QTFF specifies that the
8568 * metadata atom is a short atom, whereas ISO BMFF specifies
8569 * it's a full atom. But since so many people are doing things
8570 * differently, we actually peek into the atom to see which
8571 * variant it is */
8572 if (length < 16) {
8573 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8574 GST_FOURCC_ARGS (fourcc));
8575 break;
8576 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008577 if (AML_QT_FOURCC (buffer + 12) == AML_FOURCC_hdlr) {
8578 /* AmlVariant 1: What QTFF specifies. 'meta' is a short header which
zengliang.li5f31ef42024-05-16 08:27:38 +00008579 * starts with a 'hdlr' atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008580 aml_qtdemux_parse_container (qtdemux, node, buffer + 8, end);
8581 } else if (AML_QT_UINT32 (buffer + 8) == 0x00000000) {
8582 /* AmlVariant 2: What ISO BMFF specifies. 'meta' is a _full_ atom
zengliang.li5f31ef42024-05-16 08:27:38 +00008583 * with version/flags both set to zero */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008584 aml_qtdemux_parse_container (qtdemux, node, buffer + 12, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008585 } else
8586 GST_WARNING_OBJECT (qtdemux, "Unknown 'meta' atom format");
8587 break;
8588 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008589 case AML_FOURCC_mp4s:
zengliang.li5f31ef42024-05-16 08:27:38 +00008590 {
8591 GST_MEMDUMP_OBJECT (qtdemux, "mp4s", buffer, end - buffer);
8592 /* Skip 8 byte header, plus 8 byte version + flags + entry_count */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008593 aml_qtdemux_parse_container (qtdemux, node, buffer + 16, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008594 break;
8595 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008596 case AML_FOURCC_XiTh:
zengliang.li5f31ef42024-05-16 08:27:38 +00008597 {
8598 guint32 version;
8599 guint32 offset;
8600
8601 if (length < 16) {
8602 GST_LOG_OBJECT (qtdemux, "skipping small %" GST_FOURCC_FORMAT " box",
8603 GST_FOURCC_ARGS (fourcc));
8604 break;
8605 }
8606
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008607 version = AML_QT_UINT32 (buffer + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +00008608 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version);
8609
8610 switch (version) {
8611 case 0x00000001:
8612 offset = 0x62;
8613 break;
8614 default:
8615 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version);
8616 offset = 0;
8617 break;
8618 }
8619 if (offset) {
8620 if (length < offset) {
8621 GST_WARNING_OBJECT (qtdemux,
8622 "skipping too small %" GST_FOURCC_FORMAT " box",
8623 GST_FOURCC_ARGS (fourcc));
8624 break;
8625 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008626 aml_qtdemux_parse_container (qtdemux, node, buffer + offset, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008627 }
8628 break;
8629 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008630 case AML_FOURCC_in24:
zengliang.li5f31ef42024-05-16 08:27:38 +00008631 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008632 aml_qtdemux_parse_container (qtdemux, node, buffer + 0x34, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008633 break;
8634 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008635 case AML_FOURCC_uuid:
zengliang.li5f31ef42024-05-16 08:27:38 +00008636 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008637 aml_qtdemux_parse_uuid (qtdemux, buffer, end - buffer);
zengliang.li5f31ef42024-05-16 08:27:38 +00008638 break;
8639 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008640 case AML_FOURCC_enca:
zengliang.li5f31ef42024-05-16 08:27:38 +00008641 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008642 aml_qtdemux_parse_container (qtdemux, node, buffer + 36, end);
zengliang.li5f31ef42024-05-16 08:27:38 +00008643 break;
8644 }
8645 default:
8646 if (!strcmp (type->name, "unknown"))
8647 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
8648 break;
8649 }
8650 }
8651 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT "'",
8652 GST_FOURCC_ARGS (fourcc));
8653 return TRUE;
8654
8655/* ERRORS */
8656not_enough_data:
8657 {
8658 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8659 (_("This file is corrupt and cannot be played.")),
8660 ("Not enough data for an atom header, got only %u bytes", length));
8661 return FALSE;
8662 }
8663broken_atom_size:
8664 {
8665 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
8666 (_("This file is corrupt and cannot be played.")),
8667 ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
8668 "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
8669 length));
8670 return FALSE;
8671 }
8672}
8673
8674static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008675aml_qtdemux_do_allocation (AmlQtDemuxStream * stream, GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00008676{
8677/* FIXME: This can only reliably work if demuxers have a
8678 * separate streaming thread per srcpad. This should be
8679 * done in a demuxer base class, which integrates parts
8680 * of multiqueue
8681 *
8682 * https://bugzilla.gnome.org/show_bug.cgi?id=701856
8683 */
8684#if 0
8685 GstQuery *query;
8686
8687 query = gst_query_new_allocation (stream->caps, FALSE);
8688
8689 if (!gst_pad_peer_query (stream->pad, query)) {
8690 /* not a problem, just debug a little */
8691 GST_DEBUG_OBJECT (qtdemux, "peer ALLOCATION query failed");
8692 }
8693
8694 if (stream->allocator)
8695 gst_object_unref (stream->allocator);
8696
8697 if (gst_query_get_n_allocation_params (query) > 0) {
8698 /* try the allocator */
8699 gst_query_parse_nth_allocation_param (query, 0, &stream->allocator,
8700 &stream->params);
8701 stream->use_allocator = TRUE;
8702 } else {
8703 stream->allocator = NULL;
8704 gst_allocation_params_init (&stream->params);
8705 stream->use_allocator = FALSE;
8706 }
8707 gst_query_unref (query);
8708#endif
8709}
8710
8711static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008712aml_pad_query (const GValue * item, GValue * value, gpointer user_data)
zengliang.li5f31ef42024-05-16 08:27:38 +00008713{
8714 GstPad *pad = g_value_get_object (item);
8715 GstQuery *query = user_data;
8716 gboolean res;
8717
8718 res = gst_pad_peer_query (pad, query);
8719
8720 if (res) {
8721 g_value_set_boolean (value, TRUE);
8722 return FALSE;
8723 }
8724
8725 GST_INFO_OBJECT (pad, "pad peer query failed");
8726 return TRUE;
8727}
8728
8729static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008730gst_aml_qtdemux_run_query (GstElement * element, GstQuery * query,
zengliang.li5f31ef42024-05-16 08:27:38 +00008731 GstPadDirection direction)
8732{
8733 GstIterator *it;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008734 GstIteratorFoldFunction func = aml_pad_query;
zengliang.li5f31ef42024-05-16 08:27:38 +00008735 GValue res = { 0, };
8736
8737 g_value_init (&res, G_TYPE_BOOLEAN);
8738 g_value_set_boolean (&res, FALSE);
8739
8740 /* Ask neighbor */
8741 if (direction == GST_PAD_SRC)
8742 it = gst_element_iterate_src_pads (element);
8743 else
8744 it = gst_element_iterate_sink_pads (element);
8745
8746 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
8747 gst_iterator_resync (it);
8748
8749 gst_iterator_free (it);
8750
8751 return g_value_get_boolean (&res);
8752}
8753
8754static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008755gst_aml_qtdemux_request_protection_context (GstAmlQTDemux * qtdemux,
8756 AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00008757{
8758 GstQuery *query;
8759 GstContext *ctxt;
8760 GstElement *element = GST_ELEMENT (qtdemux);
8761 GstStructure *st;
8762 gchar **filtered_sys_ids;
8763 GValue event_list = G_VALUE_INIT;
8764 GList *walk;
8765
8766 /* 1. Check if we already have the context. */
8767 if (qtdemux->preferred_protection_system_id != NULL) {
8768 GST_LOG_OBJECT (element,
8769 "already have the protection context, no need to request it again");
8770 return;
8771 }
8772
8773 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8774 filtered_sys_ids = gst_protection_filter_systems_by_available_decryptors (
8775 (const gchar **) qtdemux->protection_system_ids->pdata);
8776
8777 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8778 qtdemux->protection_system_ids->len - 1);
8779 GST_TRACE_OBJECT (qtdemux, "detected %u protection systems, we have "
8780 "decryptors for %u of them, running context request",
8781 qtdemux->protection_system_ids->len,
8782 filtered_sys_ids ? g_strv_length (filtered_sys_ids) : 0);
8783
8784
8785 if (stream->protection_scheme_event_queue.length) {
8786 GST_TRACE_OBJECT (qtdemux, "using stream event queue, length %u",
8787 stream->protection_scheme_event_queue.length);
8788 walk = stream->protection_scheme_event_queue.tail;
8789 } else {
8790 GST_TRACE_OBJECT (qtdemux, "using demuxer event queue, length %u",
8791 qtdemux->protection_event_queue.length);
8792 walk = qtdemux->protection_event_queue.tail;
8793 }
8794
8795 g_value_init (&event_list, GST_TYPE_LIST);
8796 for (; walk; walk = g_list_previous (walk)) {
8797 GValue *event_value = g_new0 (GValue, 1);
8798 g_value_init (event_value, GST_TYPE_EVENT);
8799 g_value_set_boxed (event_value, walk->data);
8800 gst_value_list_append_and_take_value (&event_list, event_value);
8801 }
8802
8803 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
8804 * check if downstream already has a context of the specific type
8805 * 2b) Query upstream as above.
8806 */
8807 query = gst_query_new_context ("drm-preferred-decryption-system-id");
8808 st = gst_query_writable_structure (query);
8809 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8810 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8811 NULL);
8812 gst_structure_set_value (st, "stream-encryption-events", &event_list);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008813 if (gst_aml_qtdemux_run_query (element, query, GST_PAD_SRC)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008814 gst_query_parse_context (query, &ctxt);
8815 GST_INFO_OBJECT (element, "found context (%p) in downstream query", ctxt);
8816 gst_element_set_context (element, ctxt);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008817 } else if (gst_aml_qtdemux_run_query (element, query, GST_PAD_SINK)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008818 gst_query_parse_context (query, &ctxt);
8819 GST_INFO_OBJECT (element, "found context (%p) in upstream query", ctxt);
8820 gst_element_set_context (element, ctxt);
8821 } else {
8822 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
8823 * the required context type and afterwards check if a
8824 * usable context was set now as in 1). The message could
8825 * be handled by the parent bins of the element and the
8826 * application.
8827 */
8828 GstMessage *msg;
8829
8830 GST_INFO_OBJECT (element, "posting need context message");
8831 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
8832 "drm-preferred-decryption-system-id");
8833 st = (GstStructure *) gst_message_get_structure (msg);
8834 gst_structure_set (st, "track-id", G_TYPE_UINT, stream->track_id,
8835 "available-stream-encryption-systems", G_TYPE_STRV, filtered_sys_ids,
8836 NULL);
8837
8838 gst_structure_set_value (st, "stream-encryption-events", &event_list);
8839 gst_element_post_message (element, msg);
8840 }
8841
8842 g_strfreev (filtered_sys_ids);
8843 g_value_unset (&event_list);
8844 gst_query_unref (query);
8845}
8846
8847static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008848gst_aml_qtdemux_configure_protected_caps (GstAmlQTDemux * qtdemux,
8849 AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00008850{
8851 GstStructure *s;
8852 const gchar *selected_system = NULL;
8853
8854 g_return_val_if_fail (qtdemux != NULL, FALSE);
8855 g_return_val_if_fail (stream != NULL, FALSE);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008856 g_return_val_if_fail (gst_caps_get_size (AML_CUR_STREAM (stream)->caps) == 1,
zengliang.li5f31ef42024-05-16 08:27:38 +00008857 FALSE);
8858
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008859 if (stream->protection_scheme_type == AML_FOURCC_aavd) {
8860 s = gst_caps_get_structure (AML_CUR_STREAM (stream)->caps, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00008861 if (!gst_structure_has_name (s, "application/x-aavd")) {
8862 gst_structure_set (s,
8863 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
8864 NULL);
8865 gst_structure_set_name (s, "application/x-aavd");
8866 }
8867 return TRUE;
8868 }
8869
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008870 if (stream->protection_scheme_type != AML_FOURCC_cenc
xuesong.jiangec2fff52024-10-21 16:16:19 +08008871 && stream->protection_scheme_type != AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008872 && stream->protection_scheme_type != AML_FOURCC_cbcs
8873 && stream->protection_scheme_type != AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +00008874 GST_ERROR_OBJECT (qtdemux,
8875 "unsupported protection scheme: %" GST_FOURCC_FORMAT,
8876 GST_FOURCC_ARGS (stream->protection_scheme_type));
8877 return FALSE;
8878 }
8879
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008880 s = gst_caps_get_structure (AML_CUR_STREAM (stream)->caps, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00008881 if (!gst_structure_has_name (s, "application/x-cenc")) {
8882 gst_structure_set (s,
8883 "original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
xuesong.jiangec2fff52024-10-21 16:16:19 +08008884
8885 if (stream->protection_scheme_type == AML_FOURCC_cens)
8886 gst_structure_set (s, "cipher-mode", G_TYPE_STRING, "cens", NULL);
8887 else if (stream->protection_scheme_type == AML_FOURCC_cbc1)
8888 {
xuesong.jiange1316db2024-10-23 15:39:53 +08008889 gst_structure_set (s, "cipher-mode", G_TYPE_STRING, "cbc1", NULL);
xuesong.jiangec2fff52024-10-21 16:16:19 +08008890 }
8891 else if (stream->protection_scheme_type == AML_FOURCC_cbcs)
8892 gst_structure_set (s, "cipher-mode", G_TYPE_STRING, "cbcs", NULL);
8893 else
8894 gst_structure_set (s, "cipher-mode", G_TYPE_STRING, "cenc", NULL);
8895
zengliang.li5f31ef42024-05-16 08:27:38 +00008896 gst_structure_set_name (s, "application/x-cenc");
8897 }
8898
8899 if (qtdemux->protection_system_ids == NULL) {
8900 GST_DEBUG_OBJECT (qtdemux, "stream is protected using cenc, but no "
8901 "cenc protection system information has been found, not setting a "
8902 "protection system UUID");
8903 return TRUE;
8904 }
8905
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008906 gst_aml_qtdemux_request_protection_context (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00008907 if (qtdemux->preferred_protection_system_id != NULL) {
8908 const gchar *preferred_system_array[] =
8909 { qtdemux->preferred_protection_system_id, NULL };
8910
8911 selected_system = gst_protection_select_system (preferred_system_array);
8912
8913 if (selected_system) {
8914 GST_TRACE_OBJECT (qtdemux, "selected preferred system %s",
8915 qtdemux->preferred_protection_system_id);
8916 } else {
8917 GST_WARNING_OBJECT (qtdemux, "could not select preferred system %s "
8918 "because there is no available decryptor",
8919 qtdemux->preferred_protection_system_id);
8920 }
8921 }
8922
8923 if (!selected_system) {
8924 g_ptr_array_add (qtdemux->protection_system_ids, NULL);
8925 selected_system = gst_protection_select_system ((const gchar **)
8926 qtdemux->protection_system_ids->pdata);
8927 g_ptr_array_remove_index (qtdemux->protection_system_ids,
8928 qtdemux->protection_system_ids->len - 1);
8929 }
8930
8931 if (!selected_system) {
8932 GST_ERROR_OBJECT (qtdemux, "stream is protected, but no "
8933 "suitable decryptor element has been found");
8934 return FALSE;
8935 }
8936
8937 GST_DEBUG_OBJECT (qtdemux, "selected protection system is %s",
8938 selected_system);
8939
8940 gst_structure_set (s,
8941 GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
8942 NULL);
8943
8944 return TRUE;
8945}
8946
8947static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008948gst_aml_qtdemux_guess_framerate (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00008949{
8950 /* fps is calculated base on the duration of the average framerate since
8951 * qt does not have a fixed framerate. */
8952 gboolean fps_available = TRUE;
8953 guint32 first_duration = 0;
8954
8955 if (stream->n_samples > 0)
8956 first_duration = stream->samples[0].duration;
8957
8958 if ((stream->n_samples == 1 && first_duration == 0)
8959 || (qtdemux->fragmented && stream->n_samples_moof == 1)) {
8960 /* still frame */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008961 AML_CUR_STREAM (stream)->fps_n = 0;
8962 AML_CUR_STREAM (stream)->fps_d = 1;
zengliang.li5f31ef42024-05-16 08:27:38 +00008963 } else {
8964 if (stream->duration == 0 || stream->n_samples < 2) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00008965 AML_CUR_STREAM (stream)->fps_n = stream->timescale;
8966 AML_CUR_STREAM (stream)->fps_d = 1;
zengliang.li5f31ef42024-05-16 08:27:38 +00008967 fps_available = FALSE;
8968 } else {
8969 GstClockTime avg_duration;
8970 guint64 duration;
8971 guint32 n_samples;
8972
8973 /* duration and n_samples can be updated for fragmented format
8974 * so, framerate of fragmented format is calculated using data in a moof */
8975 if (qtdemux->fragmented && stream->n_samples_moof > 0
8976 && stream->duration_moof > 0) {
8977 n_samples = stream->n_samples_moof;
8978 duration = stream->duration_moof;
8979 } else {
8980 n_samples = stream->n_samples;
8981 duration = stream->duration;
8982 }
8983
8984 /* Calculate a framerate, ignoring the first sample which is sometimes truncated */
8985 /* stream->duration is guint64, timescale, n_samples are guint32 */
8986 avg_duration =
8987 gst_util_uint64_scale_round (duration -
8988 first_duration, GST_SECOND,
8989 (guint64) (stream->timescale) * (n_samples - 1));
8990
8991 GST_LOG_OBJECT (qtdemux,
8992 "Calculating avg sample duration based on stream (or moof) duration %"
8993 G_GUINT64_FORMAT
8994 " minus first sample %u, leaving %d samples gives %"
8995 GST_TIME_FORMAT, duration, first_duration,
8996 n_samples - 1, GST_TIME_ARGS (avg_duration));
8997
8998 fps_available =
8999 gst_video_guess_framerate (avg_duration,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009000 &AML_CUR_STREAM (stream)->fps_n, &AML_CUR_STREAM (stream)->fps_d);
zengliang.li5f31ef42024-05-16 08:27:38 +00009001
9002 GST_DEBUG_OBJECT (qtdemux,
9003 "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009004 stream->timescale, AML_CUR_STREAM (stream)->fps_n,
9005 AML_CUR_STREAM (stream)->fps_d);
zengliang.li5f31ef42024-05-16 08:27:38 +00009006 }
9007 }
9008
9009 return fps_available;
9010}
9011
9012static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009013gst_aml_qtdemux_configure_stream (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00009014{
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009015 if (stream->subtype == AML_FOURCC_vide) {
9016 gboolean fps_available = gst_aml_qtdemux_guess_framerate (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00009017
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009018 if (AML_CUR_STREAM (stream)->caps) {
9019 AML_CUR_STREAM (stream)->caps =
9020 gst_caps_make_writable (AML_CUR_STREAM (stream)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +00009021
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009022 if (AML_CUR_STREAM (stream)->width && AML_CUR_STREAM (stream)->height)
9023 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
9024 "width", G_TYPE_INT, AML_CUR_STREAM (stream)->width,
9025 "height", G_TYPE_INT, AML_CUR_STREAM (stream)->height, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00009026
9027 /* set framerate if calculated framerate is reliable */
9028 if (fps_available) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009029 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
9030 "framerate", GST_TYPE_FRACTION, AML_CUR_STREAM (stream)->fps_n,
9031 AML_CUR_STREAM (stream)->fps_d, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00009032 }
9033
9034 /* calculate pixel-aspect-ratio using display width and height */
9035 GST_DEBUG_OBJECT (qtdemux,
9036 "video size %dx%d, target display size %dx%d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009037 AML_CUR_STREAM (stream)->width, AML_CUR_STREAM (stream)->height,
zengliang.li5f31ef42024-05-16 08:27:38 +00009038 stream->display_width, stream->display_height);
9039 /* qt file might have pasp atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009040 if (AML_CUR_STREAM (stream)->par_w > 0 && AML_CUR_STREAM (stream)->par_h > 0) {
9041 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", AML_CUR_STREAM (stream)->par_w,
9042 AML_CUR_STREAM (stream)->par_h);
9043 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9044 GST_TYPE_FRACTION, AML_CUR_STREAM (stream)->par_w,
9045 AML_CUR_STREAM (stream)->par_h, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00009046 } else if (stream->display_width > 0 && stream->display_height > 0
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009047 && AML_CUR_STREAM (stream)->width > 0
9048 && AML_CUR_STREAM (stream)->height > 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009049 gint n, d;
9050
9051 /* calculate the pixel aspect ratio using the display and pixel w/h */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009052 n = stream->display_width * AML_CUR_STREAM (stream)->height;
9053 d = stream->display_height * AML_CUR_STREAM (stream)->width;
zengliang.li5f31ef42024-05-16 08:27:38 +00009054 if (n == d)
9055 n = d = 1;
9056 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009057 AML_CUR_STREAM (stream)->par_w = n;
9058 AML_CUR_STREAM (stream)->par_h = d;
9059 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "pixel-aspect-ratio",
9060 GST_TYPE_FRACTION, AML_CUR_STREAM (stream)->par_w,
9061 AML_CUR_STREAM (stream)->par_h, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00009062 }
9063
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009064 if (AML_CUR_STREAM (stream)->interlace_mode > 0) {
9065 if (AML_CUR_STREAM (stream)->interlace_mode == 1) {
9066 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "interlace-mode",
zengliang.li5f31ef42024-05-16 08:27:38 +00009067 G_TYPE_STRING, "progressive", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009068 } else if (AML_CUR_STREAM (stream)->interlace_mode == 2) {
9069 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "interlace-mode",
zengliang.li5f31ef42024-05-16 08:27:38 +00009070 G_TYPE_STRING, "interleaved", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009071 if (AML_CUR_STREAM (stream)->field_order == 9) {
9072 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "field-order",
zengliang.li5f31ef42024-05-16 08:27:38 +00009073 G_TYPE_STRING, "top-field-first", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009074 } else if (AML_CUR_STREAM (stream)->field_order == 14) {
9075 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "field-order",
zengliang.li5f31ef42024-05-16 08:27:38 +00009076 G_TYPE_STRING, "bottom-field-first", NULL);
9077 }
9078 }
9079 }
9080
9081 /* Create incomplete colorimetry here if needed */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009082 if (AML_CUR_STREAM (stream)->colorimetry.range ||
9083 AML_CUR_STREAM (stream)->colorimetry.matrix ||
9084 AML_CUR_STREAM (stream)->colorimetry.transfer
9085 || AML_CUR_STREAM (stream)->colorimetry.primaries) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009086 gchar *colorimetry =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009087 gst_video_colorimetry_to_string (&AML_CUR_STREAM (stream)->colorimetry);
9088 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "colorimetry",
zengliang.li5f31ef42024-05-16 08:27:38 +00009089 G_TYPE_STRING, colorimetry, NULL);
9090 g_free (colorimetry);
9091 }
9092
9093 if (stream->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
9094 guint par_w = 1, par_h = 1;
9095
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009096 if (AML_CUR_STREAM (stream)->par_w > 0 && AML_CUR_STREAM (stream)->par_h > 0) {
9097 par_w = AML_CUR_STREAM (stream)->par_w;
9098 par_h = AML_CUR_STREAM (stream)->par_h;
zengliang.li5f31ef42024-05-16 08:27:38 +00009099 }
9100
9101 if (gst_video_multiview_guess_half_aspect (stream->multiview_mode,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009102 AML_CUR_STREAM (stream)->width, AML_CUR_STREAM (stream)->height, par_w,
zengliang.li5f31ef42024-05-16 08:27:38 +00009103 par_h)) {
9104 stream->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
9105 }
9106
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009107 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
zengliang.li5f31ef42024-05-16 08:27:38 +00009108 "multiview-mode", G_TYPE_STRING,
9109 gst_video_multiview_mode_to_caps_string (stream->multiview_mode),
9110 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
9111 stream->multiview_flags, GST_FLAG_SET_MASK_EXACT, NULL);
9112 }
9113 }
9114 }
9115
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009116 else if (stream->subtype == AML_FOURCC_soun) {
9117 if (AML_CUR_STREAM (stream)->caps) {
9118 AML_CUR_STREAM (stream)->caps =
9119 gst_caps_make_writable (AML_CUR_STREAM (stream)->caps);
9120 if (AML_CUR_STREAM (stream)->rate > 0)
9121 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
9122 "rate", G_TYPE_INT, (int) AML_CUR_STREAM (stream)->rate, NULL);
9123 if (AML_CUR_STREAM (stream)->n_channels > 0)
9124 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
9125 "channels", G_TYPE_INT, AML_CUR_STREAM (stream)->n_channels, NULL);
9126 if (AML_CUR_STREAM (stream)->n_channels > 2) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009127 /* FIXME: Need to parse the 'chan' atom to get channel layouts
9128 * correctly; this is just the minimum we can do - assume
9129 * we don't actually have any channel positions. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009130 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
zengliang.li5f31ef42024-05-16 08:27:38 +00009131 "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
9132 }
9133 }
9134 }
9135
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009136 else if (stream->subtype == AML_FOURCC_clcp && AML_CUR_STREAM (stream)->caps) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009137 const GstStructure *s;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009138 AmlQtDemuxStream *fps_stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +00009139 gboolean fps_available = FALSE;
9140
9141 /* CEA608 closed caption tracks are a bit special in that each sample
9142 * can contain CCs for multiple frames, and CCs can be omitted and have to
9143 * be inferred from the duration of the sample then.
9144 *
9145 * As such we take the framerate from the (first) video track here for
9146 * CEA608 as there must be one CC byte pair for every video frame
9147 * according to the spec.
9148 *
9149 * For CEA708 all is fine and there is one sample per frame.
9150 */
9151
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009152 s = gst_caps_get_structure (AML_CUR_STREAM (stream)->caps, 0);
zengliang.li5f31ef42024-05-16 08:27:38 +00009153 if (gst_structure_has_name (s, "closedcaption/x-cea-608")) {
9154 gint i;
9155
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009156 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
9157 AmlQtDemuxStream *tmp = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +00009158
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009159 if (tmp->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009160 fps_stream = tmp;
9161 break;
9162 }
9163 }
9164
9165 if (fps_stream) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009166 fps_available = gst_aml_qtdemux_guess_framerate (qtdemux, fps_stream);
9167 AML_CUR_STREAM (stream)->fps_n = AML_CUR_STREAM (fps_stream)->fps_n;
9168 AML_CUR_STREAM (stream)->fps_d = AML_CUR_STREAM (fps_stream)->fps_d;
zengliang.li5f31ef42024-05-16 08:27:38 +00009169 }
9170 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009171 fps_available = gst_aml_qtdemux_guess_framerate (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00009172 fps_stream = stream;
9173 }
9174
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009175 AML_CUR_STREAM (stream)->caps =
9176 gst_caps_make_writable (AML_CUR_STREAM (stream)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +00009177
9178 /* set framerate if calculated framerate is reliable */
9179 if (fps_available) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009180 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
9181 "framerate", GST_TYPE_FRACTION, AML_CUR_STREAM (stream)->fps_n,
9182 AML_CUR_STREAM (stream)->fps_d, NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +00009183 }
9184 }
9185
9186 if (stream->pad) {
9187 GstCaps *prev_caps = NULL;
9188
9189 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009190 gst_pad_set_event_function (stream->pad, gst_aml_qtdemux_handle_src_event);
9191 gst_pad_set_query_function (stream->pad, gst_aml_qtdemux_handle_src_query);
zengliang.li5f31ef42024-05-16 08:27:38 +00009192 gst_pad_set_active (stream->pad, TRUE);
9193
9194 gst_pad_use_fixed_caps (stream->pad);
9195
9196 if (stream->protected) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009197 if (!gst_aml_qtdemux_configure_protected_caps (qtdemux, stream)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009198 GST_ERROR_OBJECT (qtdemux,
9199 "Failed to configure protected stream caps.");
9200 return FALSE;
9201 }
9202 }
9203
9204 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009205 AML_CUR_STREAM (stream)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +00009206 if (stream->new_stream) {
9207 GstEvent *event;
9208 GstStreamFlags stream_flags = GST_STREAM_FLAG_NONE;
9209
9210 event =
9211 gst_pad_get_sticky_event (qtdemux->sinkpad, GST_EVENT_STREAM_START,
9212 0);
9213 if (event) {
9214 gst_event_parse_stream_flags (event, &stream_flags);
9215 if (gst_event_parse_group_id (event, &qtdemux->group_id))
9216 qtdemux->have_group_id = TRUE;
9217 else
9218 qtdemux->have_group_id = FALSE;
9219 gst_event_unref (event);
9220 } else if (!qtdemux->have_group_id) {
9221 qtdemux->have_group_id = TRUE;
9222 qtdemux->group_id = gst_util_group_id_next ();
9223 }
9224
9225 stream->new_stream = FALSE;
9226 event = gst_event_new_stream_start (stream->stream_id);
9227 if (qtdemux->have_group_id)
9228 gst_event_set_group_id (event, qtdemux->group_id);
9229 if (stream->disabled)
9230 stream_flags |= GST_STREAM_FLAG_UNSELECT;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009231 if (AML_CUR_STREAM (stream)->sparse) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009232 stream_flags |= GST_STREAM_FLAG_SPARSE;
9233 } else {
9234 stream_flags &= ~GST_STREAM_FLAG_SPARSE;
9235 }
9236 gst_event_set_stream_flags (event, stream_flags);
9237 gst_pad_push_event (stream->pad, event);
9238 }
9239
9240 prev_caps = gst_pad_get_current_caps (stream->pad);
9241
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009242 if (AML_CUR_STREAM (stream)->caps) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009243 if (!prev_caps
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009244 || !gst_caps_is_equal_fixed (prev_caps, AML_CUR_STREAM (stream)->caps)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009245 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009246 AML_CUR_STREAM (stream)->caps);
9247 gst_pad_set_caps (stream->pad, AML_CUR_STREAM (stream)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +00009248 } else {
9249 GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
9250 }
9251 } else {
9252 GST_WARNING_OBJECT (qtdemux, "stream without caps");
9253 }
9254
9255 if (prev_caps)
9256 gst_caps_unref (prev_caps);
9257 stream->new_caps = FALSE;
9258 }
9259 return TRUE;
9260}
9261
9262static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009263gst_aml_qtdemux_stream_check_and_change_stsd_index (GstAmlQTDemux * demux,
9264 AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00009265{
9266 if (stream->cur_stsd_entry_index == stream->stsd_sample_description_id)
9267 return;
9268
9269 GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
9270 stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
9271 if (G_UNLIKELY (stream->stsd_sample_description_id >=
9272 stream->stsd_entries_length)) {
9273 GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
9274 (_("This file is invalid and cannot be played.")),
9275 ("New sample description id is out of bounds (%d >= %d)",
9276 stream->stsd_sample_description_id, stream->stsd_entries_length));
9277 } else {
9278 stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
9279 stream->new_caps = TRUE;
9280 }
9281}
9282
9283static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009284gst_aml_qtdemux_add_stream (GstAmlQTDemux * qtdemux,
9285 AmlQtDemuxStream * stream, GstTagList * list)
zengliang.li5f31ef42024-05-16 08:27:38 +00009286{
9287 gboolean ret = TRUE;
9288
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009289 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009290 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9291
9292 stream->pad =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009293 gst_pad_new_from_static_template (&gst_aml_qtdemux_videosrc_template, name);
zengliang.li5f31ef42024-05-16 08:27:38 +00009294 g_free (name);
9295
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009296 if (!gst_aml_qtdemux_configure_stream (qtdemux, stream)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009297 gst_object_unref (stream->pad);
9298 stream->pad = NULL;
9299 ret = FALSE;
9300 goto done;
9301 }
9302
9303 qtdemux->n_video_streams++;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009304 } else if (stream->subtype == AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009305 gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);
9306
9307 stream->pad =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009308 gst_pad_new_from_static_template (&gst_aml_qtdemux_audiosrc_template, name);
zengliang.li5f31ef42024-05-16 08:27:38 +00009309 g_free (name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009310 if (!gst_aml_qtdemux_configure_stream (qtdemux, stream)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009311 gst_object_unref (stream->pad);
9312 stream->pad = NULL;
9313 ret = FALSE;
9314 goto done;
9315 }
9316 qtdemux->n_audio_streams++;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009317 } else if (stream->subtype == AML_FOURCC_strm) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009318 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009319 } else if (stream->subtype == AML_FOURCC_subp || stream->subtype == AML_FOURCC_text
9320 || stream->subtype == AML_FOURCC_sbtl || stream->subtype == AML_FOURCC_subt
9321 || stream->subtype == AML_FOURCC_clcp || stream->subtype == AML_FOURCC_wvtt) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009322 gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);
9323
9324 stream->pad =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009325 gst_pad_new_from_static_template (&gst_aml_qtdemux_subsrc_template, name);
zengliang.li5f31ef42024-05-16 08:27:38 +00009326 g_free (name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009327 if (!gst_aml_qtdemux_configure_stream (qtdemux, stream)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009328 gst_object_unref (stream->pad);
9329 stream->pad = NULL;
9330 ret = FALSE;
9331 goto done;
9332 }
9333 qtdemux->n_sub_streams++;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009334 } else if (AML_CUR_STREAM (stream)->caps) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009335 gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);
9336
9337 stream->pad =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009338 gst_pad_new_from_static_template (&gst_aml_qtdemux_videosrc_template, name);
zengliang.li5f31ef42024-05-16 08:27:38 +00009339 g_free (name);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009340 if (!gst_aml_qtdemux_configure_stream (qtdemux, stream)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009341 gst_object_unref (stream->pad);
9342 stream->pad = NULL;
9343 ret = FALSE;
9344 goto done;
9345 }
9346 qtdemux->n_video_streams++;
9347 } else {
9348 GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
9349 goto done;
9350 }
9351
9352 if (stream->pad) {
9353 GList *l;
9354
9355 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
9356 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
9357 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
9358 GST_OBJECT_LOCK (qtdemux);
9359 gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);
9360 GST_OBJECT_UNLOCK (qtdemux);
9361
9362 if (stream->stream_tags)
9363 gst_tag_list_unref (stream->stream_tags);
9364 stream->stream_tags = list;
9365 list = NULL;
9366 /* global tags go on each pad anyway */
9367 stream->send_global_tags = TRUE;
9368 /* send upstream GST_EVENT_PROTECTION events that were received before
9369 this source pad was created */
9370 for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
9371 gst_pad_push_event (stream->pad, gst_event_ref (l->data));
9372 }
9373done:
9374 if (list)
9375 gst_tag_list_unref (list);
9376 return ret;
9377}
9378
9379/* find next atom with @fourcc starting at @offset */
9380static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009381aml_qtdemux_find_atom (GstAmlQTDemux * qtdemux, guint64 * offset,
zengliang.li5f31ef42024-05-16 08:27:38 +00009382 guint64 * length, guint32 fourcc)
9383{
9384 GstFlowReturn ret;
9385 guint32 lfourcc;
9386 GstBuffer *buf;
9387
9388 GST_LOG_OBJECT (qtdemux, "finding fourcc %" GST_FOURCC_FORMAT " at offset %"
9389 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9390
9391 while (TRUE) {
9392 GstMapInfo map;
9393
9394 buf = NULL;
9395 ret = gst_pad_pull_range (qtdemux->sinkpad, *offset, 16, &buf);
9396 if (G_UNLIKELY (ret != GST_FLOW_OK))
9397 goto locate_failed;
9398 if (G_UNLIKELY (gst_buffer_get_size (buf) != 16)) {
9399 /* likely EOF */
9400 ret = GST_FLOW_EOS;
9401 gst_buffer_unref (buf);
9402 goto locate_failed;
9403 }
9404 gst_buffer_map (buf, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009405 aml_extract_initial_length_and_fourcc (map.data, 16, length, &lfourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +00009406 gst_buffer_unmap (buf, &map);
9407 gst_buffer_unref (buf);
9408
9409 if (G_UNLIKELY (*length == 0)) {
9410 GST_DEBUG_OBJECT (qtdemux, "invalid length 0");
9411 ret = GST_FLOW_ERROR;
9412 goto locate_failed;
9413 }
9414
9415 if (lfourcc == fourcc) {
9416 GST_DEBUG_OBJECT (qtdemux, "found '%" GST_FOURCC_FORMAT " at offset %"
9417 G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), *offset);
9418 break;
9419 } else {
9420 GST_LOG_OBJECT (qtdemux,
9421 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
9422 GST_FOURCC_ARGS (lfourcc), *offset);
9423 if (*offset == G_MAXUINT64)
9424 goto locate_failed;
9425 *offset += *length;
9426 }
9427 }
9428
9429 return GST_FLOW_OK;
9430
9431locate_failed:
9432 {
9433 /* might simply have had last one */
9434 GST_DEBUG_OBJECT (qtdemux, "fourcc not found");
9435 return ret;
9436 }
9437}
9438
9439/* should only do something in pull mode */
9440/* call with OBJECT lock */
9441static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009442aml_qtdemux_add_fragmented_samples (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +00009443{
9444 guint64 length, offset;
9445 GstBuffer *buf = NULL;
9446 GstFlowReturn ret = GST_FLOW_OK;
9447 GstFlowReturn res = GST_FLOW_OK;
9448 GstMapInfo map;
9449
9450 offset = qtdemux->moof_offset;
9451 GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);
9452
9453 if (!offset) {
9454 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9455 return GST_FLOW_EOS;
9456 }
9457
9458 /* best not do pull etc with lock held */
9459 GST_OBJECT_UNLOCK (qtdemux);
9460
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009461 ret = aml_qtdemux_find_atom (qtdemux, &offset, &length, AML_FOURCC_moof);
zengliang.li5f31ef42024-05-16 08:27:38 +00009462 if (ret != GST_FLOW_OK)
9463 goto flow_failed;
9464
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009465 ret = gst_aml_qtdemux_pull_atom (qtdemux, offset, length, &buf);
zengliang.li5f31ef42024-05-16 08:27:38 +00009466 if (G_UNLIKELY (ret != GST_FLOW_OK))
9467 goto flow_failed;
9468 gst_buffer_map (buf, &map, GST_MAP_READ);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009469 if (!aml_qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009470 gst_buffer_unmap (buf, &map);
9471 gst_buffer_unref (buf);
9472 buf = NULL;
9473 goto parse_failed;
9474 }
9475
9476 gst_buffer_unmap (buf, &map);
9477 gst_buffer_unref (buf);
9478 buf = NULL;
9479
9480 offset += length;
9481 /* look for next moof */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009482 ret = aml_qtdemux_find_atom (qtdemux, &offset, &length, AML_FOURCC_moof);
zengliang.li5f31ef42024-05-16 08:27:38 +00009483 if (G_UNLIKELY (ret != GST_FLOW_OK))
9484 goto flow_failed;
9485
9486exit:
9487 GST_OBJECT_LOCK (qtdemux);
9488
9489 qtdemux->moof_offset = offset;
9490
9491 return res;
9492
9493parse_failed:
9494 {
9495 GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
9496 offset = 0;
9497 res = GST_FLOW_ERROR;
9498 goto exit;
9499 }
9500flow_failed:
9501 {
9502 /* maybe upstream temporarily flushing */
9503 if (ret != GST_FLOW_FLUSHING) {
9504 GST_DEBUG_OBJECT (qtdemux, "no next moof");
9505 offset = 0;
9506 } else {
9507 GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
9508 /* resume at current position next time */
9509 }
9510 res = ret;
9511 goto exit;
9512 }
9513}
9514
9515static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009516aml_qtdemux_merge_sample_table (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream)
zengliang.li5f31ef42024-05-16 08:27:38 +00009517{
9518 guint i;
9519 guint32 num_chunks;
9520 gint32 stts_duration;
9521 GstByteWriter stsc, stts, stsz;
9522
9523 /* Each sample has a different size, which we don't support for merging */
9524 if (stream->sample_size == 0) {
9525 GST_DEBUG_OBJECT (qtdemux,
9526 "Not all samples have the same size, not merging");
9527 return;
9528 }
9529
9530 /* The stream has a ctts table, we don't support that */
9531 if (stream->ctts_present) {
9532 GST_DEBUG_OBJECT (qtdemux, "Have ctts, not merging");
9533 return;
9534 }
9535
9536 /* If there's a sync sample table also ignore this stream */
9537 if (stream->stps_present || stream->stss_present) {
9538 GST_DEBUG_OBJECT (qtdemux, "Have stss/stps, not merging");
9539 return;
9540 }
9541
9542 /* If chunks are considered samples already ignore this stream */
9543 if (stream->chunks_are_samples) {
9544 GST_DEBUG_OBJECT (qtdemux, "Chunks are samples, not merging");
9545 return;
9546 }
9547
9548 /* Require that all samples have the same duration */
9549 if (stream->n_sample_times > 1) {
9550 GST_DEBUG_OBJECT (qtdemux, "Not all samples have the same duration");
9551 return;
9552 }
9553
9554 /* Parse the stts to get the sample duration and number of samples */
9555 gst_byte_reader_skip_unchecked (&stream->stts, 4);
9556 stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
9557
9558 /* Parse the number of chunks from the stco manually because the
9559 * reader is already behind that */
9560 num_chunks = GST_READ_UINT32_BE (stream->stco.data + 4);
9561
9562 GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
9563 num_chunks);
9564
9565 /* Now parse stsc, convert chunks into single samples and generate a
9566 * new stsc, stts and stsz from this information */
9567 gst_byte_writer_init (&stsc);
9568 gst_byte_writer_init (&stts);
9569 gst_byte_writer_init (&stsz);
9570
9571 /* Note: we skip fourccs, size, version, flags and other fields of the new
9572 * atoms as the byte readers with them are already behind that position
9573 * anyway and only update the values of those inside the stream directly.
9574 */
9575 stream->n_sample_times = 0;
9576 stream->n_samples = 0;
9577 for (i = 0; i < stream->n_samples_per_chunk; i++) {
9578 guint j;
9579 guint32 first_chunk, last_chunk, samples_per_chunk, sample_description_id;
9580
9581 first_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9582 samples_per_chunk = gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9583 sample_description_id =
9584 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
9585
9586 if (i == stream->n_samples_per_chunk - 1) {
9587 /* +1 because first_chunk is 1-based */
9588 last_chunk = num_chunks + 1;
9589 } else {
9590 last_chunk = gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
9591 }
9592
9593 GST_DEBUG_OBJECT (qtdemux,
9594 "Merging first_chunk: %u, last_chunk: %u, samples_per_chunk: %u, sample_description_id: %u",
9595 first_chunk, last_chunk, samples_per_chunk, sample_description_id);
9596
9597 gst_byte_writer_put_uint32_be (&stsc, first_chunk);
9598 /* One sample in this chunk */
9599 gst_byte_writer_put_uint32_be (&stsc, 1);
9600 gst_byte_writer_put_uint32_be (&stsc, sample_description_id);
9601
9602 /* For each chunk write a stts and stsz entry now */
9603 gst_byte_writer_put_uint32_be (&stts, last_chunk - first_chunk);
9604 gst_byte_writer_put_uint32_be (&stts, stts_duration * samples_per_chunk);
9605 for (j = first_chunk; j < last_chunk; j++) {
9606 gst_byte_writer_put_uint32_be (&stsz,
9607 stream->sample_size * samples_per_chunk);
9608 }
9609
9610 stream->n_sample_times += 1;
9611 stream->n_samples += last_chunk - first_chunk;
9612 }
9613
9614 g_assert_cmpint (stream->n_samples, ==, num_chunks);
9615
9616 GST_DEBUG_OBJECT (qtdemux, "Have %u samples and %u sample times",
9617 stream->n_samples, stream->n_sample_times);
9618
9619 /* We don't have a fixed sample size anymore */
9620 stream->sample_size = 0;
9621
9622 /* Free old data for the atoms */
9623 g_free ((gpointer) stream->stsz.data);
9624 stream->stsz.data = NULL;
9625 g_free ((gpointer) stream->stsc.data);
9626 stream->stsc.data = NULL;
9627 g_free ((gpointer) stream->stts.data);
9628 stream->stts.data = NULL;
9629
9630 /* Store new data and replace byte readers */
9631 stream->stsz.size = gst_byte_writer_get_size (&stsz);
9632 stream->stsz.data = gst_byte_writer_reset_and_get_data (&stsz);
9633 gst_byte_reader_init (&stream->stsz, stream->stsz.data, stream->stsz.size);
9634 stream->stts.size = gst_byte_writer_get_size (&stts);
9635 stream->stts.data = gst_byte_writer_reset_and_get_data (&stts);
9636 gst_byte_reader_init (&stream->stts, stream->stts.data, stream->stts.size);
9637 stream->stsc.size = gst_byte_writer_get_size (&stsc);
9638 stream->stsc.data = gst_byte_writer_reset_and_get_data (&stsc);
9639 gst_byte_reader_init (&stream->stsc, stream->stsc.data, stream->stsc.size);
9640}
9641
9642/* initialise bytereaders for stbl sub-atoms */
9643static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009644aml_qtdemux_stbl_init (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream, GNode * stbl)
zengliang.li5f31ef42024-05-16 08:27:38 +00009645{
9646 stream->stbl_index = -1; /* no samples have yet been parsed */
9647 stream->sample_index = -1;
9648
9649 /* time-to-sample atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009650 if (!aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stts, &stream->stts))
zengliang.li5f31ef42024-05-16 08:27:38 +00009651 goto corrupt_file;
9652
9653 /* copy atom data into a new buffer for later use */
9654 stream->stts.data = g_memdup2 (stream->stts.data, stream->stts.size);
9655
9656 /* skip version + flags */
9657 if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
9658 !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
9659 goto corrupt_file;
9660 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);
9661
9662 /* make sure there's enough data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009663 if (!aml_qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009664 stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
9665 GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
9666 stream->n_sample_times);
9667 if (!stream->n_sample_times)
9668 goto corrupt_file;
9669 }
9670
9671 /* sync sample atom */
9672 stream->stps_present = FALSE;
9673 if ((stream->stss_present =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009674 ! !aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stss,
zengliang.li5f31ef42024-05-16 08:27:38 +00009675 &stream->stss) ? TRUE : FALSE) == TRUE) {
9676 /* copy atom data into a new buffer for later use */
9677 stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
9678
9679 /* skip version + flags */
9680 if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
9681 !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
9682 goto corrupt_file;
9683
9684 if (stream->n_sample_syncs) {
9685 /* make sure there's enough data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009686 if (!aml_qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
zengliang.li5f31ef42024-05-16 08:27:38 +00009687 goto corrupt_file;
9688 }
9689
9690 /* partial sync sample atom */
9691 if ((stream->stps_present =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009692 ! !aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stps,
zengliang.li5f31ef42024-05-16 08:27:38 +00009693 &stream->stps) ? TRUE : FALSE) == TRUE) {
9694 /* copy atom data into a new buffer for later use */
9695 stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
9696
9697 /* skip version + flags */
9698 if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
9699 !gst_byte_reader_get_uint32_be (&stream->stps,
9700 &stream->n_sample_partial_syncs))
9701 goto corrupt_file;
9702
9703 /* if there are no entries, the stss table contains the real
9704 * sync samples */
9705 if (stream->n_sample_partial_syncs) {
9706 /* make sure there's enough data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009707 if (!aml_qt_atom_parser_has_chunks (&stream->stps,
zengliang.li5f31ef42024-05-16 08:27:38 +00009708 stream->n_sample_partial_syncs, 4))
9709 goto corrupt_file;
9710 }
9711 }
9712 }
9713
9714 /* sample size */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009715 if (!aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stsz, &stream->stsz))
zengliang.li5f31ef42024-05-16 08:27:38 +00009716 goto no_samples;
9717
9718 /* copy atom data into a new buffer for later use */
9719 stream->stsz.data = g_memdup2 (stream->stsz.data, stream->stsz.size);
9720
9721 /* skip version + flags */
9722 if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
9723 !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
9724 goto corrupt_file;
9725
9726 if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
9727 goto corrupt_file;
9728
9729 if (!stream->n_samples)
9730 goto no_samples;
9731
9732 /* sample-to-chunk atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009733 if (!aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stsc, &stream->stsc))
zengliang.li5f31ef42024-05-16 08:27:38 +00009734 goto corrupt_file;
9735
9736 /* copy atom data into a new buffer for later use */
9737 stream->stsc.data = g_memdup2 (stream->stsc.data, stream->stsc.size);
9738
9739 /* skip version + flags */
9740 if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
9741 !gst_byte_reader_get_uint32_be (&stream->stsc,
9742 &stream->n_samples_per_chunk))
9743 goto corrupt_file;
9744
9745 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
9746 stream->n_samples_per_chunk);
9747
9748 /* make sure there's enough data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009749 if (!aml_qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
zengliang.li5f31ef42024-05-16 08:27:38 +00009750 12))
9751 goto corrupt_file;
9752
9753
9754 /* chunk offset */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009755 if (aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_stco, &stream->stco))
zengliang.li5f31ef42024-05-16 08:27:38 +00009756 stream->co_size = sizeof (guint32);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009757 else if (aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_co64,
zengliang.li5f31ef42024-05-16 08:27:38 +00009758 &stream->stco))
9759 stream->co_size = sizeof (guint64);
9760 else
9761 goto corrupt_file;
9762
9763 /* copy atom data into a new buffer for later use */
9764 stream->stco.data = g_memdup2 (stream->stco.data, stream->stco.size);
9765
9766 /* skip version + flags */
9767 if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
9768 goto corrupt_file;
9769
9770 /* chunks_are_samples == TRUE means treat chunks as samples */
9771 stream->chunks_are_samples = stream->sample_size
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009772 && !AML_CUR_STREAM (stream)->sampled;
zengliang.li5f31ef42024-05-16 08:27:38 +00009773 if (stream->chunks_are_samples) {
9774 /* treat chunks as samples */
9775 if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
9776 goto corrupt_file;
9777 } else {
9778 /* skip number of entries */
9779 if (!gst_byte_reader_skip (&stream->stco, 4))
9780 goto corrupt_file;
9781
9782 /* make sure there are enough data in the stsz atom */
9783 if (!stream->sample_size) {
9784 /* different sizes for each sample */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009785 if (!aml_qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
zengliang.li5f31ef42024-05-16 08:27:38 +00009786 goto corrupt_file;
9787 }
9788 }
9789
9790 /* composition time-to-sample */
9791 if ((stream->ctts_present =
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009792 ! !aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_ctts,
zengliang.li5f31ef42024-05-16 08:27:38 +00009793 &stream->ctts) ? TRUE : FALSE) == TRUE) {
9794 GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
9795 guint8 ctts_version;
9796 gboolean checked_ctts = FALSE;
9797
9798 /* copy atom data into a new buffer for later use */
9799 stream->ctts.data = g_memdup2 (stream->ctts.data, stream->ctts.size);
9800
9801 /* version 1 has signed offsets */
9802 if (!gst_byte_reader_get_uint8 (&stream->ctts, &ctts_version))
9803 goto corrupt_file;
9804
9805 /* flags */
9806 if (!gst_byte_reader_skip (&stream->ctts, 3)
9807 || !gst_byte_reader_get_uint32_be (&stream->ctts,
9808 &stream->n_composition_times))
9809 goto corrupt_file;
9810
9811 /* make sure there's enough data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009812 if (!aml_qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
zengliang.li5f31ef42024-05-16 08:27:38 +00009813 4 + 4))
9814 goto corrupt_file;
9815
9816 /* This is optional, if missing we iterate the ctts */
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009817 if (aml_qtdemux_tree_get_child_by_type_full (stbl, AML_FOURCC_cslg, &cslg)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009818 guint8 cslg_version;
9819
9820 /* cslg version 1 has 64 bit fields */
9821 if (!gst_byte_reader_get_uint8 (&cslg, &cslg_version))
9822 goto corrupt_file;
9823
9824 /* skip flags */
9825 if (!gst_byte_reader_skip (&cslg, 3))
9826 goto corrupt_file;
9827
9828 if (cslg_version == 0) {
9829 gint32 composition_to_dts_shift;
9830
9831 if (!gst_byte_reader_get_int32_be (&cslg, &composition_to_dts_shift))
9832 goto corrupt_file;
9833
9834 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9835 } else {
9836 gint64 composition_to_dts_shift;
9837
9838 if (!gst_byte_reader_get_int64_be (&cslg, &composition_to_dts_shift))
9839 goto corrupt_file;
9840
9841 stream->cslg_shift = MAX (0, composition_to_dts_shift);
9842 }
9843 } else {
9844 gint32 cslg_least = 0;
9845 guint num_entries, pos;
9846 gint i;
9847
9848 pos = gst_byte_reader_get_pos (&stream->ctts);
9849 num_entries = stream->n_composition_times;
9850
9851 checked_ctts = TRUE;
9852
9853 stream->cslg_shift = 0;
9854
9855 for (i = 0; i < num_entries; i++) {
9856 gint32 offset;
9857
9858 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9859 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9860 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9861 * slightly inaccurate PTS could be more usable than corrupted one */
9862 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
zengliang.li055f1112024-05-16 12:50:57 +00009863 && ABS (offset) * 2 > stream->duration)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009864 GST_WARNING_OBJECT (qtdemux,
9865 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9866 " larger than duration %" G_GUINT64_FORMAT, offset,
9867 stream->duration);
9868
9869 stream->cslg_shift = 0;
9870 stream->ctts_present = FALSE;
9871 goto done;
9872 }
9873
bo.xiaof8fd4c52024-08-13 11:43:09 +08009874#if 0 //jira KAR-686, cl 272031
zengliang.li5f31ef42024-05-16 08:27:38 +00009875 /* Don't consider "no decode samples" with offset G_MININT32
9876 * for the DTS/PTS shift */
bo.xiaof8fd4c52024-08-13 11:43:09 +08009877 if (offset != G_MININT32 && offset < cslg_least)
9878 cslg_least = offset;
9879#endif
zengliang.li5f31ef42024-05-16 08:27:38 +00009880 }
9881
9882 if (cslg_least < 0)
9883 stream->cslg_shift = -cslg_least;
9884 else
9885 stream->cslg_shift = 0;
9886
9887 /* reset the reader so we can generate sample table */
9888 gst_byte_reader_set_pos (&stream->ctts, pos);
9889 }
9890
9891 /* Check if ctts values are looking reasonable if that didn't happen above */
9892 if (!checked_ctts) {
9893 guint num_entries, pos;
9894 gint i;
9895
9896 pos = gst_byte_reader_get_pos (&stream->ctts);
9897 num_entries = stream->n_composition_times;
9898
9899 for (i = 0; i < num_entries; i++) {
9900 gint32 offset;
9901
9902 gst_byte_reader_skip_unchecked (&stream->ctts, 4);
9903 offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
9904 /* HACK: if sample_offset is larger than 2 * duration, ignore the box.
9905 * slightly inaccurate PTS could be more usable than corrupted one */
9906 if (G_UNLIKELY ((ctts_version == 0 || offset != G_MININT32)
9907 && ABS (offset) / 2 > stream->duration)) {
9908 GST_WARNING_OBJECT (qtdemux,
9909 "Ignore corrupted ctts, sample_offset %" G_GINT32_FORMAT
9910 " larger than duration %" G_GUINT64_FORMAT, offset,
9911 stream->duration);
9912
9913 stream->cslg_shift = 0;
9914 stream->ctts_present = FALSE;
9915 goto done;
9916 }
9917 }
9918
9919 /* reset the reader so we can generate sample table */
9920 gst_byte_reader_set_pos (&stream->ctts, pos);
9921 }
9922 } else {
9923 /* Ensure the cslg_shift value is consistent so we can use it
9924 * unconditionally to produce TS and Segment */
9925 stream->cslg_shift = 0;
9926 }
9927
9928 GST_DEBUG_OBJECT (qtdemux, "Using clsg_shift %" G_GUINT64_FORMAT,
9929 stream->cslg_shift);
9930
9931 /* For raw audio streams especially we might want to merge the samples
9932 * to not output one audio sample per buffer. We're doing this here
9933 * before allocating the sample tables so that from this point onwards
9934 * the number of container samples are static */
9935 if (stream->min_buffer_size > 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009936 aml_qtdemux_merge_sample_table (qtdemux, stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00009937 }
9938
9939done:
9940 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009941 stream->n_samples, (guint) sizeof (AmlQtDemuxSample),
9942 stream->n_samples * sizeof (AmlQtDemuxSample) / (1024.0 * 1024.0));
zengliang.li5f31ef42024-05-16 08:27:38 +00009943
9944 if (stream->n_samples >=
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009945 AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (AmlQtDemuxSample)) {
zengliang.li5f31ef42024-05-16 08:27:38 +00009946 GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
9947 "be larger than %uMB (broken file?)", stream->n_samples,
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009948 AML_QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
zengliang.li5f31ef42024-05-16 08:27:38 +00009949 return FALSE;
9950 }
9951
9952 g_assert (stream->samples == NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009953 stream->samples = g_try_new0 (AmlQtDemuxSample, stream->n_samples);
zengliang.li5f31ef42024-05-16 08:27:38 +00009954 if (!stream->samples) {
9955 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
9956 stream->n_samples);
9957 return FALSE;
9958 }
9959
9960 return TRUE;
9961
9962corrupt_file:
9963 {
9964 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
9965 (_("This file is corrupt and cannot be played.")), (NULL));
9966 return FALSE;
9967 }
9968no_samples:
9969 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009970 gst_aml_qtdemux_stbl_free (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +00009971 if (!qtdemux->fragmented) {
9972 /* not quite good */
9973 GST_WARNING_OBJECT (qtdemux, "stream has no samples");
9974 return FALSE;
9975 } else {
9976 /* may pick up samples elsewhere */
9977 return TRUE;
9978 }
9979 }
9980}
9981
9982/* collect samples from the next sample to be parsed up to sample @n for @stream
9983 * by reading the info from @stbl
9984 *
9985 * This code can be executed from both the streaming thread and the seeking
9986 * thread so it takes the object lock to protect itself
9987 */
9988static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009989aml_qtdemux_parse_samples (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream, guint32 n)
zengliang.li5f31ef42024-05-16 08:27:38 +00009990{
9991 gint i, j, k;
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009992 AmlQtDemuxSample *samples, *first, *cur, *last;
zengliang.li5f31ef42024-05-16 08:27:38 +00009993 guint32 n_samples_per_chunk;
9994 guint32 n_samples;
9995
9996 GST_LOG_OBJECT (qtdemux, "parsing samples for stream fourcc %"
9997 GST_FOURCC_FORMAT ", pad %s",
zengliang.lia6c0cdd2024-05-18 07:15:51 +00009998 GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc),
zengliang.li5f31ef42024-05-16 08:27:38 +00009999 stream->pad ? GST_PAD_NAME (stream->pad) : "(NULL)");
10000
10001 n_samples = stream->n_samples;
10002
10003 if (n >= n_samples)
10004 goto out_of_samples;
10005
10006 GST_OBJECT_LOCK (qtdemux);
10007 if (n <= stream->stbl_index)
10008 goto already_parsed;
10009
10010 GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
10011
10012 if (!stream->stsz.data) {
10013 /* so we already parsed and passed all the moov samples;
10014 * onto fragmented ones */
10015 g_assert (qtdemux->fragmented);
10016 goto done;
10017 }
10018
10019 /* pointer to the sample table */
10020 samples = stream->samples;
10021
10022 /* starts from -1, moves to the next sample index to parse */
10023 stream->stbl_index++;
10024
10025 /* keep track of the first and last sample to fill */
10026 first = &samples[stream->stbl_index];
10027 last = &samples[n];
10028
10029 if (!stream->chunks_are_samples) {
10030 /* set the sample sizes */
10031 if (stream->sample_size == 0) {
10032 /* different sizes for each sample */
10033 for (cur = first; cur <= last; cur++) {
10034 cur->size = gst_byte_reader_get_uint32_be_unchecked (&stream->stsz);
10035 GST_LOG_OBJECT (qtdemux, "sample %d has size %u",
10036 (guint) (cur - samples), cur->size);
10037 }
10038 } else {
10039 /* samples have the same size */
10040 GST_LOG_OBJECT (qtdemux, "all samples have size %u", stream->sample_size);
10041 for (cur = first; cur <= last; cur++)
10042 cur->size = stream->sample_size;
10043 }
10044 }
10045
10046 n_samples_per_chunk = stream->n_samples_per_chunk;
10047 cur = first;
10048
10049 for (i = stream->stsc_index; i < n_samples_per_chunk; i++) {
10050 guint32 last_chunk;
10051
10052 if (stream->stsc_chunk_index >= stream->last_chunk
10053 || stream->stsc_chunk_index < stream->first_chunk) {
10054 stream->first_chunk =
10055 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10056 stream->samples_per_chunk =
10057 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc);
10058 /* starts from 1 */
10059 stream->stsd_sample_description_id =
10060 gst_byte_reader_get_uint32_be_unchecked (&stream->stsc) - 1;
10061
10062 /* chunk numbers are counted from 1 it seems */
10063 if (G_UNLIKELY (stream->first_chunk == 0))
10064 goto corrupt_file;
10065
10066 --stream->first_chunk;
10067
10068 /* the last chunk of each entry is calculated by taking the first chunk
10069 * of the next entry; except if there is no next, where we fake it with
10070 * INT_MAX */
10071 if (G_UNLIKELY (i == (stream->n_samples_per_chunk - 1))) {
10072 stream->last_chunk = G_MAXUINT32;
10073 } else {
10074 stream->last_chunk =
10075 gst_byte_reader_peek_uint32_be_unchecked (&stream->stsc);
10076 if (G_UNLIKELY (stream->last_chunk == 0))
10077 goto corrupt_file;
10078
10079 --stream->last_chunk;
10080 }
10081
10082 GST_LOG_OBJECT (qtdemux,
10083 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d"
10084 "sample desc ID: %d", i, stream->first_chunk, stream->last_chunk,
10085 stream->samples_per_chunk, stream->stsd_sample_description_id);
10086
10087 if (G_UNLIKELY (stream->last_chunk < stream->first_chunk))
10088 goto corrupt_file;
10089
10090 if (stream->last_chunk != G_MAXUINT32) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010091 if (!aml_qt_atom_parser_peek_sub (&stream->stco,
zengliang.li5f31ef42024-05-16 08:27:38 +000010092 stream->first_chunk * stream->co_size,
10093 (stream->last_chunk - stream->first_chunk) * stream->co_size,
10094 &stream->co_chunk))
10095 goto corrupt_file;
10096
10097 } else {
10098 stream->co_chunk = stream->stco;
10099 if (!gst_byte_reader_skip (&stream->co_chunk,
10100 stream->first_chunk * stream->co_size))
10101 goto corrupt_file;
10102 }
10103
10104 stream->stsc_chunk_index = stream->first_chunk;
10105 }
10106
10107 last_chunk = stream->last_chunk;
10108
10109 if (stream->chunks_are_samples) {
10110 cur = &samples[stream->stsc_chunk_index];
10111
10112 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10113 if (j > n) {
10114 /* save state */
10115 stream->stsc_chunk_index = j;
10116 goto done;
10117 }
10118
10119 cur->offset =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010120 aml_qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
zengliang.li5f31ef42024-05-16 08:27:38 +000010121 stream->co_size);
10122
10123 GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
10124 "%" G_GUINT64_FORMAT, j, cur->offset);
10125
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010126 if (AML_CUR_STREAM (stream)->samples_per_frame > 0 &&
10127 AML_CUR_STREAM (stream)->bytes_per_frame > 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010128 cur->size =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010129 (stream->samples_per_chunk * AML_CUR_STREAM (stream)->n_channels) /
10130 AML_CUR_STREAM (stream)->samples_per_frame *
10131 AML_CUR_STREAM (stream)->bytes_per_frame;
zengliang.li5f31ef42024-05-16 08:27:38 +000010132 } else {
10133 cur->size = stream->samples_per_chunk;
10134 }
10135
10136 GST_DEBUG_OBJECT (qtdemux,
10137 "keyframe sample %d: timestamp %" GST_TIME_FORMAT ", size %u",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010138 j, GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream,
zengliang.li5f31ef42024-05-16 08:27:38 +000010139 stream->stco_sample_index)), cur->size);
10140
10141 cur->timestamp = stream->stco_sample_index;
10142 cur->duration = stream->samples_per_chunk;
10143 cur->keyframe = TRUE;
10144 cur++;
10145
10146 stream->stco_sample_index += stream->samples_per_chunk;
10147 }
10148 stream->stsc_chunk_index = j;
10149 } else {
10150 for (j = stream->stsc_chunk_index; j < last_chunk; j++) {
10151 guint32 samples_per_chunk;
10152 guint64 chunk_offset;
10153
10154 if (!stream->stsc_sample_index
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010155 && !aml_qt_atom_parser_get_offset (&stream->co_chunk, stream->co_size,
zengliang.li5f31ef42024-05-16 08:27:38 +000010156 &stream->chunk_offset))
10157 goto corrupt_file;
10158
10159 samples_per_chunk = stream->samples_per_chunk;
10160 chunk_offset = stream->chunk_offset;
10161
10162 for (k = stream->stsc_sample_index; k < samples_per_chunk; k++) {
10163 GST_LOG_OBJECT (qtdemux, "creating entry %d with offset %"
10164 G_GUINT64_FORMAT " and size %d",
10165 (guint) (cur - samples), chunk_offset, cur->size);
10166
10167 cur->offset = chunk_offset;
10168 chunk_offset += cur->size;
10169 cur++;
10170
10171 if (G_UNLIKELY (cur > last)) {
10172 /* save state */
10173 stream->stsc_sample_index = k + 1;
10174 stream->chunk_offset = chunk_offset;
10175 stream->stsc_chunk_index = j;
10176 goto done2;
10177 }
10178 }
10179 stream->stsc_sample_index = 0;
10180 }
10181 stream->stsc_chunk_index = j;
10182 }
10183 stream->stsc_index++;
10184 }
10185
10186 if (stream->chunks_are_samples)
10187 goto ctts;
10188done2:
10189 {
10190 guint32 n_sample_times;
10191
10192 n_sample_times = stream->n_sample_times;
10193 cur = first;
10194
10195 for (i = stream->stts_index; i < n_sample_times; i++) {
10196 guint32 stts_samples;
10197 gint32 stts_duration;
10198 gint64 stts_time;
10199
10200 if (stream->stts_sample_index >= stream->stts_samples
10201 || !stream->stts_sample_index) {
10202
10203 stream->stts_samples =
10204 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10205 stream->stts_duration =
10206 gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
10207
10208 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u",
10209 i, stream->stts_samples, stream->stts_duration);
10210
10211 stream->stts_sample_index = 0;
10212 }
10213
10214 stts_samples = stream->stts_samples;
10215 stts_duration = stream->stts_duration;
10216 stts_time = stream->stts_time;
10217
10218 for (j = stream->stts_sample_index; j < stts_samples; j++) {
10219 GST_DEBUG_OBJECT (qtdemux,
10220 "sample %d: index %d, timestamp %" GST_TIME_FORMAT,
10221 (guint) (cur - samples), j,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010222 GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream, stts_time)));
zengliang.li5f31ef42024-05-16 08:27:38 +000010223
10224 cur->timestamp = stts_time;
10225 cur->duration = stts_duration;
10226
10227 /* avoid 32-bit wrap-around,
10228 * but still mind possible 'negative' duration */
10229 stts_time += (gint64) stts_duration;
10230 cur++;
10231
10232 if (G_UNLIKELY (cur > last)) {
10233 /* save values */
10234 stream->stts_time = stts_time;
10235 stream->stts_sample_index = j + 1;
10236 if (stream->stts_sample_index >= stream->stts_samples)
10237 stream->stts_index++;
10238 goto done3;
10239 }
10240 }
10241 stream->stts_sample_index = 0;
10242 stream->stts_time = stts_time;
10243 stream->stts_index++;
10244 }
10245 /* fill up empty timestamps with the last timestamp, this can happen when
10246 * the last samples do not decode and so we don't have timestamps for them.
10247 * We however look at the last timestamp to estimate the track length so we
10248 * need something in here. */
10249 for (; cur < last; cur++) {
10250 GST_DEBUG_OBJECT (qtdemux,
10251 "fill sample %d: timestamp %" GST_TIME_FORMAT,
10252 (guint) (cur - samples),
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010253 GST_TIME_ARGS (AML_QTSTREAMTIME_TO_GSTTIME (stream, stream->stts_time)));
zengliang.li5f31ef42024-05-16 08:27:38 +000010254 cur->timestamp = stream->stts_time;
10255 cur->duration = -1;
10256 }
10257 }
10258done3:
10259 {
10260 /* sample sync, can be NULL */
10261 if (stream->stss_present == TRUE) {
10262 guint32 n_sample_syncs;
10263
10264 n_sample_syncs = stream->n_sample_syncs;
10265
10266 if (!n_sample_syncs) {
10267 GST_DEBUG_OBJECT (qtdemux, "all samples are keyframes");
10268 stream->all_keyframe = TRUE;
10269 } else {
10270 for (i = stream->stss_index; i < n_sample_syncs; i++) {
10271 /* note that the first sample is index 1, not 0 */
10272 guint32 index;
10273
10274 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stss);
10275
10276 if (G_LIKELY (index > 0 && index <= n_samples)) {
10277 index -= 1;
10278 samples[index].keyframe = TRUE;
10279 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10280 /* and exit if we have enough samples */
10281 if (G_UNLIKELY (index >= n)) {
10282 i++;
10283 break;
10284 }
10285 }
10286 }
10287 /* save state */
10288 stream->stss_index = i;
10289 }
10290
10291 /* stps marks partial sync frames like open GOP I-Frames */
10292 if (stream->stps_present == TRUE) {
10293 guint32 n_sample_partial_syncs;
10294
10295 n_sample_partial_syncs = stream->n_sample_partial_syncs;
10296
10297 /* if there are no entries, the stss table contains the real
10298 * sync samples */
10299 if (n_sample_partial_syncs) {
10300 for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
10301 /* note that the first sample is index 1, not 0 */
10302 guint32 index;
10303
10304 index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
10305
10306 if (G_LIKELY (index > 0 && index <= n_samples)) {
10307 index -= 1;
10308 samples[index].keyframe = TRUE;
10309 GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
10310 /* and exit if we have enough samples */
10311 if (G_UNLIKELY (index >= n)) {
10312 i++;
10313 break;
10314 }
10315 }
10316 }
10317 /* save state */
10318 stream->stps_index = i;
10319 }
10320 }
10321 } else {
10322 /* no stss, all samples are keyframes */
10323 stream->all_keyframe = TRUE;
10324 GST_DEBUG_OBJECT (qtdemux, "setting all keyframes");
10325 }
10326 }
10327
10328ctts:
10329 /* composition time to sample */
10330 if (stream->ctts_present == TRUE) {
10331 guint32 n_composition_times;
10332 guint32 ctts_count;
10333 gint32 ctts_soffset;
10334
10335 /* Fill in the pts_offsets */
10336 cur = first;
10337 n_composition_times = stream->n_composition_times;
10338
10339 for (i = stream->ctts_index; i < n_composition_times; i++) {
10340 if (stream->ctts_sample_index >= stream->ctts_count
10341 || !stream->ctts_sample_index) {
10342 stream->ctts_count =
10343 gst_byte_reader_get_uint32_be_unchecked (&stream->ctts);
10344 stream->ctts_soffset =
10345 gst_byte_reader_get_int32_be_unchecked (&stream->ctts);
10346 stream->ctts_sample_index = 0;
10347 }
10348
10349 ctts_count = stream->ctts_count;
10350 ctts_soffset = stream->ctts_soffset;
10351
10352 /* FIXME: Set offset to 0 for "no decode samples". This needs
10353 * to be handled in a codec specific manner ideally. */
10354 if (ctts_soffset == G_MININT32)
10355 ctts_soffset = 0;
10356
10357 for (j = stream->ctts_sample_index; j < ctts_count; j++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010358 if (stream->elst_media_time != G_MAXUINT64 && AML_FOURCC_vide == stream->subtype)
zengliang.li4681ee42024-05-16 12:25:30 +000010359 {
10360 cur->pts_offset = ctts_soffset - stream->elst_media_time;
bo.xiao86472382024-07-23 10:24:15 +080010361 GST_DEBUG_OBJECT (qtdemux, "elst_media_time: %lld, new pts_offset: %d", stream->elst_media_time, cur->pts_offset);
zengliang.li4681ee42024-05-16 12:25:30 +000010362 } else {
10363 cur->pts_offset = ctts_soffset;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010364 }
zengliang.li5f31ef42024-05-16 08:27:38 +000010365 cur++;
10366
10367 if (G_UNLIKELY (cur > last)) {
10368 /* save state */
10369 stream->ctts_sample_index = j + 1;
10370 goto done;
10371 }
10372 }
10373 stream->ctts_sample_index = 0;
10374 stream->ctts_index++;
10375 }
10376 }
10377done:
10378 stream->stbl_index = n;
10379 /* if index has been completely parsed, free data that is no-longer needed */
10380 if (n + 1 == stream->n_samples) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010381 gst_aml_qtdemux_stbl_free (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000010382 GST_DEBUG_OBJECT (qtdemux, "parsed all available samples;");
10383 if (qtdemux->pullbased) {
10384 GST_DEBUG_OBJECT (qtdemux, "checking for more samples");
10385 while (n + 1 == stream->n_samples)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010386 if (aml_qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
zengliang.li5f31ef42024-05-16 08:27:38 +000010387 break;
10388 }
10389 }
10390 GST_OBJECT_UNLOCK (qtdemux);
10391
10392 return TRUE;
10393
10394 /* SUCCESS */
10395already_parsed:
10396 {
10397 GST_LOG_OBJECT (qtdemux,
10398 "Tried to parse up to sample %u but this sample has already been parsed",
10399 n);
10400 /* if fragmented, there may be more */
10401 if (qtdemux->fragmented && n == stream->stbl_index)
10402 goto done;
10403 GST_OBJECT_UNLOCK (qtdemux);
10404 return TRUE;
10405 }
10406 /* ERRORS */
10407out_of_samples:
10408 {
10409 GST_LOG_OBJECT (qtdemux,
10410 "Tried to parse up to sample %u but there are only %u samples", n + 1,
10411 stream->n_samples);
10412 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10413 (_("This file is corrupt and cannot be played.")), (NULL));
10414 return FALSE;
10415 }
10416corrupt_file:
10417 {
10418 GST_OBJECT_UNLOCK (qtdemux);
10419 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
10420 (_("This file is corrupt and cannot be played.")), (NULL));
10421 return FALSE;
10422 }
10423}
10424
10425/* collect all segment info for @stream.
10426 */
10427static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010428aml_qtdemux_parse_segments (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +000010429 GNode * trak)
10430{
10431 GNode *edts;
10432 /* accept edts if they contain gaps at start and there is only
10433 * one media segment */
10434 gboolean allow_pushbased_edts = TRUE;
10435 gint media_segments_count = 0;
10436
10437 /* parse and prepare segment info from the edit list */
10438 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
10439 stream->n_segments = 0;
10440 stream->segments = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010441 stream->elst_media_time = G_MAXUINT64;
10442 if ((edts = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_edts))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010443 GNode *elst;
10444 guint n_segments;
10445 guint segment_number, entry_size;
10446 guint64 time;
10447 GstClockTime stime;
10448 const guint8 *buffer;
10449 guint8 version;
10450 guint32 size;
10451
10452 GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010453 if (!(elst = aml_qtdemux_tree_get_child_by_type (edts, AML_FOURCC_elst)))
zengliang.li5f31ef42024-05-16 08:27:38 +000010454 goto done;
10455
10456 buffer = elst->data;
10457
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010458 size = AML_QT_UINT32 (buffer);
zengliang.li5f31ef42024-05-16 08:27:38 +000010459 /* version, flags, n_segments */
10460 if (size < 16) {
10461 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10462 goto done;
10463 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010464 version = AML_QT_UINT8 (buffer + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000010465 entry_size = (version == 1) ? 20 : 12;
10466
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010467 n_segments = AML_QT_UINT32 (buffer + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000010468
10469 if (n_segments > 100000 || size < 16 + n_segments * entry_size) {
10470 GST_WARNING_OBJECT (qtdemux, "Invalid edit list");
10471 goto done;
10472 }
10473
10474 /* we might allocate a bit too much, at least allocate 1 segment */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010475 stream->segments = g_new (AmlQtDemuxSegment, MAX (n_segments, 1));
zengliang.li5f31ef42024-05-16 08:27:38 +000010476
10477 /* segments always start from 0 */
10478 time = 0;
10479 stime = 0;
10480 buffer += 16;
10481 for (segment_number = 0; segment_number < n_segments; segment_number++) {
10482 guint64 duration;
10483 guint64 media_time;
10484 gboolean empty_edit = FALSE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010485 AmlQtDemuxSegment *segment;
zengliang.li5f31ef42024-05-16 08:27:38 +000010486 guint32 rate_int;
10487 GstClockTime media_start = GST_CLOCK_TIME_NONE;
10488
10489 if (version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010490 media_time = AML_QT_UINT64 (buffer + 8);
10491 duration = AML_QT_UINT64 (buffer);
zengliang.li5f31ef42024-05-16 08:27:38 +000010492 if (media_time == G_MAXUINT64)
10493 empty_edit = TRUE;
10494 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010495 media_time = AML_QT_UINT32 (buffer + 4);
10496 duration = AML_QT_UINT32 (buffer);
zengliang.li5f31ef42024-05-16 08:27:38 +000010497 if (media_time == G_MAXUINT32)
10498 empty_edit = TRUE;
10499 }
10500
10501 if (!empty_edit)
zengliang.li4681ee42024-05-16 12:25:30 +000010502 {
10503 stream->elst_media_time = MIN(media_time, stream->elst_media_time);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010504 media_start = AML_QTSTREAMTIME_TO_GSTTIME (stream, media_time);
zengliang.li4681ee42024-05-16 12:25:30 +000010505 }
zengliang.li5f31ef42024-05-16 08:27:38 +000010506
10507 segment = &stream->segments[segment_number];
10508
10509 /* time and duration expressed in global timescale */
10510 segment->time = stime;
10511 if (duration != 0 || empty_edit) {
10512 /* edge case: empty edits with duration=zero are treated here.
10513 * (files should not have these anyway). */
10514
10515 /* add non scaled values so we don't cause roundoff errors */
10516 time += duration;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010517 stime = AML_QTTIME_TO_GSTTIME (qtdemux, time);
zengliang.li5f31ef42024-05-16 08:27:38 +000010518 segment->duration = stime - segment->time;
10519 } else {
10520 /* zero duration does not imply media_start == media_stop
10521 * but, only specify media_start. The edit ends with the track. */
10522 stime = segment->duration = GST_CLOCK_TIME_NONE;
10523 /* Don't allow more edits after this one. */
10524 n_segments = segment_number + 1;
10525 }
10526 segment->stop_time = stime;
10527
10528 segment->trak_media_start = media_time;
10529 /* media_time expressed in stream timescale */
10530 if (!empty_edit) {
10531 segment->media_start = media_start;
10532 segment->media_stop = GST_CLOCK_TIME_IS_VALID (segment->duration)
10533 ? segment->media_start + segment->duration : GST_CLOCK_TIME_NONE;
10534 media_segments_count++;
10535 } else {
10536 segment->media_start = GST_CLOCK_TIME_NONE;
10537 segment->media_stop = GST_CLOCK_TIME_NONE;
10538 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010539 rate_int = AML_QT_UINT32 (buffer + ((version == 1) ? 16 : 8));
zengliang.li5f31ef42024-05-16 08:27:38 +000010540
10541 if (rate_int <= 1) {
10542 /* 0 is not allowed, some programs write 1 instead of the floating point
10543 * value */
10544 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
10545 rate_int);
10546 segment->rate = 1;
10547 } else {
10548 segment->rate = rate_int / 65536.0;
10549 }
10550
10551 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
10552 ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
10553 " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
10554 " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
10555 segment_number, GST_TIME_ARGS (segment->time),
10556 GST_TIME_ARGS (segment->duration),
10557 GST_TIME_ARGS (segment->media_start), media_time,
10558 GST_TIME_ARGS (segment->media_stop),
10559 GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
10560 stream->timescale);
10561 if (segment->stop_time > qtdemux->segment.stop &&
10562 !qtdemux->upstream_format_is_time) {
10563 GST_WARNING_OBJECT (qtdemux, "Segment %d "
10564 " extends to %" GST_TIME_FORMAT
10565 " past the end of the declared movie duration %" GST_TIME_FORMAT
10566 " movie segment will be extended", segment_number,
10567 GST_TIME_ARGS (segment->stop_time),
10568 GST_TIME_ARGS (qtdemux->segment.stop));
10569 qtdemux->segment.stop = qtdemux->segment.duration = segment->stop_time;
10570 }
10571
10572 buffer += entry_size;
10573 }
10574 GST_DEBUG_OBJECT (qtdemux, "found %d segments", n_segments);
10575 stream->n_segments = n_segments;
10576 if (media_segments_count != 1)
10577 allow_pushbased_edts = FALSE;
10578 }
10579done:
10580
10581 /* push based does not handle segments, so act accordingly here,
10582 * and warn if applicable */
10583 if (!qtdemux->pullbased && !allow_pushbased_edts) {
10584 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
10585 /* remove and use default one below, we stream like it anyway */
10586 g_free (stream->segments);
10587 stream->segments = NULL;
10588 stream->n_segments = 0;
10589 }
10590
10591 /* no segments, create one to play the complete trak */
10592 if (stream->n_segments == 0) {
10593 GstClockTime stream_duration =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010594 AML_QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);
zengliang.li5f31ef42024-05-16 08:27:38 +000010595
10596 if (stream->segments == NULL)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010597 stream->segments = g_new (AmlQtDemuxSegment, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +000010598
10599 /* represent unknown our way */
10600 if (stream_duration == 0)
10601 stream_duration = GST_CLOCK_TIME_NONE;
10602
10603 stream->segments[0].time = 0;
10604 stream->segments[0].stop_time = stream_duration;
10605 stream->segments[0].duration = stream_duration;
10606 stream->segments[0].media_start = 0;
10607 stream->segments[0].media_stop = stream_duration;
10608 stream->segments[0].rate = 1.0;
10609 stream->segments[0].trak_media_start = 0;
10610
10611 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
10612 GST_TIME_ARGS (stream_duration));
10613 stream->n_segments = 1;
10614 stream->dummy_segment = TRUE;
10615 }
10616 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);
10617
10618 return TRUE;
10619}
10620
10621/*
10622 * Parses the stsd atom of a svq3 trak looking for
10623 * the SMI and gama atoms.
10624 */
10625static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010626aml_qtdemux_parse_svq3_stsd_data (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +000010627 const guint8 * stsd_entry_data, const guint8 ** gamma, GstBuffer ** seqh)
10628{
10629 const guint8 *_gamma = NULL;
10630 GstBuffer *_seqh = NULL;
10631 const guint8 *stsd_data = stsd_entry_data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010632 guint32 length = AML_QT_UINT32 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000010633 guint16 version;
10634
10635 if (length < 32) {
10636 GST_WARNING_OBJECT (qtdemux, "stsd too short");
10637 goto end;
10638 }
10639
10640 stsd_data += 16;
10641 length -= 16;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010642 version = AML_QT_UINT16 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000010643 if (version == 3) {
10644 if (length >= 70) {
10645 length -= 70;
10646 stsd_data += 70;
10647 while (length > 8) {
10648 guint32 fourcc, size;
10649 const guint8 *data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010650 size = AML_QT_UINT32 (stsd_data);
10651 fourcc = AML_QT_FOURCC (stsd_data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000010652 data = stsd_data + 8;
10653
10654 if (size == 0) {
10655 GST_WARNING_OBJECT (qtdemux, "Atom of size 0 found, aborting "
10656 "svq3 atom parsing");
10657 goto end;
10658 }
10659
10660 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010661 case AML_FOURCC_gama:{
zengliang.li5f31ef42024-05-16 08:27:38 +000010662 if (size == 12) {
10663 _gamma = data;
10664 } else {
10665 GST_WARNING_OBJECT (qtdemux, "Unexpected size %" G_GUINT32_FORMAT
10666 " for gama atom, expected 12", size);
10667 }
10668 break;
10669 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010670 case AML_FOURCC_SMI_:{
10671 if (size > 16 && AML_QT_FOURCC (data) == AML_FOURCC_SEQH) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010672 guint32 seqh_size;
10673 if (_seqh != NULL) {
10674 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
10675 " found, ignoring");
10676 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010677 seqh_size = AML_QT_UINT32 (data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000010678 if (seqh_size > 0) {
10679 _seqh = gst_buffer_new_and_alloc (seqh_size);
10680 gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
10681 }
10682 }
10683 }
10684 break;
10685 }
10686 default:{
10687 GST_WARNING_OBJECT (qtdemux, "Unhandled atom %" GST_FOURCC_FORMAT
10688 " in SVQ3 entry in stsd atom", GST_FOURCC_ARGS (fourcc));
10689 }
10690 }
10691
10692 if (size <= length) {
10693 length -= size;
10694 stsd_data += size;
10695 }
10696 }
10697 } else {
10698 GST_WARNING_OBJECT (qtdemux, "SVQ3 entry too short in stsd atom");
10699 }
10700 } else {
10701 GST_WARNING_OBJECT (qtdemux, "Unexpected version for SVQ3 entry %"
10702 G_GUINT16_FORMAT, version);
10703 goto end;
10704 }
10705
10706end:
10707 if (gamma) {
10708 *gamma = _gamma;
10709 }
10710 if (seqh) {
10711 *seqh = _seqh;
10712 } else if (_seqh) {
10713 gst_buffer_unref (_seqh);
10714 }
10715}
10716
10717static gchar *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010718aml_qtdemux_get_rtsp_uri_from_hndl (GstAmlQTDemux * qtdemux, GNode * minf)
zengliang.li5f31ef42024-05-16 08:27:38 +000010719{
10720 GNode *dinf;
10721 GstByteReader dref;
10722 gchar *uri = NULL;
10723
10724 /*
10725 * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
10726 * atom that might contain a 'data' atom with the rtsp uri.
10727 * This case was reported in bug #597497, some info about
10728 * the hndl atom can be found in TN1195
10729 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010730 dinf = aml_qtdemux_tree_get_child_by_type (minf, AML_FOURCC_dinf);
zengliang.li5f31ef42024-05-16 08:27:38 +000010731 GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");
10732
10733 if (dinf) {
10734 guint32 dref_num_entries = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010735 if (aml_qtdemux_tree_get_child_by_type_full (dinf, AML_FOURCC_dref, &dref) &&
zengliang.li5f31ef42024-05-16 08:27:38 +000010736 gst_byte_reader_skip (&dref, 4) &&
10737 gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
10738 gint i;
10739
10740 /* search dref entries for hndl atom */
10741 for (i = 0; i < dref_num_entries; i++) {
10742 guint32 size = 0, type;
10743 guint8 string_len = 0;
10744 if (gst_byte_reader_get_uint32_be (&dref, &size) &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010745 aml_qt_atom_parser_get_fourcc (&dref, &type)) {
10746 if (type == AML_FOURCC_hndl) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010747 GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");
10748
10749 /* skip data reference handle bytes and the
10750 * following pascal string and some extra 4
10751 * bytes I have no idea what are */
10752 if (!gst_byte_reader_skip (&dref, 4) ||
10753 !gst_byte_reader_get_uint8 (&dref, &string_len) ||
10754 !gst_byte_reader_skip (&dref, string_len + 4)) {
10755 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
10756 break;
10757 }
10758
10759 /* iterate over the atoms to find the data atom */
10760 while (gst_byte_reader_get_remaining (&dref) >= 8) {
10761 guint32 atom_size;
10762 guint32 atom_type;
10763
10764 if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010765 aml_qt_atom_parser_get_fourcc (&dref, &atom_type)) {
10766 if (atom_type == AML_FOURCC_data) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010767 const guint8 *uri_aux = NULL;
10768
10769 /* found the data atom that might contain the rtsp uri */
10770 GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
10771 "hndl atom, interpreting it as an URI");
10772 if (gst_byte_reader_peek_data (&dref, atom_size - 8,
10773 &uri_aux)) {
10774 if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
10775 uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
10776 else
10777 GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
10778 "didn't contain a rtsp address");
10779 } else {
10780 GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
10781 "atom contents");
10782 }
10783 break;
10784 }
10785 /* skipping to the next entry */
10786 if (!gst_byte_reader_skip (&dref, atom_size - 8))
10787 break;
10788 } else {
10789 GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
10790 "atom header");
10791 break;
10792 }
10793 }
10794 break;
10795 }
10796 /* skip to the next entry */
10797 if (!gst_byte_reader_skip (&dref, size - 8))
10798 break;
10799 } else {
10800 GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
10801 }
10802 }
10803 GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
10804 }
10805 }
10806 return uri;
10807}
10808
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010809#define AML_AMR_NB_ALL_MODES 0x81ff
10810#define AML_AMR_WB_ALL_MODES 0x83ff
zengliang.li5f31ef42024-05-16 08:27:38 +000010811static guint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010812aml_qtdemux_parse_amr_bitrate (GstBuffer * buf, gboolean wb)
zengliang.li5f31ef42024-05-16 08:27:38 +000010813{
10814 /* The 'damr' atom is of the form:
10815 *
10816 * | vendor | decoder_ver | mode_set | mode_change_period | frames/sample |
10817 * 32 b 8 b 16 b 8 b 8 b
10818 *
10819 * The highest set bit of the first 7 (AMR-NB) or 8 (AMR-WB) bits of mode_set
10820 * represents the highest mode used in the stream (and thus the maximum
10821 * bitrate), with a couple of special cases as seen below.
10822 */
10823
10824 /* Map of frame type ID -> bitrate */
10825 static const guint nb_bitrates[] = {
10826 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200
10827 };
10828 static const guint wb_bitrates[] = {
10829 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850
10830 };
10831 GstMapInfo map;
10832 gsize max_mode;
10833 guint16 mode_set;
10834
10835 gst_buffer_map (buf, &map, GST_MAP_READ);
10836
10837 if (map.size != 0x11) {
10838 GST_DEBUG ("Atom should have size 0x11, not %" G_GSIZE_FORMAT, map.size);
10839 goto bad_data;
10840 }
10841
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010842 if (AML_QT_FOURCC (map.data + 4) != AML_FOURCC_damr) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010843 GST_DEBUG ("Unknown atom in %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010844 GST_FOURCC_ARGS (AML_QT_UINT32 (map.data + 4)));
zengliang.li5f31ef42024-05-16 08:27:38 +000010845 goto bad_data;
10846 }
10847
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010848 mode_set = AML_QT_UINT16 (map.data + 13);
zengliang.li5f31ef42024-05-16 08:27:38 +000010849
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010850 if (mode_set == (wb ? AML_AMR_WB_ALL_MODES : AML_AMR_NB_ALL_MODES))
zengliang.li5f31ef42024-05-16 08:27:38 +000010851 max_mode = 7 + (wb ? 1 : 0);
10852 else
10853 /* AMR-NB modes fo from 0-7, and AMR-WB modes go from 0-8 */
10854 max_mode = g_bit_nth_msf ((gulong) mode_set & (wb ? 0x1ff : 0xff), -1);
10855
10856 if (max_mode == -1) {
10857 GST_DEBUG ("No mode indication was found (mode set) = %x",
10858 (guint) mode_set);
10859 goto bad_data;
10860 }
10861
10862 gst_buffer_unmap (buf, &map);
10863 return wb ? wb_bitrates[max_mode] : nb_bitrates[max_mode];
10864
10865bad_data:
10866 gst_buffer_unmap (buf, &map);
10867 return 0;
10868}
10869
10870static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010871aml_qtdemux_parse_transformation_matrix (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +000010872 GstByteReader * reader, guint32 * matrix, const gchar * atom)
10873{
10874 /*
10875 * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
10876 * [0 1 2]
10877 * [3 4 5]
10878 * [6 7 8]
10879 */
10880
10881 if (gst_byte_reader_get_remaining (reader) < 36)
10882 return FALSE;
10883
10884 matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
10885 matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
10886 matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
10887 matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
10888 matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
10889 matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
10890 matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
10891 matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
10892 matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
10893
10894 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
10895 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
10896 matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
10897 matrix[2] & 0xFF);
10898 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
10899 matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
10900 matrix[5] & 0xFF);
10901 GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
10902 matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
10903 matrix[8] & 0xFF);
10904
10905 return TRUE;
10906}
10907
10908static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010909aml_qtdemux_inspect_transformation_matrix (GstAmlQTDemux * qtdemux,
10910 AmlQtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
zengliang.li5f31ef42024-05-16 08:27:38 +000010911{
10912
10913/* [a b c]
10914 * [d e f]
10915 * [g h i]
10916 *
10917 * This macro will only compare value abdegh, it expects cfi to have already
10918 * been checked
10919 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010920#define AML_QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
zengliang.li5f31ef42024-05-16 08:27:38 +000010921 (m)[3] == (d << 16) && (m)[4] == (e << 16))
10922
10923 /* only handle the cases where the last column has standard values */
10924 if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
10925 const gchar *rotation_tag = NULL;
10926
10927 /* no rotation needed */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010928 if (AML_QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010929 /* NOP */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010930 } else if (AML_QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010931 rotation_tag = "rotate-90";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010932 } else if (AML_QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010933 rotation_tag = "rotate-180";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010934 } else if (AML_QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000010935 rotation_tag = "rotate-270";
10936 } else {
10937 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10938 }
10939
10940 GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
10941 GST_STR_NULL (rotation_tag));
10942 if (rotation_tag != NULL) {
10943 if (*taglist == NULL)
10944 *taglist = gst_tag_list_new_empty ();
10945 gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
10946 GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
10947 }
10948 } else {
10949 GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
10950 }
10951}
10952
10953static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010954aml_qtdemux_parse_protection_aavd (GstAmlQTDemux * qtdemux,
10955 AmlQtDemuxStream * stream, GNode * container, guint32 * original_fmt)
zengliang.li5f31ef42024-05-16 08:27:38 +000010956{
10957 GNode *adrm;
10958 guint32 adrm_size;
10959 GstBuffer *adrm_buf = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010960 AmlQtDemuxAavdEncryptionInfo *info;
zengliang.li5f31ef42024-05-16 08:27:38 +000010961
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010962 adrm = aml_qtdemux_tree_get_child_by_type (container, AML_FOURCC_adrm);
zengliang.li5f31ef42024-05-16 08:27:38 +000010963 if (G_UNLIKELY (!adrm)) {
10964 GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
10965 return FALSE;
10966 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010967 adrm_size = AML_QT_UINT32 (adrm->data);
bo.xiao86472382024-07-23 10:24:15 +080010968
10969#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
10970 adrm_buf = gst_buffer_new_memdup (adrm->data, adrm_size);
10971#else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010972 adrm_buf = gst_buffer_new_wrapped (g_memdup (adrm->data, adrm_size), adrm_size);
bo.xiao86472382024-07-23 10:24:15 +080010973#endif
zengliang.li5f31ef42024-05-16 08:27:38 +000010974
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010975 stream->protection_scheme_type = AML_FOURCC_aavd;
zengliang.li5f31ef42024-05-16 08:27:38 +000010976
10977 if (!stream->protection_scheme_info)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010978 stream->protection_scheme_info = g_new0 (AmlQtDemuxAavdEncryptionInfo, 1);
zengliang.li5f31ef42024-05-16 08:27:38 +000010979
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010980 info = (AmlQtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +000010981
10982 if (info->default_properties)
10983 gst_structure_free (info->default_properties);
10984 info->default_properties = gst_structure_new ("application/x-aavd",
10985 "encrypted", G_TYPE_BOOLEAN, TRUE,
10986 "adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
10987 gst_buffer_unref (adrm_buf);
10988
zengliang.lia6c0cdd2024-05-18 07:15:51 +000010989 *original_fmt = AML_FOURCC_mp4a;
zengliang.li5f31ef42024-05-16 08:27:38 +000010990 return TRUE;
10991}
10992
10993/* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
10994 * protected streams (sinf, frma, schm and schi); if the protection scheme is
10995 * Common Encryption (cenc), the function will also parse the tenc box (defined
10996 * in ISO/IEC 23001-7). @container points to the node that contains these boxes
10997 * (typically an enc[v|a|t|s] sample entry); the function will set
10998 * @original_fmt to the fourcc of the original unencrypted stream format.
10999 * Returns TRUE if successful; FALSE otherwise. */
11000static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011001aml_qtdemux_parse_protection_scheme_info (GstAmlQTDemux * qtdemux,
11002 AmlQtDemuxStream * stream, GNode * container, guint32 * original_fmt)
zengliang.li5f31ef42024-05-16 08:27:38 +000011003{
11004 GNode *sinf;
11005 GNode *frma;
11006 GNode *schm;
11007 GNode *schi;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011008 AmlQtDemuxCencSampleSetInfo *info;
zengliang.li5f31ef42024-05-16 08:27:38 +000011009 GNode *tenc;
11010 const guint8 *tenc_data;
11011
11012 g_return_val_if_fail (qtdemux != NULL, FALSE);
11013 g_return_val_if_fail (stream != NULL, FALSE);
11014 g_return_val_if_fail (container != NULL, FALSE);
11015 g_return_val_if_fail (original_fmt != NULL, FALSE);
11016
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011017 sinf = aml_qtdemux_tree_get_child_by_type (container, AML_FOURCC_sinf);
zengliang.li5f31ef42024-05-16 08:27:38 +000011018 if (G_UNLIKELY (!sinf)) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011019 if (stream->protection_scheme_type == AML_FOURCC_cenc
xuesong.jiangec2fff52024-10-21 16:16:19 +080011020 || stream->protection_scheme_type == AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011021 || stream->protection_scheme_type == AML_FOURCC_cbcs
11022 || stream->protection_scheme_type == AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011023 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
11024 "mandatory for Common Encryption");
11025 return FALSE;
11026 }
11027 return TRUE;
11028 }
11029
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011030 frma = aml_qtdemux_tree_get_child_by_type (sinf, AML_FOURCC_frma);
zengliang.li5f31ef42024-05-16 08:27:38 +000011031 if (G_UNLIKELY (!frma)) {
11032 GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
11033 return FALSE;
11034 }
11035
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011036 *original_fmt = AML_QT_FOURCC ((const guint8 *) frma->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000011037 GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
11038 GST_FOURCC_ARGS (*original_fmt));
11039
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011040 schm = aml_qtdemux_tree_get_child_by_type (sinf, AML_FOURCC_schm);
zengliang.li5f31ef42024-05-16 08:27:38 +000011041 if (!schm) {
11042 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
11043 return FALSE;
11044 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011045 stream->protection_scheme_type = AML_QT_FOURCC ((const guint8 *) schm->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000011046 stream->protection_scheme_version =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011047 AML_QT_UINT32 ((const guint8 *) schm->data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000011048
11049 GST_DEBUG_OBJECT (qtdemux,
11050 "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
11051 "protection_scheme_version: %#010x",
11052 GST_FOURCC_ARGS (stream->protection_scheme_type),
11053 stream->protection_scheme_version);
11054
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011055 schi = aml_qtdemux_tree_get_child_by_type (sinf, AML_FOURCC_schi);
zengliang.li5f31ef42024-05-16 08:27:38 +000011056 if (!schi) {
11057 GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
11058 return FALSE;
11059 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011060 if (stream->protection_scheme_type != AML_FOURCC_cenc &&
11061 stream->protection_scheme_type != AML_FOURCC_piff &&
xuesong.jiangec2fff52024-10-21 16:16:19 +080011062 stream->protection_scheme_type != AML_FOURCC_cbc1 &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011063 stream->protection_scheme_type != AML_FOURCC_cbcs &&
11064 stream->protection_scheme_type != AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011065 GST_ERROR_OBJECT (qtdemux,
11066 "Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
11067 GST_FOURCC_ARGS (stream->protection_scheme_type));
11068 return FALSE;
11069 }
11070
11071 if (G_UNLIKELY (!stream->protection_scheme_info))
11072 stream->protection_scheme_info =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011073 g_malloc0 (sizeof (AmlQtDemuxCencSampleSetInfo));
zengliang.li5f31ef42024-05-16 08:27:38 +000011074
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011075 info = (AmlQtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
zengliang.li5f31ef42024-05-16 08:27:38 +000011076
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011077 if (stream->protection_scheme_type == AML_FOURCC_cenc
xuesong.jiangec2fff52024-10-21 16:16:19 +080011078 || stream->protection_scheme_type == AML_FOURCC_cbc1
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011079 || stream->protection_scheme_type == AML_FOURCC_cbcs
11080 || stream->protection_scheme_type == AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011081 guint8 is_encrypted;
11082 guint8 iv_size;
11083 guint8 constant_iv_size = 0;
11084 const guint8 *default_kid;
11085 guint8 crypt_byte_block = 0;
11086 guint8 skip_byte_block = 0;
11087 const guint8 *constant_iv = NULL;
11088
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011089 tenc = aml_qtdemux_tree_get_child_by_type (schi, AML_FOURCC_tenc);
zengliang.li5f31ef42024-05-16 08:27:38 +000011090 if (!tenc) {
11091 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11092 "which is mandatory for Common Encryption");
11093 return FALSE;
11094 }
11095 tenc_data = (const guint8 *) tenc->data + 12;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011096 is_encrypted = AML_QT_UINT8 (tenc_data + 2);
11097 iv_size = AML_QT_UINT8 (tenc_data + 3);
zengliang.li5f31ef42024-05-16 08:27:38 +000011098 default_kid = (tenc_data + 4);
xuesong.jiang45374612024-10-10 20:10:13 +080011099 if (stream->protection_scheme_type == AML_FOURCC_cbcs || stream->protection_scheme_type == AML_FOURCC_cens) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011100 guint8 possible_pattern_info;
11101 if (iv_size == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011102 constant_iv_size = AML_QT_UINT8 (tenc_data + 20);
zengliang.li5f31ef42024-05-16 08:27:38 +000011103 if (constant_iv_size != 8 && constant_iv_size != 16) {
11104 GST_ERROR_OBJECT (qtdemux,
11105 "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
11106 return FALSE;
11107 }
11108 constant_iv = (tenc_data + 21);
11109 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011110 possible_pattern_info = AML_QT_UINT8 (tenc_data + 1);
zengliang.li5f31ef42024-05-16 08:27:38 +000011111 crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
11112 skip_byte_block = possible_pattern_info & 0x0f;
11113 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011114 aml_qtdemux_update_default_sample_cenc_settings (qtdemux, info,
zengliang.li5f31ef42024-05-16 08:27:38 +000011115 is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
11116 crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011117 } else if (stream->protection_scheme_type == AML_FOURCC_piff) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011118 GstByteReader br;
11119 static const guint8 piff_track_encryption_uuid[] = {
11120 0x89, 0x74, 0xdb, 0xce, 0x7b, 0xe7, 0x4c, 0x51,
11121 0x84, 0xf9, 0x71, 0x48, 0xf9, 0x88, 0x25, 0x54
11122 };
11123
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011124 tenc = aml_qtdemux_tree_get_child_by_type (schi, AML_FOURCC_uuid);
zengliang.li5f31ef42024-05-16 08:27:38 +000011125 if (!tenc) {
11126 GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
11127 "which is mandatory for Common Encryption");
11128 return FALSE;
11129 }
11130
11131 tenc_data = (const guint8 *) tenc->data + 8;
11132 if (memcmp (tenc_data, piff_track_encryption_uuid, 16) != 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011133 gchar *box_uuid = aml_qtdemux_uuid_bytes_to_string (tenc_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011134 GST_ERROR_OBJECT (qtdemux,
11135 "Unsupported track encryption box with uuid: %s", box_uuid);
11136 g_free (box_uuid);
11137 return FALSE;
11138 }
11139 tenc_data = (const guint8 *) tenc->data + 16 + 12;
11140 gst_byte_reader_init (&br, tenc_data, 20);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011141 if (!aml_qtdemux_update_default_piff_encryption_settings (qtdemux, info, &br)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011142 GST_ERROR_OBJECT (qtdemux, "PIFF track box parsing error");
11143 return FALSE;
11144 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011145 stream->protection_scheme_type = AML_FOURCC_cenc;
zengliang.li5f31ef42024-05-16 08:27:38 +000011146 }
11147
11148 return TRUE;
11149}
11150
11151static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011152aml_qtdemux_track_id_compare_func (AmlQtDemuxStream ** stream1,
11153 AmlQtDemuxStream ** stream2)
zengliang.li5f31ef42024-05-16 08:27:38 +000011154{
11155 return (gint) (*stream1)->track_id - (gint) (*stream2)->track_id;
11156}
11157
zengliang.lib0f27cd2024-05-17 06:58:54 +000011158
11159static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011160aml_qtdemux_parse_dvcc (guint32 sampleEntry, guint32 dvConfig, const guint8 * data, GstCaps *caps)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011161{
11162 gint len;
11163 const guint8 *config_data;
11164 guint16 buf;
bo.xiao4c245f22024-08-22 11:18:25 +080011165 guint8 profile;
11166 guint8 level;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011167 guint8 rpu_present_flag;
bo.xiao4c245f22024-08-22 11:18:25 +080011168 gboolean bl_present_flag = FALSE;
11169 gboolean el_present_flag = FALSE;
11170 guint8 bl_signal_compatibility_id;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011171 gboolean bRejectPlay = FALSE;
11172
11173 if (NULL == data || NULL == caps)
11174 {
11175 return 0;
11176 }
11177
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011178 len = AML_QT_UINT32 (data);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011179 config_data = data + 0x8;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011180 buf = (config_data[2] << 8) | config_data[3];
bo.xiao4c245f22024-08-22 11:18:25 +080011181 profile = (buf >> 9) & 0x7f; // 7 bits
11182 level = (buf >> 3) & 0x3f; // 6 bits
zengliang.lib0f27cd2024-05-17 06:58:54 +000011183 rpu_present_flag = (buf >> 2) & 0x01; // 1 bit
bo.xiao4c245f22024-08-22 11:18:25 +080011184 el_present_flag = (buf >> 1) & 0x01; // 1 bit
11185 bl_present_flag = buf & 0x01; // 1 bit
11186 bl_signal_compatibility_id = (config_data[4] >> 4) & 0x0f; // 4 bit
zengliang.lib0f27cd2024-05-17 06:58:54 +000011187
hanghang.luo3ec92cb2024-12-11 21:11:24 +080011188 if (((1<<30) < len)
11189 || !((20 == profile && 3 == config_data[0])|| (20 != profile && 1 == config_data[0]))
11190 || (0 != config_data[1]))
11191 {
11192 GST_DEBUG ("len: %d profile: %d config_data[0]): %d config_data[1]): %d", len, profile, config_data[0], config_data[1]);
11193 return 0;
11194 }
11195
zengliang.lib0f27cd2024-05-17 06:58:54 +000011196 GST_DEBUG ("entry type: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
11197 GST_DEBUG ("dvConfig type: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
bo.xiao4c245f22024-08-22 11:18:25 +080011198 GST_DEBUG ("profile: %d, level: %d, rpu_flag: %d, el_present_flag: %d, bl_present_flag: %d, compatibility_id: %d",
11199 profile, level, rpu_present_flag, el_present_flag, bl_present_flag, bl_signal_compatibility_id);
11200
11201 if (bl_signal_compatibility_id != 0
11202 && bl_signal_compatibility_id != 1
11203 && bl_signal_compatibility_id != 2
11204 && bl_signal_compatibility_id != 4
11205 && bl_signal_compatibility_id != 6)
11206 {
11207 GST_WARNING("2.6 reject play for invalid ompatibility_id: %d, but 5.2 not reject", bl_signal_compatibility_id);
11208 //return -1;
11209 }
zengliang.lib0f27cd2024-05-17 06:58:54 +000011210
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011211 if (sampleEntry == AML_FOURCC_hev1 || sampleEntry == AML_FOURCC_hvc1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011212 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011213 if (dvConfig == AML_FOURCC_dvcC || dvConfig == AML_FOURCC_dvvC || dvConfig == AML_FOURCC_dvwC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011214 {
bo.xiao4c245f22024-08-22 11:18:25 +080011215 if (profile == 4 || profile == 7 || profile == 8)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011216 {
11217 GST_DEBUG ("Playback Dolby Vision");
11218 }
11219 else
11220 {
11221 GST_DEBUG ("invalid profile, playback as non-dv.");
bo.xiao4c245f22024-08-22 11:18:25 +080011222 el_present_flag = 0;
11223 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011224 }
11225 }
11226 else
11227 {
11228 GST_DEBUG ("invalid dvConfig type, playback as non-dv.");
bo.xiao4c245f22024-08-22 11:18:25 +080011229 el_present_flag = 0;
11230 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011231 }
11232 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011233 else if (sampleEntry == AML_FOURCC_dvhe || sampleEntry == AML_FOURCC_dvh1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011234 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011235 if (dvConfig == AML_FOURCC_dvcC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011236 {
bo.xiao4c245f22024-08-22 11:18:25 +080011237 if (profile == 5)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011238 GST_DEBUG ("Playback Dolby Vision");
11239 else
11240 {
bo.xiao4c245f22024-08-22 11:18:25 +080011241 GST_WARNING("reject play for invalid profile:%d", profile);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011242 bRejectPlay = TRUE;
11243 }
11244 }
11245 else
11246 {
bo.xiao4c245f22024-08-22 11:18:25 +080011247 GST_WARNING ("reject play for invalid config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
zengliang.lib0f27cd2024-05-17 06:58:54 +000011248 bRejectPlay = TRUE;
11249 }
11250 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011251 else if (sampleEntry == AML_FOURCC_avc1 || sampleEntry == AML_FOURCC_avc3 || sampleEntry == AML_FOURCC_avc2 || sampleEntry == AML_FOURCC_avc4)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011252 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011253 if (dvConfig == AML_FOURCC_dvcC || dvConfig == AML_FOURCC_dvvC || dvConfig == AML_FOURCC_dvwC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011254 {
bo.xiao4c245f22024-08-22 11:18:25 +080011255 if (profile == 9)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011256 GST_DEBUG ("Playback Dolby Vision");
11257 else
11258 {
11259 GST_DEBUG ("invalid profile, Playback HEVC bitstream using base-layer");
bo.xiao4c245f22024-08-22 11:18:25 +080011260 el_present_flag = 0;
11261 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011262 }
11263 }
11264 else
11265 {
bo.xiao4c245f22024-08-22 11:18:25 +080011266 GST_DEBUG ("invalid config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11267 el_present_flag = 0;
11268 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011269 }
11270 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011271 else if (sampleEntry == AML_FOURCC_dvav || sampleEntry == AML_FOURCC_dva1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011272 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011273 if (dvConfig == AML_FOURCC_dvcC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011274 {
bo.xiao4c245f22024-08-22 11:18:25 +080011275 if (profile == 1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011276 {
bo.xiao4c245f22024-08-22 11:18:25 +080011277 GST_WARNING("reject play for invalid profile:%d", profile);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011278 bRejectPlay = TRUE;
11279 }
11280 }
11281 else
11282 {
bo.xiao4c245f22024-08-22 11:18:25 +080011283 GST_WARNING ("reject play for invalid config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
zengliang.lib0f27cd2024-05-17 06:58:54 +000011284 bRejectPlay = TRUE;
11285 }
11286 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011287 else if (sampleEntry == AML_FOURCC_av01)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011288 {
11289 GST_DEBUG ("ToDo sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
11290 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011291 else if (sampleEntry == AML_FOURCC_dav1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011292 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011293 if (dvConfig == AML_FOURCC_dvvC || dvConfig == AML_FOURCC_dvwC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011294 {
bo.xiao4c245f22024-08-22 11:18:25 +080011295 if (profile != 10)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011296 {
bo.xiao4c245f22024-08-22 11:18:25 +080011297 GST_WARNING("reject play for invalid profile:%d", profile);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011298 bRejectPlay = TRUE;
11299 }
11300 }
11301 else
11302 {
11303 GST_WARNING ("reject play for invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11304 bRejectPlay = TRUE;
11305 }
11306 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011307 else if (sampleEntry == AML_FOURCC_vvcN || sampleEntry == AML_FOURCC_vvc1 || sampleEntry == AML_FOURCC_vvi1 || sampleEntry == AML_FOURCC_vvs1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011308 {
11309 GST_DEBUG ("invalid sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
bo.xiao4c245f22024-08-22 11:18:25 +080011310 el_present_flag = 0;
11311 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011312 }
11313
bo.xiao4c245f22024-08-22 11:18:25 +080011314 gst_caps_set_simple (caps, "bl_present_flag", G_TYPE_BOOLEAN, bl_present_flag, NULL);
11315 gst_caps_set_simple (caps, "el_present_flag", G_TYPE_BOOLEAN, el_present_flag, NULL);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011316
11317 return bRejectPlay ? -1 : 0;
11318}
11319
11320static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011321aml_qtdemux_parse_sgpd_av1M (GstAmlQTDemux * qtdemux, GNode * node)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011322{
11323 GNode *sgpd;
11324 const guint8 *sgpd_data;
11325 gint sgpd_size;
11326 guint32 sgpd_fourcc;
11327 gint sgpd_version;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011328 guint32 sgpd_group_type;
11329 gint sgpd_default_len = -1;
11330 guint sgpd_entry_cnt;
11331 guint32 group_fourcc;
bo.xiao86472382024-07-23 10:24:15 +080011332 guint metadata_type = 0;
11333 guint metadata_specific_parameters = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011334 gboolean bRejectPlay = FALSE;
11335
11336 GST_DEBUG_OBJECT (qtdemux, "try to find sgpd");
11337
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011338 sgpd = aml_qtdemux_tree_get_child_by_type (node, AML_FOURCC_sgpd);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011339 if (sgpd)
11340 {
11341 int rIdx = 0;
11342 int entryIdx = 0;
11343 sgpd_data = (const guint8 *) sgpd->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011344 sgpd_size = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011345 rIdx += 4;
11346
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011347 sgpd_fourcc = AML_QT_FOURCC (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011348 rIdx += 4;
11349
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011350 sgpd_version = AML_QT_UINT8 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011351 rIdx += 4; // 1 byte version + 3 byte flags
11352
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011353 sgpd_group_type = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011354 rIdx += 4;
11355
11356 if (sgpd_version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011357 sgpd_default_len = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011358 rIdx += 4;
11359 }
11360 else if (sgpd_version >= 2)
11361 rIdx += 4; // 4 byte default_sample_descriotion_index
11362
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011363 sgpd_entry_cnt = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011364 rIdx += 4;
11365
11366 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",
11367 sgpd_size, sgpd_fourcc, sgpd_version, sgpd_group_type, sgpd_entry_cnt);
11368
11369 for (entryIdx = 0; entryIdx < sgpd_entry_cnt; entryIdx++)
11370 {
11371 if (sgpd_version == 1 && sgpd_default_len == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011372 sgpd_default_len = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011373 rIdx += 4;
11374 }
11375
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011376 group_fourcc = AML_QT_FOURCC (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011377 rIdx += 4;
11378
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011379 if (group_fourcc == AML_FOURCC_av1M) {
11380 metadata_type = AML_QT_UINT8 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011381 rIdx += 1;
11382
bo.xiao4c245f22024-08-22 11:18:25 +080011383 metadata_specific_parameters = AML_QT_UINT24(sgpd_data + rIdx);
11384 rIdx += 3;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011385
11386 if ( metadata_type != 4 || metadata_specific_parameters != 0xB5003B) {
11387 // METADATA_TYPE_ITUT_T35 and 0xB5003B
11388 bRejectPlay = TRUE;
11389 }
11390 }
11391 GST_WARNING_OBJECT (qtdemux, "group_fourcc=0x%x, metadata_type=0x%x, metadata_specific_parameters=0x%x",
11392 group_fourcc, metadata_type, metadata_specific_parameters);
11393
11394 }
11395 }
11396 else
11397 {
11398 GST_DEBUG_OBJECT (qtdemux, "no sgpd");
11399 }
11400
11401 return bRejectPlay ? -1 : 0;
11402}
11403
zengliang.li5f31ef42024-05-16 08:27:38 +000011404static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011405aml_qtdemux_parse_stereo_svmi_atom (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +000011406 GNode * stbl)
11407{
11408 GNode *svmi;
11409
11410 /*parse svmi header if existing */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011411 svmi = aml_qtdemux_tree_get_child_by_type (stbl, AML_FOURCC_svmi);
zengliang.li5f31ef42024-05-16 08:27:38 +000011412 if (svmi) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011413 guint32 len = AML_QT_UINT32 ((guint8 *) svmi->data);
11414 guint32 version = AML_QT_UINT32 ((guint8 *) svmi->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000011415 if (!version) {
11416 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11417 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11418 guint8 frame_type, frame_layout;
11419 guint32 stereo_mono_change_count;
11420
11421 if (len < 18)
11422 return FALSE;
11423
11424 /* MPEG-A stereo video */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011425 if (qtdemux->major_brand == AML_FOURCC_ss02)
zengliang.li5f31ef42024-05-16 08:27:38 +000011426 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11427
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011428 frame_type = AML_QT_UINT8 ((guint8 *) svmi->data + 12);
11429 frame_layout = AML_QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11430 stereo_mono_change_count = AML_QT_UINT32 ((guint8 *) svmi->data + 14);
zengliang.li5f31ef42024-05-16 08:27:38 +000011431
11432 switch (frame_type) {
11433 case 0:
11434 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11435 break;
11436 case 1:
11437 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11438 break;
11439 case 2:
11440 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11441 break;
11442 case 3:
11443 /* mode 3 is primary/secondary view sequence, ie
11444 * left/right views in separate tracks. See section 7.2
11445 * of ISO/IEC 23000-11:2009 */
11446 /* In the future this might be supported using related
11447 * streams, like an enhancement track - if files like this
11448 * ever exist */
11449 GST_FIXME_OBJECT (qtdemux,
11450 "Implement stereo video in separate streams");
11451 }
11452
11453 if ((frame_layout & 0x1) == 0)
11454 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11455
11456 GST_LOG_OBJECT (qtdemux,
11457 "StereoVideo: composition type: %u, is_left_first: %u",
11458 frame_type, frame_layout);
11459
11460 if (stereo_mono_change_count > 1) {
11461 GST_FIXME_OBJECT (qtdemux,
11462 "Mixed-mono flags are not yet supported in qtdemux.");
11463 }
11464
11465 stream->multiview_mode = mode;
11466 stream->multiview_flags = flags;
11467 }
11468 }
11469
11470 return TRUE;
11471}
11472
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011473/*
11474 * gst_aml_video_color_matrix_from_iso_compat:
11475 * @value: a ITU-T H.273 matrix coefficients value
11476 *
11477 * Converts the @value to the #GstVideoColorMatrix
11478 * The matrix coefficients (MatrixCoefficients) value is
11479 * defined by "ISO/IEC 23001-8 Section 7.3 Table 4"
11480 * and "ITU-T H.273 Table 4".
11481 * "H.264 Table E-5" and "H.265 Table E.5" share the identical values.
11482 *
11483 * Returns: the matched #GstVideoColorMatrix
11484 *
11485 * Compatible with yocto3.1, rdk, and yocto4.
11486 */
11487GstVideoColorMatrix
11488gst_aml_video_color_matrix_from_iso_compat (guint value)
11489{
11490 switch (value) {
11491 case 0:
11492 return GST_VIDEO_COLOR_MATRIX_RGB;
11493 case 1:
11494 return GST_VIDEO_COLOR_MATRIX_BT709;
11495 case 4:
11496 return GST_VIDEO_COLOR_MATRIX_FCC;
11497 case 5:
11498 case 6:
11499 return GST_VIDEO_COLOR_MATRIX_BT601;
11500 case 7:
11501 return GST_VIDEO_COLOR_MATRIX_SMPTE240M;
11502 case 9:
11503 return GST_VIDEO_COLOR_MATRIX_BT2020;
11504 case 2:
11505 default:
11506 return GST_VIDEO_COLOR_MATRIX_UNKNOWN;
11507 }
11508}
11509
11510/*
11511 * gst_video_transfer_function_from_iso_compat_compat:
11512 * @value: a ITU-T H.273 transfer characteristics value
11513 *
11514 * Converts the @value to the #GstVideoTransferFunction
11515 * The transfer characteristics (TransferCharacteristics) value is
11516 * defined by "ISO/IEC 23001-8 Section 7.2 Table 3"
11517 * and "ITU-T H.273 Table 3".
11518 * "H.264 Table E-4" and "H.265 Table E.4" share the identical values.
11519 *
11520 * Returns: the matched #GstVideoTransferFunction
11521 *
11522 * Compatible with yocto3.1, rdk, and yocto4.
11523 */
11524GstVideoTransferFunction
11525gst_aml_video_transfer_function_from_iso_compat (guint value)
11526{
11527 switch (value) {
11528 case 1:
11529 return GST_VIDEO_TRANSFER_BT709;
11530 case 4:
11531 return GST_VIDEO_TRANSFER_GAMMA22;
11532 case 5:
11533 return GST_VIDEO_TRANSFER_GAMMA28;
11534 case 7:
11535 return GST_VIDEO_TRANSFER_SMPTE240M;
11536 case 8:
11537 return GST_VIDEO_TRANSFER_GAMMA10;
11538 case 9:
11539 return GST_VIDEO_TRANSFER_LOG100;
11540 case 10:
11541 return GST_VIDEO_TRANSFER_LOG316;
11542 case 13:
11543 return GST_VIDEO_TRANSFER_SRGB;
11544 case 14:
11545 return GST_VIDEO_TRANSFER_BT2020_10;
11546 case 15:
11547 return GST_VIDEO_TRANSFER_BT2020_12;
11548 case 18:
11549 return GST_VIDEO_TRANSFER_ARIB_STD_B67;
11550 case 2:
11551 default:
11552 return GST_VIDEO_TRANSFER_UNKNOWN;
11553 }
11554}
11555
11556/*
11557 * gst_aml_video_color_primaries_from_iso_compat:
11558 * @value: a ITU-T H.273 colour primaries value
11559 *
11560 * Converts the @value to the #GstVideoColorPrimaries
11561 * The colour primaries (ColourPrimaries) value is
11562 * defined by "ISO/IEC 23001-8 Section 7.1 Table 2" and "ITU-T H.273 Table 2".
11563 * "H.264 Table E-3" and "H.265 Table E.3" share the identical values.
11564 *
11565 * Returns: the matched #GstVideoColorPrimaries
11566 *
11567 * Compatible with yocto3.1, rdk, and yocto4.
11568 */
11569GstVideoColorPrimaries
11570gst_aml_video_color_primaries_from_iso_compat (guint value)
11571{
11572 switch (value) {
11573 case 1:
11574 return GST_VIDEO_COLOR_PRIMARIES_BT709;
11575 case 4:
11576 return GST_VIDEO_COLOR_PRIMARIES_BT470M;
11577 case 5:
11578 return GST_VIDEO_COLOR_PRIMARIES_BT470BG;
11579 case 6:
11580 return GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
11581 case 7:
11582 return GST_VIDEO_COLOR_PRIMARIES_SMPTE240M;
11583 case 8:
11584 return GST_VIDEO_COLOR_PRIMARIES_FILM;
11585 case 9:
11586 return GST_VIDEO_COLOR_PRIMARIES_BT2020;
11587 case 10:
11588 return GST_VIDEO_COLOR_PRIMARIES_SMPTEST428;
11589 case 11:
11590 return GST_VIDEO_COLOR_PRIMARIES_SMPTERP431;
11591 case 12:
11592 return GST_VIDEO_COLOR_PRIMARIES_SMPTEEG432;
11593 case 22:
11594 return GST_VIDEO_COLOR_PRIMARIES_EBU3213;
11595 case 2:
11596 default:
11597 return GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
11598 }
11599}
11600
zengliang.li5f31ef42024-05-16 08:27:38 +000011601/* parse the traks.
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011602 * With each track we associate a new AmlQtDemuxStream that contains all the info
zengliang.li5f31ef42024-05-16 08:27:38 +000011603 * about the trak.
11604 * traks that do not decode to something (like strm traks) will not have a pad.
11605 */
11606static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011607aml_qtdemux_parse_trak (GstAmlQTDemux * qtdemux, GNode * trak)
zengliang.li5f31ef42024-05-16 08:27:38 +000011608{
11609 GstByteReader tkhd;
11610 int offset;
11611 GNode *mdia;
11612 GNode *mdhd;
11613 GNode *hdlr;
11614 GNode *minf;
11615 GNode *stbl;
11616 GNode *stsd;
11617 GNode *mp4a;
11618 GNode *mp4v;
11619 GNode *esds;
11620 GNode *tref;
11621 GNode *udta;
11622
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011623 AmlQtDemuxStream *stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000011624 const guint8 *stsd_data;
11625 const guint8 *stsd_entry_data;
11626 guint remaining_stsd_len;
11627 guint stsd_entry_count;
11628 guint stsd_index;
11629 guint16 lang_code; /* quicktime lang code or packed iso code */
11630 guint32 version;
11631 guint32 tkhd_flags = 0;
11632 guint8 tkhd_version = 0;
11633 guint32 w = 0, h = 0;
11634 guint value_size, stsd_len, len;
11635 guint32 track_id;
11636 guint32 dummy;
11637
11638 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11639
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011640 if (!aml_qtdemux_tree_get_child_by_type_full (trak, AML_FOURCC_tkhd, &tkhd)
zengliang.li5f31ef42024-05-16 08:27:38 +000011641 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11642 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11643 goto corrupt_file;
11644
11645 /* pick between 64 or 32 bits */
11646 value_size = tkhd_version == 1 ? 8 : 4;
11647 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11648 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11649 goto corrupt_file;
11650
11651 /* Check if current moov has duplicated track_id */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011652 if (aml_qtdemux_find_stream (qtdemux, track_id))
zengliang.li5f31ef42024-05-16 08:27:38 +000011653 goto existing_stream;
11654
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011655 stream = _aml_create_stream (qtdemux, track_id);
zengliang.li5f31ef42024-05-16 08:27:38 +000011656 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11657
11658 /* need defaults for fragments */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011659 aml_qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
zengliang.li5f31ef42024-05-16 08:27:38 +000011660
11661 if ((tkhd_flags & 1) == 0)
11662 stream->disabled = TRUE;
11663
11664 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11665 tkhd_version, tkhd_flags, stream->track_id);
11666
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011667 if (!(mdia = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_mdia)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011668 goto corrupt_file;
11669
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011670 if (!(mdhd = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_mdhd))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011671 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011672 if (qtdemux->major_brand != AML_FOURCC_mjp2 ||
11673 !(mdhd = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_mhdr)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011674 goto corrupt_file;
11675 }
11676
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011677 len = AML_QT_UINT32 ((guint8 *) mdhd->data);
11678 version = AML_QT_UINT32 ((guint8 *) mdhd->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000011679 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11680 if (version == 0x01000000) {
11681 if (len < 42)
11682 goto corrupt_file;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011683 stream->timescale = AML_QT_UINT32 ((guint8 *) mdhd->data + 28);
11684 stream->duration = AML_QT_UINT64 ((guint8 *) mdhd->data + 32);
11685 lang_code = AML_QT_UINT16 ((guint8 *) mdhd->data + 40);
zengliang.li5f31ef42024-05-16 08:27:38 +000011686 } else {
11687 if (len < 30)
11688 goto corrupt_file;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011689 stream->timescale = AML_QT_UINT32 ((guint8 *) mdhd->data + 20);
11690 stream->duration = AML_QT_UINT32 ((guint8 *) mdhd->data + 24);
11691 lang_code = AML_QT_UINT16 ((guint8 *) mdhd->data + 28);
zengliang.li5f31ef42024-05-16 08:27:38 +000011692 }
11693
11694 if (lang_code < 0x400) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011695 aml_qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
zengliang.li5f31ef42024-05-16 08:27:38 +000011696 } else if (lang_code == 0x7fff) {
11697 stream->lang_id[0] = 0; /* unspecified */
11698 } else {
11699 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11700 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11701 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11702 stream->lang_id[3] = 0;
11703 }
11704
11705 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11706 stream->timescale);
11707 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11708 stream->duration);
11709 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11710 lang_code, stream->lang_id);
11711
11712 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11713 goto corrupt_file;
11714
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011715 if ((tref = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_tref))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011716 /* chapters track reference */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011717 GNode *chap = aml_qtdemux_tree_get_child_by_type (tref, AML_FOURCC_chap);
zengliang.li5f31ef42024-05-16 08:27:38 +000011718 if (chap) {
11719 gsize length = GST_READ_UINT32_BE (chap->data);
11720 if (qtdemux->chapters_track_id)
11721 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11722
11723 if (length >= 12) {
11724 qtdemux->chapters_track_id =
11725 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11726 }
11727 }
11728 }
11729
11730 /* fragmented files may have bogus duration in moov */
11731 if (!qtdemux->fragmented &&
11732 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11733 guint64 tdur1, tdur2;
11734
11735 /* don't overflow */
11736 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11737 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11738
11739 /* HACK:
11740 * some of those trailers, nowadays, have prologue images that are
11741 * themselves video tracks as well. I haven't really found a way to
11742 * identify those yet, except for just looking at their duration. */
11743 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11744 GST_WARNING_OBJECT (qtdemux,
11745 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11746 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11747 "found, assuming preview image or something; skipping track",
11748 stream->duration, stream->timescale, qtdemux->duration,
11749 qtdemux->timescale);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011750 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000011751 return TRUE;
11752 }
11753 }
11754
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011755 if (!(hdlr = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_hdlr)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011756 goto corrupt_file;
11757
11758 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011759 GST_FOURCC_ARGS (AML_QT_FOURCC ((guint8 *) hdlr->data + 12)));
zengliang.li5f31ef42024-05-16 08:27:38 +000011760
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011761 len = AML_QT_UINT32 ((guint8 *) hdlr->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011762 if (len >= 20)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011763 stream->subtype = AML_QT_FOURCC ((guint8 *) hdlr->data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000011764 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11765 GST_FOURCC_ARGS (stream->subtype));
11766
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011767 if (!(minf = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_minf)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011768 goto corrupt_file;
11769
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011770 if (!(stbl = aml_qtdemux_tree_get_child_by_type (minf, AML_FOURCC_stbl)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011771 goto corrupt_file;
11772
11773 /* Parse out svmi (and later st3d/sv3d) atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011774 if (!aml_qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
zengliang.li5f31ef42024-05-16 08:27:38 +000011775 goto corrupt_file;
11776
11777 /* parse rest of tkhd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011778 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011779 guint32 matrix[9];
11780
11781 /* version 1 uses some 64-bit ints */
11782 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11783 goto corrupt_file;
11784
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011785 if (!aml_qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
zengliang.li5f31ef42024-05-16 08:27:38 +000011786 goto corrupt_file;
11787
11788 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11789 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11790 goto corrupt_file;
11791
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011792 aml_qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
zengliang.li5f31ef42024-05-16 08:27:38 +000011793 &stream->stream_tags);
11794 }
11795
11796 /* parse stsd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011797 if (!(stsd = aml_qtdemux_tree_get_child_by_type (stbl, AML_FOURCC_stsd)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011798 goto corrupt_file;
11799 stsd_data = (const guint8 *) stsd->data;
11800
11801 /* stsd should at least have one entry */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011802 stsd_len = AML_QT_UINT32 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011803 if (stsd_len < 24) {
11804 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011805 if (stream->subtype == AML_FOURCC_vivo) {
11806 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000011807 return TRUE;
11808 } else {
11809 goto corrupt_file;
11810 }
11811 }
11812
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011813 stream->stsd_entries_length = stsd_entry_count = AML_QT_UINT32 (stsd_data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000011814 /* each stsd entry must contain at least 8 bytes */
11815 if (stream->stsd_entries_length == 0
11816 || stream->stsd_entries_length > stsd_len / 8) {
11817 stream->stsd_entries_length = 0;
11818 goto corrupt_file;
11819 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011820 stream->stsd_entries = g_new0 (AmlQtDemuxStreamStsdEntry, stsd_entry_count);
zengliang.li5f31ef42024-05-16 08:27:38 +000011821 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11822 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11823
11824 stsd_entry_data = stsd_data + 16;
11825 remaining_stsd_len = stsd_len - 16;
11826 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11827 guint32 fourcc;
11828 gchar *codec = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011829 AmlQtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
zengliang.li5f31ef42024-05-16 08:27:38 +000011830
11831 /* and that entry should fit within stsd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011832 len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011833 if (len > remaining_stsd_len)
11834 goto corrupt_file;
11835
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011836 entry->fourcc = fourcc = AML_QT_FOURCC (stsd_entry_data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000011837 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11838 GST_FOURCC_ARGS (entry->fourcc));
11839 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11840
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011841 if ((fourcc == AML_FOURCC_drms) || (fourcc == AML_FOURCC_drmi))
zengliang.li5f31ef42024-05-16 08:27:38 +000011842 goto error_encrypted;
11843
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011844 if (fourcc == AML_FOURCC_aavd) {
11845 if (stream->subtype != AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011846 GST_ERROR_OBJECT (qtdemux,
11847 "Unexpeced stsd type 'aavd' outside 'soun' track");
11848 } else {
11849 /* encrypted audio with sound sample description v0 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011850 GNode *enc = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000011851 stream->protected = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011852 if (!aml_qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
zengliang.li5f31ef42024-05-16 08:27:38 +000011853 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11854 }
11855 }
11856
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011857 if (fourcc == AML_FOURCC_encv || fourcc == AML_FOURCC_enca) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011858 /* FIXME this looks wrong, there might be multiple children
11859 * with the same type */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011860 GNode *enc = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000011861 stream->protected = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011862 if (!aml_qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
zengliang.li5f31ef42024-05-16 08:27:38 +000011863 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11864 }
11865
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011866 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011867 GNode *colr;
11868 GNode *fiel;
11869 GNode *pasp;
11870 gboolean gray;
11871 gint depth, palette_size, palette_count;
11872 guint32 *palette_data = NULL;
11873
11874 entry->sampled = TRUE;
11875
11876 stream->display_width = w >> 16;
11877 stream->display_height = h >> 16;
11878
11879 offset = 16;
11880 if (len < 86) /* TODO verify */
11881 goto corrupt_file;
11882
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011883 entry->width = AML_QT_UINT16 (stsd_entry_data + offset + 16);
11884 entry->height = AML_QT_UINT16 (stsd_entry_data + offset + 18);
zengliang.li5f31ef42024-05-16 08:27:38 +000011885 entry->fps_n = 0; /* this is filled in later */
11886 entry->fps_d = 0; /* this is filled in later */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011887 entry->bits_per_sample = AML_QT_UINT16 (stsd_entry_data + offset + 66);
11888 entry->color_table_id = AML_QT_UINT16 (stsd_entry_data + offset + 68);
zengliang.li5f31ef42024-05-16 08:27:38 +000011889
11890 /* if color_table_id is 0, ctab atom must follow; however some files
11891 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11892 * if color table is not present we'll correct the value */
11893 if (entry->color_table_id == 0 &&
11894 (len < 90
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011895 || AML_QT_FOURCC (stsd_entry_data + offset + 70) != AML_FOURCC_ctab)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011896 entry->color_table_id = -1;
11897 }
11898
11899 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11900 entry->width, entry->height, entry->bits_per_sample,
11901 entry->color_table_id);
11902
11903 depth = entry->bits_per_sample;
11904
11905 /* more than 32 bits means grayscale */
11906 gray = (depth > 32);
11907 /* low 32 bits specify the depth */
11908 depth &= 0x1F;
11909
11910 /* different number of palette entries is determined by depth. */
11911 palette_count = 0;
11912 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11913 palette_count = (1 << depth);
11914 palette_size = palette_count * 4;
11915
11916 if (entry->color_table_id) {
11917 switch (palette_count) {
11918 case 0:
11919 break;
11920 case 2:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011921 palette_data = g_memdup2 (aml_ff_qt_default_palette_2, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011922 break;
11923 case 4:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011924 palette_data = g_memdup2 (aml_ff_qt_default_palette_4, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011925 break;
11926 case 16:
11927 if (gray)
11928 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011929 g_memdup2 (aml_ff_qt_grayscale_palette_16, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011930 else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011931 palette_data = g_memdup2 (aml_ff_qt_default_palette_16, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011932 break;
11933 case 256:
11934 if (gray)
11935 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011936 g_memdup2 (aml_ff_qt_grayscale_palette_256, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011937 else
11938 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011939 g_memdup2 (aml_ff_qt_default_palette_256, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011940 break;
11941 default:
11942 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11943 (_("The video in this file might not play correctly.")),
11944 ("unsupported palette depth %d", depth));
11945 break;
11946 }
11947 } else {
11948 guint i, j, start, end;
11949
11950 if (len < 94)
11951 goto corrupt_file;
11952
11953 /* read table */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011954 start = AML_QT_UINT32 (stsd_entry_data + offset + 70);
11955 palette_count = AML_QT_UINT16 (stsd_entry_data + offset + 74);
11956 end = AML_QT_UINT16 (stsd_entry_data + offset + 76);
zengliang.li5f31ef42024-05-16 08:27:38 +000011957
11958 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11959 start, end, palette_count);
11960
11961 if (end > 255)
11962 end = 255;
11963 if (start > end)
11964 start = end;
11965
11966 if (len < 94 + (end - start) * 8)
11967 goto corrupt_file;
11968
11969 /* palette is always the same size */
11970 palette_data = g_malloc0 (256 * 4);
11971 palette_size = 256 * 4;
11972
11973 for (j = 0, i = start; i <= end; j++, i++) {
11974 guint32 a, r, g, b;
11975
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011976 a = AML_QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11977 r = AML_QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11978 g = AML_QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11979 b = AML_QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
zengliang.li5f31ef42024-05-16 08:27:38 +000011980
11981 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11982 (g & 0xff00) | (b >> 8);
11983 }
11984 }
11985
11986 if (entry->caps)
11987 gst_caps_unref (entry->caps);
11988
11989 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011990 aml_qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000011991 &codec);
11992 if (G_UNLIKELY (!entry->caps)) {
11993 g_free (palette_data);
11994 goto unknown_stream;
11995 }
11996
11997 if (codec) {
11998 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
11999 GST_TAG_VIDEO_CODEC, codec, NULL);
12000 g_free (codec);
12001 codec = NULL;
12002 }
12003
12004 if (palette_data) {
12005 GstStructure *s;
12006
12007 if (entry->rgb8_palette)
12008 gst_memory_unref (entry->rgb8_palette);
12009 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
12010 palette_data, palette_size, 0, palette_size, palette_data, g_free);
12011
12012 s = gst_caps_get_structure (entry->caps, 0);
12013
12014 /* non-raw video has a palette_data property. raw video has the palette as
12015 * an extra plane that we append to the output buffers before we push
12016 * them*/
12017 if (!gst_structure_has_name (s, "video/x-raw")) {
12018 GstBuffer *palette;
12019
12020 palette = gst_buffer_new ();
12021 gst_buffer_append_memory (palette, entry->rgb8_palette);
12022 entry->rgb8_palette = NULL;
12023
12024 gst_caps_set_simple (entry->caps, "palette_data",
12025 GST_TYPE_BUFFER, palette, NULL);
12026 gst_buffer_unref (palette);
12027 }
12028 } else if (palette_count != 0) {
12029 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
12030 (NULL), ("Unsupported palette depth %d", depth));
12031 }
12032
12033 GST_LOG_OBJECT (qtdemux, "frame count: %u",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012034 AML_QT_UINT16 (stsd_entry_data + offset + 32));
zengliang.li5f31ef42024-05-16 08:27:38 +000012035
12036 esds = NULL;
12037 pasp = NULL;
12038 colr = NULL;
12039 fiel = NULL;
12040 /* pick 'the' stsd child */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012041 mp4v = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012042 // We should skip parsing the stsd for non-protected streams if
12043 // the entry doesn't match the fourcc, since they don't change
12044 // format. However, for protected streams we can have partial
12045 // encryption, where parts of the stream are encrypted and parts
12046 // not. For both parts of such streams, we should ensure the
12047 // esds overrides are parsed for both from the stsd.
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012048 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
12049 if (stream->protected && AML_QTDEMUX_TREE_NODE_FOURCC (mp4v) != AML_FOURCC_encv)
zengliang.li5f31ef42024-05-16 08:27:38 +000012050 mp4v = NULL;
12051 else if (!stream->protected)
12052 mp4v = NULL;
12053 }
12054
12055 if (mp4v) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012056 esds = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_esds);
12057 pasp = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_pasp);
12058 colr = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_colr);
12059 fiel = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_fiel);
zengliang.li5f31ef42024-05-16 08:27:38 +000012060 }
12061
12062 if (pasp) {
12063 const guint8 *pasp_data = (const guint8 *) pasp->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012064 guint len = AML_QT_UINT32 (pasp_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012065
12066 if (len == 16) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012067 AML_CUR_STREAM (stream)->par_w = AML_QT_UINT32 (pasp_data + 8);
12068 AML_CUR_STREAM (stream)->par_h = AML_QT_UINT32 (pasp_data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000012069 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012070 AML_CUR_STREAM (stream)->par_w = 0;
12071 AML_CUR_STREAM (stream)->par_h = 0;
zengliang.li5f31ef42024-05-16 08:27:38 +000012072 }
12073 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012074 AML_CUR_STREAM (stream)->par_w = 0;
12075 AML_CUR_STREAM (stream)->par_h = 0;
zengliang.li5f31ef42024-05-16 08:27:38 +000012076 }
12077
12078 if (fiel) {
12079 const guint8 *fiel_data = (const guint8 *) fiel->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012080 guint len = AML_QT_UINT32 (fiel_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012081
12082 if (len == 10) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012083 AML_CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
12084 AML_CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
zengliang.li5f31ef42024-05-16 08:27:38 +000012085 }
12086 }
12087
12088 if (colr) {
12089 const guint8 *colr_data = (const guint8 *) colr->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012090 guint len = AML_QT_UINT32 (colr_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012091
12092 if (len == 19 || len == 18) {
12093 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
12094
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012095 if (color_type == AML_FOURCC_nclx || color_type == AML_FOURCC_nclc) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012096 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
12097 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
12098 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
12099 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
12100
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012101 AML_CUR_STREAM (stream)->colorimetry.primaries =
12102 gst_aml_video_color_primaries_from_iso_compat (primaries);
12103 AML_CUR_STREAM (stream)->colorimetry.transfer =
12104 gst_aml_video_transfer_function_from_iso_compat (transfer_function);
12105 AML_CUR_STREAM (stream)->colorimetry.matrix =
12106 gst_aml_video_color_matrix_from_iso_compat (matrix);
12107 AML_CUR_STREAM (stream)->colorimetry.range =
zengliang.li5f31ef42024-05-16 08:27:38 +000012108 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
12109 GST_VIDEO_COLOR_RANGE_16_235;
12110 } else {
12111 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
12112 }
12113 } else {
12114 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
12115 }
12116 }
12117
12118 if (esds) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012119 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000012120 stream->stream_tags);
12121 } else {
12122 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012123 case AML_FOURCC_H264:
12124 case AML_FOURCC_avc1:
12125 case AML_FOURCC_avc3:
12126 case AML_FOURCC_dav1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012127 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012128 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012129 len = len <= 0x56 ? 0 : len - 0x56;
12130 const guint8 *avc_data = stsd_entry_data + 0x56;
12131
12132 /* find avcC */
12133 while (len >= 0x8) {
12134 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012135 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000012136
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012137 if (AML_QT_UINT32 (avc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012138 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012139 else if (AML_QT_UINT32 (avc_data) <= len)
12140 size = AML_QT_UINT32 (avc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012141 else
12142 size = len - 0x8;
12143
12144 if (size < 1)
12145 /* No real data, so break out */
12146 break;
12147
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012148 dvconfig = AML_QT_FOURCC (avc_data + 0x4);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012149 switch (dvconfig) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012150 case AML_FOURCC_dvcC:
12151 case AML_FOURCC_dvvC:
12152 case AML_FOURCC_dvwC:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012153 {
bo.xiao4c245f22024-08-22 11:18:25 +080012154 gint ret = aml_qtdemux_parse_dvcc (fourcc, dvconfig, avc_data, entry->caps);
12155 if (ret == -1)
12156 {
12157 GST_WARNING_OBJECT (qtdemux, "reject play!");
12158 goto corrupt_file;
12159 }
12160
zengliang.lib0f27cd2024-05-17 06:58:54 +000012161 break;
12162 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012163 case AML_FOURCC_avcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012164 {
12165 /* parse, if found */
12166 GstBuffer *buf;
12167
12168 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
12169
12170 /* First 4 bytes are the length of the atom, the next 4 bytes
12171 * are the fourcc, the next 1 byte is the version, and the
12172 * subsequent bytes are profile_tier_level structure like data. */
12173 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
12174 avc_data + 8 + 1, size - 1);
12175 buf = gst_buffer_new_and_alloc (size);
12176 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
12177 gst_caps_set_simple (entry->caps,
12178 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12179 gst_buffer_unref (buf);
12180
12181 break;
12182 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012183 case AML_FOURCC_av1C:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012184 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012185 GST_DEBUG_OBJECT (qtdemux, "found av1C in stsd,so try to find sgpd");
zengliang.lib0f27cd2024-05-17 06:58:54 +000012186
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012187 if (aml_qtdemux_parse_sgpd_av1M(qtdemux, stbl) == -1)
12188 {
12189 GST_WARNING_OBJECT (qtdemux, "reject play for invalid metadata_type and specific_parameters");
12190 goto corrupt_file;
12191 }
12192 break;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012193 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012194 case AML_FOURCC_strf:
zengliang.li5f31ef42024-05-16 08:27:38 +000012195 {
12196 GstBuffer *buf;
12197
12198 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
12199
12200 /* First 4 bytes are the length of the atom, the next 4 bytes
12201 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
12202 * next 1 byte is the version, and the
12203 * subsequent bytes are sequence parameter set like data. */
12204
12205 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
12206 if (size > 1) {
12207 gst_codec_utils_h264_caps_set_level_and_profile
12208 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
12209
12210 buf = gst_buffer_new_and_alloc (size);
12211 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
12212 gst_caps_set_simple (entry->caps,
12213 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12214 gst_buffer_unref (buf);
12215 }
12216 break;
12217 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012218 case AML_FOURCC_btrt:
zengliang.li5f31ef42024-05-16 08:27:38 +000012219 {
12220 guint avg_bitrate, max_bitrate;
12221
12222 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
12223 if (size < 12)
12224 break;
12225
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012226 max_bitrate = AML_QT_UINT32 (avc_data + 0xc);
12227 avg_bitrate = AML_QT_UINT32 (avc_data + 0x10);
zengliang.li5f31ef42024-05-16 08:27:38 +000012228
12229 if (!max_bitrate && !avg_bitrate)
12230 break;
12231
12232 /* Some muxers seem to swap the average and maximum bitrates
12233 * (I'm looking at you, YouTube), so we swap for sanity. */
12234 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
12235 guint temp = avg_bitrate;
12236
12237 avg_bitrate = max_bitrate;
12238 max_bitrate = temp;
12239 }
12240
12241 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12242 gst_tag_list_add (stream->stream_tags,
12243 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
12244 max_bitrate, NULL);
12245 }
12246 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12247 gst_tag_list_add (stream->stream_tags,
12248 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
12249 NULL);
12250 }
12251
12252 break;
12253 }
12254
12255 default:
12256 break;
12257 }
12258
12259 len -= size + 8;
12260 avc_data += size + 8;
12261 }
12262
12263 break;
12264 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012265 case AML_FOURCC_H265:
12266 case AML_FOURCC_hvc1:
12267 case AML_FOURCC_hev1:
12268 case AML_FOURCC_dvh1:
12269 case AML_FOURCC_dvhe:
zengliang.li5f31ef42024-05-16 08:27:38 +000012270 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012271 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012272 len = len <= 0x56 ? 0 : len - 0x56;
12273 const guint8 *hevc_data = stsd_entry_data + 0x56;
12274
12275 /* find hevc */
12276 while (len >= 0x8) {
12277 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012278 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000012279
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012280 if (AML_QT_UINT32 (hevc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012281 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012282 else if (AML_QT_UINT32 (hevc_data) <= len)
12283 size = AML_QT_UINT32 (hevc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012284 else
12285 size = len - 0x8;
12286
12287 if (size < 1)
12288 /* No real data, so break out */
12289 break;
12290
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012291 dvconfig = AML_QT_FOURCC (hevc_data + 0x4);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012292
12293 switch (dvconfig) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012294 case AML_FOURCC_dvcC:
12295 case AML_FOURCC_dvvC:
12296 case AML_FOURCC_dvwC:
12297 case AML_FOURCC_dvzC:
12298 case AML_FOURCC_dvxC:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012299 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012300 gint ret = aml_qtdemux_parse_dvcc (fourcc, dvconfig, hevc_data, entry->caps);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012301 if (ret == -1)
12302 {
12303 GST_WARNING_OBJECT (qtdemux, "reject play!");
12304 goto corrupt_file;
12305 }
12306 break;
12307 }
12308
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012309 case AML_FOURCC_hvcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012310 {
12311 /* parse, if found */
12312 GstBuffer *buf;
12313
12314 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
12315
12316 /* First 4 bytes are the length of the atom, the next 4 bytes
12317 * are the fourcc, the next 1 byte is the version, and the
12318 * subsequent bytes are sequence parameter set like data. */
12319 gst_codec_utils_h265_caps_set_level_tier_and_profile
12320 (entry->caps, hevc_data + 8 + 1, size - 1);
12321
12322 buf = gst_buffer_new_and_alloc (size);
12323 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
12324 gst_caps_set_simple (entry->caps,
12325 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12326 gst_buffer_unref (buf);
12327 break;
12328 }
12329 default:
12330 break;
12331 }
12332 len -= size + 8;
12333 hevc_data += size + 8;
12334 }
12335 break;
12336 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012337 case AML_FOURCC_mp4v:
12338 case AML_FOURCC_MP4V:
12339 case AML_FOURCC_fmp4:
12340 case AML_FOURCC_FMP4:
12341 case AML_FOURCC_xvid:
12342 case AML_FOURCC_XVID:
zengliang.li5f31ef42024-05-16 08:27:38 +000012343 {
12344 GNode *glbl;
12345
12346 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
12347 GST_FOURCC_ARGS (fourcc));
12348
12349 /* codec data might be in glbl extension atom */
12350 glbl = mp4v ?
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012351 aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_glbl) : NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000012352 if (glbl) {
12353 guint8 *data;
12354 GstBuffer *buf;
12355 guint len;
12356
12357 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
12358 data = glbl->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012359 len = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012360 if (len > 0x8) {
12361 len -= 0x8;
12362 buf = gst_buffer_new_and_alloc (len);
12363 gst_buffer_fill (buf, 0, data + 8, len);
12364 gst_caps_set_simple (entry->caps,
12365 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12366 gst_buffer_unref (buf);
12367 }
12368 }
12369 break;
12370 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012371 case AML_FOURCC_mjp2:
zengliang.li5f31ef42024-05-16 08:27:38 +000012372 {
12373 /* see annex I of the jpeg2000 spec */
12374 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
12375 const guint8 *data;
12376 const gchar *colorspace = NULL;
12377 gint ncomp = 0;
12378 guint32 ncomp_map = 0;
12379 gint32 *comp_map = NULL;
12380 guint32 nchan_def = 0;
12381 gint32 *chan_def = NULL;
12382
12383 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12384 /* some required atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012385 mjp2 = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012386 if (!mjp2)
12387 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012388 jp2h = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_jp2h);
zengliang.li5f31ef42024-05-16 08:27:38 +000012389 if (!jp2h)
12390 break;
12391
12392 /* number of components; redundant with info in codestream, but useful
12393 to a muxer */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012394 ihdr = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_ihdr);
12395 if (!ihdr || AML_QT_UINT32 (ihdr->data) != 22)
zengliang.li5f31ef42024-05-16 08:27:38 +000012396 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012397 ncomp = AML_QT_UINT16 (((guint8 *) ihdr->data) + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000012398
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012399 colr = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_colr);
zengliang.li5f31ef42024-05-16 08:27:38 +000012400 if (!colr)
12401 break;
12402 GST_DEBUG_OBJECT (qtdemux, "found colr");
12403 /* extract colour space info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012404 if (AML_QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12405 switch (AML_QT_UINT32 ((guint8 *) colr->data + 11)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012406 case 16:
12407 colorspace = "sRGB";
12408 break;
12409 case 17:
12410 colorspace = "GRAY";
12411 break;
12412 case 18:
12413 colorspace = "sYUV";
12414 break;
12415 default:
12416 colorspace = NULL;
12417 break;
12418 }
12419 }
12420 if (!colorspace)
12421 /* colr is required, and only values 16, 17, and 18 are specified,
12422 so error if we have no colorspace */
12423 break;
12424
12425 /* extract component mapping */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012426 cmap = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_cmap);
zengliang.li5f31ef42024-05-16 08:27:38 +000012427 if (cmap) {
12428 guint32 cmap_len = 0;
12429 int i;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012430 cmap_len = AML_QT_UINT32 (cmap->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012431 if (cmap_len >= 8) {
12432 /* normal box, subtract off header */
12433 cmap_len -= 8;
12434 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12435 if (cmap_len % 4 == 0) {
12436 ncomp_map = (cmap_len / 4);
12437 comp_map = g_new0 (gint32, ncomp_map);
12438 for (i = 0; i < ncomp_map; i++) {
12439 guint16 cmp;
12440 guint8 mtyp, pcol;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012441 cmp = AML_QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12442 mtyp = AML_QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12443 pcol = AML_QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
zengliang.li5f31ef42024-05-16 08:27:38 +000012444 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12445 }
12446 }
12447 }
12448 }
12449 /* extract channel definitions */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012450 cdef = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_cdef);
zengliang.li5f31ef42024-05-16 08:27:38 +000012451 if (cdef) {
12452 guint32 cdef_len = 0;
12453 int i;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012454 cdef_len = AML_QT_UINT32 (cdef->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012455 if (cdef_len >= 10) {
12456 /* normal box, subtract off header and len */
12457 cdef_len -= 10;
12458 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12459 if (cdef_len % 6 == 0) {
12460 nchan_def = (cdef_len / 6);
12461 chan_def = g_new0 (gint32, nchan_def);
12462 for (i = 0; i < nchan_def; i++)
12463 chan_def[i] = -1;
12464 for (i = 0; i < nchan_def; i++) {
12465 guint16 cn, typ, asoc;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012466 cn = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12467 typ = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12468 asoc = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000012469 if (cn < nchan_def) {
12470 switch (typ) {
12471 case 0:
12472 chan_def[cn] = asoc;
12473 break;
12474 case 1:
12475 chan_def[cn] = 0; /* alpha */
12476 break;
12477 default:
12478 chan_def[cn] = -typ;
12479 }
12480 }
12481 }
12482 }
12483 }
12484 }
12485
12486 gst_caps_set_simple (entry->caps,
12487 "num-components", G_TYPE_INT, ncomp, NULL);
12488 gst_caps_set_simple (entry->caps,
12489 "colorspace", G_TYPE_STRING, colorspace, NULL);
12490
12491 if (comp_map) {
12492 GValue arr = { 0, };
12493 GValue elt = { 0, };
12494 int i;
12495 g_value_init (&arr, GST_TYPE_ARRAY);
12496 g_value_init (&elt, G_TYPE_INT);
12497 for (i = 0; i < ncomp_map; i++) {
12498 g_value_set_int (&elt, comp_map[i]);
12499 gst_value_array_append_value (&arr, &elt);
12500 }
12501 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12502 "component-map", &arr);
12503 g_value_unset (&elt);
12504 g_value_unset (&arr);
12505 g_free (comp_map);
12506 }
12507
12508 if (chan_def) {
12509 GValue arr = { 0, };
12510 GValue elt = { 0, };
12511 int i;
12512 g_value_init (&arr, GST_TYPE_ARRAY);
12513 g_value_init (&elt, G_TYPE_INT);
12514 for (i = 0; i < nchan_def; i++) {
12515 g_value_set_int (&elt, chan_def[i]);
12516 gst_value_array_append_value (&arr, &elt);
12517 }
12518 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12519 "channel-definitions", &arr);
12520 g_value_unset (&elt);
12521 g_value_unset (&arr);
12522 g_free (chan_def);
12523 }
12524
12525 /* some optional atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012526 field = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_fiel);
12527 prefix = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_jp2x);
zengliang.li5f31ef42024-05-16 08:27:38 +000012528
12529 /* indicate possible fields in caps */
12530 if (field) {
12531 data = (guint8 *) field->data + 8;
12532 if (*data != 1)
12533 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12534 (gint) * data, NULL);
12535 }
12536 /* add codec_data if provided */
12537 if (prefix) {
12538 GstBuffer *buf;
12539 guint len;
12540
12541 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12542 data = prefix->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012543 len = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012544 if (len > 0x8) {
12545 len -= 0x8;
12546 buf = gst_buffer_new_and_alloc (len);
12547 gst_buffer_fill (buf, 0, data + 8, len);
12548 gst_caps_set_simple (entry->caps,
12549 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12550 gst_buffer_unref (buf);
12551 }
12552 }
12553 break;
12554 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012555 case AML_FOURCC_SVQ3:
12556 case AML_FOURCC_VP31:
zengliang.li5f31ef42024-05-16 08:27:38 +000012557 {
12558 GstBuffer *buf;
12559 GstBuffer *seqh = NULL;
12560 const guint8 *gamma_data = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012561 guint len = AML_QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
zengliang.li5f31ef42024-05-16 08:27:38 +000012562
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012563 aml_qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000012564 &seqh);
12565 if (gamma_data) {
12566 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012567 AML_QT_FP32 (gamma_data), NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +000012568 }
12569 if (seqh) {
12570 /* sorry for the bad name, but we don't know what this is, other
12571 * than its own fourcc */
12572 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12573 NULL);
12574 gst_buffer_unref (seqh);
12575 }
12576
12577 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12578 buf = gst_buffer_new_and_alloc (len);
12579 gst_buffer_fill (buf, 0, stsd_data, len);
12580 gst_caps_set_simple (entry->caps,
12581 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12582 gst_buffer_unref (buf);
12583 break;
12584 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012585 case AML_FOURCC_jpeg:
zengliang.li5f31ef42024-05-16 08:27:38 +000012586 {
12587 /* https://developer.apple.com/standards/qtff-2001.pdf,
12588 * page 92, "Video Sample Description", under table 3.1 */
12589 GstByteReader br;
12590
12591 const gint compressor_offset =
12592 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12593 const gint min_size = compressor_offset + 32 + 2 + 2;
12594 GNode *jpeg;
12595 guint32 len;
12596 guint16 color_table_id = 0;
12597 gboolean ok;
12598
12599 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12600
12601 /* recover information on interlaced/progressive */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012602 jpeg = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_jpeg);
zengliang.li5f31ef42024-05-16 08:27:38 +000012603 if (!jpeg)
12604 break;
12605
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012606 len = AML_QT_UINT32 (jpeg->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012607 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12608 min_size);
12609 if (len >= min_size) {
12610 gst_byte_reader_init (&br, jpeg->data, len);
12611
12612 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12613 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12614 if (color_table_id != 0) {
12615 /* the spec says there can be concatenated chunks in the data, and we want
12616 * to find one called field. Walk through them. */
12617 gint offset = min_size;
12618 while (offset + 8 < len) {
12619 guint32 size = 0, tag;
12620 ok = gst_byte_reader_get_uint32_le (&br, &size);
12621 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12622 if (!ok || size < 8) {
12623 GST_WARNING_OBJECT (qtdemux,
12624 "Failed to walk optional chunk list");
12625 break;
12626 }
12627 GST_DEBUG_OBJECT (qtdemux,
12628 "Found optional %4.4s chunk, size %u",
12629 (const char *) &tag, size);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012630 if (tag == AML_FOURCC_fiel) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012631 guint8 n_fields = 0, ordering = 0;
12632 gst_byte_reader_get_uint8 (&br, &n_fields);
12633 gst_byte_reader_get_uint8 (&br, &ordering);
12634 if (n_fields == 1 || n_fields == 2) {
12635 GST_DEBUG_OBJECT (qtdemux,
12636 "Found fiel tag with %u fields, ordering %u",
12637 n_fields, ordering);
12638 if (n_fields == 2)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012639 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
zengliang.li5f31ef42024-05-16 08:27:38 +000012640 "interlace-mode", G_TYPE_STRING, "interleaved",
12641 NULL);
12642 } else {
12643 GST_WARNING_OBJECT (qtdemux,
12644 "Found fiel tag with invalid fields (%u)", n_fields);
12645 }
12646 }
12647 offset += size;
12648 }
12649 } else {
12650 GST_DEBUG_OBJECT (qtdemux,
12651 "Color table ID is 0, not trying to get interlacedness");
12652 }
12653 } else {
12654 GST_WARNING_OBJECT (qtdemux,
12655 "Length of jpeg chunk is too small, not trying to get interlacedness");
12656 }
12657
12658 break;
12659 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012660 case AML_FOURCC_rle_:
12661 case AML_FOURCC_WRLE:
zengliang.li5f31ef42024-05-16 08:27:38 +000012662 {
12663 gst_caps_set_simple (entry->caps,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012664 "depth", G_TYPE_INT, AML_QT_UINT16 (stsd_entry_data + offset + 66),
zengliang.li5f31ef42024-05-16 08:27:38 +000012665 NULL);
12666 break;
12667 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012668 case AML_FOURCC_XiTh:
zengliang.li5f31ef42024-05-16 08:27:38 +000012669 {
12670 GNode *xith, *xdxt;
12671
12672 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012673 xith = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012674 if (!xith)
12675 break;
12676
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012677 xdxt = aml_qtdemux_tree_get_child_by_type (xith, AML_FOURCC_XdxT);
zengliang.li5f31ef42024-05-16 08:27:38 +000012678 if (!xdxt)
12679 break;
12680
12681 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12682 /* collect the headers and store them in a stream list so that we can
12683 * send them out first */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012684 aml_qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
zengliang.li5f31ef42024-05-16 08:27:38 +000012685 break;
12686 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012687 case AML_FOURCC_ovc1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012688 {
12689 GNode *ovc1;
12690 guint8 *ovc1_data;
12691 guint ovc1_len;
12692 GstBuffer *buf;
12693
12694 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012695 ovc1 = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012696 if (!ovc1)
12697 break;
12698 ovc1_data = ovc1->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012699 ovc1_len = AML_QT_UINT32 (ovc1_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012700 if (ovc1_len <= 198) {
12701 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12702 break;
12703 }
12704 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12705 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12706 gst_caps_set_simple (entry->caps,
12707 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12708 gst_buffer_unref (buf);
12709 break;
12710 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012711 case AML_FOURCC_vc_1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012712 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012713 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012714 len = len <= 0x56 ? 0 : len - 0x56;
12715 const guint8 *vc1_data = stsd_entry_data + 0x56;
12716
12717 /* find dvc1 */
12718 while (len >= 8) {
12719 guint size;
12720
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012721 if (AML_QT_UINT32 (vc1_data) <= 8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012722 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012723 else if (AML_QT_UINT32 (vc1_data) <= len)
12724 size = AML_QT_UINT32 (vc1_data) - 8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012725 else
12726 size = len - 8;
12727
12728 if (size < 1)
12729 /* No real data, so break out */
12730 break;
12731
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012732 switch (AML_QT_FOURCC (vc1_data + 0x4)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012733 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12734 {
12735 GstBuffer *buf;
12736
12737 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12738 buf = gst_buffer_new_and_alloc (size);
12739 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12740 gst_caps_set_simple (entry->caps,
12741 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12742 gst_buffer_unref (buf);
12743 break;
12744 }
12745 default:
12746 break;
12747 }
12748 len -= size + 8;
12749 vc1_data += size + 8;
12750 }
12751 break;
12752 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012753 case AML_FOURCC_av01:
zengliang.li5f31ef42024-05-16 08:27:38 +000012754 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012755 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012756 len = len <= 0x56 ? 0 : len - 0x56;
12757 const guint8 *av1_data = stsd_entry_data + 0x56;
12758
12759 /* find av1C */
12760 while (len >= 0x8) {
12761 guint size;
12762
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012763 if (AML_QT_UINT32 (av1_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012764 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012765 else if (AML_QT_UINT32 (av1_data) <= len)
12766 size = AML_QT_UINT32 (av1_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012767 else
12768 size = len - 0x8;
12769
12770 if (size < 1)
12771 /* No real data, so break out */
12772 break;
12773
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012774 switch (AML_QT_FOURCC (av1_data + 0x4)) {
12775 case AML_FOURCC_av1C:
zengliang.li5f31ef42024-05-16 08:27:38 +000012776 {
12777 /* parse, if found */
12778 GstBuffer *buf;
12779
12780 GST_DEBUG_OBJECT (qtdemux,
12781 "found av1C codec_data in stsd of size %d", size);
12782
12783 /* not enough data, just ignore and hope for the best */
12784 if (size < 4)
12785 break;
12786
12787 /* Content is:
12788 * 4 bytes: atom length
12789 * 4 bytes: fourcc
12790 *
12791 * version 1 (marker=1):
12792 *
12793 * unsigned int (1) marker = 1;
12794 * unsigned int (7) version = 1;
12795 * unsigned int (3) seq_profile;
12796 * unsigned int (5) seq_level_idx_0;
12797 * unsigned int (1) seq_tier_0;
12798 * unsigned int (1) high_bitdepth;
12799 * unsigned int (1) twelve_bit;
12800 * unsigned int (1) monochrome;
12801 * unsigned int (1) chroma_subsampling_x;
12802 * unsigned int (1) chroma_subsampling_y;
12803 * unsigned int (2) chroma_sample_position;
12804 * unsigned int (3) reserved = 0;
12805 *
12806 * unsigned int (1) initial_presentation_delay_present;
12807 * if (initial_presentation_delay_present) {
12808 * unsigned int (4) initial_presentation_delay_minus_one;
12809 * } else {
12810 * unsigned int (4) reserved = 0;
12811 * }
12812 *
12813 * unsigned int (8) configOBUs[];
12814 *
12815 * rest: OBUs.
12816 */
12817
12818 switch (av1_data[8]) {
12819 case 0x81:{
12820 guint8 pres_delay_field;
12821
12822 /* We let profile and the other parts be figured out by
12823 * av1parse and only include the presentation delay here
12824 * if present */
12825 /* We skip initial_presentation_delay* for now */
12826 pres_delay_field = *(av1_data + 11);
12827 if (pres_delay_field & (1 << 5)) {
12828 gst_caps_set_simple (entry->caps,
12829 "presentation-delay", G_TYPE_INT,
12830 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12831 }
12832
12833 buf = gst_buffer_new_and_alloc (size);
12834 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12835 gst_buffer_fill (buf, 0, av1_data + 8, size);
12836 gst_caps_set_simple (entry->caps,
12837 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12838 gst_buffer_unref (buf);
12839 break;
12840 }
12841 default:
12842 GST_WARNING ("Unknown version 0x%02x of av1C box",
12843 av1_data[8]);
12844 break;
12845 }
12846
12847 break;
12848 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012849 case AML_FOURCC_dvcC:
12850 case AML_FOURCC_dvvC:
12851 case AML_FOURCC_dvwC:
zengliang.li54713f02024-05-17 02:46:54 +000012852 {
bo.xiao4c245f22024-08-22 11:18:25 +080012853 gint ret = aml_qtdemux_parse_dvcc (fourcc, AML_QT_FOURCC (av1_data + 0x4), av1_data, entry->caps);
12854 if (ret == -1)
12855 {
12856 GST_WARNING_OBJECT (qtdemux, "reject play!");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012857 goto corrupt_file;
zengliang.li54713f02024-05-17 02:46:54 +000012858 }
bo.xiao4c245f22024-08-22 11:18:25 +080012859 break;
12860 }
zengliang.li5f31ef42024-05-16 08:27:38 +000012861 default:
bo.xiao4c245f22024-08-22 11:18:25 +080012862 GST_DEBUG_OBJECT (qtdemux, "find fourCC =0x%x", AML_QT_FOURCC (av1_data + 0x4));
zengliang.li5f31ef42024-05-16 08:27:38 +000012863 break;
12864 }
12865
12866 len -= size + 8;
12867 av1_data += size + 8;
12868 }
12869
12870 break;
12871 }
12872
12873 /* TODO: Need to parse vpcC for VP8 codec too.
12874 * Note that VPCodecConfigurationBox (vpcC) is defined for
12875 * vp08, vp09, and vp10 fourcc. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012876 case AML_FOURCC_vp09:
zengliang.li5f31ef42024-05-16 08:27:38 +000012877 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012878 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012879 len = len <= 0x56 ? 0 : len - 0x56;
12880 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12881
12882 /* find vpcC */
12883 while (len >= 0x8) {
12884 guint size;
12885
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012886 if (AML_QT_UINT32 (vpcc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012887 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012888 else if (AML_QT_UINT32 (vpcc_data) <= len)
12889 size = AML_QT_UINT32 (vpcc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012890 else
12891 size = len - 0x8;
12892
12893 if (size < 1)
12894 /* No real data, so break out */
12895 break;
12896
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012897 switch (AML_QT_FOURCC (vpcc_data + 0x4)) {
12898 case AML_FOURCC_vpcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012899 {
12900 const gchar *profile_str = NULL;
12901 const gchar *chroma_format_str = NULL;
12902 guint8 profile;
12903 guint8 bitdepth;
12904 guint8 chroma_format;
12905 GstVideoColorimetry cinfo;
12906
12907 /* parse, if found */
12908 GST_DEBUG_OBJECT (qtdemux,
12909 "found vp codec_data in stsd of size %d", size);
12910
12911 /* the meaning of "size" is length of the atom body, excluding
12912 * atom length and fourcc fields */
12913 if (size < 12)
12914 break;
12915
12916 /* Content is:
12917 * 4 bytes: atom length
12918 * 4 bytes: fourcc
12919 * 1 byte: version
12920 * 3 bytes: flags
12921 * 1 byte: profile
12922 * 1 byte: level
12923 * 4 bits: bitDepth
12924 * 3 bits: chromaSubsampling
12925 * 1 bit: videoFullRangeFlag
12926 * 1 byte: colourPrimaries
12927 * 1 byte: transferCharacteristics
12928 * 1 byte: matrixCoefficients
12929 * 2 bytes: codecInitializationDataSize (should be zero for vp8 and vp9)
12930 * rest: codecInitializationData (not used for vp8 and vp9)
12931 */
12932
12933 if (vpcc_data[8] != 1) {
12934 GST_WARNING_OBJECT (qtdemux,
12935 "unknown vpcC version %d", vpcc_data[8]);
12936 break;
12937 }
12938
12939 profile = vpcc_data[12];
12940 switch (profile) {
12941 case 0:
12942 profile_str = "0";
12943 break;
12944 case 1:
12945 profile_str = "1";
12946 break;
12947 case 2:
12948 profile_str = "2";
12949 break;
12950 case 3:
12951 profile_str = "3";
12952 break;
12953 default:
12954 break;
12955 }
12956
12957 if (profile_str) {
12958 gst_caps_set_simple (entry->caps,
12959 "profile", G_TYPE_STRING, profile_str, NULL);
12960 }
12961
12962 /* skip level, the VP9 spec v0.6 defines only one level atm,
12963 * but webm spec define various ones. Add level to caps
12964 * if we really need it then */
12965
12966 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
12967 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
12968 gst_caps_set_simple (entry->caps,
12969 "bit-depth-luma", G_TYPE_UINT, bitdepth,
12970 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
12971 }
12972
12973 chroma_format = (vpcc_data[14] & 0xe) >> 1;
12974 switch (chroma_format) {
12975 case 0:
12976 case 1:
12977 chroma_format_str = "4:2:0";
12978 break;
12979 case 2:
12980 chroma_format_str = "4:2:2";
12981 break;
12982 case 3:
12983 chroma_format_str = "4:4:4";
12984 break;
12985 default:
12986 break;
12987 }
12988
12989 if (chroma_format_str) {
12990 gst_caps_set_simple (entry->caps,
12991 "chroma-format", G_TYPE_STRING, chroma_format_str,
12992 NULL);
12993 }
12994
12995 if ((vpcc_data[14] & 0x1) != 0)
12996 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
12997 else
12998 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
12999 cinfo.primaries =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013000 gst_aml_video_color_primaries_from_iso_compat (vpcc_data[15]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013001 cinfo.transfer =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013002 gst_aml_video_transfer_function_from_iso_compat (vpcc_data[16]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013003 cinfo.matrix =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013004 gst_aml_video_color_matrix_from_iso_compat (vpcc_data[17]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013005
13006 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
13007 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
13008 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
13009 /* set this only if all values are known, otherwise this
13010 * might overwrite valid ones parsed from other color box */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013011 AML_CUR_STREAM (stream)->colorimetry = cinfo;
zengliang.li5f31ef42024-05-16 08:27:38 +000013012 }
13013 break;
13014 }
13015 default:
13016 break;
13017 }
13018
13019 len -= size + 8;
13020 vpcc_data += size + 8;
13021 }
13022
13023 break;
13024 }
13025 default:
13026 break;
13027 }
13028 }
13029
13030 GST_INFO_OBJECT (qtdemux,
13031 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13032 GST_FOURCC_ARGS (fourcc), entry->caps);
13033
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013034 } else if (stream->subtype == AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013035 GNode *wave;
13036 guint version, samplesize;
13037 guint16 compression_id;
13038 gboolean amrwb = FALSE;
13039
13040 offset = 16;
13041 /* sample description entry (16) + sound sample description v0 (20) */
13042 if (len < 36)
13043 goto corrupt_file;
13044
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013045 version = AML_QT_UINT32 (stsd_entry_data + offset);
13046 entry->n_channels = AML_QT_UINT16 (stsd_entry_data + offset + 8);
13047 samplesize = AML_QT_UINT16 (stsd_entry_data + offset + 10);
13048 compression_id = AML_QT_UINT16 (stsd_entry_data + offset + 12);
13049 entry->rate = AML_QT_FP32 (stsd_entry_data + offset + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013050
13051 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
13052 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013053 AML_QT_UINT32 (stsd_entry_data + offset + 4));
zengliang.li5f31ef42024-05-16 08:27:38 +000013054 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13055 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
13056 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
13057 GST_LOG_OBJECT (qtdemux, "packet size: %d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013058 AML_QT_UINT16 (stsd_entry_data + offset + 14));
zengliang.li5f31ef42024-05-16 08:27:38 +000013059 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13060
13061 if (compression_id == 0xfffe)
13062 entry->sampled = TRUE;
13063
13064 /* first assume uncompressed audio */
13065 entry->bytes_per_sample = samplesize / 8;
13066 entry->samples_per_frame = entry->n_channels;
13067 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13068 entry->samples_per_packet = entry->samples_per_frame;
13069 entry->bytes_per_packet = entry->bytes_per_sample;
13070
13071 offset = 36;
13072
13073 if (version == 0x00010000) {
13074 /* sample description entry (16) + sound sample description v1 (20+16) */
13075 if (len < 52)
13076 goto corrupt_file;
13077
13078 /* take information from here over the normal sample description */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013079 entry->samples_per_packet = AML_QT_UINT32 (stsd_entry_data + offset);
13080 entry->bytes_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 4);
13081 entry->bytes_per_frame = AML_QT_UINT32 (stsd_entry_data + offset + 8);
13082 entry->bytes_per_sample = AML_QT_UINT32 (stsd_entry_data + offset + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000013083
13084 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
13085 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
13086 entry->samples_per_packet);
13087 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13088 entry->bytes_per_packet);
13089 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
13090 entry->bytes_per_frame);
13091 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
13092 entry->bytes_per_sample);
13093
13094 if (!entry->sampled && entry->bytes_per_packet) {
13095 entry->samples_per_frame = (entry->bytes_per_frame /
13096 entry->bytes_per_packet) * entry->samples_per_packet;
13097 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
13098 entry->samples_per_frame);
13099 }
13100 } else if (version == 0x00020000) {
13101 /* sample description entry (16) + sound sample description v2 (56) */
13102 if (len < 72)
13103 goto corrupt_file;
13104
13105 /* take information from here over the normal sample description */
13106 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013107 entry->n_channels = AML_QT_UINT32 (stsd_entry_data + offset + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000013108 entry->samples_per_frame = entry->n_channels;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013109 entry->bytes_per_sample = AML_QT_UINT32 (stsd_entry_data + offset + 20) / 8;
13110 entry->bytes_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 28);
13111 entry->samples_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 32);
zengliang.li5f31ef42024-05-16 08:27:38 +000013112 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
13113
13114 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
13115 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13116 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13117 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
13118 entry->bytes_per_sample * 8);
13119 GST_LOG_OBJECT (qtdemux, "format flags: %X",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013120 AML_QT_UINT32 (stsd_entry_data + offset + 24));
zengliang.li5f31ef42024-05-16 08:27:38 +000013121 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13122 entry->bytes_per_packet);
13123 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
13124 entry->samples_per_packet);
13125 } else if (version != 0x00000) {
13126 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
13127 version);
13128 }
13129
13130 switch (fourcc) {
13131 /* Yes, these have to be hard-coded */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013132 case AML_FOURCC_MAC6:
zengliang.li5f31ef42024-05-16 08:27:38 +000013133 {
13134 entry->samples_per_packet = 6;
13135 entry->bytes_per_packet = 1;
13136 entry->bytes_per_frame = 1 * entry->n_channels;
13137 entry->bytes_per_sample = 1;
13138 entry->samples_per_frame = 6 * entry->n_channels;
13139 break;
13140 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013141 case AML_FOURCC_MAC3:
zengliang.li5f31ef42024-05-16 08:27:38 +000013142 {
13143 entry->samples_per_packet = 3;
13144 entry->bytes_per_packet = 1;
13145 entry->bytes_per_frame = 1 * entry->n_channels;
13146 entry->bytes_per_sample = 1;
13147 entry->samples_per_frame = 3 * entry->n_channels;
13148 break;
13149 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013150 case AML_FOURCC_ima4:
zengliang.li5f31ef42024-05-16 08:27:38 +000013151 {
13152 entry->samples_per_packet = 64;
13153 entry->bytes_per_packet = 34;
13154 entry->bytes_per_frame = 34 * entry->n_channels;
13155 entry->bytes_per_sample = 2;
13156 entry->samples_per_frame = 64 * entry->n_channels;
13157 break;
13158 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013159 case AML_FOURCC_ulaw:
13160 case AML_FOURCC_alaw:
zengliang.li5f31ef42024-05-16 08:27:38 +000013161 {
13162 entry->samples_per_packet = 1;
13163 entry->bytes_per_packet = 1;
13164 entry->bytes_per_frame = 1 * entry->n_channels;
13165 entry->bytes_per_sample = 1;
13166 entry->samples_per_frame = 1 * entry->n_channels;
13167 break;
13168 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013169 case AML_FOURCC_agsm:
zengliang.li5f31ef42024-05-16 08:27:38 +000013170 {
13171 entry->samples_per_packet = 160;
13172 entry->bytes_per_packet = 33;
13173 entry->bytes_per_frame = 33 * entry->n_channels;
13174 entry->bytes_per_sample = 2;
13175 entry->samples_per_frame = 160 * entry->n_channels;
13176 break;
13177 }
13178 /* fix up any invalid header information from above */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013179 case AML_FOURCC_twos:
13180 case AML_FOURCC_sowt:
13181 case AML_FOURCC_raw_:
13182 case AML_FOURCC_lpcm:
zengliang.li5f31ef42024-05-16 08:27:38 +000013183 /* Sometimes these are set to 0 in the sound sample descriptions so
13184 * let's try to infer useful values from the other information we
13185 * have available */
13186 if (entry->bytes_per_sample == 0)
13187 entry->bytes_per_sample =
13188 entry->bytes_per_frame / entry->n_channels;
13189 if (entry->bytes_per_sample == 0)
13190 entry->bytes_per_sample = samplesize / 8;
13191
13192 if (entry->bytes_per_frame == 0)
13193 entry->bytes_per_frame =
13194 entry->bytes_per_sample * entry->n_channels;
13195
13196 if (entry->bytes_per_packet == 0)
13197 entry->bytes_per_packet = entry->bytes_per_sample;
13198
13199 if (entry->samples_per_frame == 0)
13200 entry->samples_per_frame = entry->n_channels;
13201
13202 if (entry->samples_per_packet == 0)
13203 entry->samples_per_packet = entry->samples_per_frame;
13204
13205 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013206 case AML_FOURCC_in24:
13207 case AML_FOURCC_in32:
13208 case AML_FOURCC_fl32:
13209 case AML_FOURCC_fl64:
13210 case AML_FOURCC_s16l:{
zengliang.li5f31ef42024-05-16 08:27:38 +000013211 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013212 case AML_FOURCC_in24:
zengliang.li5f31ef42024-05-16 08:27:38 +000013213 entry->bytes_per_sample = 3;
13214 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013215 case AML_FOURCC_in32:
13216 case AML_FOURCC_fl32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013217 entry->bytes_per_sample = 4;
13218 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013219 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013220 entry->bytes_per_sample = 8;
13221 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013222 case AML_FOURCC_s16l:
zengliang.li5f31ef42024-05-16 08:27:38 +000013223 entry->bytes_per_sample = 2;
13224 break;
13225 default:
13226 g_assert_not_reached ();
13227 break;
13228 }
13229 entry->samples_per_frame = entry->n_channels;
13230 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13231 entry->samples_per_packet = entry->samples_per_frame;
13232 entry->bytes_per_packet = entry->bytes_per_sample;
13233 break;
13234 }
13235 default:
13236 break;
13237 }
13238
13239 if (entry->caps)
13240 gst_caps_unref (entry->caps);
13241
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013242 entry->caps = aml_qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000013243 stsd_entry_data + 32, len - 16, &codec);
13244
13245 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013246 case AML_FOURCC_in24:
13247 case AML_FOURCC_in32:
13248 case AML_FOURCC_fl32:
13249 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013250 {
13251 GNode *enda;
13252 GNode *fmt;
13253
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013254 fmt = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000013255
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013256 enda = aml_qtdemux_tree_get_child_by_type (fmt, AML_FOURCC_enda);
zengliang.li5f31ef42024-05-16 08:27:38 +000013257 if (!enda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013258 wave = aml_qtdemux_tree_get_child_by_type (fmt, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013259 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013260 enda = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_enda);
zengliang.li5f31ef42024-05-16 08:27:38 +000013261 }
13262 if (enda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013263 int enda_value = AML_QT_UINT16 ((guint8 *) enda->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000013264 const gchar *format_str;
13265
13266 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013267 case AML_FOURCC_in24:
zengliang.li5f31ef42024-05-16 08:27:38 +000013268 format_str = (enda_value) ? "S24LE" : "S24BE";
13269 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013270 case AML_FOURCC_in32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013271 format_str = (enda_value) ? "S32LE" : "S32BE";
13272 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013273 case AML_FOURCC_fl32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013274 format_str = (enda_value) ? "F32LE" : "F32BE";
13275 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013276 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013277 format_str = (enda_value) ? "F64LE" : "F64BE";
13278 break;
13279 default:
13280 g_assert_not_reached ();
13281 break;
13282 }
13283 gst_caps_set_simple (entry->caps,
13284 "format", G_TYPE_STRING, format_str, NULL);
13285 }
13286 break;
13287 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013288 case AML_FOURCC_owma:
zengliang.li5f31ef42024-05-16 08:27:38 +000013289 {
13290 const guint8 *owma_data;
13291 const gchar *codec_name = NULL;
13292 guint owma_len;
13293 GstBuffer *buf;
13294 gint version = 1;
13295 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13296 /* FIXME this should also be gst_riff_strf_auds,
13297 * but the latter one is actually missing bits-per-sample :( */
13298 typedef struct
13299 {
13300 gint16 wFormatTag;
13301 gint16 nChannels;
13302 gint32 nSamplesPerSec;
13303 gint32 nAvgBytesPerSec;
13304 gint16 nBlockAlign;
13305 gint16 wBitsPerSample;
13306 gint16 cbSize;
13307 } WAVEFORMATEX;
13308 WAVEFORMATEX *wfex;
13309
13310 GST_DEBUG_OBJECT (qtdemux, "parse owma");
13311 owma_data = stsd_entry_data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013312 owma_len = AML_QT_UINT32 (owma_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013313 if (owma_len <= 54) {
13314 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
13315 break;
13316 }
13317 wfex = (WAVEFORMATEX *) (owma_data + 36);
13318 buf = gst_buffer_new_and_alloc (owma_len - 54);
13319 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
13320 if (wfex->wFormatTag == 0x0161) {
13321 codec_name = "Windows Media Audio";
13322 version = 2;
13323 } else if (wfex->wFormatTag == 0x0162) {
13324 codec_name = "Windows Media Audio 9 Pro";
13325 version = 3;
13326 } else if (wfex->wFormatTag == 0x0163) {
13327 codec_name = "Windows Media Audio 9 Lossless";
13328 /* is that correct? gstffmpegcodecmap.c is missing it, but
13329 * fluendo codec seems to support it */
13330 version = 4;
13331 }
13332
13333 gst_caps_set_simple (entry->caps,
13334 "codec_data", GST_TYPE_BUFFER, buf,
13335 "wmaversion", G_TYPE_INT, version,
13336 "block_align", G_TYPE_INT,
13337 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
13338 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13339 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13340 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13341 gst_buffer_unref (buf);
13342
13343 if (codec_name) {
13344 g_free (codec);
13345 codec = g_strdup (codec_name);
13346 }
13347 break;
13348 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013349 case AML_FOURCC_wma_:
zengliang.li5f31ef42024-05-16 08:27:38 +000013350 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013351 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013352 len = len <= offset ? 0 : len - offset;
13353 const guint8 *wfex_data = stsd_entry_data + offset;
13354 const gchar *codec_name = NULL;
13355 gint version = 1;
13356 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13357 /* FIXME this should also be gst_riff_strf_auds,
13358 * but the latter one is actually missing bits-per-sample :( */
13359 typedef struct
13360 {
13361 gint16 wFormatTag;
13362 gint16 nChannels;
13363 gint32 nSamplesPerSec;
13364 gint32 nAvgBytesPerSec;
13365 gint16 nBlockAlign;
13366 gint16 wBitsPerSample;
13367 gint16 cbSize;
13368 } WAVEFORMATEX;
13369 WAVEFORMATEX wfex;
13370
13371 /* FIXME: unify with similar wavformatex parsing code above */
13372 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13373
13374 /* find wfex */
13375 while (len >= 8) {
13376 guint size;
13377
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013378 if (AML_QT_UINT32 (wfex_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000013379 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013380 else if (AML_QT_UINT32 (wfex_data) <= len)
13381 size = AML_QT_UINT32 (wfex_data) - 8;
zengliang.li5f31ef42024-05-16 08:27:38 +000013382 else
13383 size = len - 8;
13384
13385 if (size < 1)
13386 /* No real data, so break out */
13387 break;
13388
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013389 switch (AML_QT_FOURCC (wfex_data + 4)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013390 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13391 {
13392 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13393
13394 if (size < 8 + 18)
13395 break;
13396
13397 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13398 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13399 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13400 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13401 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13402 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13403 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13404
13405 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13406 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13407 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13408 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13409 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13410 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13411
13412 if (wfex.wFormatTag == 0x0161) {
13413 codec_name = "Windows Media Audio";
13414 version = 2;
13415 } else if (wfex.wFormatTag == 0x0162) {
13416 codec_name = "Windows Media Audio 9 Pro";
13417 version = 3;
13418 } else if (wfex.wFormatTag == 0x0163) {
13419 codec_name = "Windows Media Audio 9 Lossless";
13420 /* is that correct? gstffmpegcodecmap.c is missing it, but
13421 * fluendo codec seems to support it */
13422 version = 4;
13423 }
13424
13425 gst_caps_set_simple (entry->caps,
13426 "wmaversion", G_TYPE_INT, version,
13427 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13428 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13429 "width", G_TYPE_INT, wfex.wBitsPerSample,
13430 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13431
13432 if (size > wfex.cbSize) {
13433 GstBuffer *buf;
13434
13435 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13436 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13437 size - wfex.cbSize);
13438 gst_caps_set_simple (entry->caps,
13439 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13440 gst_buffer_unref (buf);
13441 } else {
13442 GST_WARNING_OBJECT (qtdemux, "no codec data");
13443 }
13444
13445 if (codec_name) {
13446 g_free (codec);
13447 codec = g_strdup (codec_name);
13448 }
13449 break;
13450 }
13451 default:
13452 break;
13453 }
13454 len -= size + 8;
13455 wfex_data += size + 8;
13456 }
13457 break;
13458 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013459 case AML_FOURCC_opus:
zengliang.li5f31ef42024-05-16 08:27:38 +000013460 {
13461 const guint8 *dops_data;
13462 guint8 *channel_mapping = NULL;
13463 guint32 rate;
13464 guint8 channels;
13465 guint8 channel_mapping_family;
13466 guint8 stream_count;
13467 guint8 coupled_count;
13468 guint8 i;
13469
13470 version = GST_READ_UINT16_BE (stsd_entry_data + 16);
13471 if (version == 1)
13472 dops_data = stsd_entry_data + 51;
13473 else
13474 dops_data = stsd_entry_data + 35;
13475
13476 channels = GST_READ_UINT8 (dops_data + 10);
13477 rate = GST_READ_UINT32_LE (dops_data + 13);
13478 channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
13479 stream_count = GST_READ_UINT8 (dops_data + 20);
13480 coupled_count = GST_READ_UINT8 (dops_data + 21);
13481
13482 if (channels > 0) {
13483 channel_mapping = g_malloc (channels * sizeof (guint8));
13484 for (i = 0; i < channels; i++)
13485 channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
13486 }
13487
13488 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
13489 channel_mapping_family, stream_count, coupled_count,
13490 channel_mapping);
13491 g_free (channel_mapping);
13492 break;
13493 }
13494 default:
13495 break;
13496 }
13497
13498 if (codec) {
13499 GstStructure *s;
13500 gint bitrate = 0;
13501
13502 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13503 GST_TAG_AUDIO_CODEC, codec, NULL);
13504 g_free (codec);
13505 codec = NULL;
13506
13507 /* some bitrate info may have ended up in caps */
13508 s = gst_caps_get_structure (entry->caps, 0);
13509 gst_structure_get_int (s, "bitrate", &bitrate);
13510 if (bitrate > 0)
13511 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13512 GST_TAG_BITRATE, bitrate, NULL);
13513 }
13514
13515 esds = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013516 mp4a = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
13517 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013518 if (stream->protected) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013519 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) == AML_FOURCC_aavd) {
13520 esds = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013521 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013522 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) != AML_FOURCC_enca) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013523 mp4a = NULL;
13524 }
13525 } else {
13526 mp4a = NULL;
13527 }
13528 }
13529
13530 wave = NULL;
13531 if (mp4a) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013532 wave = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013533 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013534 esds = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013535 if (!esds)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013536 esds = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013537 }
13538
13539
13540 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13541 16 bits is a byte-swapped wave-style codec identifier,
13542 and we can find a WAVE header internally to a 'wave' atom here.
13543 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13544 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13545 is big-endian).
13546 */
13547 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13548 if (len < offset + 20) {
13549 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13550 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013551 guint32 datalen = AML_QT_UINT32 (stsd_entry_data + offset + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013552 const guint8 *data = stsd_entry_data + offset + 16;
13553 GNode *wavenode;
13554 GNode *waveheadernode;
13555
13556 wavenode = g_node_new ((guint8 *) data);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013557 if (aml_qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013558 const guint8 *waveheader;
13559 guint32 headerlen;
13560
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013561 waveheadernode = aml_qtdemux_tree_get_child_by_type (wavenode, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000013562 if (waveheadernode) {
13563 waveheader = (const guint8 *) waveheadernode->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013564 headerlen = AML_QT_UINT32 (waveheader);
zengliang.li5f31ef42024-05-16 08:27:38 +000013565
13566 if (headerlen > 8) {
13567 gst_riff_strf_auds *header = NULL;
13568 GstBuffer *headerbuf;
13569 GstBuffer *extra;
13570
13571 waveheader += 8;
13572 headerlen -= 8;
13573
13574 headerbuf = gst_buffer_new_and_alloc (headerlen);
13575 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13576
13577 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13578 headerbuf, &header, &extra)) {
13579 gst_caps_unref (entry->caps);
13580 /* FIXME: Need to do something with the channel reorder map */
13581 entry->caps =
13582 gst_riff_create_audio_caps (header->format, NULL, header,
13583 extra, NULL, NULL, NULL);
13584
13585 if (extra)
13586 gst_buffer_unref (extra);
13587 g_free (header);
13588 }
13589 }
13590 } else
13591 GST_DEBUG ("Didn't find waveheadernode for this codec");
13592 }
13593 g_node_destroy (wavenode);
13594 }
13595 } else if (esds) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013596 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000013597 stream->stream_tags);
13598 } else {
13599 switch (fourcc) {
13600#if 0
13601 /* FIXME: what is in the chunk? */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013602 case AML_FOURCC_QDMC:
zengliang.li5f31ef42024-05-16 08:27:38 +000013603 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013604 gint len = AML_QT_UINT32 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013605
13606 /* seems to be always = 116 = 0x74 */
13607 break;
13608 }
13609#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013610 case AML_FOURCC_QDM2:
zengliang.li5f31ef42024-05-16 08:27:38 +000013611 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013612 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013613
13614 if (len > 0x3C) {
13615 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13616
13617 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13618 gst_caps_set_simple (entry->caps,
13619 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13620 gst_buffer_unref (buf);
13621 }
13622 gst_caps_set_simple (entry->caps,
13623 "samplesize", G_TYPE_INT, samplesize, NULL);
13624 break;
13625 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013626 case AML_FOURCC_alac:
zengliang.li5f31ef42024-05-16 08:27:38 +000013627 {
13628 GNode *alac, *wave = NULL;
13629
13630 /* apparently, m4a has this atom appended directly in the stsd entry,
13631 * while mov has it in a wave atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013632 alac = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013633 if (alac) {
13634 /* alac now refers to stsd entry atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013635 wave = aml_qtdemux_tree_get_child_by_type (alac, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013636 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013637 alac = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013638 else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013639 alac = aml_qtdemux_tree_get_child_by_type (alac, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013640 }
13641 if (alac) {
13642 const guint8 *alac_data = alac->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013643 gint len = AML_QT_UINT32 (alac->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013644 GstBuffer *buf;
13645
13646 if (len < 36) {
13647 GST_DEBUG_OBJECT (qtdemux,
13648 "discarding alac atom with unexpected len %d", len);
13649 } else {
13650 /* codec-data contains alac atom size and prefix,
13651 * ffmpeg likes it that way, not quite gst-ish though ...*/
13652 buf = gst_buffer_new_and_alloc (len);
13653 gst_buffer_fill (buf, 0, alac->data, len);
13654 gst_caps_set_simple (entry->caps,
13655 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13656 gst_buffer_unref (buf);
13657
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013658 entry->bytes_per_frame = AML_QT_UINT32 (alac_data + 12);
13659 entry->n_channels = AML_QT_UINT8 (alac_data + 21);
13660 entry->rate = AML_QT_UINT32 (alac_data + 32);
13661 samplesize = AML_QT_UINT8 (alac_data + 16 + 1);
zengliang.li5f31ef42024-05-16 08:27:38 +000013662 }
13663 }
13664 gst_caps_set_simple (entry->caps,
13665 "samplesize", G_TYPE_INT, samplesize, NULL);
13666 break;
13667 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013668 case AML_FOURCC_fLaC:
zengliang.li5f31ef42024-05-16 08:27:38 +000013669 {
13670 /* The codingname of the sample entry is 'fLaC' */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013671 GNode *flac = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_fLaC);
zengliang.li5f31ef42024-05-16 08:27:38 +000013672
13673 if (flac) {
13674 /* The 'dfLa' box is added to the sample entry to convey
13675 initializing information for the decoder. */
13676 const GNode *dfla =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013677 aml_qtdemux_tree_get_child_by_type (flac, AML_FOURCC_dfLa);
zengliang.li5f31ef42024-05-16 08:27:38 +000013678
13679 if (dfla) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013680 const guint32 len = AML_QT_UINT32 (dfla->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013681
13682 /* Must contain at least dfLa box header (12),
13683 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13684 if (len < 50) {
13685 GST_DEBUG_OBJECT (qtdemux,
13686 "discarding dfla atom with unexpected len %d", len);
13687 } else {
13688 /* skip dfLa header to get the METADATA_BLOCKs */
13689 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13690 const guint32 metadata_blocks_len = len - 12;
13691
13692 gchar *stream_marker = g_strdup ("fLaC");
13693 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13694 strlen (stream_marker));
13695
13696 guint32 index = 0;
13697 guint32 remainder = 0;
13698 guint32 block_size = 0;
13699 gboolean is_last = FALSE;
13700
13701 GValue array = G_VALUE_INIT;
13702 GValue value = G_VALUE_INIT;
13703
13704 g_value_init (&array, GST_TYPE_ARRAY);
13705 g_value_init (&value, GST_TYPE_BUFFER);
13706
13707 gst_value_set_buffer (&value, block);
13708 gst_value_array_append_value (&array, &value);
13709 g_value_reset (&value);
13710
13711 gst_buffer_unref (block);
13712
13713 /* check there's at least one METADATA_BLOCK_HEADER's worth
13714 * of data, and we haven't already finished parsing */
13715 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13716 remainder = metadata_blocks_len - index;
13717
13718 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13719 block_size = 4 +
13720 (metadata_blocks[index + 1] << 16) +
13721 (metadata_blocks[index + 2] << 8) +
13722 metadata_blocks[index + 3];
13723
13724 /* be careful not to read off end of box */
13725 if (block_size > remainder) {
13726 break;
13727 }
13728
13729 is_last = metadata_blocks[index] >> 7;
13730
13731 block = gst_buffer_new_and_alloc (block_size);
13732
13733 gst_buffer_fill (block, 0, &metadata_blocks[index],
13734 block_size);
13735
13736 gst_value_set_buffer (&value, block);
13737 gst_value_array_append_value (&array, &value);
13738 g_value_reset (&value);
13739
13740 gst_buffer_unref (block);
13741
13742 index += block_size;
13743 }
13744
13745 /* only append the metadata if we successfully read all of it */
13746 if (is_last) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013747 gst_structure_set_value (gst_caps_get_structure (AML_CUR_STREAM
zengliang.li5f31ef42024-05-16 08:27:38 +000013748 (stream)->caps, 0), "streamheader", &array);
13749 } else {
13750 GST_WARNING_OBJECT (qtdemux,
13751 "discarding all METADATA_BLOCKs due to invalid "
13752 "block_size %d at idx %d, rem %d", block_size, index,
13753 remainder);
13754 }
13755
13756 g_value_unset (&value);
13757 g_value_unset (&array);
13758
13759 /* The sample rate obtained from the stsd may not be accurate
13760 * since it cannot represent rates greater than 65535Hz, so
13761 * override that value with the sample rate from the
13762 * METADATA_BLOCK_STREAMINFO block */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013763 AML_CUR_STREAM (stream)->rate =
13764 (AML_QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
zengliang.li5f31ef42024-05-16 08:27:38 +000013765 }
13766 }
13767 }
13768 break;
13769 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013770 case AML_FOURCC_sawb:
zengliang.li5f31ef42024-05-16 08:27:38 +000013771 /* Fallthrough! */
13772 amrwb = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013773 case AML_FOURCC_samr:
zengliang.li5f31ef42024-05-16 08:27:38 +000013774 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013775 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013776
13777 if (len > 0x24) {
13778 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13779 guint bitrate;
13780
13781 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13782
13783 /* If we have enough data, let's try to get the 'damr' atom. See
13784 * the 3GPP container spec (26.244) for more details. */
13785 if ((len - 0x34) > 8 &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013786 (bitrate = aml_qtdemux_parse_amr_bitrate (buf, amrwb))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013787 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13788 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13789 }
13790
13791 gst_caps_set_simple (entry->caps,
13792 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13793 gst_buffer_unref (buf);
13794 }
13795 break;
13796 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013797 case AML_FOURCC_mp4a:
zengliang.li5f31ef42024-05-16 08:27:38 +000013798 {
13799 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013800 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013801 guint16 sound_version = 0;
13802 /* FIXME: Can this be determined somehow? There doesn't seem to be
13803 * anything in mp4a atom that specifis compression */
13804 gint profile = 2;
13805 guint16 channels = entry->n_channels;
13806 guint32 time_scale = (guint32) entry->rate;
13807 gint sample_rate_index = -1;
13808
13809 if (len >= 34) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013810 sound_version = AML_QT_UINT16 (stsd_entry_data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013811
13812 if (sound_version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013813 channels = AML_QT_UINT16 (stsd_entry_data + 24);
13814 time_scale = AML_QT_UINT32 (stsd_entry_data + 30);
zengliang.li5f31ef42024-05-16 08:27:38 +000013815 } else {
13816 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13817 sound_version);
13818 }
13819 } else {
13820 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13821 len);
13822 }
13823
13824 sample_rate_index =
13825 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13826 if (sample_rate_index >= 0 && channels > 0) {
13827 guint8 codec_data[2];
13828 GstBuffer *buf;
13829
13830 /* build AAC codec data */
13831 codec_data[0] = profile << 3;
13832 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13833 codec_data[1] = (sample_rate_index & 0x01) << 7;
13834 codec_data[1] |= (channels & 0xF) << 3;
13835
13836 buf = gst_buffer_new_and_alloc (2);
13837 gst_buffer_fill (buf, 0, codec_data, 2);
13838 gst_caps_set_simple (entry->caps,
13839 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13840 gst_buffer_unref (buf);
13841 }
13842 break;
13843 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013844 case AML_FOURCC_lpcm:
13845 case AML_FOURCC_in24:
13846 case AML_FOURCC_in32:
13847 case AML_FOURCC_fl32:
13848 case AML_FOURCC_fl64:
13849 case AML_FOURCC_s16l:
zengliang.li5f31ef42024-05-16 08:27:38 +000013850 /* Fully handled elsewhere */
13851 break;
13852 default:
13853 GST_INFO_OBJECT (qtdemux,
13854 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13855 break;
13856 }
13857 }
13858 GST_INFO_OBJECT (qtdemux,
13859 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13860 GST_FOURCC_ARGS (fourcc), entry->caps);
13861
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013862 } else if (stream->subtype == AML_FOURCC_strm) {
13863 if (fourcc == AML_FOURCC_rtsp) {
13864 stream->redirect_uri = aml_qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
zengliang.li5f31ef42024-05-16 08:27:38 +000013865 } else {
13866 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
13867 GST_FOURCC_ARGS (fourcc));
13868 goto unknown_stream;
13869 }
13870 entry->sampled = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013871 } else if (stream->subtype == AML_FOURCC_subp || stream->subtype == AML_FOURCC_text
13872 || stream->subtype == AML_FOURCC_sbtl || stream->subtype == AML_FOURCC_subt
13873 || stream->subtype == AML_FOURCC_clcp || stream->subtype == AML_FOURCC_wvtt) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013874
13875 entry->sampled = TRUE;
13876 entry->sparse = TRUE;
13877
13878 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013879 aml_qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000013880 &codec);
13881 if (codec) {
13882 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13883 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13884 g_free (codec);
13885 codec = NULL;
13886 }
13887
13888 /* hunt for sort-of codec data */
13889 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013890 case AML_FOURCC_mp4s:
zengliang.li5f31ef42024-05-16 08:27:38 +000013891 {
13892 GNode *mp4s = NULL;
13893 GNode *esds = NULL;
13894
13895 /* look for palette in a stsd->mp4s->esds sub-atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013896 mp4s = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_mp4s);
zengliang.li5f31ef42024-05-16 08:27:38 +000013897 if (mp4s)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013898 esds = aml_qtdemux_tree_get_child_by_type (mp4s, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013899 if (esds == NULL) {
13900 /* Invalid STSD */
13901 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13902 break;
13903 }
13904
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013905 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000013906 stream->stream_tags);
13907 break;
13908 }
13909 default:
13910 GST_INFO_OBJECT (qtdemux,
13911 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13912 break;
13913 }
13914 GST_INFO_OBJECT (qtdemux,
13915 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13916 GST_FOURCC_ARGS (fourcc), entry->caps);
13917 } else {
13918 /* everything in 1 sample */
13919 entry->sampled = TRUE;
13920
13921 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013922 aml_qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000013923 &codec);
13924
13925 if (entry->caps == NULL)
13926 goto unknown_stream;
13927
13928 if (codec) {
13929 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13930 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13931 g_free (codec);
13932 codec = NULL;
13933 }
13934 }
13935
13936 /* promote to sampled format */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013937 if (entry->fourcc == AML_FOURCC_samr) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013938 /* force mono 8000 Hz for AMR */
13939 entry->sampled = TRUE;
13940 entry->n_channels = 1;
13941 entry->rate = 8000;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013942 } else if (entry->fourcc == AML_FOURCC_sawb) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013943 /* force mono 16000 Hz for AMR-WB */
13944 entry->sampled = TRUE;
13945 entry->n_channels = 1;
13946 entry->rate = 16000;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013947 } else if (entry->fourcc == AML_FOURCC_mp4a) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013948 entry->sampled = TRUE;
13949 }
13950
13951
13952 stsd_entry_data += len;
13953 remaining_stsd_len -= len;
13954
13955 }
13956
13957 /* collect sample information */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013958 if (!aml_qtdemux_stbl_init (qtdemux, stream, stbl))
zengliang.li5f31ef42024-05-16 08:27:38 +000013959 goto samples_failed;
13960
13961 if (qtdemux->fragmented) {
13962 guint64 offset;
13963
13964 /* need all moov samples as basis; probably not many if any at all */
13965 /* prevent moof parsing taking of at this time */
13966 offset = qtdemux->moof_offset;
13967 qtdemux->moof_offset = 0;
13968 if (stream->n_samples &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013969 !aml_qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013970 qtdemux->moof_offset = offset;
13971 goto samples_failed;
13972 }
13973 qtdemux->moof_offset = offset;
13974 /* movie duration more reliable in this case (e.g. mehd) */
13975 if (qtdemux->segment.duration &&
13976 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13977 stream->duration =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013978 AML_GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
zengliang.li5f31ef42024-05-16 08:27:38 +000013979 }
13980
13981 /* configure segments */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013982 if (!aml_qtdemux_parse_segments (qtdemux, stream, trak))
zengliang.li5f31ef42024-05-16 08:27:38 +000013983 goto segments_failed;
13984
13985 /* add some language tag, if useful */
13986 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13987 strcmp (stream->lang_id, "und")) {
13988 const gchar *lang_code;
13989
13990 /* convert ISO 639-2 code to ISO 639-1 */
13991 lang_code = gst_tag_get_language_code (stream->lang_id);
13992 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13993 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
13994 }
13995
13996 /* Check for UDTA tags */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013997 if ((udta = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_udta))) {
13998 aml_qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000013999 }
14000
14001 /* Insert and sort new stream in track-id order.
14002 * This will help in comparing old/new streams during stream update check */
14003 g_ptr_array_add (qtdemux->active_streams, stream);
14004 g_ptr_array_sort (qtdemux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014005 (GCompareFunc) aml_qtdemux_track_id_compare_func);
zengliang.li5f31ef42024-05-16 08:27:38 +000014006 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014007 AML_QTDEMUX_N_STREAMS (qtdemux));
zengliang.li5f31ef42024-05-16 08:27:38 +000014008
14009 return TRUE;
14010
14011/* ERRORS */
14012corrupt_file:
14013 {
14014 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
14015 (_("This file is corrupt and cannot be played.")), (NULL));
14016 if (stream)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014017 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014018 return FALSE;
14019 }
14020error_encrypted:
14021 {
14022 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014023 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014024 return FALSE;
14025 }
14026samples_failed:
14027segments_failed:
14028 {
14029 /* we posted an error already */
14030 /* free stbl sub-atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014031 gst_aml_qtdemux_stbl_free (stream);
14032 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014033 return FALSE;
14034 }
14035existing_stream:
14036 {
14037 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
14038 track_id);
14039 return TRUE;
14040 }
14041unknown_stream:
14042 {
14043 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
14044 GST_FOURCC_ARGS (stream->subtype));
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014045 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014046 return TRUE;
14047 }
14048}
14049
14050/* If we can estimate the overall bitrate, and don't have information about the
14051 * stream bitrate for exactly one stream, this guesses the stream bitrate as
14052 * the overall bitrate minus the sum of the bitrates of all other streams. This
14053 * should be useful for the common case where we have one audio and one video
14054 * stream and can estimate the bitrate of one, but not the other. */
14055static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014056gst_aml_qtdemux_guess_bitrate (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014057{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014058 AmlQtDemuxStream *stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000014059 gint64 size, sys_bitrate, sum_bitrate = 0;
14060 GstClockTime duration;
14061 guint bitrate;
14062 gint i;
14063
14064 if (qtdemux->fragmented)
14065 return;
14066
14067 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
14068
14069 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
14070 || size <= 0) {
14071 GST_DEBUG_OBJECT (qtdemux,
14072 "Size in bytes of the stream not known - bailing");
14073 return;
14074 }
14075
14076 /* Subtract the header size */
14077 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
14078 size, qtdemux->header_size);
14079
14080 if (size < qtdemux->header_size)
14081 return;
14082
14083 size = size - qtdemux->header_size;
14084
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014085 if (!gst_aml_qtdemux_get_duration (qtdemux, &duration)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014086 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
14087 return;
14088 }
14089
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014090 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14091 AmlQtDemuxStream *str = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014092 switch (str->subtype) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014093 case AML_FOURCC_soun:
14094 case AML_FOURCC_vide:
zengliang.li5f31ef42024-05-16 08:27:38 +000014095 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014096 AML_CUR_STREAM (str)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +000014097 /* retrieve bitrate, prefer avg then max */
14098 bitrate = 0;
14099 if (str->stream_tags) {
14100 if (gst_tag_list_get_uint (str->stream_tags,
14101 GST_TAG_MAXIMUM_BITRATE, &bitrate))
14102 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
14103 if (gst_tag_list_get_uint (str->stream_tags,
14104 GST_TAG_NOMINAL_BITRATE, &bitrate))
14105 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
14106 if (gst_tag_list_get_uint (str->stream_tags,
14107 GST_TAG_BITRATE, &bitrate))
14108 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
14109 }
14110 if (bitrate)
14111 sum_bitrate += bitrate;
14112 else {
14113 if (stream) {
14114 GST_DEBUG_OBJECT (qtdemux,
14115 ">1 stream with unknown bitrate - bailing");
14116 return;
14117 } else
14118 stream = str;
14119 }
14120
14121 default:
14122 /* For other subtypes, we assume no significant impact on bitrate */
14123 break;
14124 }
14125 }
14126
14127 if (!stream) {
14128 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
14129 return;
14130 }
14131
14132 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
14133
14134 if (sys_bitrate < sum_bitrate) {
14135 /* This can happen, since sum_bitrate might be derived from maximum
14136 * bitrates and not average bitrates */
14137 GST_DEBUG_OBJECT (qtdemux,
14138 "System bitrate less than sum bitrate - bailing");
14139 return;
14140 }
14141
14142 bitrate = sys_bitrate - sum_bitrate;
14143 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
14144 ", Stream bitrate = %u", sys_bitrate, bitrate);
14145
14146 if (!stream->stream_tags)
14147 stream->stream_tags = gst_tag_list_new_empty ();
14148 else
14149 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
14150
14151 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14152 GST_TAG_BITRATE, bitrate, NULL);
14153}
14154
14155static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014156aml_qtdemux_prepare_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014157{
14158 GstFlowReturn ret = GST_FLOW_OK;
14159 gint i;
14160
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014161 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", AML_QTDEMUX_N_STREAMS (qtdemux));
zengliang.li5f31ef42024-05-16 08:27:38 +000014162
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014163 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14164 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014165 guint32 sample_num = 0;
14166
14167 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014168 stream->track_id, GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc));
zengliang.li5f31ef42024-05-16 08:27:38 +000014169
14170 if (qtdemux->fragmented && qtdemux->pullbased) {
14171 /* need all moov samples first */
14172 GST_OBJECT_LOCK (qtdemux);
14173 while (stream->n_samples == 0)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014174 if ((ret = aml_qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
zengliang.li5f31ef42024-05-16 08:27:38 +000014175 break;
14176 GST_OBJECT_UNLOCK (qtdemux);
14177 } else {
14178 /* discard any stray moof */
14179 qtdemux->moof_offset = 0;
14180 }
14181
14182 /* prepare braking */
14183 if (ret != GST_FLOW_ERROR)
14184 ret = GST_FLOW_OK;
14185
14186 /* in pull mode, we should have parsed some sample info by now;
14187 * and quite some code will not handle no samples.
14188 * in push mode, we'll just have to deal with it */
14189 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
14190 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
14191 g_ptr_array_remove_index (qtdemux->active_streams, i);
14192 i--;
14193 continue;
14194 } else if (stream->track_id == qtdemux->chapters_track_id &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014195 (stream->subtype == AML_FOURCC_text || stream->subtype == AML_FOURCC_sbtl)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014196 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
14197 so that it doesn't look like a subtitle track */
14198 g_ptr_array_remove_index (qtdemux->active_streams, i);
14199 i--;
14200 continue;
14201 }
14202
14203 /* parse the initial sample for use in setting the frame rate cap */
14204 while (sample_num == 0 && sample_num < stream->n_samples) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014205 if (!aml_qtdemux_parse_samples (qtdemux, stream, sample_num))
zengliang.li5f31ef42024-05-16 08:27:38 +000014206 break;
14207 ++sample_num;
14208 }
14209 }
14210
14211 return ret;
14212}
14213
14214static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014215_aml_stream_equal_func (const AmlQtDemuxStream * stream, const gchar * stream_id)
zengliang.li5f31ef42024-05-16 08:27:38 +000014216{
14217 return g_strcmp0 (stream->stream_id, stream_id) == 0;
14218}
14219
14220static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014221aml_qtdemux_is_streams_update (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014222{
14223 gint i;
14224
14225 /* Different length, updated */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014226 if (AML_QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
zengliang.li5f31ef42024-05-16 08:27:38 +000014227 return TRUE;
14228
14229 /* streams in list are sorted in track-id order */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014230 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014231 /* Different stream-id, updated */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014232 if (g_strcmp0 (AML_QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
14233 AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
zengliang.li5f31ef42024-05-16 08:27:38 +000014234 return TRUE;
14235 }
14236
14237 return FALSE;
14238}
14239
14240static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014241aml_qtdemux_reuse_and_configure_stream (GstAmlQTDemux * qtdemux,
14242 AmlQtDemuxStream * oldstream, AmlQtDemuxStream * newstream)
zengliang.li5f31ef42024-05-16 08:27:38 +000014243{
14244 /* Connect old stream's srcpad to new stream */
14245 newstream->pad = oldstream->pad;
14246 oldstream->pad = NULL;
14247
14248 /* unset new_stream to prevent stream-start event, unless we are EOS in which
14249 * case we need to force one through */
14250 newstream->new_stream = newstream->pad != NULL
14251 && GST_PAD_IS_EOS (newstream->pad);
14252
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014253 return gst_aml_qtdemux_configure_stream (qtdemux, newstream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014254}
14255
14256static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014257aml_qtdemux_update_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014258{
14259 gint i;
14260 g_assert (qtdemux->streams_aware);
14261
14262 /* At below, figure out which stream in active_streams has identical stream-id
14263 * with that of in old_streams. If there is matching stream-id,
14264 * corresponding newstream will not be exposed again,
14265 * but demux will reuse srcpad of matched old stream
14266 *
14267 * active_streams : newly created streams from the latest moov
14268 * old_streams : existing streams (belong to previous moov)
14269 */
14270
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014271 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14272 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
14273 AmlQtDemuxStream *oldstream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000014274 guint target;
14275
14276 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014277 stream->track_id, GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc));
zengliang.li5f31ef42024-05-16 08:27:38 +000014278
14279 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014280 stream->stream_id, (GEqualFunc) _aml_stream_equal_func, &target)) {
14281 oldstream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
zengliang.li5f31ef42024-05-16 08:27:38 +000014282
14283 /* null pad stream cannot be reused */
14284 if (oldstream->pad == NULL)
14285 oldstream = NULL;
14286 }
14287
14288 if (oldstream) {
14289 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
14290
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014291 if (!aml_qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
zengliang.li5f31ef42024-05-16 08:27:38 +000014292 return FALSE;
14293
14294 /* we don't need to preserve order of old streams */
14295 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
14296 } else {
14297 GstTagList *list;
14298
14299 /* now we have all info and can expose */
14300 list = stream->stream_tags;
14301 stream->stream_tags = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014302 if (!gst_aml_qtdemux_add_stream (qtdemux, stream, list))
zengliang.li5f31ef42024-05-16 08:27:38 +000014303 return FALSE;
14304 }
14305 }
14306
14307 return TRUE;
14308}
14309
14310/* Must be called with expose lock */
14311static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014312aml_qtdemux_expose_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014313{
14314 gint i;
14315
14316 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
14317
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014318 if (!aml_qtdemux_is_streams_update (qtdemux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014319 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014320 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14321 AmlQtDemuxStream *new_stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
14322 AmlQtDemuxStream *old_stream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14323 if (!aml_qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
zengliang.li5f31ef42024-05-16 08:27:38 +000014324 return GST_FLOW_ERROR;
14325 }
14326
14327 g_ptr_array_set_size (qtdemux->old_streams, 0);
14328 qtdemux->need_segment = TRUE;
14329
14330 return GST_FLOW_OK;
14331 }
14332
14333 if (qtdemux->streams_aware) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014334 if (!aml_qtdemux_update_streams (qtdemux))
zengliang.li5f31ef42024-05-16 08:27:38 +000014335 return GST_FLOW_ERROR;
14336 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014337 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14338 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014339 GstTagList *list;
14340
14341 /* now we have all info and can expose */
14342 list = stream->stream_tags;
14343 stream->stream_tags = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014344 if (!gst_aml_qtdemux_add_stream (qtdemux, stream, list))
zengliang.li5f31ef42024-05-16 08:27:38 +000014345 return GST_FLOW_ERROR;
14346
14347 }
14348 }
14349
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014350 gst_aml_qtdemux_guess_bitrate (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014351
14352 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14353
14354 /* If we have still old_streams, it's no more used stream */
14355 for (i = 0; i < qtdemux->old_streams->len; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014356 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014357
14358 if (stream->pad) {
14359 GstEvent *event;
14360
14361 event = gst_event_new_eos ();
14362 if (qtdemux->segment_seqnum)
14363 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14364
14365 gst_pad_push_event (stream->pad, event);
14366 }
14367 }
14368
14369 g_ptr_array_set_size (qtdemux->old_streams, 0);
14370
14371 /* check if we should post a redirect in case there is a single trak
14372 * and it is a redirecting trak */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014373 if (AML_QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14374 AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014375 GstMessage *m;
14376
14377 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14378 "an external content");
14379 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14380 gst_structure_new ("redirect",
14381 "new-location", G_TYPE_STRING,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014382 AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
zengliang.li5f31ef42024-05-16 08:27:38 +000014383 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14384 g_free (qtdemux->redirect_location);
14385 qtdemux->redirect_location =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014386 g_strdup (AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
zengliang.li5f31ef42024-05-16 08:27:38 +000014387 }
14388
14389 g_ptr_array_foreach (qtdemux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014390 (GFunc) aml_qtdemux_do_allocation, qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014391
14392 qtdemux->need_segment = TRUE;
14393
14394 qtdemux->exposed = TRUE;
14395 return GST_FLOW_OK;
14396}
14397
14398typedef struct
14399{
14400 GstStructure *structure; /* helper for sort function */
14401 gchar *location;
14402 guint min_req_bitrate;
14403 guint min_req_qt_version;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014404} GstAmlQtReference;
zengliang.li5f31ef42024-05-16 08:27:38 +000014405
14406static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014407aml_qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
zengliang.li5f31ef42024-05-16 08:27:38 +000014408{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014409 GstAmlQtReference *ref_a = (GstAmlQtReference *) a;
14410 GstAmlQtReference *ref_b = (GstAmlQtReference *) b;
zengliang.li5f31ef42024-05-16 08:27:38 +000014411
14412 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14413 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14414
14415 /* known bitrates go before unknown; higher bitrates go first */
14416 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14417}
14418
14419/* sort the redirects and post a message for the application.
14420 */
14421static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014422aml_qtdemux_process_redirects (GstAmlQTDemux * qtdemux, GList * references)
zengliang.li5f31ef42024-05-16 08:27:38 +000014423{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014424 GstAmlQtReference *best;
zengliang.li5f31ef42024-05-16 08:27:38 +000014425 GstStructure *s;
14426 GstMessage *msg;
14427 GValue list_val = { 0, };
14428 GList *l;
14429
14430 g_assert (references != NULL);
14431
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014432 references = g_list_sort (references, aml_qtdemux_redirects_sort_func);
zengliang.li5f31ef42024-05-16 08:27:38 +000014433
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014434 best = (GstAmlQtReference *) references->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014435
14436 g_value_init (&list_val, GST_TYPE_LIST);
14437
14438 for (l = references; l != NULL; l = l->next) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014439 GstAmlQtReference *ref = (GstAmlQtReference *) l->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014440 GValue struct_val = { 0, };
14441
14442 ref->structure = gst_structure_new ("redirect",
14443 "new-location", G_TYPE_STRING, ref->location, NULL);
14444
14445 if (ref->min_req_bitrate > 0) {
14446 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14447 ref->min_req_bitrate, NULL);
14448 }
14449
14450 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14451 g_value_set_boxed (&struct_val, ref->structure);
14452 gst_value_list_append_value (&list_val, &struct_val);
14453 g_value_unset (&struct_val);
14454 /* don't free anything here yet, since we need best->structure below */
14455 }
14456
14457 g_assert (best != NULL);
14458 s = gst_structure_copy (best->structure);
14459
14460 if (g_list_length (references) > 1) {
14461 gst_structure_set_value (s, "locations", &list_val);
14462 }
14463
14464 g_value_unset (&list_val);
14465
14466 for (l = references; l != NULL; l = l->next) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014467 GstAmlQtReference *ref = (GstAmlQtReference *) l->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014468
14469 gst_structure_free (ref->structure);
14470 g_free (ref->location);
14471 g_free (ref);
14472 }
14473 g_list_free (references);
14474
14475 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14476 g_free (qtdemux->redirect_location);
14477 qtdemux->redirect_location =
14478 g_strdup (gst_structure_get_string (s, "new-location"));
14479 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14480 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14481}
14482
14483/* look for redirect nodes, collect all redirect information and
14484 * process it.
14485 */
14486static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014487aml_qtdemux_parse_redirects (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014488{
14489 GNode *rmra, *rmda, *rdrf;
14490
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014491 rmra = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_rmra);
zengliang.li5f31ef42024-05-16 08:27:38 +000014492 if (rmra) {
14493 GList *redirects = NULL;
14494
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014495 rmda = aml_qtdemux_tree_get_child_by_type (rmra, AML_FOURCC_rmda);
zengliang.li5f31ef42024-05-16 08:27:38 +000014496 while (rmda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014497 GstAmlQtReference ref = { NULL, NULL, 0, 0 };
zengliang.li5f31ef42024-05-16 08:27:38 +000014498 GNode *rmdr, *rmvc;
14499
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014500 if ((rmdr = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rmdr))) {
14501 ref.min_req_bitrate = AML_QT_UINT32 ((guint8 *) rmdr->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000014502 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14503 ref.min_req_bitrate);
14504 }
14505
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014506 if ((rmvc = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rmvc))) {
14507 guint32 package = AML_QT_FOURCC ((guint8 *) rmvc->data + 12);
14508 guint version = AML_QT_UINT32 ((guint8 *) rmvc->data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000014509
14510#ifndef GST_DISABLE_GST_DEBUG
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014511 guint bitmask = AML_QT_UINT32 ((guint8 *) rmvc->data + 20);
zengliang.li5f31ef42024-05-16 08:27:38 +000014512#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014513 guint check_type = AML_QT_UINT16 ((guint8 *) rmvc->data + 24);
zengliang.li5f31ef42024-05-16 08:27:38 +000014514
14515 GST_LOG_OBJECT (qtdemux,
14516 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14517 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14518 bitmask, check_type);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014519 if (package == AML_FOURCC_qtim && check_type == 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014520 ref.min_req_qt_version = version;
14521 }
14522 }
14523
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014524 rdrf = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rdrf);
zengliang.li5f31ef42024-05-16 08:27:38 +000014525 if (rdrf) {
14526 guint32 ref_type;
14527 guint8 *ref_data;
14528 guint ref_len;
14529
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014530 ref_len = AML_QT_UINT32 ((guint8 *) rdrf->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014531 if (ref_len > 20) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014532 ref_type = AML_QT_FOURCC ((guint8 *) rdrf->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000014533 ref_data = (guint8 *) rdrf->data + 20;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014534 if (ref_type == AML_FOURCC_alis) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014535 guint record_len, record_version, fn_len;
14536
14537 if (ref_len > 70) {
14538 /* MacOSX alias record, google for alias-layout.txt */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014539 record_len = AML_QT_UINT16 (ref_data + 4);
14540 record_version = AML_QT_UINT16 (ref_data + 4 + 2);
14541 fn_len = AML_QT_UINT8 (ref_data + 50);
zengliang.li5f31ef42024-05-16 08:27:38 +000014542 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14543 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14544 }
14545 } else {
14546 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14547 ref_len);
14548 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014549 } else if (ref_type == AML_FOURCC_url_) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014550 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14551 } else {
14552 GST_DEBUG_OBJECT (qtdemux,
14553 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14554 GST_FOURCC_ARGS (ref_type));
14555 }
14556 if (ref.location != NULL) {
14557 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14558 redirects =
14559 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14560 } else {
14561 GST_WARNING_OBJECT (qtdemux,
14562 "Failed to extract redirect location from rdrf atom");
14563 }
14564 } else {
14565 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14566 }
14567 }
14568
14569 /* look for others */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014570 rmda = aml_qtdemux_tree_get_sibling_by_type (rmda, AML_FOURCC_rmda);
zengliang.li5f31ef42024-05-16 08:27:38 +000014571 }
14572
14573 if (redirects != NULL) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014574 aml_qtdemux_process_redirects (qtdemux, redirects);
zengliang.li5f31ef42024-05-16 08:27:38 +000014575 }
14576 }
14577 return TRUE;
14578}
14579
14580static GstTagList *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014581aml_qtdemux_add_container_format (GstAmlQTDemux * qtdemux, GstTagList * tags)
zengliang.li5f31ef42024-05-16 08:27:38 +000014582{
14583 const gchar *fmt;
14584
14585 if (tags == NULL) {
14586 tags = gst_tag_list_new_empty ();
14587 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14588 }
14589
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014590 if (qtdemux->major_brand == AML_FOURCC_mjp2)
zengliang.li5f31ef42024-05-16 08:27:38 +000014591 fmt = "Motion JPEG 2000";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014592 else if ((qtdemux->major_brand & 0xffff) == AML_FOURCC_3g__)
zengliang.li5f31ef42024-05-16 08:27:38 +000014593 fmt = "3GP";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014594 else if (qtdemux->major_brand == AML_FOURCC_qt__)
zengliang.li5f31ef42024-05-16 08:27:38 +000014595 fmt = "Quicktime";
14596 else if (qtdemux->fragmented)
14597 fmt = "ISO fMP4";
14598 else
14599 fmt = "ISO MP4/M4A";
14600
14601 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14602 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14603
14604 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14605 fmt, NULL);
14606
14607 return tags;
14608}
14609
14610/* we have read the complete moov node now.
14611 * This function parses all of the relevant info, creates the traks and
14612 * prepares all data structures for playback
14613 */
14614static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014615aml_qtdemux_parse_tree (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014616{
14617 GNode *mvhd;
14618 GNode *trak;
14619 GNode *udta;
14620 GNode *mvex;
14621 GNode *pssh;
14622 guint64 creation_time;
14623 GstDateTime *datetime = NULL;
14624 gint version;
14625
14626 /* make sure we have a usable taglist */
14627 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14628
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014629 mvhd = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_mvhd);
zengliang.li5f31ef42024-05-16 08:27:38 +000014630 if (mvhd == NULL) {
14631 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014632 return aml_qtdemux_parse_redirects (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014633 }
14634
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014635 version = AML_QT_UINT8 ((guint8 *) mvhd->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000014636 if (version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014637 creation_time = AML_QT_UINT64 ((guint8 *) mvhd->data + 12);
14638 qtdemux->timescale = AML_QT_UINT32 ((guint8 *) mvhd->data + 28);
14639 qtdemux->duration = AML_QT_UINT64 ((guint8 *) mvhd->data + 32);
zengliang.li5f31ef42024-05-16 08:27:38 +000014640 } else if (version == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014641 creation_time = AML_QT_UINT32 ((guint8 *) mvhd->data + 12);
14642 qtdemux->timescale = AML_QT_UINT32 ((guint8 *) mvhd->data + 20);
14643 qtdemux->duration = AML_QT_UINT32 ((guint8 *) mvhd->data + 24);
zengliang.li5f31ef42024-05-16 08:27:38 +000014644 } else {
14645 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14646 return FALSE;
14647 }
14648
14649 /* Moving qt creation time (secs since 1904) to unix time */
14650 if (creation_time != 0) {
14651 /* Try to use epoch first as it should be faster and more commonly found */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014652 if (creation_time >= AML_QTDEMUX_SECONDS_FROM_1904_TO_1970) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014653 gint64 now_s;
14654
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014655 creation_time -= AML_QTDEMUX_SECONDS_FROM_1904_TO_1970;
zengliang.li5f31ef42024-05-16 08:27:38 +000014656 /* some data cleansing sanity */
14657 now_s = g_get_real_time () / G_USEC_PER_SEC;
14658 if (now_s + 24 * 3600 < creation_time) {
14659 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14660 } else {
14661 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14662 }
14663 } else {
14664 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14665 GDateTime *dt, *dt_local;
14666
14667 dt = g_date_time_add_seconds (base_dt, creation_time);
14668 dt_local = g_date_time_to_local (dt);
14669 datetime = gst_date_time_new_from_g_date_time (dt_local);
14670
14671 g_date_time_unref (base_dt);
14672 g_date_time_unref (dt);
14673 }
14674 }
14675 if (datetime) {
14676 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14677 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14678 datetime, NULL);
14679 gst_date_time_unref (datetime);
14680 }
14681
14682 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14683 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14684
14685 /* check for fragmented file and get some (default) data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014686 mvex = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_mvex);
zengliang.li5f31ef42024-05-16 08:27:38 +000014687 if (mvex) {
14688 GNode *mehd;
14689 GstByteReader mehd_data;
14690
14691 /* let track parsing or anyone know weird stuff might happen ... */
14692 qtdemux->fragmented = TRUE;
14693
14694 /* compensate for total duration */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014695 mehd = aml_qtdemux_tree_get_child_by_type_full (mvex, AML_FOURCC_mehd, &mehd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014696 if (mehd)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014697 aml_qtdemux_parse_mehd (qtdemux, &mehd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014698 }
14699
14700 /* Update the movie segment duration, unless it was directly given to us
14701 * by upstream. Otherwise let it as is, as we don't want to mangle the
14702 * duration provided by upstream that may come e.g. from a MPD file. */
14703 if (!qtdemux->upstream_format_is_time) {
14704 GstClockTime duration;
14705 /* set duration in the segment info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014706 gst_aml_qtdemux_get_duration (qtdemux, &duration);
zengliang.li5f31ef42024-05-16 08:27:38 +000014707 qtdemux->segment.duration = duration;
14708 /* also do not exceed duration; stop is set that way post seek anyway,
14709 * and segment activation falls back to duration,
14710 * whereas loop only checks stop, so let's align this here as well */
14711 qtdemux->segment.stop = duration;
14712 }
14713
14714 /* parse all traks */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014715 trak = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014716 while (trak) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014717 aml_qtdemux_parse_trak (qtdemux, trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014718 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014719 trak = aml_qtdemux_tree_get_sibling_by_type (trak, AML_FOURCC_trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014720 }
14721
14722 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14723
14724 /* find tags */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014725 udta = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014726 if (udta) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014727 aml_qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014728 } else {
14729 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14730 }
14731
14732 /* maybe also some tags in meta box */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014733 udta = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_meta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014734 if (udta) {
14735 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014736 aml_qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014737 } else {
14738 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14739 }
14740
14741 /* parse any protection system info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014742 pssh = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +000014743 while (pssh) {
14744 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014745 aml_qtdemux_parse_pssh (qtdemux, pssh);
14746 pssh = aml_qtdemux_tree_get_sibling_by_type (pssh, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +000014747 }
14748
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014749 qtdemux->tag_list = aml_qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
zengliang.li5f31ef42024-05-16 08:27:38 +000014750
14751 return TRUE;
14752}
14753
14754/* taken from ffmpeg */
14755static int
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014756aml_read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
zengliang.li5f31ef42024-05-16 08:27:38 +000014757{
14758 int count = 4;
14759 int len = 0;
14760
14761 while (count--) {
14762 int c;
14763
14764 if (ptr >= end)
14765 return -1;
14766
14767 c = *ptr++;
14768 len = (len << 7) | (c & 0x7f);
14769 if (!(c & 0x80))
14770 break;
14771 }
14772 *end_out = ptr;
14773 return len;
14774}
14775
14776static GList *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014777aml_parse_xiph_stream_headers (GstAmlQTDemux * qtdemux, gpointer codec_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000014778 gsize codec_data_size)
14779{
14780 GList *list = NULL;
14781 guint8 *p = codec_data;
14782 gint i, offset, num_packets;
14783 guint *length, last;
14784
14785 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14786
14787 if (codec_data == NULL || codec_data_size == 0)
14788 goto error;
14789
14790 /* start of the stream and vorbis audio or theora video, need to
14791 * send the codec_priv data as first three packets */
14792 num_packets = p[0] + 1;
14793 GST_DEBUG_OBJECT (qtdemux,
14794 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14795 (guint) num_packets, codec_data_size);
14796
14797 /* Let's put some limits, Don't think there even is a xiph codec
14798 * with more than 3-4 headers */
14799 if (G_UNLIKELY (num_packets > 16)) {
14800 GST_WARNING_OBJECT (qtdemux,
14801 "Unlikely number of xiph headers, most likely not valid");
14802 goto error;
14803 }
14804
14805 length = g_alloca (num_packets * sizeof (guint));
14806 last = 0;
14807 offset = 1;
14808
14809 /* first packets, read length values */
14810 for (i = 0; i < num_packets - 1; i++) {
14811 length[i] = 0;
14812 while (offset < codec_data_size) {
14813 length[i] += p[offset];
14814 if (p[offset++] != 0xff)
14815 break;
14816 }
14817 last += length[i];
14818 }
14819 if (offset + last > codec_data_size)
14820 goto error;
14821
14822 /* last packet is the remaining size */
14823 length[i] = codec_data_size - offset - last;
14824
14825 for (i = 0; i < num_packets; i++) {
14826 GstBuffer *hdr;
14827
14828 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14829
14830 if (offset + length[i] > codec_data_size)
14831 goto error;
14832
bo.xiao86472382024-07-23 10:24:15 +080014833#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
14834 hdr = gst_buffer_new_memdup (p + offset, length[i]);
14835#else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014836 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
bo.xiao86472382024-07-23 10:24:15 +080014837#endif
14838
zengliang.li5f31ef42024-05-16 08:27:38 +000014839 list = g_list_append (list, hdr);
14840
14841 offset += length[i];
14842 }
14843
14844 return list;
14845
14846 /* ERRORS */
14847error:
14848 {
14849 if (list != NULL)
14850 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14851 return NULL;
14852 }
14853
14854}
14855
14856/* this can change the codec originally present in @list */
14857static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014858gst_aml_qtdemux_handle_esds (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
14859 AmlQtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
zengliang.li5f31ef42024-05-16 08:27:38 +000014860{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014861 int len = AML_QT_UINT32 (esds->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014862 guint8 *ptr = esds->data;
14863 guint8 *end = ptr + len;
14864 int tag;
14865 guint8 *data_ptr = NULL;
14866 int data_len = 0;
14867 guint8 object_type_id = 0;
14868 guint8 stream_type = 0;
14869 const char *codec_name = NULL;
14870 GstCaps *caps = NULL;
14871
14872 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14873 ptr += 8;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014874 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", AML_QT_UINT32 (ptr));
zengliang.li5f31ef42024-05-16 08:27:38 +000014875 ptr += 4;
14876 while (ptr + 1 < end) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014877 tag = AML_QT_UINT8 (ptr);
zengliang.li5f31ef42024-05-16 08:27:38 +000014878 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14879 ptr++;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014880 len = aml_read_descr_size (ptr, end, &ptr);
zengliang.li5f31ef42024-05-16 08:27:38 +000014881 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14882
14883 /* Check the stated amount of data is available for reading */
14884 if (len < 0 || ptr + len > end)
14885 break;
14886
14887 switch (tag) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014888 case AML_ES_DESCRIPTOR_TAG:
14889 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", AML_QT_UINT16 (ptr));
14890 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", AML_QT_UINT8 (ptr + 2));
zengliang.li5f31ef42024-05-16 08:27:38 +000014891 ptr += 3;
14892 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014893 case AML_DECODER_CONFIG_DESC_TAG:{
zengliang.li5f31ef42024-05-16 08:27:38 +000014894 guint max_bitrate, avg_bitrate;
14895
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014896 object_type_id = AML_QT_UINT8 (ptr);
14897 stream_type = AML_QT_UINT8 (ptr + 1) >> 2;
14898 max_bitrate = AML_QT_UINT32 (ptr + 5);
14899 avg_bitrate = AML_QT_UINT32 (ptr + 9);
zengliang.li5f31ef42024-05-16 08:27:38 +000014900 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14901 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014902 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", AML_QT_UINT24 (ptr + 2));
zengliang.li5f31ef42024-05-16 08:27:38 +000014903 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14904 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14905 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14906 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14907 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14908 }
14909 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14910 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14911 avg_bitrate, NULL);
14912 }
14913 ptr += 13;
14914 break;
14915 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014916 case AML_DECODER_SPECIFIC_INFO_TAG:
zengliang.li5f31ef42024-05-16 08:27:38 +000014917 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14918 if (object_type_id == 0xe0 && len == 0x40) {
14919 guint8 *data;
14920 GstStructure *s;
14921 guint32 clut[16];
14922 gint i;
14923
14924 GST_DEBUG_OBJECT (qtdemux,
14925 "Have VOBSUB palette. Creating palette event");
14926 /* move to decConfigDescr data and read palette */
14927 data = ptr;
14928 for (i = 0; i < 16; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014929 clut[i] = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014930 data += 4;
14931 }
14932
14933 s = gst_structure_new ("application/x-gst-dvd", "event",
14934 G_TYPE_STRING, "dvd-spu-clut-change",
14935 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14936 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14937 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14938 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14939 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14940 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14941 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14942 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14943 NULL);
14944
14945 /* store event and trigger custom processing */
14946 stream->pending_event =
14947 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14948 } else {
14949 /* Generic codec_data handler puts it on the caps */
14950 data_ptr = ptr;
14951 data_len = len;
14952 }
14953
14954 ptr += len;
14955 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014956 case AML_SL_CONFIG_DESC_TAG:
14957 GST_DEBUG_OBJECT (qtdemux, "data %02x", AML_QT_UINT8 (ptr));
zengliang.li5f31ef42024-05-16 08:27:38 +000014958 ptr += 1;
14959 break;
14960 default:
14961 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14962 tag);
14963 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14964 ptr += len;
14965 break;
14966 }
14967 }
14968
14969 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14970 * in use, and should also be used to override some other parameters for some
14971 * codecs. */
14972 switch (object_type_id) {
14973 case 0x20: /* MPEG-4 */
14974 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14975 * profile_and_level_indication */
14976 if (data_ptr != NULL && data_len >= 5 &&
14977 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14978 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14979 data_ptr + 4, data_len - 4);
14980 }
14981 break; /* Nothing special needed here */
14982 case 0x21: /* H.264 */
14983 codec_name = "H.264 / AVC";
14984 caps = gst_caps_new_simple ("video/x-h264",
14985 "stream-format", G_TYPE_STRING, "avc",
14986 "alignment", G_TYPE_STRING, "au", NULL);
14987 break;
14988 case 0x40: /* AAC (any) */
14989 case 0x66: /* AAC Main */
14990 case 0x67: /* AAC LC */
14991 case 0x68: /* AAC SSR */
14992 /* Override channels and rate based on the codec_data, as it's often
14993 * wrong. */
14994 /* Only do so for basic setup without HE-AAC extension */
14995 if (data_ptr && data_len == 2) {
14996 guint channels, rate;
14997
14998 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
14999 if (channels > 0)
15000 entry->n_channels = channels;
15001
15002 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15003 if (rate > 0)
15004 entry->rate = rate;
15005 }
15006
15007 /* Set level and profile if possible */
15008 if (data_ptr != NULL && data_len >= 2) {
15009 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15010 data_ptr, data_len);
15011 } else {
15012 const gchar *profile_str = NULL;
15013 GstBuffer *buffer;
15014 GstMapInfo map;
15015 guint8 *codec_data;
15016 gint rate_idx, profile;
15017
15018 /* No codec_data, let's invent something.
15019 * FIXME: This is wrong for SBR! */
15020
15021 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15022
15023 buffer = gst_buffer_new_and_alloc (2);
15024 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15025 codec_data = map.data;
15026
15027 rate_idx =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015028 gst_codec_utils_aac_get_index_from_sample_rate (AML_CUR_STREAM
zengliang.li5f31ef42024-05-16 08:27:38 +000015029 (stream)->rate);
15030
15031 switch (object_type_id) {
15032 case 0x66:
15033 profile_str = "main";
15034 profile = 0;
15035 break;
15036 case 0x67:
15037 profile_str = "lc";
15038 profile = 1;
15039 break;
15040 case 0x68:
15041 profile_str = "ssr";
15042 profile = 2;
15043 break;
15044 default:
15045 profile = 3;
15046 break;
15047 }
15048
15049 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15050 codec_data[1] =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015051 ((rate_idx & 0x1) << 7) | (AML_CUR_STREAM (stream)->n_channels << 3);
zengliang.li5f31ef42024-05-16 08:27:38 +000015052
15053 gst_buffer_unmap (buffer, &map);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015054 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "codec_data",
zengliang.li5f31ef42024-05-16 08:27:38 +000015055 GST_TYPE_BUFFER, buffer, NULL);
15056 gst_buffer_unref (buffer);
15057
15058 if (profile_str) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015059 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "profile",
zengliang.li5f31ef42024-05-16 08:27:38 +000015060 G_TYPE_STRING, profile_str, NULL);
15061 }
15062 }
15063 break;
15064 case 0x60: /* MPEG-2, various profiles */
15065 case 0x61:
15066 case 0x62:
15067 case 0x63:
15068 case 0x64:
15069 case 0x65:
15070 codec_name = "MPEG-2 video";
15071 caps = gst_caps_new_simple ("video/mpeg",
15072 "mpegversion", G_TYPE_INT, 2,
15073 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15074 break;
15075 case 0x69: /* MPEG-2 BC audio */
15076 case 0x6B: /* MPEG-1 audio */
15077 caps = gst_caps_new_simple ("audio/mpeg",
15078 "mpegversion", G_TYPE_INT, 1, NULL);
15079 codec_name = "MPEG-1 audio";
15080 break;
15081 case 0x6A: /* MPEG-1 */
15082 codec_name = "MPEG-1 video";
15083 caps = gst_caps_new_simple ("video/mpeg",
15084 "mpegversion", G_TYPE_INT, 1,
15085 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15086 break;
15087 case 0x6C: /* MJPEG */
15088 caps =
15089 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15090 NULL);
15091 codec_name = "Motion-JPEG";
15092 break;
15093 case 0x6D: /* PNG */
15094 caps =
15095 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15096 NULL);
15097 codec_name = "PNG still images";
15098 break;
15099 case 0x6E: /* JPEG2000 */
15100 codec_name = "JPEG-2000";
15101 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15102 break;
15103 case 0xA4: /* Dirac */
15104 codec_name = "Dirac";
15105 caps = gst_caps_new_empty_simple ("video/x-dirac");
15106 break;
15107 case 0xA5: /* AC3 */
15108 codec_name = "AC-3 audio";
15109 caps = gst_caps_new_simple ("audio/x-ac3",
15110 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15111 break;
15112 case 0xA9: /* AC3 */
15113 codec_name = "DTS audio";
15114 caps = gst_caps_new_simple ("audio/x-dts",
15115 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15116 break;
15117 case 0xDD:
15118 if (stream_type == 0x05 && data_ptr) {
15119 GList *headers =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015120 aml_parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
zengliang.li5f31ef42024-05-16 08:27:38 +000015121 if (headers) {
15122 GList *tmp;
15123 GValue arr_val = G_VALUE_INIT;
15124 GValue buf_val = G_VALUE_INIT;
15125 GstStructure *s;
15126
15127 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15128 codec_name = "Vorbis";
15129 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15130 g_value_init (&arr_val, GST_TYPE_ARRAY);
15131 g_value_init (&buf_val, GST_TYPE_BUFFER);
15132 for (tmp = headers; tmp; tmp = tmp->next) {
15133 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15134 gst_value_array_append_value (&arr_val, &buf_val);
15135 }
15136 s = gst_caps_get_structure (caps, 0);
15137 gst_structure_take_value (s, "streamheader", &arr_val);
15138 g_value_unset (&buf_val);
15139 g_list_free (headers);
15140
15141 data_ptr = NULL;
15142 data_len = 0;
15143 }
15144 }
15145 break;
15146 case 0xE1: /* QCELP */
15147 /* QCELP, the codec_data is a riff tag (little endian) with
15148 * 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). */
15149 caps = gst_caps_new_empty_simple ("audio/qcelp");
15150 codec_name = "QCELP";
15151 break;
15152 default:
15153 break;
15154 }
15155
15156 /* If we have a replacement caps, then change our caps for this stream */
15157 if (caps) {
15158 gst_caps_unref (entry->caps);
15159 entry->caps = caps;
15160 }
15161
15162 if (codec_name && list)
15163 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15164 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15165
15166 /* Add the codec_data attribute to caps, if we have it */
15167 if (data_ptr) {
15168 GstBuffer *buffer;
15169
15170 buffer = gst_buffer_new_and_alloc (data_len);
15171 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15172
15173 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15174 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15175
15176 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15177 buffer, NULL);
15178 gst_buffer_unref (buffer);
15179 }
15180
15181}
15182
15183static inline GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015184_aml_get_unknown_codec_name (const gchar * type, guint32 fourcc)
zengliang.li5f31ef42024-05-16 08:27:38 +000015185{
15186 GstCaps *caps;
15187 guint i;
15188 char *s, fourstr[5];
15189
15190 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15191 for (i = 0; i < 4; i++) {
15192 if (!g_ascii_isalnum (fourstr[i]))
15193 fourstr[i] = '_';
15194 }
15195 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15196 caps = gst_caps_new_empty_simple (s);
15197 g_free (s);
15198 return caps;
15199}
15200
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015201#define _aml_codec(name) \
zengliang.li5f31ef42024-05-16 08:27:38 +000015202 do { \
15203 if (codec_name) { \
15204 *codec_name = g_strdup (name); \
15205 } \
15206 } while (0)
15207
15208static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015209aml_qtdemux_video_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
15210 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000015211 const guint8 * stsd_entry_data, gchar ** codec_name)
15212{
15213 GstCaps *caps = NULL;
15214 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15215
15216 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015217 case AML_FOURCC_png:
15218 _aml_codec ("PNG still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015219 caps = gst_caps_new_empty_simple ("image/png");
15220 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015221 case AML_FOURCC_jpeg:
15222 _aml_codec ("JPEG still images");
bo.xiaof8fd4c52024-08-13 11:43:09 +080015223 GST_DEBUG_OBJECT (qtdemux, "JPEG still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015224 caps =
bo.xiao86472382024-07-23 10:24:15 +080015225 gst_caps_new_simple ("video/mjpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
zengliang.li5f31ef42024-05-16 08:27:38 +000015226 NULL);
15227 break;
15228 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15229 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15230 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15231 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015232 _aml_codec ("Motion-JPEG");
bo.xiaof8fd4c52024-08-13 11:43:09 +080015233 GST_DEBUG_OBJECT (qtdemux, "Motion-JPEG");
zengliang.li5f31ef42024-05-16 08:27:38 +000015234 caps =
bo.xiao86472382024-07-23 10:24:15 +080015235 gst_caps_new_simple ("video/mjpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
zengliang.li5f31ef42024-05-16 08:27:38 +000015236 NULL);
15237 break;
15238 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015239 _aml_codec ("Motion-JPEG format B");
zengliang.li5f31ef42024-05-16 08:27:38 +000015240 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15241 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015242 case AML_FOURCC_mjp2:
15243 _aml_codec ("JPEG-2000");
zengliang.li5f31ef42024-05-16 08:27:38 +000015244 /* override to what it should be according to spec, avoid palette_data */
15245 entry->bits_per_sample = 24;
15246 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15247 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015248 case AML_FOURCC_SVQ3:
15249 _aml_codec ("Sorensen video v.3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015250 caps = gst_caps_new_simple ("video/x-svq",
15251 "svqversion", G_TYPE_INT, 3, NULL);
15252 break;
15253 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15254 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015255 _aml_codec ("Sorensen video v.1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015256 caps = gst_caps_new_simple ("video/x-svq",
15257 "svqversion", G_TYPE_INT, 1, NULL);
15258 break;
15259 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15260 caps = gst_caps_new_empty_simple ("video/x-raw");
15261 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015262 _aml_codec ("Windows Raw RGB");
zengliang.li5f31ef42024-05-16 08:27:38 +000015263 stream->alignment = 32;
15264 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015265 case AML_FOURCC_raw_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015266 {
15267 guint16 bps;
15268
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015269 bps = AML_QT_UINT16 (stsd_entry_data + 82);
zengliang.li5f31ef42024-05-16 08:27:38 +000015270 switch (bps) {
15271 case 15:
15272 format = GST_VIDEO_FORMAT_RGB15;
15273 break;
15274 case 16:
15275 format = GST_VIDEO_FORMAT_RGB16;
15276 break;
15277 case 24:
15278 format = GST_VIDEO_FORMAT_RGB;
15279 break;
15280 case 32:
15281 format = GST_VIDEO_FORMAT_ARGB;
15282 break;
15283 default:
15284 /* unknown */
15285 break;
15286 }
15287 break;
15288 }
15289 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15290 format = GST_VIDEO_FORMAT_I420;
15291 break;
15292 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15293 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15294 format = GST_VIDEO_FORMAT_I420;
15295 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015296 case AML_FOURCC_2vuy:
zengliang.li5f31ef42024-05-16 08:27:38 +000015297 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15298 format = GST_VIDEO_FORMAT_UYVY;
15299 break;
15300 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15301 format = GST_VIDEO_FORMAT_v308;
15302 break;
15303 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15304 format = GST_VIDEO_FORMAT_v216;
15305 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015306 case AML_FOURCC_v210:
zengliang.li5f31ef42024-05-16 08:27:38 +000015307 format = GST_VIDEO_FORMAT_v210;
15308 break;
15309 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15310 format = GST_VIDEO_FORMAT_r210;
15311 break;
15312 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15313 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15314 format = GST_VIDEO_FORMAT_v410;
15315 break;
15316 */
15317 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15318 * but different order than AYUV
15319 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15320 format = GST_VIDEO_FORMAT_v408;
15321 break;
15322 */
15323 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15324 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015325 _aml_codec ("MPEG-1 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015326 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15327 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15328 break;
15329 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15330 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15331 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15332 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15333 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15334 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15335 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15336 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15337 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15338 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15339 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15340 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15341 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15342 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15343 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15344 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15345 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15346 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15347 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15348 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15349 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15350 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15351 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15352 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15353 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15354 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15355 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15356 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15357 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15358 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15359 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15360 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15361 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15362 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15363 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15364 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15365 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15366 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15367 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15368 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15369 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15370 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15371 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15372 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15373 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15374 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15375 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015376 _aml_codec ("MPEG-2 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015377 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15378 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15379 break;
15380 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015381 _aml_codec ("GIF still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015382 caps = gst_caps_new_empty_simple ("image/gif");
15383 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015384 case AML_FOURCC_h263:
zengliang.li5f31ef42024-05-16 08:27:38 +000015385 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015386 case AML_FOURCC_s263:
zengliang.li5f31ef42024-05-16 08:27:38 +000015387 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015388 _aml_codec ("H.263");
zengliang.li5f31ef42024-05-16 08:27:38 +000015389 /* ffmpeg uses the height/width props, don't know why */
15390 caps = gst_caps_new_simple ("video/x-h263",
15391 "variant", G_TYPE_STRING, "itu", NULL);
15392 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015393 case AML_FOURCC_mp4v:
15394 case AML_FOURCC_MP4V:
15395 _aml_codec ("MPEG-4 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015396 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15397 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15398 break;
15399 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15400 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015401 _aml_codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
zengliang.li5f31ef42024-05-16 08:27:38 +000015402 caps = gst_caps_new_simple ("video/x-msmpeg",
15403 "msmpegversion", G_TYPE_INT, 43, NULL);
15404 break;
15405 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015406 _aml_codec ("DivX 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015407 caps = gst_caps_new_simple ("video/x-divx",
15408 "divxversion", G_TYPE_INT, 3, NULL);
15409 break;
15410 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15411 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015412 _aml_codec ("DivX 4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015413 caps = gst_caps_new_simple ("video/x-divx",
15414 "divxversion", G_TYPE_INT, 4, NULL);
15415 break;
15416 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015417 _aml_codec ("DivX 5");
zengliang.li5f31ef42024-05-16 08:27:38 +000015418 caps = gst_caps_new_simple ("video/x-divx",
15419 "divxversion", G_TYPE_INT, 5, NULL);
15420 break;
15421
15422 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015423 _aml_codec ("FFV1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015424 caps = gst_caps_new_simple ("video/x-ffv",
15425 "ffvversion", G_TYPE_INT, 1, NULL);
15426 break;
15427
15428 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15429 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015430 case AML_FOURCC_XVID:
15431 case AML_FOURCC_xvid:
15432 case AML_FOURCC_FMP4:
15433 case AML_FOURCC_fmp4:
zengliang.li5f31ef42024-05-16 08:27:38 +000015434 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15435 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15436 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015437 _aml_codec ("MPEG-4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015438 break;
15439
15440 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015441 _aml_codec ("Cinepak");
zengliang.li5f31ef42024-05-16 08:27:38 +000015442 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15443 break;
15444 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015445 _aml_codec ("Apple QuickDraw");
zengliang.li5f31ef42024-05-16 08:27:38 +000015446 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15447 break;
15448 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015449 _aml_codec ("Apple video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015450 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15451 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015452 case AML_FOURCC_H264:
15453 case AML_FOURCC_avc1:
15454 case AML_FOURCC_dva1:
15455 _aml_codec ("H.264 / AVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015456 caps = gst_caps_new_simple ("video/x-h264",
15457 "stream-format", G_TYPE_STRING, "avc",
15458 "alignment", G_TYPE_STRING, "au", NULL);
15459 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015460 case AML_FOURCC_avc3:
15461 case AML_FOURCC_dvav:
15462 _aml_codec ("H.264 / AVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015463 caps = gst_caps_new_simple ("video/x-h264",
15464 "stream-format", G_TYPE_STRING, "avc3",
15465 "alignment", G_TYPE_STRING, "au", NULL);
15466 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015467 case AML_FOURCC_H265:
15468 case AML_FOURCC_hvc1:
15469 case AML_FOURCC_dvh1:
15470 _aml_codec ("H.265 / HEVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015471 caps = gst_caps_new_simple ("video/x-h265",
15472 "stream-format", G_TYPE_STRING, "hvc1",
15473 "alignment", G_TYPE_STRING, "au", NULL);
15474 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015475 case AML_FOURCC_hev1:
15476 case AML_FOURCC_dvhe:
15477 _aml_codec ("H.265 / HEVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015478 caps = gst_caps_new_simple ("video/x-h265",
15479 "stream-format", G_TYPE_STRING, "hev1",
15480 "alignment", G_TYPE_STRING, "au", NULL);
15481 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015482 case AML_FOURCC_rle_:
15483 _aml_codec ("Run-length encoding");
zengliang.li5f31ef42024-05-16 08:27:38 +000015484 caps = gst_caps_new_simple ("video/x-rle",
15485 "layout", G_TYPE_STRING, "quicktime", NULL);
15486 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015487 case AML_FOURCC_WRLE:
15488 _aml_codec ("Run-length encoding");
zengliang.li5f31ef42024-05-16 08:27:38 +000015489 caps = gst_caps_new_simple ("video/x-rle",
15490 "layout", G_TYPE_STRING, "microsoft", NULL);
15491 break;
15492 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15493 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015494 _aml_codec ("Indeo Video 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015495 caps = gst_caps_new_simple ("video/x-indeo",
15496 "indeoversion", G_TYPE_INT, 3, NULL);
15497 break;
15498 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15499 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015500 _aml_codec ("Intel Video 4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015501 caps = gst_caps_new_simple ("video/x-indeo",
15502 "indeoversion", G_TYPE_INT, 4, NULL);
15503 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015504 case AML_FOURCC_dvcp:
15505 case AML_FOURCC_dvc_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015506 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15507 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15508 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15509 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15510 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15511 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015512 _aml_codec ("DV Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015513 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15514 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15515 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015516 case AML_FOURCC_dv5n: /* DVCPRO50 NTSC */
15517 case AML_FOURCC_dv5p: /* DVCPRO50 PAL */
15518 _aml_codec ("DVCPro50 Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015519 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15520 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15521 break;
15522 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15523 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015524 _aml_codec ("DVCProHD Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015525 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15526 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15527 break;
15528 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015529 _aml_codec ("Apple Graphics (SMC)");
zengliang.li5f31ef42024-05-16 08:27:38 +000015530 caps = gst_caps_new_empty_simple ("video/x-smc");
15531 break;
15532 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015533 _aml_codec ("VP3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015534 caps = gst_caps_new_empty_simple ("video/x-vp3");
15535 break;
15536 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015537 _aml_codec ("VP6 Flash");
zengliang.li5f31ef42024-05-16 08:27:38 +000015538 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15539 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015540 case AML_FOURCC_XiTh:
15541 _aml_codec ("Theora");
zengliang.li5f31ef42024-05-16 08:27:38 +000015542 caps = gst_caps_new_empty_simple ("video/x-theora");
15543 /* theora uses one byte of padding in the data stream because it does not
15544 * allow 0 sized packets while theora does */
15545 entry->padding = 1;
15546 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015547 case AML_FOURCC_drac:
15548 _aml_codec ("Dirac");
zengliang.li5f31ef42024-05-16 08:27:38 +000015549 caps = gst_caps_new_empty_simple ("video/x-dirac");
15550 break;
15551 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015552 _aml_codec ("TIFF still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015553 caps = gst_caps_new_empty_simple ("image/tiff");
15554 break;
15555 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015556 _aml_codec ("Apple Intermediate Codec");
zengliang.li5f31ef42024-05-16 08:27:38 +000015557 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15558 break;
15559 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015560 _aml_codec ("AVID DNxHD");
zengliang.li5f31ef42024-05-16 08:27:38 +000015561 caps = gst_caps_from_string ("video/x-dnxhd");
15562 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015563 case AML_FOURCC_VP80:
15564 case AML_FOURCC_vp08:
15565 _aml_codec ("On2 VP8");
zengliang.li5f31ef42024-05-16 08:27:38 +000015566 caps = gst_caps_from_string ("video/x-vp8");
15567 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015568 case AML_FOURCC_vp09:
15569 _aml_codec ("Google VP9");
zengliang.li5f31ef42024-05-16 08:27:38 +000015570 caps = gst_caps_from_string ("video/x-vp9");
15571 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015572 case AML_FOURCC_apcs:
15573 _aml_codec ("Apple ProRes LT");
zengliang.li5f31ef42024-05-16 08:27:38 +000015574 caps =
15575 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15576 NULL);
15577 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015578 case AML_FOURCC_apch:
15579 _aml_codec ("Apple ProRes HQ");
zengliang.li5f31ef42024-05-16 08:27:38 +000015580 caps =
15581 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15582 NULL);
15583 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015584 case AML_FOURCC_apcn:
15585 _aml_codec ("Apple ProRes");
zengliang.li5f31ef42024-05-16 08:27:38 +000015586 caps =
15587 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15588 "standard", NULL);
15589 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015590 case AML_FOURCC_apco:
15591 _aml_codec ("Apple ProRes Proxy");
zengliang.li5f31ef42024-05-16 08:27:38 +000015592 caps =
15593 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15594 "proxy", NULL);
15595 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015596 case AML_FOURCC_ap4h:
15597 _aml_codec ("Apple ProRes 4444");
zengliang.li5f31ef42024-05-16 08:27:38 +000015598 caps =
15599 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15600 "4444", NULL);
15601
15602 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15603 if (entry->bits_per_sample > 0) {
15604 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15605 NULL);
15606 }
15607 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015608 case AML_FOURCC_ap4x:
15609 _aml_codec ("Apple ProRes 4444 XQ");
zengliang.li5f31ef42024-05-16 08:27:38 +000015610 caps =
15611 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15612 "4444xq", NULL);
15613
15614 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15615 if (entry->bits_per_sample > 0) {
15616 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15617 NULL);
15618 }
15619 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015620 case AML_FOURCC_cfhd:
15621 _aml_codec ("GoPro CineForm");
zengliang.li5f31ef42024-05-16 08:27:38 +000015622 caps = gst_caps_from_string ("video/x-cineform");
15623 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015624 case AML_FOURCC_vc_1:
15625 case AML_FOURCC_ovc1:
15626 _aml_codec ("VC-1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015627 caps = gst_caps_new_simple ("video/x-wmv",
15628 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15629 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015630 case AML_FOURCC_av01:
bo.xiao4c245f22024-08-22 11:18:25 +080015631 case AML_FOURCC_dav1:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015632 _aml_codec ("AV1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015633 caps = gst_caps_new_simple ("video/x-av1",
15634 "stream-format", G_TYPE_STRING, "obu-stream",
15635 "alignment", G_TYPE_STRING, "tu", NULL);
15636 break;
15637 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15638 default:
15639 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015640 caps = _aml_get_unknown_codec_name ("video", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000015641 break;
15642 }
15643 }
15644
15645 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15646 GstVideoInfo info;
15647
15648 gst_video_info_init (&info);
15649 gst_video_info_set_format (&info, format, entry->width, entry->height);
15650
15651 caps = gst_video_info_to_caps (&info);
15652 *codec_name = gst_pb_utils_get_codec_description (caps);
15653
15654 /* enable clipping for raw video streams */
15655 stream->need_clip = TRUE;
15656 stream->alignment = 32;
15657 }
15658
15659 return caps;
15660}
15661
15662static guint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015663aml_round_up_pow2 (guint n)
zengliang.li5f31ef42024-05-16 08:27:38 +000015664{
15665 n = n - 1;
15666 n = n | (n >> 1);
15667 n = n | (n >> 2);
15668 n = n | (n >> 4);
15669 n = n | (n >> 8);
15670 n = n | (n >> 16);
15671 return n + 1;
15672}
15673
15674static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015675aml_qtdemux_audio_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
15676 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
zengliang.li5f31ef42024-05-16 08:27:38 +000015677 int len, gchar ** codec_name)
15678{
15679 GstCaps *caps;
15680 const GstStructure *s;
15681 const gchar *name;
15682 gint endian = 0;
15683 GstAudioFormat format = 0;
15684 gint depth;
15685
15686 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15687
15688 depth = entry->bytes_per_packet * 8;
15689
15690 switch (fourcc) {
15691 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015692 case AML_FOURCC_raw_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015693 /* 8-bit audio is unsigned */
15694 if (depth == 8)
15695 format = GST_AUDIO_FORMAT_U8;
15696 /* otherwise it's signed and big-endian just like 'twos' */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015697 case AML_FOURCC_twos:
zengliang.li5f31ef42024-05-16 08:27:38 +000015698 endian = G_BIG_ENDIAN;
15699 /* fall-through */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015700 case AML_FOURCC_sowt:
zengliang.li5f31ef42024-05-16 08:27:38 +000015701 {
15702 gchar *str;
15703
15704 if (!endian)
15705 endian = G_LITTLE_ENDIAN;
15706
15707 if (!format)
15708 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15709
15710 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015711 _aml_codec (str);
zengliang.li5f31ef42024-05-16 08:27:38 +000015712 g_free (str);
15713
15714 caps = gst_caps_new_simple ("audio/x-raw",
15715 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15716 "layout", G_TYPE_STRING, "interleaved", NULL);
15717 stream->alignment = GST_ROUND_UP_8 (depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015718 stream->alignment = aml_round_up_pow2 (stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +000015719 break;
15720 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015721 case AML_FOURCC_fl64:
15722 _aml_codec ("Raw 64-bit floating-point audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015723 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15724 * endian later */
15725 caps = gst_caps_new_simple ("audio/x-raw",
15726 "format", G_TYPE_STRING, "F64BE",
15727 "layout", G_TYPE_STRING, "interleaved", NULL);
15728 stream->alignment = 8;
15729 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015730 case AML_FOURCC_fl32:
15731 _aml_codec ("Raw 32-bit floating-point audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015732 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15733 * endian later */
15734 caps = gst_caps_new_simple ("audio/x-raw",
15735 "format", G_TYPE_STRING, "F32BE",
15736 "layout", G_TYPE_STRING, "interleaved", NULL);
15737 stream->alignment = 4;
15738 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015739 case AML_FOURCC_in24:
15740 _aml_codec ("Raw 24-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015741 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15742 * endian later */
15743 caps = gst_caps_new_simple ("audio/x-raw",
15744 "format", G_TYPE_STRING, "S24BE",
15745 "layout", G_TYPE_STRING, "interleaved", NULL);
15746 stream->alignment = 4;
15747 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015748 case AML_FOURCC_in32:
15749 _aml_codec ("Raw 32-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015750 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15751 * endian later */
15752 caps = gst_caps_new_simple ("audio/x-raw",
15753 "format", G_TYPE_STRING, "S32BE",
15754 "layout", G_TYPE_STRING, "interleaved", NULL);
15755 stream->alignment = 4;
15756 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015757 case AML_FOURCC_s16l:
15758 _aml_codec ("Raw 16-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015759 caps = gst_caps_new_simple ("audio/x-raw",
15760 "format", G_TYPE_STRING, "S16LE",
15761 "layout", G_TYPE_STRING, "interleaved", NULL);
15762 stream->alignment = 2;
15763 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015764 case AML_FOURCC_ulaw:
15765 _aml_codec ("Mu-law audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015766 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15767 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015768 case AML_FOURCC_alaw:
15769 _aml_codec ("A-law audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015770 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15771 break;
15772 case 0x0200736d:
15773 case 0x6d730002:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015774 _aml_codec ("Microsoft ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015775 /* Microsoft ADPCM-ACM code 2 */
15776 caps = gst_caps_new_simple ("audio/x-adpcm",
15777 "layout", G_TYPE_STRING, "microsoft", NULL);
15778 break;
15779 case 0x1100736d:
15780 case 0x6d730011:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015781 _aml_codec ("DVI/IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015782 caps = gst_caps_new_simple ("audio/x-adpcm",
15783 "layout", G_TYPE_STRING, "dvi", NULL);
15784 break;
15785 case 0x1700736d:
15786 case 0x6d730017:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015787 _aml_codec ("DVI/Intel IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015788 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15789 caps = gst_caps_new_simple ("audio/x-adpcm",
15790 "layout", G_TYPE_STRING, "quicktime", NULL);
15791 break;
15792 case 0x5500736d:
15793 case 0x6d730055:
15794 /* MPEG layer 3, CBR only (pre QT4.1) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015795 case AML_FOURCC__mp3:
15796 case AML_FOURCC_mp3_:
15797 _aml_codec ("MPEG-1 layer 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015798 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15799 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15800 "mpegversion", G_TYPE_INT, 1, NULL);
15801 break;
15802 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015803 _aml_codec ("MPEG-1 layer 2");
zengliang.li5f31ef42024-05-16 08:27:38 +000015804 /* MPEG layer 2 */
15805 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15806 "mpegversion", G_TYPE_INT, 1, NULL);
15807 break;
15808 case 0x20736d:
15809 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015810 _aml_codec ("EAC-3 audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015811 caps = gst_caps_new_simple ("audio/x-eac3",
15812 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15813 entry->sampled = TRUE;
15814 break;
15815 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015816 case AML_FOURCC_ac_3:
15817 _aml_codec ("AC-3 audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015818 caps = gst_caps_new_simple ("audio/x-ac3",
15819 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15820 entry->sampled = TRUE;
15821 break;
15822 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15823 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
kaiqiang.xianga582a842024-10-30 15:49:46 +080015824 case GST_MAKE_FOURCC ('d', 't', 's', 'x'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015825 _aml_codec ("DTS audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015826 caps = gst_caps_new_simple ("audio/x-dts",
15827 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15828 entry->sampled = TRUE;
15829 break;
15830 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15831 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015832 _aml_codec ("DTS-HD audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015833 caps = gst_caps_new_simple ("audio/x-dts",
15834 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15835 entry->sampled = TRUE;
15836 break;
zengliang.li469a5ca2024-05-16 12:03:55 +000015837 case GST_MAKE_FOURCC ('d', 't', 's', 'e'): // DTS Low Bit Rate (LBR)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015838 _aml_codec ("DTS LBR audio");
zengliang.li469a5ca2024-05-16 12:03:55 +000015839 caps = gst_caps_new_simple ("audio/x-dts",
15840 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15841 entry->sampled = TRUE;
15842 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015843 case AML_FOURCC_MAC3:
15844 _aml_codec ("MACE-3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015845 caps = gst_caps_new_simple ("audio/x-mace",
15846 "maceversion", G_TYPE_INT, 3, NULL);
15847 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015848 case AML_FOURCC_MAC6:
15849 _aml_codec ("MACE-6");
zengliang.li5f31ef42024-05-16 08:27:38 +000015850 caps = gst_caps_new_simple ("audio/x-mace",
15851 "maceversion", G_TYPE_INT, 6, NULL);
15852 break;
15853 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15854 /* ogg/vorbis */
15855 caps = gst_caps_new_empty_simple ("application/ogg");
15856 break;
15857 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015858 _aml_codec ("DV audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015859 caps = gst_caps_new_empty_simple ("audio/x-dv");
15860 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015861 case AML_FOURCC_mp4a:
15862 _aml_codec ("MPEG-4 AAC audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015863 caps = gst_caps_new_simple ("audio/mpeg",
15864 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15865 "stream-format", G_TYPE_STRING, "raw", NULL);
15866 break;
15867 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015868 _aml_codec ("QDesign Music");
zengliang.li5f31ef42024-05-16 08:27:38 +000015869 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15870 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015871 case AML_FOURCC_QDM2:
15872 _aml_codec ("QDesign Music v.2");
zengliang.li5f31ef42024-05-16 08:27:38 +000015873 /* FIXME: QDesign music version 2 (no constant) */
15874 if (FALSE && data) {
15875 caps = gst_caps_new_simple ("audio/x-qdm2",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015876 "framesize", G_TYPE_INT, AML_QT_UINT32 (data + 52),
15877 "bitrate", G_TYPE_INT, AML_QT_UINT32 (data + 40),
15878 "blocksize", G_TYPE_INT, AML_QT_UINT32 (data + 44), NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +000015879 } else {
15880 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15881 }
15882 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015883 case AML_FOURCC_agsm:
15884 _aml_codec ("GSM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015885 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15886 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015887 case AML_FOURCC_samr:
15888 _aml_codec ("AMR audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015889 caps = gst_caps_new_empty_simple ("audio/AMR");
15890 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015891 case AML_FOURCC_sawb:
15892 _aml_codec ("AMR-WB audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015893 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15894 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015895 case AML_FOURCC_ima4:
15896 _aml_codec ("Quicktime IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015897 caps = gst_caps_new_simple ("audio/x-adpcm",
15898 "layout", G_TYPE_STRING, "quicktime", NULL);
15899 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015900 case AML_FOURCC_alac:
15901 _aml_codec ("Apple lossless audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015902 caps = gst_caps_new_empty_simple ("audio/x-alac");
15903 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015904 case AML_FOURCC_fLaC:
15905 _aml_codec ("Free Lossless Audio Codec");
zengliang.li5f31ef42024-05-16 08:27:38 +000015906 caps = gst_caps_new_simple ("audio/x-flac",
15907 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15908 break;
15909 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015910 _aml_codec ("QualComm PureVoice");
zengliang.li5f31ef42024-05-16 08:27:38 +000015911 caps = gst_caps_from_string ("audio/qcelp");
15912 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015913 case AML_FOURCC_wma_:
15914 case AML_FOURCC_owma:
15915 _aml_codec ("WMA");
zengliang.li5f31ef42024-05-16 08:27:38 +000015916 caps = gst_caps_new_empty_simple ("audio/x-wma");
15917 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015918 case AML_FOURCC_opus:
15919 _aml_codec ("Opus");
zengliang.li5f31ef42024-05-16 08:27:38 +000015920 caps = gst_caps_new_empty_simple ("audio/x-opus");
15921 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015922 case AML_FOURCC_lpcm:
zengliang.li5f31ef42024-05-16 08:27:38 +000015923 {
15924 guint32 flags = 0;
15925 guint32 depth = 0;
15926 guint32 width = 0;
15927 GstAudioFormat format;
15928 enum
15929 {
15930 FLAG_IS_FLOAT = 0x1,
15931 FLAG_IS_BIG_ENDIAN = 0x2,
15932 FLAG_IS_SIGNED = 0x4,
15933 FLAG_IS_PACKED = 0x8,
15934 FLAG_IS_ALIGNED_HIGH = 0x10,
15935 FLAG_IS_NON_INTERLEAVED = 0x20
15936 };
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015937 _aml_codec ("Raw LPCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015938
15939 if (data && len >= 36) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015940 depth = AML_QT_UINT32 (data + 24);
15941 flags = AML_QT_UINT32 (data + 28);
15942 width = AML_QT_UINT32 (data + 32) * 8 / entry->n_channels;
zengliang.li5f31ef42024-05-16 08:27:38 +000015943 }
15944 if ((flags & FLAG_IS_FLOAT) == 0) {
15945 if (depth == 0)
15946 depth = 16;
15947 if (width == 0)
15948 width = 16;
15949 if ((flags & FLAG_IS_ALIGNED_HIGH))
15950 depth = width;
15951
15952 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15953 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15954 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15955 caps = gst_caps_new_simple ("audio/x-raw",
15956 "format", G_TYPE_STRING,
15957 format !=
15958 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15959 "UNKNOWN", "layout", G_TYPE_STRING,
15960 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15961 "interleaved", NULL);
15962 stream->alignment = GST_ROUND_UP_8 (depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015963 stream->alignment = aml_round_up_pow2 (stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +000015964 } else {
15965 if (width == 0)
15966 width = 32;
15967 if (width == 64) {
15968 if (flags & FLAG_IS_BIG_ENDIAN)
15969 format = GST_AUDIO_FORMAT_F64BE;
15970 else
15971 format = GST_AUDIO_FORMAT_F64LE;
15972 } else {
15973 if (flags & FLAG_IS_BIG_ENDIAN)
15974 format = GST_AUDIO_FORMAT_F32BE;
15975 else
15976 format = GST_AUDIO_FORMAT_F32LE;
15977 }
15978 caps = gst_caps_new_simple ("audio/x-raw",
15979 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15980 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15981 "non-interleaved" : "interleaved", NULL);
15982 stream->alignment = width / 8;
15983 }
15984 break;
15985 }
15986 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
15987 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015988 _aml_codec ("AC4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015989 caps = gst_caps_new_empty_simple ("audio/x-ac4");
bo.xiaoa6d06072024-05-30 11:04:57 +080015990 entry->sampled = TRUE;
zengliang.li5f31ef42024-05-16 08:27:38 +000015991 break;
15992 }
15993 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
15994 /* ? */
15995 default:
15996 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015997 caps = _aml_get_unknown_codec_name ("audio", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000015998 break;
15999 }
16000 }
16001
16002 if (caps) {
16003 GstCaps *templ_caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016004 gst_static_pad_template_get_caps (&gst_aml_qtdemux_audiosrc_template);
zengliang.li5f31ef42024-05-16 08:27:38 +000016005 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16006 gst_caps_unref (caps);
16007 gst_caps_unref (templ_caps);
16008 caps = intersection;
16009 }
16010
16011 /* enable clipping for raw audio streams */
16012 s = gst_caps_get_structure (caps, 0);
16013 name = gst_structure_get_name (s);
16014 if (g_str_has_prefix (name, "audio/x-raw")) {
16015 stream->need_clip = TRUE;
16016 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
16017 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
16018 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
16019 stream->max_buffer_size);
16020 }
16021 return caps;
16022}
16023
16024static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016025aml_qtdemux_sub_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
16026 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000016027 const guint8 * stsd_entry_data, gchar ** codec_name)
16028{
16029 GstCaps *caps;
16030
16031 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16032
16033 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016034 case AML_FOURCC_mp4s:
16035 _aml_codec ("DVD subtitle");
zengliang.li5f31ef42024-05-16 08:27:38 +000016036 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016037 stream->process_func = gst_aml_qtdemux_process_buffer_dvd;
zengliang.li5f31ef42024-05-16 08:27:38 +000016038 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016039 case AML_FOURCC_text:
16040 _aml_codec ("Quicktime timed text");
zengliang.li5f31ef42024-05-16 08:27:38 +000016041 goto text;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016042 case AML_FOURCC_tx3g:
16043 _aml_codec ("3GPP timed text");
zengliang.li5f31ef42024-05-16 08:27:38 +000016044 text:
16045 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16046 "utf8", NULL);
16047 /* actual text piece needs to be extracted */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016048 stream->process_func = gst_aml_qtdemux_process_buffer_text;
zengliang.li5f31ef42024-05-16 08:27:38 +000016049 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016050 case AML_FOURCC_stpp:
16051 _aml_codec ("XML subtitles");
zengliang.li5f31ef42024-05-16 08:27:38 +000016052 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16053 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016054 case AML_FOURCC_wvtt:
zengliang.li5f31ef42024-05-16 08:27:38 +000016055 {
16056 GstBuffer *buffer;
16057 const gchar *buf = "WEBVTT\n\n";
16058
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016059 _aml_codec ("WebVTT subtitles");
zengliang.li5f31ef42024-05-16 08:27:38 +000016060 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016061 stream->process_func = gst_aml_qtdemux_process_buffer_wvtt;
zengliang.li5f31ef42024-05-16 08:27:38 +000016062
16063 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
16064 buffer = gst_buffer_new_and_alloc (8);
16065 gst_buffer_fill (buffer, 0, buf, 8);
16066 stream->buffers = g_slist_append (stream->buffers, buffer);
16067
16068 break;
16069 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016070 case AML_FOURCC_c608:
16071 _aml_codec ("CEA 608 Closed Caption");
zengliang.li5f31ef42024-05-16 08:27:38 +000016072 caps =
16073 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16074 G_TYPE_STRING, "s334-1a", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016075 stream->process_func = gst_aml_qtdemux_process_buffer_clcp;
zengliang.li5f31ef42024-05-16 08:27:38 +000016076 stream->need_split = TRUE;
16077 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016078 case AML_FOURCC_c708:
16079 _aml_codec ("CEA 708 Closed Caption");
zengliang.li5f31ef42024-05-16 08:27:38 +000016080 caps =
16081 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16082 G_TYPE_STRING, "cdp", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016083 stream->process_func = gst_aml_qtdemux_process_buffer_clcp;
zengliang.li5f31ef42024-05-16 08:27:38 +000016084 break;
16085
16086 default:
16087 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016088 caps = _aml_get_unknown_codec_name ("text", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000016089 break;
16090 }
16091 }
16092 return caps;
16093}
16094
16095static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016096aml_qtdemux_generic_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
16097 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000016098 const guint8 * stsd_entry_data, gchar ** codec_name)
16099{
16100 GstCaps *caps;
16101
16102 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016103 case AML_FOURCC_m1v:
16104 _aml_codec ("MPEG 1 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000016105 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16106 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16107 break;
16108 default:
16109 caps = NULL;
16110 break;
16111 }
16112 return caps;
16113}
16114
16115static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016116gst_aml_qtdemux_append_protection_system_id (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +000016117 const gchar * system_id)
16118{
16119 gint i;
16120
16121 if (!qtdemux->protection_system_ids)
16122 qtdemux->protection_system_ids =
16123 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16124 /* Check whether we already have an entry for this system ID. */
16125 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16126 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16127 if (g_ascii_strcasecmp (system_id, id) == 0) {
16128 return;
16129 }
16130 }
16131 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16132 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,
16133 -1));
16134}