blob: 07032a27478e9e676f8b833867c79992cbb1aecf [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.xiaoe35cbc32025-01-14 21:06:11 +080011255 if (profile == 0)
11256 {
11257 GST_DEBUG ("profile: 0, Playback avc bitstream using base-layer");
11258 el_present_flag = 0;
11259 bl_present_flag = 0;
11260 }
11261 else if (profile == 9)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011262 GST_DEBUG ("Playback Dolby Vision");
11263 else
11264 {
11265 GST_DEBUG ("invalid profile, Playback HEVC bitstream using base-layer");
bo.xiao4c245f22024-08-22 11:18:25 +080011266 el_present_flag = 0;
11267 bl_present_flag = 0;
bo.xiaoe35cbc32025-01-14 21:06:11 +080011268 if (profile == 127 && bl_signal_compatibility_id == 3)
11269 {
11270 GST_WARNING ("reject play for profile(127), CCID(3).");
11271 bRejectPlay = TRUE;
11272 }
zengliang.lib0f27cd2024-05-17 06:58:54 +000011273 }
11274 }
11275 else
11276 {
bo.xiao4c245f22024-08-22 11:18:25 +080011277 GST_DEBUG ("invalid config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11278 el_present_flag = 0;
11279 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011280 }
11281 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011282 else if (sampleEntry == AML_FOURCC_dvav || sampleEntry == AML_FOURCC_dva1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011283 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011284 if (dvConfig == AML_FOURCC_dvcC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011285 {
bo.xiao4c245f22024-08-22 11:18:25 +080011286 if (profile == 1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011287 {
bo.xiao4c245f22024-08-22 11:18:25 +080011288 GST_WARNING("reject play for invalid profile:%d", profile);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011289 bRejectPlay = TRUE;
11290 }
11291 }
11292 else
11293 {
bo.xiao4c245f22024-08-22 11:18:25 +080011294 GST_WARNING ("reject play for invalid config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
zengliang.lib0f27cd2024-05-17 06:58:54 +000011295 bRejectPlay = TRUE;
11296 }
11297 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011298 else if (sampleEntry == AML_FOURCC_av01)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011299 {
11300 GST_DEBUG ("ToDo sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
11301 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011302 else if (sampleEntry == AML_FOURCC_dav1)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011303 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011304 if (dvConfig == AML_FOURCC_dvvC || dvConfig == AML_FOURCC_dvwC)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011305 {
bo.xiao4c245f22024-08-22 11:18:25 +080011306 if (profile != 10)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011307 {
bo.xiao4c245f22024-08-22 11:18:25 +080011308 GST_WARNING("reject play for invalid profile:%d", profile);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011309 bRejectPlay = TRUE;
11310 }
11311 }
11312 else
11313 {
11314 GST_WARNING ("reject play for invalid dv config %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (dvConfig));
11315 bRejectPlay = TRUE;
11316 }
11317 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011318 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 +000011319 {
11320 GST_DEBUG ("invalid sampleEntry %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (sampleEntry));
bo.xiao4c245f22024-08-22 11:18:25 +080011321 el_present_flag = 0;
11322 bl_present_flag = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011323 }
11324
bo.xiao4c245f22024-08-22 11:18:25 +080011325 gst_caps_set_simple (caps, "bl_present_flag", G_TYPE_BOOLEAN, bl_present_flag, NULL);
11326 gst_caps_set_simple (caps, "el_present_flag", G_TYPE_BOOLEAN, el_present_flag, NULL);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011327
11328 return bRejectPlay ? -1 : 0;
11329}
11330
11331static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011332aml_qtdemux_parse_sgpd_av1M (GstAmlQTDemux * qtdemux, GNode * node)
zengliang.lib0f27cd2024-05-17 06:58:54 +000011333{
11334 GNode *sgpd;
11335 const guint8 *sgpd_data;
11336 gint sgpd_size;
11337 guint32 sgpd_fourcc;
11338 gint sgpd_version;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011339 guint32 sgpd_group_type;
11340 gint sgpd_default_len = -1;
11341 guint sgpd_entry_cnt;
11342 guint32 group_fourcc;
bo.xiao86472382024-07-23 10:24:15 +080011343 guint metadata_type = 0;
11344 guint metadata_specific_parameters = 0;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011345 gboolean bRejectPlay = FALSE;
11346
11347 GST_DEBUG_OBJECT (qtdemux, "try to find sgpd");
11348
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011349 sgpd = aml_qtdemux_tree_get_child_by_type (node, AML_FOURCC_sgpd);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011350 if (sgpd)
11351 {
11352 int rIdx = 0;
11353 int entryIdx = 0;
11354 sgpd_data = (const guint8 *) sgpd->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011355 sgpd_size = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011356 rIdx += 4;
11357
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011358 sgpd_fourcc = AML_QT_FOURCC (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011359 rIdx += 4;
11360
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011361 sgpd_version = AML_QT_UINT8 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011362 rIdx += 4; // 1 byte version + 3 byte flags
11363
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011364 sgpd_group_type = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011365 rIdx += 4;
11366
11367 if (sgpd_version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011368 sgpd_default_len = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011369 rIdx += 4;
11370 }
11371 else if (sgpd_version >= 2)
11372 rIdx += 4; // 4 byte default_sample_descriotion_index
11373
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011374 sgpd_entry_cnt = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011375 rIdx += 4;
11376
11377 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",
11378 sgpd_size, sgpd_fourcc, sgpd_version, sgpd_group_type, sgpd_entry_cnt);
11379
11380 for (entryIdx = 0; entryIdx < sgpd_entry_cnt; entryIdx++)
11381 {
11382 if (sgpd_version == 1 && sgpd_default_len == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011383 sgpd_default_len = AML_QT_UINT32 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011384 rIdx += 4;
11385 }
11386
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011387 group_fourcc = AML_QT_FOURCC (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011388 rIdx += 4;
11389
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011390 if (group_fourcc == AML_FOURCC_av1M) {
11391 metadata_type = AML_QT_UINT8 (sgpd_data + rIdx);
zengliang.lib0f27cd2024-05-17 06:58:54 +000011392 rIdx += 1;
11393
bo.xiao4c245f22024-08-22 11:18:25 +080011394 metadata_specific_parameters = AML_QT_UINT24(sgpd_data + rIdx);
11395 rIdx += 3;
zengliang.lib0f27cd2024-05-17 06:58:54 +000011396
11397 if ( metadata_type != 4 || metadata_specific_parameters != 0xB5003B) {
11398 // METADATA_TYPE_ITUT_T35 and 0xB5003B
11399 bRejectPlay = TRUE;
11400 }
11401 }
11402 GST_WARNING_OBJECT (qtdemux, "group_fourcc=0x%x, metadata_type=0x%x, metadata_specific_parameters=0x%x",
11403 group_fourcc, metadata_type, metadata_specific_parameters);
11404
11405 }
11406 }
11407 else
11408 {
11409 GST_DEBUG_OBJECT (qtdemux, "no sgpd");
11410 }
11411
11412 return bRejectPlay ? -1 : 0;
11413}
11414
zengliang.li5f31ef42024-05-16 08:27:38 +000011415static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011416aml_qtdemux_parse_stereo_svmi_atom (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
zengliang.li5f31ef42024-05-16 08:27:38 +000011417 GNode * stbl)
11418{
11419 GNode *svmi;
11420
11421 /*parse svmi header if existing */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011422 svmi = aml_qtdemux_tree_get_child_by_type (stbl, AML_FOURCC_svmi);
zengliang.li5f31ef42024-05-16 08:27:38 +000011423 if (svmi) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011424 guint32 len = AML_QT_UINT32 ((guint8 *) svmi->data);
11425 guint32 version = AML_QT_UINT32 ((guint8 *) svmi->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000011426 if (!version) {
11427 GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
11428 GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
11429 guint8 frame_type, frame_layout;
11430 guint32 stereo_mono_change_count;
11431
11432 if (len < 18)
11433 return FALSE;
11434
11435 /* MPEG-A stereo video */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011436 if (qtdemux->major_brand == AML_FOURCC_ss02)
zengliang.li5f31ef42024-05-16 08:27:38 +000011437 flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;
11438
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011439 frame_type = AML_QT_UINT8 ((guint8 *) svmi->data + 12);
11440 frame_layout = AML_QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
11441 stereo_mono_change_count = AML_QT_UINT32 ((guint8 *) svmi->data + 14);
zengliang.li5f31ef42024-05-16 08:27:38 +000011442
11443 switch (frame_type) {
11444 case 0:
11445 mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
11446 break;
11447 case 1:
11448 mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
11449 break;
11450 case 2:
11451 mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
11452 break;
11453 case 3:
11454 /* mode 3 is primary/secondary view sequence, ie
11455 * left/right views in separate tracks. See section 7.2
11456 * of ISO/IEC 23000-11:2009 */
11457 /* In the future this might be supported using related
11458 * streams, like an enhancement track - if files like this
11459 * ever exist */
11460 GST_FIXME_OBJECT (qtdemux,
11461 "Implement stereo video in separate streams");
11462 }
11463
11464 if ((frame_layout & 0x1) == 0)
11465 flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
11466
11467 GST_LOG_OBJECT (qtdemux,
11468 "StereoVideo: composition type: %u, is_left_first: %u",
11469 frame_type, frame_layout);
11470
11471 if (stereo_mono_change_count > 1) {
11472 GST_FIXME_OBJECT (qtdemux,
11473 "Mixed-mono flags are not yet supported in qtdemux.");
11474 }
11475
11476 stream->multiview_mode = mode;
11477 stream->multiview_flags = flags;
11478 }
11479 }
11480
11481 return TRUE;
11482}
11483
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011484/*
11485 * gst_aml_video_color_matrix_from_iso_compat:
11486 * @value: a ITU-T H.273 matrix coefficients value
11487 *
11488 * Converts the @value to the #GstVideoColorMatrix
11489 * The matrix coefficients (MatrixCoefficients) value is
11490 * defined by "ISO/IEC 23001-8 Section 7.3 Table 4"
11491 * and "ITU-T H.273 Table 4".
11492 * "H.264 Table E-5" and "H.265 Table E.5" share the identical values.
11493 *
11494 * Returns: the matched #GstVideoColorMatrix
11495 *
11496 * Compatible with yocto3.1, rdk, and yocto4.
11497 */
11498GstVideoColorMatrix
11499gst_aml_video_color_matrix_from_iso_compat (guint value)
11500{
11501 switch (value) {
11502 case 0:
11503 return GST_VIDEO_COLOR_MATRIX_RGB;
11504 case 1:
11505 return GST_VIDEO_COLOR_MATRIX_BT709;
11506 case 4:
11507 return GST_VIDEO_COLOR_MATRIX_FCC;
11508 case 5:
11509 case 6:
11510 return GST_VIDEO_COLOR_MATRIX_BT601;
11511 case 7:
11512 return GST_VIDEO_COLOR_MATRIX_SMPTE240M;
11513 case 9:
11514 return GST_VIDEO_COLOR_MATRIX_BT2020;
11515 case 2:
11516 default:
11517 return GST_VIDEO_COLOR_MATRIX_UNKNOWN;
11518 }
11519}
11520
11521/*
11522 * gst_video_transfer_function_from_iso_compat_compat:
11523 * @value: a ITU-T H.273 transfer characteristics value
11524 *
11525 * Converts the @value to the #GstVideoTransferFunction
11526 * The transfer characteristics (TransferCharacteristics) value is
11527 * defined by "ISO/IEC 23001-8 Section 7.2 Table 3"
11528 * and "ITU-T H.273 Table 3".
11529 * "H.264 Table E-4" and "H.265 Table E.4" share the identical values.
11530 *
11531 * Returns: the matched #GstVideoTransferFunction
11532 *
11533 * Compatible with yocto3.1, rdk, and yocto4.
11534 */
11535GstVideoTransferFunction
11536gst_aml_video_transfer_function_from_iso_compat (guint value)
11537{
11538 switch (value) {
11539 case 1:
11540 return GST_VIDEO_TRANSFER_BT709;
11541 case 4:
11542 return GST_VIDEO_TRANSFER_GAMMA22;
11543 case 5:
11544 return GST_VIDEO_TRANSFER_GAMMA28;
11545 case 7:
11546 return GST_VIDEO_TRANSFER_SMPTE240M;
11547 case 8:
11548 return GST_VIDEO_TRANSFER_GAMMA10;
11549 case 9:
11550 return GST_VIDEO_TRANSFER_LOG100;
11551 case 10:
11552 return GST_VIDEO_TRANSFER_LOG316;
11553 case 13:
11554 return GST_VIDEO_TRANSFER_SRGB;
11555 case 14:
11556 return GST_VIDEO_TRANSFER_BT2020_10;
11557 case 15:
11558 return GST_VIDEO_TRANSFER_BT2020_12;
11559 case 18:
11560 return GST_VIDEO_TRANSFER_ARIB_STD_B67;
11561 case 2:
11562 default:
11563 return GST_VIDEO_TRANSFER_UNKNOWN;
11564 }
11565}
11566
11567/*
11568 * gst_aml_video_color_primaries_from_iso_compat:
11569 * @value: a ITU-T H.273 colour primaries value
11570 *
11571 * Converts the @value to the #GstVideoColorPrimaries
11572 * The colour primaries (ColourPrimaries) value is
11573 * defined by "ISO/IEC 23001-8 Section 7.1 Table 2" and "ITU-T H.273 Table 2".
11574 * "H.264 Table E-3" and "H.265 Table E.3" share the identical values.
11575 *
11576 * Returns: the matched #GstVideoColorPrimaries
11577 *
11578 * Compatible with yocto3.1, rdk, and yocto4.
11579 */
11580GstVideoColorPrimaries
11581gst_aml_video_color_primaries_from_iso_compat (guint value)
11582{
11583 switch (value) {
11584 case 1:
11585 return GST_VIDEO_COLOR_PRIMARIES_BT709;
11586 case 4:
11587 return GST_VIDEO_COLOR_PRIMARIES_BT470M;
11588 case 5:
11589 return GST_VIDEO_COLOR_PRIMARIES_BT470BG;
11590 case 6:
11591 return GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
11592 case 7:
11593 return GST_VIDEO_COLOR_PRIMARIES_SMPTE240M;
11594 case 8:
11595 return GST_VIDEO_COLOR_PRIMARIES_FILM;
11596 case 9:
11597 return GST_VIDEO_COLOR_PRIMARIES_BT2020;
11598 case 10:
11599 return GST_VIDEO_COLOR_PRIMARIES_SMPTEST428;
11600 case 11:
11601 return GST_VIDEO_COLOR_PRIMARIES_SMPTERP431;
11602 case 12:
11603 return GST_VIDEO_COLOR_PRIMARIES_SMPTEEG432;
11604 case 22:
11605 return GST_VIDEO_COLOR_PRIMARIES_EBU3213;
11606 case 2:
11607 default:
11608 return GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
11609 }
11610}
11611
zengliang.li5f31ef42024-05-16 08:27:38 +000011612/* parse the traks.
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011613 * With each track we associate a new AmlQtDemuxStream that contains all the info
zengliang.li5f31ef42024-05-16 08:27:38 +000011614 * about the trak.
11615 * traks that do not decode to something (like strm traks) will not have a pad.
11616 */
11617static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011618aml_qtdemux_parse_trak (GstAmlQTDemux * qtdemux, GNode * trak)
zengliang.li5f31ef42024-05-16 08:27:38 +000011619{
11620 GstByteReader tkhd;
11621 int offset;
11622 GNode *mdia;
11623 GNode *mdhd;
11624 GNode *hdlr;
11625 GNode *minf;
11626 GNode *stbl;
11627 GNode *stsd;
11628 GNode *mp4a;
11629 GNode *mp4v;
11630 GNode *esds;
11631 GNode *tref;
11632 GNode *udta;
11633
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011634 AmlQtDemuxStream *stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000011635 const guint8 *stsd_data;
11636 const guint8 *stsd_entry_data;
11637 guint remaining_stsd_len;
11638 guint stsd_entry_count;
11639 guint stsd_index;
11640 guint16 lang_code; /* quicktime lang code or packed iso code */
11641 guint32 version;
11642 guint32 tkhd_flags = 0;
11643 guint8 tkhd_version = 0;
11644 guint32 w = 0, h = 0;
11645 guint value_size, stsd_len, len;
11646 guint32 track_id;
11647 guint32 dummy;
11648
11649 GST_DEBUG_OBJECT (qtdemux, "parse_trak");
11650
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011651 if (!aml_qtdemux_tree_get_child_by_type_full (trak, AML_FOURCC_tkhd, &tkhd)
zengliang.li5f31ef42024-05-16 08:27:38 +000011652 || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
11653 || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
11654 goto corrupt_file;
11655
11656 /* pick between 64 or 32 bits */
11657 value_size = tkhd_version == 1 ? 8 : 4;
11658 if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
11659 !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
11660 goto corrupt_file;
11661
11662 /* Check if current moov has duplicated track_id */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011663 if (aml_qtdemux_find_stream (qtdemux, track_id))
zengliang.li5f31ef42024-05-16 08:27:38 +000011664 goto existing_stream;
11665
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011666 stream = _aml_create_stream (qtdemux, track_id);
zengliang.li5f31ef42024-05-16 08:27:38 +000011667 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
11668
11669 /* need defaults for fragments */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011670 aml_qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);
zengliang.li5f31ef42024-05-16 08:27:38 +000011671
11672 if ((tkhd_flags & 1) == 0)
11673 stream->disabled = TRUE;
11674
11675 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
11676 tkhd_version, tkhd_flags, stream->track_id);
11677
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011678 if (!(mdia = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_mdia)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011679 goto corrupt_file;
11680
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011681 if (!(mdhd = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_mdhd))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011682 /* be nice for some crooked mjp2 files that use mhdr for mdhd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011683 if (qtdemux->major_brand != AML_FOURCC_mjp2 ||
11684 !(mdhd = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_mhdr)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011685 goto corrupt_file;
11686 }
11687
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011688 len = AML_QT_UINT32 ((guint8 *) mdhd->data);
11689 version = AML_QT_UINT32 ((guint8 *) mdhd->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000011690 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
11691 if (version == 0x01000000) {
11692 if (len < 42)
11693 goto corrupt_file;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011694 stream->timescale = AML_QT_UINT32 ((guint8 *) mdhd->data + 28);
11695 stream->duration = AML_QT_UINT64 ((guint8 *) mdhd->data + 32);
11696 lang_code = AML_QT_UINT16 ((guint8 *) mdhd->data + 40);
zengliang.li5f31ef42024-05-16 08:27:38 +000011697 } else {
11698 if (len < 30)
11699 goto corrupt_file;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011700 stream->timescale = AML_QT_UINT32 ((guint8 *) mdhd->data + 20);
11701 stream->duration = AML_QT_UINT32 ((guint8 *) mdhd->data + 24);
11702 lang_code = AML_QT_UINT16 ((guint8 *) mdhd->data + 28);
zengliang.li5f31ef42024-05-16 08:27:38 +000011703 }
11704
11705 if (lang_code < 0x400) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011706 aml_qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
zengliang.li5f31ef42024-05-16 08:27:38 +000011707 } else if (lang_code == 0x7fff) {
11708 stream->lang_id[0] = 0; /* unspecified */
11709 } else {
11710 stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
11711 stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
11712 stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
11713 stream->lang_id[3] = 0;
11714 }
11715
11716 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
11717 stream->timescale);
11718 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
11719 stream->duration);
11720 GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
11721 lang_code, stream->lang_id);
11722
11723 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
11724 goto corrupt_file;
11725
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011726 if ((tref = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_tref))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011727 /* chapters track reference */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011728 GNode *chap = aml_qtdemux_tree_get_child_by_type (tref, AML_FOURCC_chap);
zengliang.li5f31ef42024-05-16 08:27:38 +000011729 if (chap) {
11730 gsize length = GST_READ_UINT32_BE (chap->data);
11731 if (qtdemux->chapters_track_id)
11732 GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");
11733
11734 if (length >= 12) {
11735 qtdemux->chapters_track_id =
11736 GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
11737 }
11738 }
11739 }
11740
11741 /* fragmented files may have bogus duration in moov */
11742 if (!qtdemux->fragmented &&
11743 qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
11744 guint64 tdur1, tdur2;
11745
11746 /* don't overflow */
11747 tdur1 = stream->timescale * (guint64) qtdemux->duration;
11748 tdur2 = qtdemux->timescale * (guint64) stream->duration;
11749
11750 /* HACK:
11751 * some of those trailers, nowadays, have prologue images that are
11752 * themselves video tracks as well. I haven't really found a way to
11753 * identify those yet, except for just looking at their duration. */
11754 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
11755 GST_WARNING_OBJECT (qtdemux,
11756 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
11757 " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
11758 "found, assuming preview image or something; skipping track",
11759 stream->duration, stream->timescale, qtdemux->duration,
11760 qtdemux->timescale);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011761 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000011762 return TRUE;
11763 }
11764 }
11765
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011766 if (!(hdlr = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_hdlr)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011767 goto corrupt_file;
11768
11769 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011770 GST_FOURCC_ARGS (AML_QT_FOURCC ((guint8 *) hdlr->data + 12)));
zengliang.li5f31ef42024-05-16 08:27:38 +000011771
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011772 len = AML_QT_UINT32 ((guint8 *) hdlr->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011773 if (len >= 20)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011774 stream->subtype = AML_QT_FOURCC ((guint8 *) hdlr->data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000011775 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
11776 GST_FOURCC_ARGS (stream->subtype));
11777
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011778 if (!(minf = aml_qtdemux_tree_get_child_by_type (mdia, AML_FOURCC_minf)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011779 goto corrupt_file;
11780
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011781 if (!(stbl = aml_qtdemux_tree_get_child_by_type (minf, AML_FOURCC_stbl)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011782 goto corrupt_file;
11783
11784 /* Parse out svmi (and later st3d/sv3d) atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011785 if (!aml_qtdemux_parse_stereo_svmi_atom (qtdemux, stream, stbl))
zengliang.li5f31ef42024-05-16 08:27:38 +000011786 goto corrupt_file;
11787
11788 /* parse rest of tkhd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011789 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011790 guint32 matrix[9];
11791
11792 /* version 1 uses some 64-bit ints */
11793 if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
11794 goto corrupt_file;
11795
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011796 if (!aml_qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
zengliang.li5f31ef42024-05-16 08:27:38 +000011797 goto corrupt_file;
11798
11799 if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
11800 || !gst_byte_reader_get_uint32_be (&tkhd, &h))
11801 goto corrupt_file;
11802
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011803 aml_qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
zengliang.li5f31ef42024-05-16 08:27:38 +000011804 &stream->stream_tags);
11805 }
11806
11807 /* parse stsd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011808 if (!(stsd = aml_qtdemux_tree_get_child_by_type (stbl, AML_FOURCC_stsd)))
zengliang.li5f31ef42024-05-16 08:27:38 +000011809 goto corrupt_file;
11810 stsd_data = (const guint8 *) stsd->data;
11811
11812 /* stsd should at least have one entry */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011813 stsd_len = AML_QT_UINT32 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011814 if (stsd_len < 24) {
11815 /* .. but skip stream with empty stsd produced by some Vivotek cameras */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011816 if (stream->subtype == AML_FOURCC_vivo) {
11817 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000011818 return TRUE;
11819 } else {
11820 goto corrupt_file;
11821 }
11822 }
11823
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011824 stream->stsd_entries_length = stsd_entry_count = AML_QT_UINT32 (stsd_data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000011825 /* each stsd entry must contain at least 8 bytes */
11826 if (stream->stsd_entries_length == 0
11827 || stream->stsd_entries_length > stsd_len / 8) {
11828 stream->stsd_entries_length = 0;
11829 goto corrupt_file;
11830 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011831 stream->stsd_entries = g_new0 (AmlQtDemuxStreamStsdEntry, stsd_entry_count);
zengliang.li5f31ef42024-05-16 08:27:38 +000011832 GST_LOG_OBJECT (qtdemux, "stsd len: %d", stsd_len);
11833 GST_LOG_OBJECT (qtdemux, "stsd entry count: %u", stsd_entry_count);
11834
11835 stsd_entry_data = stsd_data + 16;
11836 remaining_stsd_len = stsd_len - 16;
11837 for (stsd_index = 0; stsd_index < stsd_entry_count; stsd_index++) {
11838 guint32 fourcc;
11839 gchar *codec = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011840 AmlQtDemuxStreamStsdEntry *entry = &stream->stsd_entries[stsd_index];
zengliang.li5f31ef42024-05-16 08:27:38 +000011841
11842 /* and that entry should fit within stsd */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011843 len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000011844 if (len > remaining_stsd_len)
11845 goto corrupt_file;
11846
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011847 entry->fourcc = fourcc = AML_QT_FOURCC (stsd_entry_data + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000011848 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
11849 GST_FOURCC_ARGS (entry->fourcc));
11850 GST_LOG_OBJECT (qtdemux, "stsd type len: %d", len);
11851
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011852 if ((fourcc == AML_FOURCC_drms) || (fourcc == AML_FOURCC_drmi))
zengliang.li5f31ef42024-05-16 08:27:38 +000011853 goto error_encrypted;
11854
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011855 if (fourcc == AML_FOURCC_aavd) {
11856 if (stream->subtype != AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011857 GST_ERROR_OBJECT (qtdemux,
11858 "Unexpeced stsd type 'aavd' outside 'soun' track");
11859 } else {
11860 /* encrypted audio with sound sample description v0 */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011861 GNode *enc = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000011862 stream->protected = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011863 if (!aml_qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
zengliang.li5f31ef42024-05-16 08:27:38 +000011864 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11865 }
11866 }
11867
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011868 if (fourcc == AML_FOURCC_encv || fourcc == AML_FOURCC_enca) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011869 /* FIXME this looks wrong, there might be multiple children
11870 * with the same type */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011871 GNode *enc = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000011872 stream->protected = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011873 if (!aml_qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
zengliang.li5f31ef42024-05-16 08:27:38 +000011874 GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
11875 }
11876
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011877 if (stream->subtype == AML_FOURCC_vide) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011878 GNode *colr;
11879 GNode *fiel;
11880 GNode *pasp;
11881 gboolean gray;
11882 gint depth, palette_size, palette_count;
11883 guint32 *palette_data = NULL;
11884
11885 entry->sampled = TRUE;
11886
11887 stream->display_width = w >> 16;
11888 stream->display_height = h >> 16;
11889
11890 offset = 16;
11891 if (len < 86) /* TODO verify */
11892 goto corrupt_file;
11893
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011894 entry->width = AML_QT_UINT16 (stsd_entry_data + offset + 16);
11895 entry->height = AML_QT_UINT16 (stsd_entry_data + offset + 18);
zengliang.li5f31ef42024-05-16 08:27:38 +000011896 entry->fps_n = 0; /* this is filled in later */
11897 entry->fps_d = 0; /* this is filled in later */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011898 entry->bits_per_sample = AML_QT_UINT16 (stsd_entry_data + offset + 66);
11899 entry->color_table_id = AML_QT_UINT16 (stsd_entry_data + offset + 68);
zengliang.li5f31ef42024-05-16 08:27:38 +000011900
11901 /* if color_table_id is 0, ctab atom must follow; however some files
11902 * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
11903 * if color table is not present we'll correct the value */
11904 if (entry->color_table_id == 0 &&
11905 (len < 90
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011906 || AML_QT_FOURCC (stsd_entry_data + offset + 70) != AML_FOURCC_ctab)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000011907 entry->color_table_id = -1;
11908 }
11909
11910 GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
11911 entry->width, entry->height, entry->bits_per_sample,
11912 entry->color_table_id);
11913
11914 depth = entry->bits_per_sample;
11915
11916 /* more than 32 bits means grayscale */
11917 gray = (depth > 32);
11918 /* low 32 bits specify the depth */
11919 depth &= 0x1F;
11920
11921 /* different number of palette entries is determined by depth. */
11922 palette_count = 0;
11923 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
11924 palette_count = (1 << depth);
11925 palette_size = palette_count * 4;
11926
11927 if (entry->color_table_id) {
11928 switch (palette_count) {
11929 case 0:
11930 break;
11931 case 2:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011932 palette_data = g_memdup2 (aml_ff_qt_default_palette_2, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011933 break;
11934 case 4:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011935 palette_data = g_memdup2 (aml_ff_qt_default_palette_4, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011936 break;
11937 case 16:
11938 if (gray)
11939 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011940 g_memdup2 (aml_ff_qt_grayscale_palette_16, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011941 else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011942 palette_data = g_memdup2 (aml_ff_qt_default_palette_16, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011943 break;
11944 case 256:
11945 if (gray)
11946 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011947 g_memdup2 (aml_ff_qt_grayscale_palette_256, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011948 else
11949 palette_data =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011950 g_memdup2 (aml_ff_qt_default_palette_256, palette_size);
zengliang.li5f31ef42024-05-16 08:27:38 +000011951 break;
11952 default:
11953 GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
11954 (_("The video in this file might not play correctly.")),
11955 ("unsupported palette depth %d", depth));
11956 break;
11957 }
11958 } else {
11959 guint i, j, start, end;
11960
11961 if (len < 94)
11962 goto corrupt_file;
11963
11964 /* read table */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011965 start = AML_QT_UINT32 (stsd_entry_data + offset + 70);
11966 palette_count = AML_QT_UINT16 (stsd_entry_data + offset + 74);
11967 end = AML_QT_UINT16 (stsd_entry_data + offset + 76);
zengliang.li5f31ef42024-05-16 08:27:38 +000011968
11969 GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
11970 start, end, palette_count);
11971
11972 if (end > 255)
11973 end = 255;
11974 if (start > end)
11975 start = end;
11976
11977 if (len < 94 + (end - start) * 8)
11978 goto corrupt_file;
11979
11980 /* palette is always the same size */
11981 palette_data = g_malloc0 (256 * 4);
11982 palette_size = 256 * 4;
11983
11984 for (j = 0, i = start; i <= end; j++, i++) {
11985 guint32 a, r, g, b;
11986
zengliang.lia6c0cdd2024-05-18 07:15:51 +000011987 a = AML_QT_UINT16 (stsd_entry_data + offset + 78 + (j * 8));
11988 r = AML_QT_UINT16 (stsd_entry_data + offset + 80 + (j * 8));
11989 g = AML_QT_UINT16 (stsd_entry_data + offset + 82 + (j * 8));
11990 b = AML_QT_UINT16 (stsd_entry_data + offset + 84 + (j * 8));
zengliang.li5f31ef42024-05-16 08:27:38 +000011991
11992 palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
11993 (g & 0xff00) | (b >> 8);
11994 }
11995 }
11996
11997 if (entry->caps)
11998 gst_caps_unref (entry->caps);
11999
12000 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012001 aml_qtdemux_video_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000012002 &codec);
12003 if (G_UNLIKELY (!entry->caps)) {
12004 g_free (palette_data);
12005 goto unknown_stream;
12006 }
12007
12008 if (codec) {
12009 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
12010 GST_TAG_VIDEO_CODEC, codec, NULL);
12011 g_free (codec);
12012 codec = NULL;
12013 }
12014
12015 if (palette_data) {
12016 GstStructure *s;
12017
12018 if (entry->rgb8_palette)
12019 gst_memory_unref (entry->rgb8_palette);
12020 entry->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
12021 palette_data, palette_size, 0, palette_size, palette_data, g_free);
12022
12023 s = gst_caps_get_structure (entry->caps, 0);
12024
12025 /* non-raw video has a palette_data property. raw video has the palette as
12026 * an extra plane that we append to the output buffers before we push
12027 * them*/
12028 if (!gst_structure_has_name (s, "video/x-raw")) {
12029 GstBuffer *palette;
12030
12031 palette = gst_buffer_new ();
12032 gst_buffer_append_memory (palette, entry->rgb8_palette);
12033 entry->rgb8_palette = NULL;
12034
12035 gst_caps_set_simple (entry->caps, "palette_data",
12036 GST_TYPE_BUFFER, palette, NULL);
12037 gst_buffer_unref (palette);
12038 }
12039 } else if (palette_count != 0) {
12040 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
12041 (NULL), ("Unsupported palette depth %d", depth));
12042 }
12043
12044 GST_LOG_OBJECT (qtdemux, "frame count: %u",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012045 AML_QT_UINT16 (stsd_entry_data + offset + 32));
zengliang.li5f31ef42024-05-16 08:27:38 +000012046
12047 esds = NULL;
12048 pasp = NULL;
12049 colr = NULL;
12050 fiel = NULL;
12051 /* pick 'the' stsd child */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012052 mp4v = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012053 // We should skip parsing the stsd for non-protected streams if
12054 // the entry doesn't match the fourcc, since they don't change
12055 // format. However, for protected streams we can have partial
12056 // encryption, where parts of the stream are encrypted and parts
12057 // not. For both parts of such streams, we should ensure the
12058 // esds overrides are parsed for both from the stsd.
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012059 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4v) != fourcc) {
12060 if (stream->protected && AML_QTDEMUX_TREE_NODE_FOURCC (mp4v) != AML_FOURCC_encv)
zengliang.li5f31ef42024-05-16 08:27:38 +000012061 mp4v = NULL;
12062 else if (!stream->protected)
12063 mp4v = NULL;
12064 }
12065
12066 if (mp4v) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012067 esds = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_esds);
12068 pasp = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_pasp);
12069 colr = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_colr);
12070 fiel = aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_fiel);
zengliang.li5f31ef42024-05-16 08:27:38 +000012071 }
12072
12073 if (pasp) {
12074 const guint8 *pasp_data = (const guint8 *) pasp->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012075 guint len = AML_QT_UINT32 (pasp_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012076
12077 if (len == 16) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012078 AML_CUR_STREAM (stream)->par_w = AML_QT_UINT32 (pasp_data + 8);
12079 AML_CUR_STREAM (stream)->par_h = AML_QT_UINT32 (pasp_data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000012080 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012081 AML_CUR_STREAM (stream)->par_w = 0;
12082 AML_CUR_STREAM (stream)->par_h = 0;
zengliang.li5f31ef42024-05-16 08:27:38 +000012083 }
12084 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012085 AML_CUR_STREAM (stream)->par_w = 0;
12086 AML_CUR_STREAM (stream)->par_h = 0;
zengliang.li5f31ef42024-05-16 08:27:38 +000012087 }
12088
12089 if (fiel) {
12090 const guint8 *fiel_data = (const guint8 *) fiel->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012091 guint len = AML_QT_UINT32 (fiel_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012092
12093 if (len == 10) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012094 AML_CUR_STREAM (stream)->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
12095 AML_CUR_STREAM (stream)->field_order = GST_READ_UINT8 (fiel_data + 9);
zengliang.li5f31ef42024-05-16 08:27:38 +000012096 }
12097 }
12098
12099 if (colr) {
12100 const guint8 *colr_data = (const guint8 *) colr->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012101 guint len = AML_QT_UINT32 (colr_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012102
12103 if (len == 19 || len == 18) {
12104 guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);
12105
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012106 if (color_type == AML_FOURCC_nclx || color_type == AML_FOURCC_nclc) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012107 guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
12108 guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
12109 guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
12110 gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
12111
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012112 AML_CUR_STREAM (stream)->colorimetry.primaries =
12113 gst_aml_video_color_primaries_from_iso_compat (primaries);
12114 AML_CUR_STREAM (stream)->colorimetry.transfer =
12115 gst_aml_video_transfer_function_from_iso_compat (transfer_function);
12116 AML_CUR_STREAM (stream)->colorimetry.matrix =
12117 gst_aml_video_color_matrix_from_iso_compat (matrix);
12118 AML_CUR_STREAM (stream)->colorimetry.range =
zengliang.li5f31ef42024-05-16 08:27:38 +000012119 full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
12120 GST_VIDEO_COLOR_RANGE_16_235;
12121 } else {
12122 GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
12123 }
12124 } else {
12125 GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
12126 }
12127 }
12128
12129 if (esds) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012130 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000012131 stream->stream_tags);
12132 } else {
12133 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012134 case AML_FOURCC_H264:
12135 case AML_FOURCC_avc1:
12136 case AML_FOURCC_avc3:
12137 case AML_FOURCC_dav1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012138 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012139 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012140 len = len <= 0x56 ? 0 : len - 0x56;
12141 const guint8 *avc_data = stsd_entry_data + 0x56;
12142
12143 /* find avcC */
12144 while (len >= 0x8) {
12145 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012146 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000012147
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012148 if (AML_QT_UINT32 (avc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012149 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012150 else if (AML_QT_UINT32 (avc_data) <= len)
12151 size = AML_QT_UINT32 (avc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012152 else
12153 size = len - 0x8;
12154
12155 if (size < 1)
12156 /* No real data, so break out */
12157 break;
12158
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012159 dvconfig = AML_QT_FOURCC (avc_data + 0x4);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012160 switch (dvconfig) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012161 case AML_FOURCC_dvcC:
12162 case AML_FOURCC_dvvC:
12163 case AML_FOURCC_dvwC:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012164 {
bo.xiao4c245f22024-08-22 11:18:25 +080012165 gint ret = aml_qtdemux_parse_dvcc (fourcc, dvconfig, avc_data, entry->caps);
12166 if (ret == -1)
12167 {
12168 GST_WARNING_OBJECT (qtdemux, "reject play!");
12169 goto corrupt_file;
12170 }
12171
zengliang.lib0f27cd2024-05-17 06:58:54 +000012172 break;
12173 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012174 case AML_FOURCC_avcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012175 {
12176 /* parse, if found */
12177 GstBuffer *buf;
12178
12179 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
12180
12181 /* First 4 bytes are the length of the atom, the next 4 bytes
12182 * are the fourcc, the next 1 byte is the version, and the
12183 * subsequent bytes are profile_tier_level structure like data. */
12184 gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
12185 avc_data + 8 + 1, size - 1);
12186 buf = gst_buffer_new_and_alloc (size);
12187 gst_buffer_fill (buf, 0, avc_data + 0x8, size);
12188 gst_caps_set_simple (entry->caps,
12189 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12190 gst_buffer_unref (buf);
12191
12192 break;
12193 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012194 case AML_FOURCC_av1C:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012195 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012196 GST_DEBUG_OBJECT (qtdemux, "found av1C in stsd,so try to find sgpd");
zengliang.lib0f27cd2024-05-17 06:58:54 +000012197
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012198 if (aml_qtdemux_parse_sgpd_av1M(qtdemux, stbl) == -1)
12199 {
12200 GST_WARNING_OBJECT (qtdemux, "reject play for invalid metadata_type and specific_parameters");
12201 goto corrupt_file;
12202 }
12203 break;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012204 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012205 case AML_FOURCC_strf:
zengliang.li5f31ef42024-05-16 08:27:38 +000012206 {
12207 GstBuffer *buf;
12208
12209 GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
12210
12211 /* First 4 bytes are the length of the atom, the next 4 bytes
12212 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
12213 * next 1 byte is the version, and the
12214 * subsequent bytes are sequence parameter set like data. */
12215
12216 size -= 40; /* we'll be skipping BITMAPINFOHEADER */
12217 if (size > 1) {
12218 gst_codec_utils_h264_caps_set_level_and_profile
12219 (entry->caps, avc_data + 8 + 40 + 1, size - 1);
12220
12221 buf = gst_buffer_new_and_alloc (size);
12222 gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
12223 gst_caps_set_simple (entry->caps,
12224 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12225 gst_buffer_unref (buf);
12226 }
12227 break;
12228 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012229 case AML_FOURCC_btrt:
zengliang.li5f31ef42024-05-16 08:27:38 +000012230 {
12231 guint avg_bitrate, max_bitrate;
12232
12233 /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
12234 if (size < 12)
12235 break;
12236
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012237 max_bitrate = AML_QT_UINT32 (avc_data + 0xc);
12238 avg_bitrate = AML_QT_UINT32 (avc_data + 0x10);
zengliang.li5f31ef42024-05-16 08:27:38 +000012239
12240 if (!max_bitrate && !avg_bitrate)
12241 break;
12242
12243 /* Some muxers seem to swap the average and maximum bitrates
12244 * (I'm looking at you, YouTube), so we swap for sanity. */
12245 if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
12246 guint temp = avg_bitrate;
12247
12248 avg_bitrate = max_bitrate;
12249 max_bitrate = temp;
12250 }
12251
12252 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
12253 gst_tag_list_add (stream->stream_tags,
12254 GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE,
12255 max_bitrate, NULL);
12256 }
12257 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
12258 gst_tag_list_add (stream->stream_tags,
12259 GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, avg_bitrate,
12260 NULL);
12261 }
12262
12263 break;
12264 }
12265
12266 default:
12267 break;
12268 }
12269
12270 len -= size + 8;
12271 avc_data += size + 8;
12272 }
12273
12274 break;
12275 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012276 case AML_FOURCC_H265:
12277 case AML_FOURCC_hvc1:
12278 case AML_FOURCC_hev1:
12279 case AML_FOURCC_dvh1:
12280 case AML_FOURCC_dvhe:
zengliang.li5f31ef42024-05-16 08:27:38 +000012281 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012282 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012283 len = len <= 0x56 ? 0 : len - 0x56;
12284 const guint8 *hevc_data = stsd_entry_data + 0x56;
12285
12286 /* find hevc */
12287 while (len >= 0x8) {
12288 guint size;
zengliang.lib0f27cd2024-05-17 06:58:54 +000012289 guint32 dvconfig;
zengliang.li5f31ef42024-05-16 08:27:38 +000012290
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012291 if (AML_QT_UINT32 (hevc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012292 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012293 else if (AML_QT_UINT32 (hevc_data) <= len)
12294 size = AML_QT_UINT32 (hevc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012295 else
12296 size = len - 0x8;
12297
12298 if (size < 1)
12299 /* No real data, so break out */
12300 break;
12301
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012302 dvconfig = AML_QT_FOURCC (hevc_data + 0x4);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012303
12304 switch (dvconfig) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012305 case AML_FOURCC_dvcC:
12306 case AML_FOURCC_dvvC:
12307 case AML_FOURCC_dvwC:
12308 case AML_FOURCC_dvzC:
12309 case AML_FOURCC_dvxC:
zengliang.lib0f27cd2024-05-17 06:58:54 +000012310 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012311 gint ret = aml_qtdemux_parse_dvcc (fourcc, dvconfig, hevc_data, entry->caps);
zengliang.lib0f27cd2024-05-17 06:58:54 +000012312 if (ret == -1)
12313 {
12314 GST_WARNING_OBJECT (qtdemux, "reject play!");
12315 goto corrupt_file;
12316 }
12317 break;
12318 }
12319
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012320 case AML_FOURCC_hvcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012321 {
12322 /* parse, if found */
12323 GstBuffer *buf;
12324
12325 GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
12326
12327 /* First 4 bytes are the length of the atom, the next 4 bytes
12328 * are the fourcc, the next 1 byte is the version, and the
12329 * subsequent bytes are sequence parameter set like data. */
12330 gst_codec_utils_h265_caps_set_level_tier_and_profile
12331 (entry->caps, hevc_data + 8 + 1, size - 1);
12332
12333 buf = gst_buffer_new_and_alloc (size);
12334 gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
12335 gst_caps_set_simple (entry->caps,
12336 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12337 gst_buffer_unref (buf);
12338 break;
12339 }
12340 default:
12341 break;
12342 }
12343 len -= size + 8;
12344 hevc_data += size + 8;
12345 }
12346 break;
12347 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012348 case AML_FOURCC_mp4v:
12349 case AML_FOURCC_MP4V:
12350 case AML_FOURCC_fmp4:
12351 case AML_FOURCC_FMP4:
12352 case AML_FOURCC_xvid:
12353 case AML_FOURCC_XVID:
zengliang.li5f31ef42024-05-16 08:27:38 +000012354 {
12355 GNode *glbl;
12356
12357 GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
12358 GST_FOURCC_ARGS (fourcc));
12359
12360 /* codec data might be in glbl extension atom */
12361 glbl = mp4v ?
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012362 aml_qtdemux_tree_get_child_by_type (mp4v, AML_FOURCC_glbl) : NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000012363 if (glbl) {
12364 guint8 *data;
12365 GstBuffer *buf;
12366 guint len;
12367
12368 GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
12369 data = glbl->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012370 len = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012371 if (len > 0x8) {
12372 len -= 0x8;
12373 buf = gst_buffer_new_and_alloc (len);
12374 gst_buffer_fill (buf, 0, data + 8, len);
12375 gst_caps_set_simple (entry->caps,
12376 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12377 gst_buffer_unref (buf);
12378 }
12379 }
12380 break;
12381 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012382 case AML_FOURCC_mjp2:
zengliang.li5f31ef42024-05-16 08:27:38 +000012383 {
12384 /* see annex I of the jpeg2000 spec */
12385 GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
12386 const guint8 *data;
12387 const gchar *colorspace = NULL;
12388 gint ncomp = 0;
12389 guint32 ncomp_map = 0;
12390 gint32 *comp_map = NULL;
12391 guint32 nchan_def = 0;
12392 gint32 *chan_def = NULL;
12393
12394 GST_DEBUG_OBJECT (qtdemux, "found mjp2");
12395 /* some required atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012396 mjp2 = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012397 if (!mjp2)
12398 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012399 jp2h = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_jp2h);
zengliang.li5f31ef42024-05-16 08:27:38 +000012400 if (!jp2h)
12401 break;
12402
12403 /* number of components; redundant with info in codestream, but useful
12404 to a muxer */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012405 ihdr = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_ihdr);
12406 if (!ihdr || AML_QT_UINT32 (ihdr->data) != 22)
zengliang.li5f31ef42024-05-16 08:27:38 +000012407 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012408 ncomp = AML_QT_UINT16 (((guint8 *) ihdr->data) + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000012409
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012410 colr = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_colr);
zengliang.li5f31ef42024-05-16 08:27:38 +000012411 if (!colr)
12412 break;
12413 GST_DEBUG_OBJECT (qtdemux, "found colr");
12414 /* extract colour space info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012415 if (AML_QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
12416 switch (AML_QT_UINT32 ((guint8 *) colr->data + 11)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012417 case 16:
12418 colorspace = "sRGB";
12419 break;
12420 case 17:
12421 colorspace = "GRAY";
12422 break;
12423 case 18:
12424 colorspace = "sYUV";
12425 break;
12426 default:
12427 colorspace = NULL;
12428 break;
12429 }
12430 }
12431 if (!colorspace)
12432 /* colr is required, and only values 16, 17, and 18 are specified,
12433 so error if we have no colorspace */
12434 break;
12435
12436 /* extract component mapping */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012437 cmap = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_cmap);
zengliang.li5f31ef42024-05-16 08:27:38 +000012438 if (cmap) {
12439 guint32 cmap_len = 0;
12440 int i;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012441 cmap_len = AML_QT_UINT32 (cmap->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012442 if (cmap_len >= 8) {
12443 /* normal box, subtract off header */
12444 cmap_len -= 8;
12445 /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
12446 if (cmap_len % 4 == 0) {
12447 ncomp_map = (cmap_len / 4);
12448 comp_map = g_new0 (gint32, ncomp_map);
12449 for (i = 0; i < ncomp_map; i++) {
12450 guint16 cmp;
12451 guint8 mtyp, pcol;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012452 cmp = AML_QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
12453 mtyp = AML_QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
12454 pcol = AML_QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
zengliang.li5f31ef42024-05-16 08:27:38 +000012455 comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
12456 }
12457 }
12458 }
12459 }
12460 /* extract channel definitions */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012461 cdef = aml_qtdemux_tree_get_child_by_type (jp2h, AML_FOURCC_cdef);
zengliang.li5f31ef42024-05-16 08:27:38 +000012462 if (cdef) {
12463 guint32 cdef_len = 0;
12464 int i;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012465 cdef_len = AML_QT_UINT32 (cdef->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012466 if (cdef_len >= 10) {
12467 /* normal box, subtract off header and len */
12468 cdef_len -= 10;
12469 /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
12470 if (cdef_len % 6 == 0) {
12471 nchan_def = (cdef_len / 6);
12472 chan_def = g_new0 (gint32, nchan_def);
12473 for (i = 0; i < nchan_def; i++)
12474 chan_def[i] = -1;
12475 for (i = 0; i < nchan_def; i++) {
12476 guint16 cn, typ, asoc;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012477 cn = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
12478 typ = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
12479 asoc = AML_QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
zengliang.li5f31ef42024-05-16 08:27:38 +000012480 if (cn < nchan_def) {
12481 switch (typ) {
12482 case 0:
12483 chan_def[cn] = asoc;
12484 break;
12485 case 1:
12486 chan_def[cn] = 0; /* alpha */
12487 break;
12488 default:
12489 chan_def[cn] = -typ;
12490 }
12491 }
12492 }
12493 }
12494 }
12495 }
12496
12497 gst_caps_set_simple (entry->caps,
12498 "num-components", G_TYPE_INT, ncomp, NULL);
12499 gst_caps_set_simple (entry->caps,
12500 "colorspace", G_TYPE_STRING, colorspace, NULL);
12501
12502 if (comp_map) {
12503 GValue arr = { 0, };
12504 GValue elt = { 0, };
12505 int i;
12506 g_value_init (&arr, GST_TYPE_ARRAY);
12507 g_value_init (&elt, G_TYPE_INT);
12508 for (i = 0; i < ncomp_map; i++) {
12509 g_value_set_int (&elt, comp_map[i]);
12510 gst_value_array_append_value (&arr, &elt);
12511 }
12512 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12513 "component-map", &arr);
12514 g_value_unset (&elt);
12515 g_value_unset (&arr);
12516 g_free (comp_map);
12517 }
12518
12519 if (chan_def) {
12520 GValue arr = { 0, };
12521 GValue elt = { 0, };
12522 int i;
12523 g_value_init (&arr, GST_TYPE_ARRAY);
12524 g_value_init (&elt, G_TYPE_INT);
12525 for (i = 0; i < nchan_def; i++) {
12526 g_value_set_int (&elt, chan_def[i]);
12527 gst_value_array_append_value (&arr, &elt);
12528 }
12529 gst_structure_set_value (gst_caps_get_structure (entry->caps, 0),
12530 "channel-definitions", &arr);
12531 g_value_unset (&elt);
12532 g_value_unset (&arr);
12533 g_free (chan_def);
12534 }
12535
12536 /* some optional atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012537 field = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_fiel);
12538 prefix = aml_qtdemux_tree_get_child_by_type (mjp2, AML_FOURCC_jp2x);
zengliang.li5f31ef42024-05-16 08:27:38 +000012539
12540 /* indicate possible fields in caps */
12541 if (field) {
12542 data = (guint8 *) field->data + 8;
12543 if (*data != 1)
12544 gst_caps_set_simple (entry->caps, "fields", G_TYPE_INT,
12545 (gint) * data, NULL);
12546 }
12547 /* add codec_data if provided */
12548 if (prefix) {
12549 GstBuffer *buf;
12550 guint len;
12551
12552 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
12553 data = prefix->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012554 len = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012555 if (len > 0x8) {
12556 len -= 0x8;
12557 buf = gst_buffer_new_and_alloc (len);
12558 gst_buffer_fill (buf, 0, data + 8, len);
12559 gst_caps_set_simple (entry->caps,
12560 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12561 gst_buffer_unref (buf);
12562 }
12563 }
12564 break;
12565 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012566 case AML_FOURCC_SVQ3:
12567 case AML_FOURCC_VP31:
zengliang.li5f31ef42024-05-16 08:27:38 +000012568 {
12569 GstBuffer *buf;
12570 GstBuffer *seqh = NULL;
12571 const guint8 *gamma_data = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012572 guint len = AML_QT_UINT32 (stsd_data); /* FIXME review - why put the whole stsd in codec data? */
zengliang.li5f31ef42024-05-16 08:27:38 +000012573
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012574 aml_qtdemux_parse_svq3_stsd_data (qtdemux, stsd_entry_data, &gamma_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000012575 &seqh);
12576 if (gamma_data) {
12577 gst_caps_set_simple (entry->caps, "applied-gamma", G_TYPE_DOUBLE,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012578 AML_QT_FP32 (gamma_data), NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +000012579 }
12580 if (seqh) {
12581 /* sorry for the bad name, but we don't know what this is, other
12582 * than its own fourcc */
12583 gst_caps_set_simple (entry->caps, "seqh", GST_TYPE_BUFFER, seqh,
12584 NULL);
12585 gst_buffer_unref (seqh);
12586 }
12587
12588 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
12589 buf = gst_buffer_new_and_alloc (len);
12590 gst_buffer_fill (buf, 0, stsd_data, len);
12591 gst_caps_set_simple (entry->caps,
12592 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12593 gst_buffer_unref (buf);
12594 break;
12595 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012596 case AML_FOURCC_jpeg:
zengliang.li5f31ef42024-05-16 08:27:38 +000012597 {
12598 /* https://developer.apple.com/standards/qtff-2001.pdf,
12599 * page 92, "Video Sample Description", under table 3.1 */
12600 GstByteReader br;
12601
12602 const gint compressor_offset =
12603 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
12604 const gint min_size = compressor_offset + 32 + 2 + 2;
12605 GNode *jpeg;
12606 guint32 len;
12607 guint16 color_table_id = 0;
12608 gboolean ok;
12609
12610 GST_DEBUG_OBJECT (qtdemux, "found jpeg");
12611
12612 /* recover information on interlaced/progressive */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012613 jpeg = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_jpeg);
zengliang.li5f31ef42024-05-16 08:27:38 +000012614 if (!jpeg)
12615 break;
12616
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012617 len = AML_QT_UINT32 (jpeg->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012618 GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
12619 min_size);
12620 if (len >= min_size) {
12621 gst_byte_reader_init (&br, jpeg->data, len);
12622
12623 gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
12624 gst_byte_reader_get_uint16_le (&br, &color_table_id);
12625 if (color_table_id != 0) {
12626 /* the spec says there can be concatenated chunks in the data, and we want
12627 * to find one called field. Walk through them. */
12628 gint offset = min_size;
12629 while (offset + 8 < len) {
12630 guint32 size = 0, tag;
12631 ok = gst_byte_reader_get_uint32_le (&br, &size);
12632 ok &= gst_byte_reader_get_uint32_le (&br, &tag);
12633 if (!ok || size < 8) {
12634 GST_WARNING_OBJECT (qtdemux,
12635 "Failed to walk optional chunk list");
12636 break;
12637 }
12638 GST_DEBUG_OBJECT (qtdemux,
12639 "Found optional %4.4s chunk, size %u",
12640 (const char *) &tag, size);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012641 if (tag == AML_FOURCC_fiel) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012642 guint8 n_fields = 0, ordering = 0;
12643 gst_byte_reader_get_uint8 (&br, &n_fields);
12644 gst_byte_reader_get_uint8 (&br, &ordering);
12645 if (n_fields == 1 || n_fields == 2) {
12646 GST_DEBUG_OBJECT (qtdemux,
12647 "Found fiel tag with %u fields, ordering %u",
12648 n_fields, ordering);
12649 if (n_fields == 2)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012650 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps,
zengliang.li5f31ef42024-05-16 08:27:38 +000012651 "interlace-mode", G_TYPE_STRING, "interleaved",
12652 NULL);
12653 } else {
12654 GST_WARNING_OBJECT (qtdemux,
12655 "Found fiel tag with invalid fields (%u)", n_fields);
12656 }
12657 }
12658 offset += size;
12659 }
12660 } else {
12661 GST_DEBUG_OBJECT (qtdemux,
12662 "Color table ID is 0, not trying to get interlacedness");
12663 }
12664 } else {
12665 GST_WARNING_OBJECT (qtdemux,
12666 "Length of jpeg chunk is too small, not trying to get interlacedness");
12667 }
12668
12669 break;
12670 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012671 case AML_FOURCC_rle_:
12672 case AML_FOURCC_WRLE:
zengliang.li5f31ef42024-05-16 08:27:38 +000012673 {
12674 gst_caps_set_simple (entry->caps,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012675 "depth", G_TYPE_INT, AML_QT_UINT16 (stsd_entry_data + offset + 66),
zengliang.li5f31ef42024-05-16 08:27:38 +000012676 NULL);
12677 break;
12678 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012679 case AML_FOURCC_XiTh:
zengliang.li5f31ef42024-05-16 08:27:38 +000012680 {
12681 GNode *xith, *xdxt;
12682
12683 GST_DEBUG_OBJECT (qtdemux, "found XiTh");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012684 xith = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012685 if (!xith)
12686 break;
12687
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012688 xdxt = aml_qtdemux_tree_get_child_by_type (xith, AML_FOURCC_XdxT);
zengliang.li5f31ef42024-05-16 08:27:38 +000012689 if (!xdxt)
12690 break;
12691
12692 GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
12693 /* collect the headers and store them in a stream list so that we can
12694 * send them out first */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012695 aml_qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
zengliang.li5f31ef42024-05-16 08:27:38 +000012696 break;
12697 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012698 case AML_FOURCC_ovc1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012699 {
12700 GNode *ovc1;
12701 guint8 *ovc1_data;
12702 guint ovc1_len;
12703 GstBuffer *buf;
12704
12705 GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012706 ovc1 = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
zengliang.li5f31ef42024-05-16 08:27:38 +000012707 if (!ovc1)
12708 break;
12709 ovc1_data = ovc1->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012710 ovc1_len = AML_QT_UINT32 (ovc1_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012711 if (ovc1_len <= 198) {
12712 GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
12713 break;
12714 }
12715 buf = gst_buffer_new_and_alloc (ovc1_len - 198);
12716 gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
12717 gst_caps_set_simple (entry->caps,
12718 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12719 gst_buffer_unref (buf);
12720 break;
12721 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012722 case AML_FOURCC_vc_1:
zengliang.li5f31ef42024-05-16 08:27:38 +000012723 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012724 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012725 len = len <= 0x56 ? 0 : len - 0x56;
12726 const guint8 *vc1_data = stsd_entry_data + 0x56;
12727
12728 /* find dvc1 */
12729 while (len >= 8) {
12730 guint size;
12731
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012732 if (AML_QT_UINT32 (vc1_data) <= 8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012733 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012734 else if (AML_QT_UINT32 (vc1_data) <= len)
12735 size = AML_QT_UINT32 (vc1_data) - 8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012736 else
12737 size = len - 8;
12738
12739 if (size < 1)
12740 /* No real data, so break out */
12741 break;
12742
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012743 switch (AML_QT_FOURCC (vc1_data + 0x4)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000012744 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
12745 {
12746 GstBuffer *buf;
12747
12748 GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
12749 buf = gst_buffer_new_and_alloc (size);
12750 gst_buffer_fill (buf, 0, vc1_data + 8, size);
12751 gst_caps_set_simple (entry->caps,
12752 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12753 gst_buffer_unref (buf);
12754 break;
12755 }
12756 default:
12757 break;
12758 }
12759 len -= size + 8;
12760 vc1_data += size + 8;
12761 }
12762 break;
12763 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012764 case AML_FOURCC_av01:
zengliang.li5f31ef42024-05-16 08:27:38 +000012765 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012766 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012767 len = len <= 0x56 ? 0 : len - 0x56;
12768 const guint8 *av1_data = stsd_entry_data + 0x56;
12769
12770 /* find av1C */
12771 while (len >= 0x8) {
12772 guint size;
12773
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012774 if (AML_QT_UINT32 (av1_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012775 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012776 else if (AML_QT_UINT32 (av1_data) <= len)
12777 size = AML_QT_UINT32 (av1_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012778 else
12779 size = len - 0x8;
12780
12781 if (size < 1)
12782 /* No real data, so break out */
12783 break;
12784
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012785 switch (AML_QT_FOURCC (av1_data + 0x4)) {
12786 case AML_FOURCC_av1C:
zengliang.li5f31ef42024-05-16 08:27:38 +000012787 {
12788 /* parse, if found */
12789 GstBuffer *buf;
12790
12791 GST_DEBUG_OBJECT (qtdemux,
12792 "found av1C codec_data in stsd of size %d", size);
12793
12794 /* not enough data, just ignore and hope for the best */
12795 if (size < 4)
12796 break;
12797
12798 /* Content is:
12799 * 4 bytes: atom length
12800 * 4 bytes: fourcc
12801 *
12802 * version 1 (marker=1):
12803 *
12804 * unsigned int (1) marker = 1;
12805 * unsigned int (7) version = 1;
12806 * unsigned int (3) seq_profile;
12807 * unsigned int (5) seq_level_idx_0;
12808 * unsigned int (1) seq_tier_0;
12809 * unsigned int (1) high_bitdepth;
12810 * unsigned int (1) twelve_bit;
12811 * unsigned int (1) monochrome;
12812 * unsigned int (1) chroma_subsampling_x;
12813 * unsigned int (1) chroma_subsampling_y;
12814 * unsigned int (2) chroma_sample_position;
12815 * unsigned int (3) reserved = 0;
12816 *
12817 * unsigned int (1) initial_presentation_delay_present;
12818 * if (initial_presentation_delay_present) {
12819 * unsigned int (4) initial_presentation_delay_minus_one;
12820 * } else {
12821 * unsigned int (4) reserved = 0;
12822 * }
12823 *
12824 * unsigned int (8) configOBUs[];
12825 *
12826 * rest: OBUs.
12827 */
12828
12829 switch (av1_data[8]) {
12830 case 0x81:{
12831 guint8 pres_delay_field;
12832
12833 /* We let profile and the other parts be figured out by
12834 * av1parse and only include the presentation delay here
12835 * if present */
12836 /* We skip initial_presentation_delay* for now */
12837 pres_delay_field = *(av1_data + 11);
12838 if (pres_delay_field & (1 << 5)) {
12839 gst_caps_set_simple (entry->caps,
12840 "presentation-delay", G_TYPE_INT,
12841 (gint) (pres_delay_field & 0x0F) + 1, NULL);
12842 }
12843
12844 buf = gst_buffer_new_and_alloc (size);
12845 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
12846 gst_buffer_fill (buf, 0, av1_data + 8, size);
12847 gst_caps_set_simple (entry->caps,
12848 "codec_data", GST_TYPE_BUFFER, buf, NULL);
12849 gst_buffer_unref (buf);
12850 break;
12851 }
12852 default:
12853 GST_WARNING ("Unknown version 0x%02x of av1C box",
12854 av1_data[8]);
12855 break;
12856 }
12857
12858 break;
12859 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012860 case AML_FOURCC_dvcC:
12861 case AML_FOURCC_dvvC:
12862 case AML_FOURCC_dvwC:
zengliang.li54713f02024-05-17 02:46:54 +000012863 {
bo.xiao4c245f22024-08-22 11:18:25 +080012864 gint ret = aml_qtdemux_parse_dvcc (fourcc, AML_QT_FOURCC (av1_data + 0x4), av1_data, entry->caps);
12865 if (ret == -1)
12866 {
12867 GST_WARNING_OBJECT (qtdemux, "reject play!");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012868 goto corrupt_file;
zengliang.li54713f02024-05-17 02:46:54 +000012869 }
bo.xiao4c245f22024-08-22 11:18:25 +080012870 break;
12871 }
zengliang.li5f31ef42024-05-16 08:27:38 +000012872 default:
bo.xiao4c245f22024-08-22 11:18:25 +080012873 GST_DEBUG_OBJECT (qtdemux, "find fourCC =0x%x", AML_QT_FOURCC (av1_data + 0x4));
zengliang.li5f31ef42024-05-16 08:27:38 +000012874 break;
12875 }
12876
12877 len -= size + 8;
12878 av1_data += size + 8;
12879 }
12880
12881 break;
12882 }
12883
12884 /* TODO: Need to parse vpcC for VP8 codec too.
12885 * Note that VPCodecConfigurationBox (vpcC) is defined for
12886 * vp08, vp09, and vp10 fourcc. */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012887 case AML_FOURCC_vp09:
zengliang.li5f31ef42024-05-16 08:27:38 +000012888 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012889 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000012890 len = len <= 0x56 ? 0 : len - 0x56;
12891 const guint8 *vpcc_data = stsd_entry_data + 0x56;
12892
12893 /* find vpcC */
12894 while (len >= 0x8) {
12895 guint size;
12896
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012897 if (AML_QT_UINT32 (vpcc_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000012898 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012899 else if (AML_QT_UINT32 (vpcc_data) <= len)
12900 size = AML_QT_UINT32 (vpcc_data) - 0x8;
zengliang.li5f31ef42024-05-16 08:27:38 +000012901 else
12902 size = len - 0x8;
12903
12904 if (size < 1)
12905 /* No real data, so break out */
12906 break;
12907
zengliang.lia6c0cdd2024-05-18 07:15:51 +000012908 switch (AML_QT_FOURCC (vpcc_data + 0x4)) {
12909 case AML_FOURCC_vpcC:
zengliang.li5f31ef42024-05-16 08:27:38 +000012910 {
12911 const gchar *profile_str = NULL;
12912 const gchar *chroma_format_str = NULL;
12913 guint8 profile;
12914 guint8 bitdepth;
12915 guint8 chroma_format;
12916 GstVideoColorimetry cinfo;
12917
12918 /* parse, if found */
12919 GST_DEBUG_OBJECT (qtdemux,
12920 "found vp codec_data in stsd of size %d", size);
12921
12922 /* the meaning of "size" is length of the atom body, excluding
12923 * atom length and fourcc fields */
12924 if (size < 12)
12925 break;
12926
12927 /* Content is:
12928 * 4 bytes: atom length
12929 * 4 bytes: fourcc
12930 * 1 byte: version
12931 * 3 bytes: flags
12932 * 1 byte: profile
12933 * 1 byte: level
12934 * 4 bits: bitDepth
12935 * 3 bits: chromaSubsampling
12936 * 1 bit: videoFullRangeFlag
12937 * 1 byte: colourPrimaries
12938 * 1 byte: transferCharacteristics
12939 * 1 byte: matrixCoefficients
12940 * 2 bytes: codecInitializationDataSize (should be zero for vp8 and vp9)
12941 * rest: codecInitializationData (not used for vp8 and vp9)
12942 */
12943
12944 if (vpcc_data[8] != 1) {
12945 GST_WARNING_OBJECT (qtdemux,
12946 "unknown vpcC version %d", vpcc_data[8]);
12947 break;
12948 }
12949
12950 profile = vpcc_data[12];
12951 switch (profile) {
12952 case 0:
12953 profile_str = "0";
12954 break;
12955 case 1:
12956 profile_str = "1";
12957 break;
12958 case 2:
12959 profile_str = "2";
12960 break;
12961 case 3:
12962 profile_str = "3";
12963 break;
12964 default:
12965 break;
12966 }
12967
12968 if (profile_str) {
12969 gst_caps_set_simple (entry->caps,
12970 "profile", G_TYPE_STRING, profile_str, NULL);
12971 }
12972
12973 /* skip level, the VP9 spec v0.6 defines only one level atm,
12974 * but webm spec define various ones. Add level to caps
12975 * if we really need it then */
12976
12977 bitdepth = (vpcc_data[14] & 0xf0) >> 4;
12978 if (bitdepth == 8 || bitdepth == 10 || bitdepth == 12) {
12979 gst_caps_set_simple (entry->caps,
12980 "bit-depth-luma", G_TYPE_UINT, bitdepth,
12981 "bit-depth-chroma", G_TYPE_UINT, bitdepth, NULL);
12982 }
12983
12984 chroma_format = (vpcc_data[14] & 0xe) >> 1;
12985 switch (chroma_format) {
12986 case 0:
12987 case 1:
12988 chroma_format_str = "4:2:0";
12989 break;
12990 case 2:
12991 chroma_format_str = "4:2:2";
12992 break;
12993 case 3:
12994 chroma_format_str = "4:4:4";
12995 break;
12996 default:
12997 break;
12998 }
12999
13000 if (chroma_format_str) {
13001 gst_caps_set_simple (entry->caps,
13002 "chroma-format", G_TYPE_STRING, chroma_format_str,
13003 NULL);
13004 }
13005
13006 if ((vpcc_data[14] & 0x1) != 0)
13007 cinfo.range = GST_VIDEO_COLOR_RANGE_0_255;
13008 else
13009 cinfo.range = GST_VIDEO_COLOR_RANGE_16_235;
13010 cinfo.primaries =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013011 gst_aml_video_color_primaries_from_iso_compat (vpcc_data[15]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013012 cinfo.transfer =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013013 gst_aml_video_transfer_function_from_iso_compat (vpcc_data[16]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013014 cinfo.matrix =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013015 gst_aml_video_color_matrix_from_iso_compat (vpcc_data[17]);
zengliang.li5f31ef42024-05-16 08:27:38 +000013016
13017 if (cinfo.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
13018 cinfo.transfer != GST_VIDEO_TRANSFER_UNKNOWN &&
13019 cinfo.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
13020 /* set this only if all values are known, otherwise this
13021 * might overwrite valid ones parsed from other color box */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013022 AML_CUR_STREAM (stream)->colorimetry = cinfo;
zengliang.li5f31ef42024-05-16 08:27:38 +000013023 }
13024 break;
13025 }
13026 default:
13027 break;
13028 }
13029
13030 len -= size + 8;
13031 vpcc_data += size + 8;
13032 }
13033
13034 break;
13035 }
13036 default:
13037 break;
13038 }
13039 }
13040
13041 GST_INFO_OBJECT (qtdemux,
13042 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13043 GST_FOURCC_ARGS (fourcc), entry->caps);
13044
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013045 } else if (stream->subtype == AML_FOURCC_soun) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013046 GNode *wave;
13047 guint version, samplesize;
13048 guint16 compression_id;
13049 gboolean amrwb = FALSE;
13050
13051 offset = 16;
13052 /* sample description entry (16) + sound sample description v0 (20) */
13053 if (len < 36)
13054 goto corrupt_file;
13055
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013056 version = AML_QT_UINT32 (stsd_entry_data + offset);
13057 entry->n_channels = AML_QT_UINT16 (stsd_entry_data + offset + 8);
13058 samplesize = AML_QT_UINT16 (stsd_entry_data + offset + 10);
13059 compression_id = AML_QT_UINT16 (stsd_entry_data + offset + 12);
13060 entry->rate = AML_QT_FP32 (stsd_entry_data + offset + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013061
13062 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version);
13063 GST_LOG_OBJECT (qtdemux, "vendor: %08x",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013064 AML_QT_UINT32 (stsd_entry_data + offset + 4));
zengliang.li5f31ef42024-05-16 08:27:38 +000013065 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13066 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize);
13067 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id);
13068 GST_LOG_OBJECT (qtdemux, "packet size: %d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013069 AML_QT_UINT16 (stsd_entry_data + offset + 14));
zengliang.li5f31ef42024-05-16 08:27:38 +000013070 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13071
13072 if (compression_id == 0xfffe)
13073 entry->sampled = TRUE;
13074
13075 /* first assume uncompressed audio */
13076 entry->bytes_per_sample = samplesize / 8;
13077 entry->samples_per_frame = entry->n_channels;
13078 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13079 entry->samples_per_packet = entry->samples_per_frame;
13080 entry->bytes_per_packet = entry->bytes_per_sample;
13081
13082 offset = 36;
13083
13084 if (version == 0x00010000) {
13085 /* sample description entry (16) + sound sample description v1 (20+16) */
13086 if (len < 52)
13087 goto corrupt_file;
13088
13089 /* take information from here over the normal sample description */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013090 entry->samples_per_packet = AML_QT_UINT32 (stsd_entry_data + offset);
13091 entry->bytes_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 4);
13092 entry->bytes_per_frame = AML_QT_UINT32 (stsd_entry_data + offset + 8);
13093 entry->bytes_per_sample = AML_QT_UINT32 (stsd_entry_data + offset + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000013094
13095 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 1");
13096 GST_LOG_OBJECT (qtdemux, "samples/packet: %d",
13097 entry->samples_per_packet);
13098 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13099 entry->bytes_per_packet);
13100 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d",
13101 entry->bytes_per_frame);
13102 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d",
13103 entry->bytes_per_sample);
13104
13105 if (!entry->sampled && entry->bytes_per_packet) {
13106 entry->samples_per_frame = (entry->bytes_per_frame /
13107 entry->bytes_per_packet) * entry->samples_per_packet;
13108 GST_LOG_OBJECT (qtdemux, "samples/frame: %d",
13109 entry->samples_per_frame);
13110 }
13111 } else if (version == 0x00020000) {
13112 /* sample description entry (16) + sound sample description v2 (56) */
13113 if (len < 72)
13114 goto corrupt_file;
13115
13116 /* take information from here over the normal sample description */
13117 entry->rate = GST_READ_DOUBLE_BE (stsd_entry_data + offset + 4);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013118 entry->n_channels = AML_QT_UINT32 (stsd_entry_data + offset + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000013119 entry->samples_per_frame = entry->n_channels;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013120 entry->bytes_per_sample = AML_QT_UINT32 (stsd_entry_data + offset + 20) / 8;
13121 entry->bytes_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 28);
13122 entry->samples_per_packet = AML_QT_UINT32 (stsd_entry_data + offset + 32);
zengliang.li5f31ef42024-05-16 08:27:38 +000013123 entry->bytes_per_frame = entry->bytes_per_sample * entry->n_channels;
13124
13125 GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
13126 GST_LOG_OBJECT (qtdemux, "sample rate: %g", entry->rate);
13127 GST_LOG_OBJECT (qtdemux, "n_channels: %d", entry->n_channels);
13128 GST_LOG_OBJECT (qtdemux, "bits/channel: %d",
13129 entry->bytes_per_sample * 8);
13130 GST_LOG_OBJECT (qtdemux, "format flags: %X",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013131 AML_QT_UINT32 (stsd_entry_data + offset + 24));
zengliang.li5f31ef42024-05-16 08:27:38 +000013132 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d",
13133 entry->bytes_per_packet);
13134 GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
13135 entry->samples_per_packet);
13136 } else if (version != 0x00000) {
13137 GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x",
13138 version);
13139 }
13140
13141 switch (fourcc) {
13142 /* Yes, these have to be hard-coded */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013143 case AML_FOURCC_MAC6:
zengliang.li5f31ef42024-05-16 08:27:38 +000013144 {
13145 entry->samples_per_packet = 6;
13146 entry->bytes_per_packet = 1;
13147 entry->bytes_per_frame = 1 * entry->n_channels;
13148 entry->bytes_per_sample = 1;
13149 entry->samples_per_frame = 6 * entry->n_channels;
13150 break;
13151 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013152 case AML_FOURCC_MAC3:
zengliang.li5f31ef42024-05-16 08:27:38 +000013153 {
13154 entry->samples_per_packet = 3;
13155 entry->bytes_per_packet = 1;
13156 entry->bytes_per_frame = 1 * entry->n_channels;
13157 entry->bytes_per_sample = 1;
13158 entry->samples_per_frame = 3 * entry->n_channels;
13159 break;
13160 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013161 case AML_FOURCC_ima4:
zengliang.li5f31ef42024-05-16 08:27:38 +000013162 {
13163 entry->samples_per_packet = 64;
13164 entry->bytes_per_packet = 34;
13165 entry->bytes_per_frame = 34 * entry->n_channels;
13166 entry->bytes_per_sample = 2;
13167 entry->samples_per_frame = 64 * entry->n_channels;
13168 break;
13169 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013170 case AML_FOURCC_ulaw:
13171 case AML_FOURCC_alaw:
zengliang.li5f31ef42024-05-16 08:27:38 +000013172 {
13173 entry->samples_per_packet = 1;
13174 entry->bytes_per_packet = 1;
13175 entry->bytes_per_frame = 1 * entry->n_channels;
13176 entry->bytes_per_sample = 1;
13177 entry->samples_per_frame = 1 * entry->n_channels;
13178 break;
13179 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013180 case AML_FOURCC_agsm:
zengliang.li5f31ef42024-05-16 08:27:38 +000013181 {
13182 entry->samples_per_packet = 160;
13183 entry->bytes_per_packet = 33;
13184 entry->bytes_per_frame = 33 * entry->n_channels;
13185 entry->bytes_per_sample = 2;
13186 entry->samples_per_frame = 160 * entry->n_channels;
13187 break;
13188 }
13189 /* fix up any invalid header information from above */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013190 case AML_FOURCC_twos:
13191 case AML_FOURCC_sowt:
13192 case AML_FOURCC_raw_:
13193 case AML_FOURCC_lpcm:
zengliang.li5f31ef42024-05-16 08:27:38 +000013194 /* Sometimes these are set to 0 in the sound sample descriptions so
13195 * let's try to infer useful values from the other information we
13196 * have available */
13197 if (entry->bytes_per_sample == 0)
13198 entry->bytes_per_sample =
13199 entry->bytes_per_frame / entry->n_channels;
13200 if (entry->bytes_per_sample == 0)
13201 entry->bytes_per_sample = samplesize / 8;
13202
13203 if (entry->bytes_per_frame == 0)
13204 entry->bytes_per_frame =
13205 entry->bytes_per_sample * entry->n_channels;
13206
13207 if (entry->bytes_per_packet == 0)
13208 entry->bytes_per_packet = entry->bytes_per_sample;
13209
13210 if (entry->samples_per_frame == 0)
13211 entry->samples_per_frame = entry->n_channels;
13212
13213 if (entry->samples_per_packet == 0)
13214 entry->samples_per_packet = entry->samples_per_frame;
13215
13216 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013217 case AML_FOURCC_in24:
13218 case AML_FOURCC_in32:
13219 case AML_FOURCC_fl32:
13220 case AML_FOURCC_fl64:
13221 case AML_FOURCC_s16l:{
zengliang.li5f31ef42024-05-16 08:27:38 +000013222 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013223 case AML_FOURCC_in24:
zengliang.li5f31ef42024-05-16 08:27:38 +000013224 entry->bytes_per_sample = 3;
13225 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013226 case AML_FOURCC_in32:
13227 case AML_FOURCC_fl32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013228 entry->bytes_per_sample = 4;
13229 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013230 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013231 entry->bytes_per_sample = 8;
13232 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013233 case AML_FOURCC_s16l:
zengliang.li5f31ef42024-05-16 08:27:38 +000013234 entry->bytes_per_sample = 2;
13235 break;
13236 default:
13237 g_assert_not_reached ();
13238 break;
13239 }
13240 entry->samples_per_frame = entry->n_channels;
13241 entry->bytes_per_frame = entry->n_channels * entry->bytes_per_sample;
13242 entry->samples_per_packet = entry->samples_per_frame;
13243 entry->bytes_per_packet = entry->bytes_per_sample;
13244 break;
13245 }
13246 default:
13247 break;
13248 }
13249
13250 if (entry->caps)
13251 gst_caps_unref (entry->caps);
13252
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013253 entry->caps = aml_qtdemux_audio_caps (qtdemux, stream, entry, fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000013254 stsd_entry_data + 32, len - 16, &codec);
13255
13256 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013257 case AML_FOURCC_in24:
13258 case AML_FOURCC_in32:
13259 case AML_FOURCC_fl32:
13260 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013261 {
13262 GNode *enda;
13263 GNode *fmt;
13264
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013265 fmt = aml_qtdemux_tree_get_child_by_type (stsd, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000013266
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013267 enda = aml_qtdemux_tree_get_child_by_type (fmt, AML_FOURCC_enda);
zengliang.li5f31ef42024-05-16 08:27:38 +000013268 if (!enda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013269 wave = aml_qtdemux_tree_get_child_by_type (fmt, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013270 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013271 enda = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_enda);
zengliang.li5f31ef42024-05-16 08:27:38 +000013272 }
13273 if (enda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013274 int enda_value = AML_QT_UINT16 ((guint8 *) enda->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000013275 const gchar *format_str;
13276
13277 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013278 case AML_FOURCC_in24:
zengliang.li5f31ef42024-05-16 08:27:38 +000013279 format_str = (enda_value) ? "S24LE" : "S24BE";
13280 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013281 case AML_FOURCC_in32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013282 format_str = (enda_value) ? "S32LE" : "S32BE";
13283 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013284 case AML_FOURCC_fl32:
zengliang.li5f31ef42024-05-16 08:27:38 +000013285 format_str = (enda_value) ? "F32LE" : "F32BE";
13286 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013287 case AML_FOURCC_fl64:
zengliang.li5f31ef42024-05-16 08:27:38 +000013288 format_str = (enda_value) ? "F64LE" : "F64BE";
13289 break;
13290 default:
13291 g_assert_not_reached ();
13292 break;
13293 }
13294 gst_caps_set_simple (entry->caps,
13295 "format", G_TYPE_STRING, format_str, NULL);
13296 }
13297 break;
13298 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013299 case AML_FOURCC_owma:
zengliang.li5f31ef42024-05-16 08:27:38 +000013300 {
13301 const guint8 *owma_data;
13302 const gchar *codec_name = NULL;
13303 guint owma_len;
13304 GstBuffer *buf;
13305 gint version = 1;
13306 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13307 /* FIXME this should also be gst_riff_strf_auds,
13308 * but the latter one is actually missing bits-per-sample :( */
13309 typedef struct
13310 {
13311 gint16 wFormatTag;
13312 gint16 nChannels;
13313 gint32 nSamplesPerSec;
13314 gint32 nAvgBytesPerSec;
13315 gint16 nBlockAlign;
13316 gint16 wBitsPerSample;
13317 gint16 cbSize;
13318 } WAVEFORMATEX;
13319 WAVEFORMATEX *wfex;
13320
13321 GST_DEBUG_OBJECT (qtdemux, "parse owma");
13322 owma_data = stsd_entry_data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013323 owma_len = AML_QT_UINT32 (owma_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013324 if (owma_len <= 54) {
13325 GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
13326 break;
13327 }
13328 wfex = (WAVEFORMATEX *) (owma_data + 36);
13329 buf = gst_buffer_new_and_alloc (owma_len - 54);
13330 gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
13331 if (wfex->wFormatTag == 0x0161) {
13332 codec_name = "Windows Media Audio";
13333 version = 2;
13334 } else if (wfex->wFormatTag == 0x0162) {
13335 codec_name = "Windows Media Audio 9 Pro";
13336 version = 3;
13337 } else if (wfex->wFormatTag == 0x0163) {
13338 codec_name = "Windows Media Audio 9 Lossless";
13339 /* is that correct? gstffmpegcodecmap.c is missing it, but
13340 * fluendo codec seems to support it */
13341 version = 4;
13342 }
13343
13344 gst_caps_set_simple (entry->caps,
13345 "codec_data", GST_TYPE_BUFFER, buf,
13346 "wmaversion", G_TYPE_INT, version,
13347 "block_align", G_TYPE_INT,
13348 GST_READ_UINT16_LE (&wfex->nBlockAlign), "bitrate", G_TYPE_INT,
13349 GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec), "width", G_TYPE_INT,
13350 GST_READ_UINT16_LE (&wfex->wBitsPerSample), "depth", G_TYPE_INT,
13351 GST_READ_UINT16_LE (&wfex->wBitsPerSample), NULL);
13352 gst_buffer_unref (buf);
13353
13354 if (codec_name) {
13355 g_free (codec);
13356 codec = g_strdup (codec_name);
13357 }
13358 break;
13359 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013360 case AML_FOURCC_wma_:
zengliang.li5f31ef42024-05-16 08:27:38 +000013361 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013362 guint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013363 len = len <= offset ? 0 : len - offset;
13364 const guint8 *wfex_data = stsd_entry_data + offset;
13365 const gchar *codec_name = NULL;
13366 gint version = 1;
13367 /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
13368 /* FIXME this should also be gst_riff_strf_auds,
13369 * but the latter one is actually missing bits-per-sample :( */
13370 typedef struct
13371 {
13372 gint16 wFormatTag;
13373 gint16 nChannels;
13374 gint32 nSamplesPerSec;
13375 gint32 nAvgBytesPerSec;
13376 gint16 nBlockAlign;
13377 gint16 wBitsPerSample;
13378 gint16 cbSize;
13379 } WAVEFORMATEX;
13380 WAVEFORMATEX wfex;
13381
13382 /* FIXME: unify with similar wavformatex parsing code above */
13383 GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");
13384
13385 /* find wfex */
13386 while (len >= 8) {
13387 guint size;
13388
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013389 if (AML_QT_UINT32 (wfex_data) <= 0x8)
zengliang.li5f31ef42024-05-16 08:27:38 +000013390 size = 0;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013391 else if (AML_QT_UINT32 (wfex_data) <= len)
13392 size = AML_QT_UINT32 (wfex_data) - 8;
zengliang.li5f31ef42024-05-16 08:27:38 +000013393 else
13394 size = len - 8;
13395
13396 if (size < 1)
13397 /* No real data, so break out */
13398 break;
13399
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013400 switch (AML_QT_FOURCC (wfex_data + 4)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013401 case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
13402 {
13403 GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");
13404
13405 if (size < 8 + 18)
13406 break;
13407
13408 wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
13409 wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
13410 wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
13411 wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
13412 wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
13413 wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
13414 wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);
13415
13416 GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
13417 GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
13418 "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
13419 "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
13420 wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
13421 wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);
13422
13423 if (wfex.wFormatTag == 0x0161) {
13424 codec_name = "Windows Media Audio";
13425 version = 2;
13426 } else if (wfex.wFormatTag == 0x0162) {
13427 codec_name = "Windows Media Audio 9 Pro";
13428 version = 3;
13429 } else if (wfex.wFormatTag == 0x0163) {
13430 codec_name = "Windows Media Audio 9 Lossless";
13431 /* is that correct? gstffmpegcodecmap.c is missing it, but
13432 * fluendo codec seems to support it */
13433 version = 4;
13434 }
13435
13436 gst_caps_set_simple (entry->caps,
13437 "wmaversion", G_TYPE_INT, version,
13438 "block_align", G_TYPE_INT, wfex.nBlockAlign,
13439 "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
13440 "width", G_TYPE_INT, wfex.wBitsPerSample,
13441 "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
13442
13443 if (size > wfex.cbSize) {
13444 GstBuffer *buf;
13445
13446 buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
13447 gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
13448 size - wfex.cbSize);
13449 gst_caps_set_simple (entry->caps,
13450 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13451 gst_buffer_unref (buf);
13452 } else {
13453 GST_WARNING_OBJECT (qtdemux, "no codec data");
13454 }
13455
13456 if (codec_name) {
13457 g_free (codec);
13458 codec = g_strdup (codec_name);
13459 }
13460 break;
13461 }
13462 default:
13463 break;
13464 }
13465 len -= size + 8;
13466 wfex_data += size + 8;
13467 }
13468 break;
13469 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013470 case AML_FOURCC_opus:
zengliang.li5f31ef42024-05-16 08:27:38 +000013471 {
13472 const guint8 *dops_data;
13473 guint8 *channel_mapping = NULL;
13474 guint32 rate;
13475 guint8 channels;
13476 guint8 channel_mapping_family;
13477 guint8 stream_count;
13478 guint8 coupled_count;
13479 guint8 i;
13480
13481 version = GST_READ_UINT16_BE (stsd_entry_data + 16);
13482 if (version == 1)
13483 dops_data = stsd_entry_data + 51;
13484 else
13485 dops_data = stsd_entry_data + 35;
13486
13487 channels = GST_READ_UINT8 (dops_data + 10);
13488 rate = GST_READ_UINT32_LE (dops_data + 13);
13489 channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
13490 stream_count = GST_READ_UINT8 (dops_data + 20);
13491 coupled_count = GST_READ_UINT8 (dops_data + 21);
13492
13493 if (channels > 0) {
13494 channel_mapping = g_malloc (channels * sizeof (guint8));
13495 for (i = 0; i < channels; i++)
13496 channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
13497 }
13498
13499 entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
13500 channel_mapping_family, stream_count, coupled_count,
13501 channel_mapping);
13502 g_free (channel_mapping);
13503 break;
13504 }
13505 default:
13506 break;
13507 }
13508
13509 if (codec) {
13510 GstStructure *s;
13511 gint bitrate = 0;
13512
13513 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13514 GST_TAG_AUDIO_CODEC, codec, NULL);
13515 g_free (codec);
13516 codec = NULL;
13517
13518 /* some bitrate info may have ended up in caps */
13519 s = gst_caps_get_structure (entry->caps, 0);
13520 gst_structure_get_int (s, "bitrate", &bitrate);
13521 if (bitrate > 0)
13522 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13523 GST_TAG_BITRATE, bitrate, NULL);
13524 }
13525
13526 esds = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013527 mp4a = aml_qtdemux_tree_get_child_by_index (stsd, stsd_index);
13528 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013529 if (stream->protected) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013530 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) == AML_FOURCC_aavd) {
13531 esds = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013532 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013533 if (AML_QTDEMUX_TREE_NODE_FOURCC (mp4a) != AML_FOURCC_enca) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013534 mp4a = NULL;
13535 }
13536 } else {
13537 mp4a = NULL;
13538 }
13539 }
13540
13541 wave = NULL;
13542 if (mp4a) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013543 wave = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013544 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013545 esds = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013546 if (!esds)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013547 esds = aml_qtdemux_tree_get_child_by_type (mp4a, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013548 }
13549
13550
13551 /* If the fourcc's bottom 16 bits gives 'sm', then the top
13552 16 bits is a byte-swapped wave-style codec identifier,
13553 and we can find a WAVE header internally to a 'wave' atom here.
13554 This can more clearly be thought of as 'ms' as the top 16 bits, and a
13555 codec id as the bottom 16 bits - but byte-swapped to store in QT (which
13556 is big-endian).
13557 */
13558 if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
13559 if (len < offset + 20) {
13560 GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
13561 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013562 guint32 datalen = AML_QT_UINT32 (stsd_entry_data + offset + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013563 const guint8 *data = stsd_entry_data + offset + 16;
13564 GNode *wavenode;
13565 GNode *waveheadernode;
13566
13567 wavenode = g_node_new ((guint8 *) data);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013568 if (aml_qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013569 const guint8 *waveheader;
13570 guint32 headerlen;
13571
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013572 waveheadernode = aml_qtdemux_tree_get_child_by_type (wavenode, fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000013573 if (waveheadernode) {
13574 waveheader = (const guint8 *) waveheadernode->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013575 headerlen = AML_QT_UINT32 (waveheader);
zengliang.li5f31ef42024-05-16 08:27:38 +000013576
13577 if (headerlen > 8) {
13578 gst_riff_strf_auds *header = NULL;
13579 GstBuffer *headerbuf;
13580 GstBuffer *extra;
13581
13582 waveheader += 8;
13583 headerlen -= 8;
13584
13585 headerbuf = gst_buffer_new_and_alloc (headerlen);
13586 gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
13587
13588 if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
13589 headerbuf, &header, &extra)) {
13590 gst_caps_unref (entry->caps);
13591 /* FIXME: Need to do something with the channel reorder map */
13592 entry->caps =
13593 gst_riff_create_audio_caps (header->format, NULL, header,
13594 extra, NULL, NULL, NULL);
13595
13596 if (extra)
13597 gst_buffer_unref (extra);
13598 g_free (header);
13599 }
13600 }
13601 } else
13602 GST_DEBUG ("Didn't find waveheadernode for this codec");
13603 }
13604 g_node_destroy (wavenode);
13605 }
13606 } else if (esds) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013607 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000013608 stream->stream_tags);
13609 } else {
13610 switch (fourcc) {
13611#if 0
13612 /* FIXME: what is in the chunk? */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013613 case AML_FOURCC_QDMC:
zengliang.li5f31ef42024-05-16 08:27:38 +000013614 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013615 gint len = AML_QT_UINT32 (stsd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013616
13617 /* seems to be always = 116 = 0x74 */
13618 break;
13619 }
13620#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013621 case AML_FOURCC_QDM2:
zengliang.li5f31ef42024-05-16 08:27:38 +000013622 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013623 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013624
13625 if (len > 0x3C) {
13626 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x3C);
13627
13628 gst_buffer_fill (buf, 0, stsd_entry_data + 0x3C, len - 0x3C);
13629 gst_caps_set_simple (entry->caps,
13630 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13631 gst_buffer_unref (buf);
13632 }
13633 gst_caps_set_simple (entry->caps,
13634 "samplesize", G_TYPE_INT, samplesize, NULL);
13635 break;
13636 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013637 case AML_FOURCC_alac:
zengliang.li5f31ef42024-05-16 08:27:38 +000013638 {
13639 GNode *alac, *wave = NULL;
13640
13641 /* apparently, m4a has this atom appended directly in the stsd entry,
13642 * while mov has it in a wave atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013643 alac = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013644 if (alac) {
13645 /* alac now refers to stsd entry atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013646 wave = aml_qtdemux_tree_get_child_by_type (alac, AML_FOURCC_wave);
zengliang.li5f31ef42024-05-16 08:27:38 +000013647 if (wave)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013648 alac = aml_qtdemux_tree_get_child_by_type (wave, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013649 else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013650 alac = aml_qtdemux_tree_get_child_by_type (alac, AML_FOURCC_alac);
zengliang.li5f31ef42024-05-16 08:27:38 +000013651 }
13652 if (alac) {
13653 const guint8 *alac_data = alac->data;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013654 gint len = AML_QT_UINT32 (alac->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013655 GstBuffer *buf;
13656
13657 if (len < 36) {
13658 GST_DEBUG_OBJECT (qtdemux,
13659 "discarding alac atom with unexpected len %d", len);
13660 } else {
13661 /* codec-data contains alac atom size and prefix,
13662 * ffmpeg likes it that way, not quite gst-ish though ...*/
13663 buf = gst_buffer_new_and_alloc (len);
13664 gst_buffer_fill (buf, 0, alac->data, len);
13665 gst_caps_set_simple (entry->caps,
13666 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13667 gst_buffer_unref (buf);
13668
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013669 entry->bytes_per_frame = AML_QT_UINT32 (alac_data + 12);
13670 entry->n_channels = AML_QT_UINT8 (alac_data + 21);
13671 entry->rate = AML_QT_UINT32 (alac_data + 32);
13672 samplesize = AML_QT_UINT8 (alac_data + 16 + 1);
zengliang.li5f31ef42024-05-16 08:27:38 +000013673 }
13674 }
13675 gst_caps_set_simple (entry->caps,
13676 "samplesize", G_TYPE_INT, samplesize, NULL);
13677 break;
13678 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013679 case AML_FOURCC_fLaC:
zengliang.li5f31ef42024-05-16 08:27:38 +000013680 {
13681 /* The codingname of the sample entry is 'fLaC' */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013682 GNode *flac = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_fLaC);
zengliang.li5f31ef42024-05-16 08:27:38 +000013683
13684 if (flac) {
13685 /* The 'dfLa' box is added to the sample entry to convey
13686 initializing information for the decoder. */
13687 const GNode *dfla =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013688 aml_qtdemux_tree_get_child_by_type (flac, AML_FOURCC_dfLa);
zengliang.li5f31ef42024-05-16 08:27:38 +000013689
13690 if (dfla) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013691 const guint32 len = AML_QT_UINT32 (dfla->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013692
13693 /* Must contain at least dfLa box header (12),
13694 * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
13695 if (len < 50) {
13696 GST_DEBUG_OBJECT (qtdemux,
13697 "discarding dfla atom with unexpected len %d", len);
13698 } else {
13699 /* skip dfLa header to get the METADATA_BLOCKs */
13700 const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
13701 const guint32 metadata_blocks_len = len - 12;
13702
13703 gchar *stream_marker = g_strdup ("fLaC");
13704 GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
13705 strlen (stream_marker));
13706
13707 guint32 index = 0;
13708 guint32 remainder = 0;
13709 guint32 block_size = 0;
13710 gboolean is_last = FALSE;
13711
13712 GValue array = G_VALUE_INIT;
13713 GValue value = G_VALUE_INIT;
13714
13715 g_value_init (&array, GST_TYPE_ARRAY);
13716 g_value_init (&value, GST_TYPE_BUFFER);
13717
13718 gst_value_set_buffer (&value, block);
13719 gst_value_array_append_value (&array, &value);
13720 g_value_reset (&value);
13721
13722 gst_buffer_unref (block);
13723
13724 /* check there's at least one METADATA_BLOCK_HEADER's worth
13725 * of data, and we haven't already finished parsing */
13726 while (!is_last && ((index + 3) < metadata_blocks_len)) {
13727 remainder = metadata_blocks_len - index;
13728
13729 /* add the METADATA_BLOCK_HEADER size to the signalled size */
13730 block_size = 4 +
13731 (metadata_blocks[index + 1] << 16) +
13732 (metadata_blocks[index + 2] << 8) +
13733 metadata_blocks[index + 3];
13734
13735 /* be careful not to read off end of box */
13736 if (block_size > remainder) {
13737 break;
13738 }
13739
13740 is_last = metadata_blocks[index] >> 7;
13741
13742 block = gst_buffer_new_and_alloc (block_size);
13743
13744 gst_buffer_fill (block, 0, &metadata_blocks[index],
13745 block_size);
13746
13747 gst_value_set_buffer (&value, block);
13748 gst_value_array_append_value (&array, &value);
13749 g_value_reset (&value);
13750
13751 gst_buffer_unref (block);
13752
13753 index += block_size;
13754 }
13755
13756 /* only append the metadata if we successfully read all of it */
13757 if (is_last) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013758 gst_structure_set_value (gst_caps_get_structure (AML_CUR_STREAM
zengliang.li5f31ef42024-05-16 08:27:38 +000013759 (stream)->caps, 0), "streamheader", &array);
13760 } else {
13761 GST_WARNING_OBJECT (qtdemux,
13762 "discarding all METADATA_BLOCKs due to invalid "
13763 "block_size %d at idx %d, rem %d", block_size, index,
13764 remainder);
13765 }
13766
13767 g_value_unset (&value);
13768 g_value_unset (&array);
13769
13770 /* The sample rate obtained from the stsd may not be accurate
13771 * since it cannot represent rates greater than 65535Hz, so
13772 * override that value with the sample rate from the
13773 * METADATA_BLOCK_STREAMINFO block */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013774 AML_CUR_STREAM (stream)->rate =
13775 (AML_QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
zengliang.li5f31ef42024-05-16 08:27:38 +000013776 }
13777 }
13778 }
13779 break;
13780 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013781 case AML_FOURCC_sawb:
zengliang.li5f31ef42024-05-16 08:27:38 +000013782 /* Fallthrough! */
13783 amrwb = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013784 case AML_FOURCC_samr:
zengliang.li5f31ef42024-05-16 08:27:38 +000013785 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013786 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013787
13788 if (len > 0x24) {
13789 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x24);
13790 guint bitrate;
13791
13792 gst_buffer_fill (buf, 0, stsd_entry_data + 0x24, len - 0x24);
13793
13794 /* If we have enough data, let's try to get the 'damr' atom. See
13795 * the 3GPP container spec (26.244) for more details. */
13796 if ((len - 0x34) > 8 &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013797 (bitrate = aml_qtdemux_parse_amr_bitrate (buf, amrwb))) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013798 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13799 GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
13800 }
13801
13802 gst_caps_set_simple (entry->caps,
13803 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13804 gst_buffer_unref (buf);
13805 }
13806 break;
13807 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013808 case AML_FOURCC_mp4a:
zengliang.li5f31ef42024-05-16 08:27:38 +000013809 {
13810 /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013811 gint len = AML_QT_UINT32 (stsd_entry_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000013812 guint16 sound_version = 0;
13813 /* FIXME: Can this be determined somehow? There doesn't seem to be
13814 * anything in mp4a atom that specifis compression */
13815 gint profile = 2;
13816 guint16 channels = entry->n_channels;
13817 guint32 time_scale = (guint32) entry->rate;
13818 gint sample_rate_index = -1;
13819
13820 if (len >= 34) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013821 sound_version = AML_QT_UINT16 (stsd_entry_data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000013822
13823 if (sound_version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013824 channels = AML_QT_UINT16 (stsd_entry_data + 24);
13825 time_scale = AML_QT_UINT32 (stsd_entry_data + 30);
zengliang.li5f31ef42024-05-16 08:27:38 +000013826 } else {
13827 GST_FIXME_OBJECT (qtdemux, "Unhandled mp4a atom version %d",
13828 sound_version);
13829 }
13830 } else {
13831 GST_DEBUG_OBJECT (qtdemux, "Too small stsd entry data len %d",
13832 len);
13833 }
13834
13835 sample_rate_index =
13836 gst_codec_utils_aac_get_index_from_sample_rate (time_scale);
13837 if (sample_rate_index >= 0 && channels > 0) {
13838 guint8 codec_data[2];
13839 GstBuffer *buf;
13840
13841 /* build AAC codec data */
13842 codec_data[0] = profile << 3;
13843 codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
13844 codec_data[1] = (sample_rate_index & 0x01) << 7;
13845 codec_data[1] |= (channels & 0xF) << 3;
13846
13847 buf = gst_buffer_new_and_alloc (2);
13848 gst_buffer_fill (buf, 0, codec_data, 2);
13849 gst_caps_set_simple (entry->caps,
13850 "codec_data", GST_TYPE_BUFFER, buf, NULL);
13851 gst_buffer_unref (buf);
13852 }
13853 break;
13854 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013855 case AML_FOURCC_lpcm:
13856 case AML_FOURCC_in24:
13857 case AML_FOURCC_in32:
13858 case AML_FOURCC_fl32:
13859 case AML_FOURCC_fl64:
13860 case AML_FOURCC_s16l:
zengliang.li5f31ef42024-05-16 08:27:38 +000013861 /* Fully handled elsewhere */
13862 break;
13863 default:
13864 GST_INFO_OBJECT (qtdemux,
13865 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13866 break;
13867 }
13868 }
13869 GST_INFO_OBJECT (qtdemux,
13870 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13871 GST_FOURCC_ARGS (fourcc), entry->caps);
13872
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013873 } else if (stream->subtype == AML_FOURCC_strm) {
13874 if (fourcc == AML_FOURCC_rtsp) {
13875 stream->redirect_uri = aml_qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
zengliang.li5f31ef42024-05-16 08:27:38 +000013876 } else {
13877 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
13878 GST_FOURCC_ARGS (fourcc));
13879 goto unknown_stream;
13880 }
13881 entry->sampled = TRUE;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013882 } else if (stream->subtype == AML_FOURCC_subp || stream->subtype == AML_FOURCC_text
13883 || stream->subtype == AML_FOURCC_sbtl || stream->subtype == AML_FOURCC_subt
13884 || stream->subtype == AML_FOURCC_clcp || stream->subtype == AML_FOURCC_wvtt) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013885
13886 entry->sampled = TRUE;
13887 entry->sparse = TRUE;
13888
13889 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013890 aml_qtdemux_sub_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000013891 &codec);
13892 if (codec) {
13893 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13894 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13895 g_free (codec);
13896 codec = NULL;
13897 }
13898
13899 /* hunt for sort-of codec data */
13900 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013901 case AML_FOURCC_mp4s:
zengliang.li5f31ef42024-05-16 08:27:38 +000013902 {
13903 GNode *mp4s = NULL;
13904 GNode *esds = NULL;
13905
13906 /* look for palette in a stsd->mp4s->esds sub-atom */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013907 mp4s = aml_qtdemux_tree_get_child_by_type (stsd, AML_FOURCC_mp4s);
zengliang.li5f31ef42024-05-16 08:27:38 +000013908 if (mp4s)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013909 esds = aml_qtdemux_tree_get_child_by_type (mp4s, AML_FOURCC_esds);
zengliang.li5f31ef42024-05-16 08:27:38 +000013910 if (esds == NULL) {
13911 /* Invalid STSD */
13912 GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
13913 break;
13914 }
13915
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013916 gst_aml_qtdemux_handle_esds (qtdemux, stream, entry, esds,
zengliang.li5f31ef42024-05-16 08:27:38 +000013917 stream->stream_tags);
13918 break;
13919 }
13920 default:
13921 GST_INFO_OBJECT (qtdemux,
13922 "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
13923 break;
13924 }
13925 GST_INFO_OBJECT (qtdemux,
13926 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
13927 GST_FOURCC_ARGS (fourcc), entry->caps);
13928 } else {
13929 /* everything in 1 sample */
13930 entry->sampled = TRUE;
13931
13932 entry->caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013933 aml_qtdemux_generic_caps (qtdemux, stream, entry, fourcc, stsd_entry_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000013934 &codec);
13935
13936 if (entry->caps == NULL)
13937 goto unknown_stream;
13938
13939 if (codec) {
13940 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
13941 GST_TAG_SUBTITLE_CODEC, codec, NULL);
13942 g_free (codec);
13943 codec = NULL;
13944 }
13945 }
13946
13947 /* promote to sampled format */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013948 if (entry->fourcc == AML_FOURCC_samr) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013949 /* force mono 8000 Hz for AMR */
13950 entry->sampled = TRUE;
13951 entry->n_channels = 1;
13952 entry->rate = 8000;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013953 } else if (entry->fourcc == AML_FOURCC_sawb) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013954 /* force mono 16000 Hz for AMR-WB */
13955 entry->sampled = TRUE;
13956 entry->n_channels = 1;
13957 entry->rate = 16000;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013958 } else if (entry->fourcc == AML_FOURCC_mp4a) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013959 entry->sampled = TRUE;
13960 }
13961
13962
13963 stsd_entry_data += len;
13964 remaining_stsd_len -= len;
13965
13966 }
13967
13968 /* collect sample information */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013969 if (!aml_qtdemux_stbl_init (qtdemux, stream, stbl))
zengliang.li5f31ef42024-05-16 08:27:38 +000013970 goto samples_failed;
13971
13972 if (qtdemux->fragmented) {
13973 guint64 offset;
13974
13975 /* need all moov samples as basis; probably not many if any at all */
13976 /* prevent moof parsing taking of at this time */
13977 offset = qtdemux->moof_offset;
13978 qtdemux->moof_offset = 0;
13979 if (stream->n_samples &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013980 !aml_qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000013981 qtdemux->moof_offset = offset;
13982 goto samples_failed;
13983 }
13984 qtdemux->moof_offset = offset;
13985 /* movie duration more reliable in this case (e.g. mehd) */
13986 if (qtdemux->segment.duration &&
13987 GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
13988 stream->duration =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013989 AML_GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
zengliang.li5f31ef42024-05-16 08:27:38 +000013990 }
13991
13992 /* configure segments */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000013993 if (!aml_qtdemux_parse_segments (qtdemux, stream, trak))
zengliang.li5f31ef42024-05-16 08:27:38 +000013994 goto segments_failed;
13995
13996 /* add some language tag, if useful */
13997 if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
13998 strcmp (stream->lang_id, "und")) {
13999 const gchar *lang_code;
14000
14001 /* convert ISO 639-2 code to ISO 639-1 */
14002 lang_code = gst_tag_get_language_code (stream->lang_id);
14003 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14004 GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
14005 }
14006
14007 /* Check for UDTA tags */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014008 if ((udta = aml_qtdemux_tree_get_child_by_type (trak, AML_FOURCC_udta))) {
14009 aml_qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014010 }
14011
14012 /* Insert and sort new stream in track-id order.
14013 * This will help in comparing old/new streams during stream update check */
14014 g_ptr_array_add (qtdemux->active_streams, stream);
14015 g_ptr_array_sort (qtdemux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014016 (GCompareFunc) aml_qtdemux_track_id_compare_func);
zengliang.li5f31ef42024-05-16 08:27:38 +000014017 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014018 AML_QTDEMUX_N_STREAMS (qtdemux));
zengliang.li5f31ef42024-05-16 08:27:38 +000014019
14020 return TRUE;
14021
14022/* ERRORS */
14023corrupt_file:
14024 {
14025 GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
14026 (_("This file is corrupt and cannot be played.")), (NULL));
14027 if (stream)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014028 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014029 return FALSE;
14030 }
14031error_encrypted:
14032 {
14033 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014034 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014035 return FALSE;
14036 }
14037samples_failed:
14038segments_failed:
14039 {
14040 /* we posted an error already */
14041 /* free stbl sub-atoms */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014042 gst_aml_qtdemux_stbl_free (stream);
14043 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014044 return FALSE;
14045 }
14046existing_stream:
14047 {
14048 GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
14049 track_id);
14050 return TRUE;
14051 }
14052unknown_stream:
14053 {
14054 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
14055 GST_FOURCC_ARGS (stream->subtype));
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014056 gst_aml_qtdemux_stream_unref (stream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014057 return TRUE;
14058 }
14059}
14060
14061/* If we can estimate the overall bitrate, and don't have information about the
14062 * stream bitrate for exactly one stream, this guesses the stream bitrate as
14063 * the overall bitrate minus the sum of the bitrates of all other streams. This
14064 * should be useful for the common case where we have one audio and one video
14065 * stream and can estimate the bitrate of one, but not the other. */
14066static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014067gst_aml_qtdemux_guess_bitrate (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014068{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014069 AmlQtDemuxStream *stream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000014070 gint64 size, sys_bitrate, sum_bitrate = 0;
14071 GstClockTime duration;
14072 guint bitrate;
14073 gint i;
14074
14075 if (qtdemux->fragmented)
14076 return;
14077
14078 GST_DEBUG_OBJECT (qtdemux, "Looking for streams with unknown bitrate");
14079
14080 if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES, &size)
14081 || size <= 0) {
14082 GST_DEBUG_OBJECT (qtdemux,
14083 "Size in bytes of the stream not known - bailing");
14084 return;
14085 }
14086
14087 /* Subtract the header size */
14088 GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u",
14089 size, qtdemux->header_size);
14090
14091 if (size < qtdemux->header_size)
14092 return;
14093
14094 size = size - qtdemux->header_size;
14095
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014096 if (!gst_aml_qtdemux_get_duration (qtdemux, &duration)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014097 GST_DEBUG_OBJECT (qtdemux, "Stream duration not known - bailing");
14098 return;
14099 }
14100
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014101 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14102 AmlQtDemuxStream *str = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014103 switch (str->subtype) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014104 case AML_FOURCC_soun:
14105 case AML_FOURCC_vide:
zengliang.li5f31ef42024-05-16 08:27:38 +000014106 GST_DEBUG_OBJECT (qtdemux, "checking bitrate for %" GST_PTR_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014107 AML_CUR_STREAM (str)->caps);
zengliang.li5f31ef42024-05-16 08:27:38 +000014108 /* retrieve bitrate, prefer avg then max */
14109 bitrate = 0;
14110 if (str->stream_tags) {
14111 if (gst_tag_list_get_uint (str->stream_tags,
14112 GST_TAG_MAXIMUM_BITRATE, &bitrate))
14113 GST_DEBUG_OBJECT (qtdemux, "max-bitrate: %u", bitrate);
14114 if (gst_tag_list_get_uint (str->stream_tags,
14115 GST_TAG_NOMINAL_BITRATE, &bitrate))
14116 GST_DEBUG_OBJECT (qtdemux, "nominal-bitrate: %u", bitrate);
14117 if (gst_tag_list_get_uint (str->stream_tags,
14118 GST_TAG_BITRATE, &bitrate))
14119 GST_DEBUG_OBJECT (qtdemux, "bitrate: %u", bitrate);
14120 }
14121 if (bitrate)
14122 sum_bitrate += bitrate;
14123 else {
14124 if (stream) {
14125 GST_DEBUG_OBJECT (qtdemux,
14126 ">1 stream with unknown bitrate - bailing");
14127 return;
14128 } else
14129 stream = str;
14130 }
14131
14132 default:
14133 /* For other subtypes, we assume no significant impact on bitrate */
14134 break;
14135 }
14136 }
14137
14138 if (!stream) {
14139 GST_DEBUG_OBJECT (qtdemux, "All stream bitrates are known");
14140 return;
14141 }
14142
14143 sys_bitrate = gst_util_uint64_scale (size, GST_SECOND * 8, duration);
14144
14145 if (sys_bitrate < sum_bitrate) {
14146 /* This can happen, since sum_bitrate might be derived from maximum
14147 * bitrates and not average bitrates */
14148 GST_DEBUG_OBJECT (qtdemux,
14149 "System bitrate less than sum bitrate - bailing");
14150 return;
14151 }
14152
14153 bitrate = sys_bitrate - sum_bitrate;
14154 GST_DEBUG_OBJECT (qtdemux, "System bitrate = %" G_GINT64_FORMAT
14155 ", Stream bitrate = %u", sys_bitrate, bitrate);
14156
14157 if (!stream->stream_tags)
14158 stream->stream_tags = gst_tag_list_new_empty ();
14159 else
14160 stream->stream_tags = gst_tag_list_make_writable (stream->stream_tags);
14161
14162 gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
14163 GST_TAG_BITRATE, bitrate, NULL);
14164}
14165
14166static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014167aml_qtdemux_prepare_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014168{
14169 GstFlowReturn ret = GST_FLOW_OK;
14170 gint i;
14171
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014172 GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", AML_QTDEMUX_N_STREAMS (qtdemux));
zengliang.li5f31ef42024-05-16 08:27:38 +000014173
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014174 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14175 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014176 guint32 sample_num = 0;
14177
14178 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014179 stream->track_id, GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc));
zengliang.li5f31ef42024-05-16 08:27:38 +000014180
14181 if (qtdemux->fragmented && qtdemux->pullbased) {
14182 /* need all moov samples first */
14183 GST_OBJECT_LOCK (qtdemux);
14184 while (stream->n_samples == 0)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014185 if ((ret = aml_qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
zengliang.li5f31ef42024-05-16 08:27:38 +000014186 break;
14187 GST_OBJECT_UNLOCK (qtdemux);
14188 } else {
14189 /* discard any stray moof */
14190 qtdemux->moof_offset = 0;
14191 }
14192
14193 /* prepare braking */
14194 if (ret != GST_FLOW_ERROR)
14195 ret = GST_FLOW_OK;
14196
14197 /* in pull mode, we should have parsed some sample info by now;
14198 * and quite some code will not handle no samples.
14199 * in push mode, we'll just have to deal with it */
14200 if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
14201 GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
14202 g_ptr_array_remove_index (qtdemux->active_streams, i);
14203 i--;
14204 continue;
14205 } else if (stream->track_id == qtdemux->chapters_track_id &&
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014206 (stream->subtype == AML_FOURCC_text || stream->subtype == AML_FOURCC_sbtl)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014207 /* TODO - parse chapters track and expose it as GstToc; For now just ignore it
14208 so that it doesn't look like a subtitle track */
14209 g_ptr_array_remove_index (qtdemux->active_streams, i);
14210 i--;
14211 continue;
14212 }
14213
14214 /* parse the initial sample for use in setting the frame rate cap */
14215 while (sample_num == 0 && sample_num < stream->n_samples) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014216 if (!aml_qtdemux_parse_samples (qtdemux, stream, sample_num))
zengliang.li5f31ef42024-05-16 08:27:38 +000014217 break;
14218 ++sample_num;
14219 }
14220 }
14221
14222 return ret;
14223}
14224
14225static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014226_aml_stream_equal_func (const AmlQtDemuxStream * stream, const gchar * stream_id)
zengliang.li5f31ef42024-05-16 08:27:38 +000014227{
14228 return g_strcmp0 (stream->stream_id, stream_id) == 0;
14229}
14230
14231static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014232aml_qtdemux_is_streams_update (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014233{
14234 gint i;
14235
14236 /* Different length, updated */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014237 if (AML_QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
zengliang.li5f31ef42024-05-16 08:27:38 +000014238 return TRUE;
14239
14240 /* streams in list are sorted in track-id order */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014241 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014242 /* Different stream-id, updated */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014243 if (g_strcmp0 (AML_QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
14244 AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
zengliang.li5f31ef42024-05-16 08:27:38 +000014245 return TRUE;
14246 }
14247
14248 return FALSE;
14249}
14250
14251static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014252aml_qtdemux_reuse_and_configure_stream (GstAmlQTDemux * qtdemux,
14253 AmlQtDemuxStream * oldstream, AmlQtDemuxStream * newstream)
zengliang.li5f31ef42024-05-16 08:27:38 +000014254{
14255 /* Connect old stream's srcpad to new stream */
14256 newstream->pad = oldstream->pad;
14257 oldstream->pad = NULL;
14258
14259 /* unset new_stream to prevent stream-start event, unless we are EOS in which
14260 * case we need to force one through */
14261 newstream->new_stream = newstream->pad != NULL
14262 && GST_PAD_IS_EOS (newstream->pad);
14263
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014264 return gst_aml_qtdemux_configure_stream (qtdemux, newstream);
zengliang.li5f31ef42024-05-16 08:27:38 +000014265}
14266
14267static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014268aml_qtdemux_update_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014269{
14270 gint i;
14271 g_assert (qtdemux->streams_aware);
14272
14273 /* At below, figure out which stream in active_streams has identical stream-id
14274 * with that of in old_streams. If there is matching stream-id,
14275 * corresponding newstream will not be exposed again,
14276 * but demux will reuse srcpad of matched old stream
14277 *
14278 * active_streams : newly created streams from the latest moov
14279 * old_streams : existing streams (belong to previous moov)
14280 */
14281
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014282 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14283 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
14284 AmlQtDemuxStream *oldstream = NULL;
zengliang.li5f31ef42024-05-16 08:27:38 +000014285 guint target;
14286
14287 GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014288 stream->track_id, GST_FOURCC_ARGS (AML_CUR_STREAM (stream)->fourcc));
zengliang.li5f31ef42024-05-16 08:27:38 +000014289
14290 if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014291 stream->stream_id, (GEqualFunc) _aml_stream_equal_func, &target)) {
14292 oldstream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
zengliang.li5f31ef42024-05-16 08:27:38 +000014293
14294 /* null pad stream cannot be reused */
14295 if (oldstream->pad == NULL)
14296 oldstream = NULL;
14297 }
14298
14299 if (oldstream) {
14300 GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
14301
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014302 if (!aml_qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
zengliang.li5f31ef42024-05-16 08:27:38 +000014303 return FALSE;
14304
14305 /* we don't need to preserve order of old streams */
14306 g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
14307 } else {
14308 GstTagList *list;
14309
14310 /* now we have all info and can expose */
14311 list = stream->stream_tags;
14312 stream->stream_tags = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014313 if (!gst_aml_qtdemux_add_stream (qtdemux, stream, list))
zengliang.li5f31ef42024-05-16 08:27:38 +000014314 return FALSE;
14315 }
14316 }
14317
14318 return TRUE;
14319}
14320
14321/* Must be called with expose lock */
14322static GstFlowReturn
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014323aml_qtdemux_expose_streams (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014324{
14325 gint i;
14326
14327 GST_DEBUG_OBJECT (qtdemux, "exposing streams");
14328
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014329 if (!aml_qtdemux_is_streams_update (qtdemux)) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014330 GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014331 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14332 AmlQtDemuxStream *new_stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
14333 AmlQtDemuxStream *old_stream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
14334 if (!aml_qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
zengliang.li5f31ef42024-05-16 08:27:38 +000014335 return GST_FLOW_ERROR;
14336 }
14337
14338 g_ptr_array_set_size (qtdemux->old_streams, 0);
14339 qtdemux->need_segment = TRUE;
14340
14341 return GST_FLOW_OK;
14342 }
14343
14344 if (qtdemux->streams_aware) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014345 if (!aml_qtdemux_update_streams (qtdemux))
zengliang.li5f31ef42024-05-16 08:27:38 +000014346 return GST_FLOW_ERROR;
14347 } else {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014348 for (i = 0; i < AML_QTDEMUX_N_STREAMS (qtdemux); i++) {
14349 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014350 GstTagList *list;
14351
14352 /* now we have all info and can expose */
14353 list = stream->stream_tags;
14354 stream->stream_tags = NULL;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014355 if (!gst_aml_qtdemux_add_stream (qtdemux, stream, list))
zengliang.li5f31ef42024-05-16 08:27:38 +000014356 return GST_FLOW_ERROR;
14357
14358 }
14359 }
14360
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014361 gst_aml_qtdemux_guess_bitrate (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014362
14363 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
14364
14365 /* If we have still old_streams, it's no more used stream */
14366 for (i = 0; i < qtdemux->old_streams->len; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014367 AmlQtDemuxStream *stream = AML_QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
zengliang.li5f31ef42024-05-16 08:27:38 +000014368
14369 if (stream->pad) {
14370 GstEvent *event;
14371
14372 event = gst_event_new_eos ();
14373 if (qtdemux->segment_seqnum)
14374 gst_event_set_seqnum (event, qtdemux->segment_seqnum);
14375
14376 gst_pad_push_event (stream->pad, event);
14377 }
14378 }
14379
14380 g_ptr_array_set_size (qtdemux->old_streams, 0);
14381
14382 /* check if we should post a redirect in case there is a single trak
14383 * and it is a redirecting trak */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014384 if (AML_QTDEMUX_N_STREAMS (qtdemux) == 1 &&
14385 AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014386 GstMessage *m;
14387
14388 GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
14389 "an external content");
14390 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
14391 gst_structure_new ("redirect",
14392 "new-location", G_TYPE_STRING,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014393 AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
zengliang.li5f31ef42024-05-16 08:27:38 +000014394 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
14395 g_free (qtdemux->redirect_location);
14396 qtdemux->redirect_location =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014397 g_strdup (AML_QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
zengliang.li5f31ef42024-05-16 08:27:38 +000014398 }
14399
14400 g_ptr_array_foreach (qtdemux->active_streams,
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014401 (GFunc) aml_qtdemux_do_allocation, qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014402
14403 qtdemux->need_segment = TRUE;
14404
14405 qtdemux->exposed = TRUE;
14406 return GST_FLOW_OK;
14407}
14408
14409typedef struct
14410{
14411 GstStructure *structure; /* helper for sort function */
14412 gchar *location;
14413 guint min_req_bitrate;
14414 guint min_req_qt_version;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014415} GstAmlQtReference;
zengliang.li5f31ef42024-05-16 08:27:38 +000014416
14417static gint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014418aml_qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
zengliang.li5f31ef42024-05-16 08:27:38 +000014419{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014420 GstAmlQtReference *ref_a = (GstAmlQtReference *) a;
14421 GstAmlQtReference *ref_b = (GstAmlQtReference *) b;
zengliang.li5f31ef42024-05-16 08:27:38 +000014422
14423 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
14424 return ref_b->min_req_qt_version - ref_a->min_req_qt_version;
14425
14426 /* known bitrates go before unknown; higher bitrates go first */
14427 return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
14428}
14429
14430/* sort the redirects and post a message for the application.
14431 */
14432static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014433aml_qtdemux_process_redirects (GstAmlQTDemux * qtdemux, GList * references)
zengliang.li5f31ef42024-05-16 08:27:38 +000014434{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014435 GstAmlQtReference *best;
zengliang.li5f31ef42024-05-16 08:27:38 +000014436 GstStructure *s;
14437 GstMessage *msg;
14438 GValue list_val = { 0, };
14439 GList *l;
14440
14441 g_assert (references != NULL);
14442
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014443 references = g_list_sort (references, aml_qtdemux_redirects_sort_func);
zengliang.li5f31ef42024-05-16 08:27:38 +000014444
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014445 best = (GstAmlQtReference *) references->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014446
14447 g_value_init (&list_val, GST_TYPE_LIST);
14448
14449 for (l = references; l != NULL; l = l->next) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014450 GstAmlQtReference *ref = (GstAmlQtReference *) l->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014451 GValue struct_val = { 0, };
14452
14453 ref->structure = gst_structure_new ("redirect",
14454 "new-location", G_TYPE_STRING, ref->location, NULL);
14455
14456 if (ref->min_req_bitrate > 0) {
14457 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT,
14458 ref->min_req_bitrate, NULL);
14459 }
14460
14461 g_value_init (&struct_val, GST_TYPE_STRUCTURE);
14462 g_value_set_boxed (&struct_val, ref->structure);
14463 gst_value_list_append_value (&list_val, &struct_val);
14464 g_value_unset (&struct_val);
14465 /* don't free anything here yet, since we need best->structure below */
14466 }
14467
14468 g_assert (best != NULL);
14469 s = gst_structure_copy (best->structure);
14470
14471 if (g_list_length (references) > 1) {
14472 gst_structure_set_value (s, "locations", &list_val);
14473 }
14474
14475 g_value_unset (&list_val);
14476
14477 for (l = references; l != NULL; l = l->next) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014478 GstAmlQtReference *ref = (GstAmlQtReference *) l->data;
zengliang.li5f31ef42024-05-16 08:27:38 +000014479
14480 gst_structure_free (ref->structure);
14481 g_free (ref->location);
14482 g_free (ref);
14483 }
14484 g_list_free (references);
14485
14486 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s);
14487 g_free (qtdemux->redirect_location);
14488 qtdemux->redirect_location =
14489 g_strdup (gst_structure_get_string (s, "new-location"));
14490 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s);
14491 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
14492}
14493
14494/* look for redirect nodes, collect all redirect information and
14495 * process it.
14496 */
14497static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014498aml_qtdemux_parse_redirects (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014499{
14500 GNode *rmra, *rmda, *rdrf;
14501
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014502 rmra = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_rmra);
zengliang.li5f31ef42024-05-16 08:27:38 +000014503 if (rmra) {
14504 GList *redirects = NULL;
14505
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014506 rmda = aml_qtdemux_tree_get_child_by_type (rmra, AML_FOURCC_rmda);
zengliang.li5f31ef42024-05-16 08:27:38 +000014507 while (rmda) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014508 GstAmlQtReference ref = { NULL, NULL, 0, 0 };
zengliang.li5f31ef42024-05-16 08:27:38 +000014509 GNode *rmdr, *rmvc;
14510
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014511 if ((rmdr = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rmdr))) {
14512 ref.min_req_bitrate = AML_QT_UINT32 ((guint8 *) rmdr->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000014513 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
14514 ref.min_req_bitrate);
14515 }
14516
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014517 if ((rmvc = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rmvc))) {
14518 guint32 package = AML_QT_FOURCC ((guint8 *) rmvc->data + 12);
14519 guint version = AML_QT_UINT32 ((guint8 *) rmvc->data + 16);
zengliang.li5f31ef42024-05-16 08:27:38 +000014520
14521#ifndef GST_DISABLE_GST_DEBUG
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014522 guint bitmask = AML_QT_UINT32 ((guint8 *) rmvc->data + 20);
zengliang.li5f31ef42024-05-16 08:27:38 +000014523#endif
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014524 guint check_type = AML_QT_UINT16 ((guint8 *) rmvc->data + 24);
zengliang.li5f31ef42024-05-16 08:27:38 +000014525
14526 GST_LOG_OBJECT (qtdemux,
14527 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
14528 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
14529 bitmask, check_type);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014530 if (package == AML_FOURCC_qtim && check_type == 0) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014531 ref.min_req_qt_version = version;
14532 }
14533 }
14534
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014535 rdrf = aml_qtdemux_tree_get_child_by_type (rmda, AML_FOURCC_rdrf);
zengliang.li5f31ef42024-05-16 08:27:38 +000014536 if (rdrf) {
14537 guint32 ref_type;
14538 guint8 *ref_data;
14539 guint ref_len;
14540
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014541 ref_len = AML_QT_UINT32 ((guint8 *) rdrf->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014542 if (ref_len > 20) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014543 ref_type = AML_QT_FOURCC ((guint8 *) rdrf->data + 12);
zengliang.li5f31ef42024-05-16 08:27:38 +000014544 ref_data = (guint8 *) rdrf->data + 20;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014545 if (ref_type == AML_FOURCC_alis) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014546 guint record_len, record_version, fn_len;
14547
14548 if (ref_len > 70) {
14549 /* MacOSX alias record, google for alias-layout.txt */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014550 record_len = AML_QT_UINT16 (ref_data + 4);
14551 record_version = AML_QT_UINT16 (ref_data + 4 + 2);
14552 fn_len = AML_QT_UINT8 (ref_data + 50);
zengliang.li5f31ef42024-05-16 08:27:38 +000014553 if (record_len > 50 && record_version == 2 && fn_len > 0) {
14554 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
14555 }
14556 } else {
14557 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf/alis size (%u < 70)",
14558 ref_len);
14559 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014560 } else if (ref_type == AML_FOURCC_url_) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014561 ref.location = g_strndup ((gchar *) ref_data, ref_len - 8);
14562 } else {
14563 GST_DEBUG_OBJECT (qtdemux,
14564 "unknown rdrf reference type %" GST_FOURCC_FORMAT,
14565 GST_FOURCC_ARGS (ref_type));
14566 }
14567 if (ref.location != NULL) {
14568 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
14569 redirects =
14570 g_list_prepend (redirects, g_memdup2 (&ref, sizeof (ref)));
14571 } else {
14572 GST_WARNING_OBJECT (qtdemux,
14573 "Failed to extract redirect location from rdrf atom");
14574 }
14575 } else {
14576 GST_WARNING_OBJECT (qtdemux, "Invalid rdrf size (%u < 20)", ref_len);
14577 }
14578 }
14579
14580 /* look for others */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014581 rmda = aml_qtdemux_tree_get_sibling_by_type (rmda, AML_FOURCC_rmda);
zengliang.li5f31ef42024-05-16 08:27:38 +000014582 }
14583
14584 if (redirects != NULL) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014585 aml_qtdemux_process_redirects (qtdemux, redirects);
zengliang.li5f31ef42024-05-16 08:27:38 +000014586 }
14587 }
14588 return TRUE;
14589}
14590
14591static GstTagList *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014592aml_qtdemux_add_container_format (GstAmlQTDemux * qtdemux, GstTagList * tags)
zengliang.li5f31ef42024-05-16 08:27:38 +000014593{
14594 const gchar *fmt;
14595
14596 if (tags == NULL) {
14597 tags = gst_tag_list_new_empty ();
14598 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
14599 }
14600
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014601 if (qtdemux->major_brand == AML_FOURCC_mjp2)
zengliang.li5f31ef42024-05-16 08:27:38 +000014602 fmt = "Motion JPEG 2000";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014603 else if ((qtdemux->major_brand & 0xffff) == AML_FOURCC_3g__)
zengliang.li5f31ef42024-05-16 08:27:38 +000014604 fmt = "3GP";
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014605 else if (qtdemux->major_brand == AML_FOURCC_qt__)
zengliang.li5f31ef42024-05-16 08:27:38 +000014606 fmt = "Quicktime";
14607 else if (qtdemux->fragmented)
14608 fmt = "ISO fMP4";
14609 else
14610 fmt = "ISO MP4/M4A";
14611
14612 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
14613 GST_FOURCC_ARGS (qtdemux->major_brand), fmt);
14614
14615 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
14616 fmt, NULL);
14617
14618 return tags;
14619}
14620
14621/* we have read the complete moov node now.
14622 * This function parses all of the relevant info, creates the traks and
14623 * prepares all data structures for playback
14624 */
14625static gboolean
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014626aml_qtdemux_parse_tree (GstAmlQTDemux * qtdemux)
zengliang.li5f31ef42024-05-16 08:27:38 +000014627{
14628 GNode *mvhd;
14629 GNode *trak;
14630 GNode *udta;
14631 GNode *mvex;
14632 GNode *pssh;
14633 guint64 creation_time;
14634 GstDateTime *datetime = NULL;
14635 gint version;
14636
14637 /* make sure we have a usable taglist */
14638 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14639
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014640 mvhd = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_mvhd);
zengliang.li5f31ef42024-05-16 08:27:38 +000014641 if (mvhd == NULL) {
14642 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014643 return aml_qtdemux_parse_redirects (qtdemux);
zengliang.li5f31ef42024-05-16 08:27:38 +000014644 }
14645
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014646 version = AML_QT_UINT8 ((guint8 *) mvhd->data + 8);
zengliang.li5f31ef42024-05-16 08:27:38 +000014647 if (version == 1) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014648 creation_time = AML_QT_UINT64 ((guint8 *) mvhd->data + 12);
14649 qtdemux->timescale = AML_QT_UINT32 ((guint8 *) mvhd->data + 28);
14650 qtdemux->duration = AML_QT_UINT64 ((guint8 *) mvhd->data + 32);
zengliang.li5f31ef42024-05-16 08:27:38 +000014651 } else if (version == 0) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014652 creation_time = AML_QT_UINT32 ((guint8 *) mvhd->data + 12);
14653 qtdemux->timescale = AML_QT_UINT32 ((guint8 *) mvhd->data + 20);
14654 qtdemux->duration = AML_QT_UINT32 ((guint8 *) mvhd->data + 24);
zengliang.li5f31ef42024-05-16 08:27:38 +000014655 } else {
14656 GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
14657 return FALSE;
14658 }
14659
14660 /* Moving qt creation time (secs since 1904) to unix time */
14661 if (creation_time != 0) {
14662 /* Try to use epoch first as it should be faster and more commonly found */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014663 if (creation_time >= AML_QTDEMUX_SECONDS_FROM_1904_TO_1970) {
zengliang.li5f31ef42024-05-16 08:27:38 +000014664 gint64 now_s;
14665
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014666 creation_time -= AML_QTDEMUX_SECONDS_FROM_1904_TO_1970;
zengliang.li5f31ef42024-05-16 08:27:38 +000014667 /* some data cleansing sanity */
14668 now_s = g_get_real_time () / G_USEC_PER_SEC;
14669 if (now_s + 24 * 3600 < creation_time) {
14670 GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time");
14671 } else {
14672 datetime = gst_date_time_new_from_unix_epoch_utc (creation_time);
14673 }
14674 } else {
14675 GDateTime *base_dt = g_date_time_new_utc (1904, 1, 1, 0, 0, 0);
14676 GDateTime *dt, *dt_local;
14677
14678 dt = g_date_time_add_seconds (base_dt, creation_time);
14679 dt_local = g_date_time_to_local (dt);
14680 datetime = gst_date_time_new_from_g_date_time (dt_local);
14681
14682 g_date_time_unref (base_dt);
14683 g_date_time_unref (dt);
14684 }
14685 }
14686 if (datetime) {
14687 /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
14688 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
14689 datetime, NULL);
14690 gst_date_time_unref (datetime);
14691 }
14692
14693 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
14694 GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);
14695
14696 /* check for fragmented file and get some (default) data */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014697 mvex = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_mvex);
zengliang.li5f31ef42024-05-16 08:27:38 +000014698 if (mvex) {
14699 GNode *mehd;
14700 GstByteReader mehd_data;
14701
14702 /* let track parsing or anyone know weird stuff might happen ... */
14703 qtdemux->fragmented = TRUE;
14704
14705 /* compensate for total duration */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014706 mehd = aml_qtdemux_tree_get_child_by_type_full (mvex, AML_FOURCC_mehd, &mehd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014707 if (mehd)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014708 aml_qtdemux_parse_mehd (qtdemux, &mehd_data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014709 }
14710
14711 /* Update the movie segment duration, unless it was directly given to us
14712 * by upstream. Otherwise let it as is, as we don't want to mangle the
14713 * duration provided by upstream that may come e.g. from a MPD file. */
14714 if (!qtdemux->upstream_format_is_time) {
14715 GstClockTime duration;
14716 /* set duration in the segment info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014717 gst_aml_qtdemux_get_duration (qtdemux, &duration);
zengliang.li5f31ef42024-05-16 08:27:38 +000014718 qtdemux->segment.duration = duration;
14719 /* also do not exceed duration; stop is set that way post seek anyway,
14720 * and segment activation falls back to duration,
14721 * whereas loop only checks stop, so let's align this here as well */
14722 qtdemux->segment.stop = duration;
14723 }
14724
14725 /* parse all traks */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014726 trak = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014727 while (trak) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014728 aml_qtdemux_parse_trak (qtdemux, trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014729 /* iterate all siblings */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014730 trak = aml_qtdemux_tree_get_sibling_by_type (trak, AML_FOURCC_trak);
zengliang.li5f31ef42024-05-16 08:27:38 +000014731 }
14732
14733 qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
14734
14735 /* find tags */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014736 udta = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014737 if (udta) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014738 aml_qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014739 } else {
14740 GST_LOG_OBJECT (qtdemux, "No udta node found.");
14741 }
14742
14743 /* maybe also some tags in meta box */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014744 udta = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_meta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014745 if (udta) {
14746 GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014747 aml_qtdemux_parse_udta (qtdemux, qtdemux->tag_list, udta);
zengliang.li5f31ef42024-05-16 08:27:38 +000014748 } else {
14749 GST_LOG_OBJECT (qtdemux, "No meta node found.");
14750 }
14751
14752 /* parse any protection system info */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014753 pssh = aml_qtdemux_tree_get_child_by_type (qtdemux->moov_node, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +000014754 while (pssh) {
14755 GST_LOG_OBJECT (qtdemux, "Parsing pssh box.");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014756 aml_qtdemux_parse_pssh (qtdemux, pssh);
14757 pssh = aml_qtdemux_tree_get_sibling_by_type (pssh, AML_FOURCC_pssh);
zengliang.li5f31ef42024-05-16 08:27:38 +000014758 }
14759
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014760 qtdemux->tag_list = aml_qtdemux_add_container_format (qtdemux, qtdemux->tag_list);
zengliang.li5f31ef42024-05-16 08:27:38 +000014761
14762 return TRUE;
14763}
14764
14765/* taken from ffmpeg */
14766static int
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014767aml_read_descr_size (guint8 * ptr, guint8 * end, guint8 ** end_out)
zengliang.li5f31ef42024-05-16 08:27:38 +000014768{
14769 int count = 4;
14770 int len = 0;
14771
14772 while (count--) {
14773 int c;
14774
14775 if (ptr >= end)
14776 return -1;
14777
14778 c = *ptr++;
14779 len = (len << 7) | (c & 0x7f);
14780 if (!(c & 0x80))
14781 break;
14782 }
14783 *end_out = ptr;
14784 return len;
14785}
14786
14787static GList *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014788aml_parse_xiph_stream_headers (GstAmlQTDemux * qtdemux, gpointer codec_data,
zengliang.li5f31ef42024-05-16 08:27:38 +000014789 gsize codec_data_size)
14790{
14791 GList *list = NULL;
14792 guint8 *p = codec_data;
14793 gint i, offset, num_packets;
14794 guint *length, last;
14795
14796 GST_MEMDUMP_OBJECT (qtdemux, "xiph codec data", codec_data, codec_data_size);
14797
14798 if (codec_data == NULL || codec_data_size == 0)
14799 goto error;
14800
14801 /* start of the stream and vorbis audio or theora video, need to
14802 * send the codec_priv data as first three packets */
14803 num_packets = p[0] + 1;
14804 GST_DEBUG_OBJECT (qtdemux,
14805 "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
14806 (guint) num_packets, codec_data_size);
14807
14808 /* Let's put some limits, Don't think there even is a xiph codec
14809 * with more than 3-4 headers */
14810 if (G_UNLIKELY (num_packets > 16)) {
14811 GST_WARNING_OBJECT (qtdemux,
14812 "Unlikely number of xiph headers, most likely not valid");
14813 goto error;
14814 }
14815
14816 length = g_alloca (num_packets * sizeof (guint));
14817 last = 0;
14818 offset = 1;
14819
14820 /* first packets, read length values */
14821 for (i = 0; i < num_packets - 1; i++) {
14822 length[i] = 0;
14823 while (offset < codec_data_size) {
14824 length[i] += p[offset];
14825 if (p[offset++] != 0xff)
14826 break;
14827 }
14828 last += length[i];
14829 }
14830 if (offset + last > codec_data_size)
14831 goto error;
14832
14833 /* last packet is the remaining size */
14834 length[i] = codec_data_size - offset - last;
14835
14836 for (i = 0; i < num_packets; i++) {
14837 GstBuffer *hdr;
14838
14839 GST_DEBUG_OBJECT (qtdemux, "buffer %d: %u bytes", i, (guint) length[i]);
14840
14841 if (offset + length[i] > codec_data_size)
14842 goto error;
14843
bo.xiao86472382024-07-23 10:24:15 +080014844#if ((GST_VERSION_MAJOR == 1) && (GST_VERSION_MINOR >= 20))
14845 hdr = gst_buffer_new_memdup (p + offset, length[i]);
14846#else
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014847 hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
bo.xiao86472382024-07-23 10:24:15 +080014848#endif
14849
zengliang.li5f31ef42024-05-16 08:27:38 +000014850 list = g_list_append (list, hdr);
14851
14852 offset += length[i];
14853 }
14854
14855 return list;
14856
14857 /* ERRORS */
14858error:
14859 {
14860 if (list != NULL)
14861 g_list_free_full (list, (GDestroyNotify) gst_buffer_unref);
14862 return NULL;
14863 }
14864
14865}
14866
14867/* this can change the codec originally present in @list */
14868static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014869gst_aml_qtdemux_handle_esds (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
14870 AmlQtDemuxStreamStsdEntry * entry, GNode * esds, GstTagList * list)
zengliang.li5f31ef42024-05-16 08:27:38 +000014871{
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014872 int len = AML_QT_UINT32 (esds->data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014873 guint8 *ptr = esds->data;
14874 guint8 *end = ptr + len;
14875 int tag;
14876 guint8 *data_ptr = NULL;
14877 int data_len = 0;
14878 guint8 object_type_id = 0;
14879 guint8 stream_type = 0;
14880 const char *codec_name = NULL;
14881 GstCaps *caps = NULL;
14882
14883 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
14884 ptr += 8;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014885 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", AML_QT_UINT32 (ptr));
zengliang.li5f31ef42024-05-16 08:27:38 +000014886 ptr += 4;
14887 while (ptr + 1 < end) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014888 tag = AML_QT_UINT8 (ptr);
zengliang.li5f31ef42024-05-16 08:27:38 +000014889 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
14890 ptr++;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014891 len = aml_read_descr_size (ptr, end, &ptr);
zengliang.li5f31ef42024-05-16 08:27:38 +000014892 GST_DEBUG_OBJECT (qtdemux, "len = %d", len);
14893
14894 /* Check the stated amount of data is available for reading */
14895 if (len < 0 || ptr + len > end)
14896 break;
14897
14898 switch (tag) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014899 case AML_ES_DESCRIPTOR_TAG:
14900 GST_DEBUG_OBJECT (qtdemux, "ID 0x%04x", AML_QT_UINT16 (ptr));
14901 GST_DEBUG_OBJECT (qtdemux, "priority 0x%04x", AML_QT_UINT8 (ptr + 2));
zengliang.li5f31ef42024-05-16 08:27:38 +000014902 ptr += 3;
14903 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014904 case AML_DECODER_CONFIG_DESC_TAG:{
zengliang.li5f31ef42024-05-16 08:27:38 +000014905 guint max_bitrate, avg_bitrate;
14906
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014907 object_type_id = AML_QT_UINT8 (ptr);
14908 stream_type = AML_QT_UINT8 (ptr + 1) >> 2;
14909 max_bitrate = AML_QT_UINT32 (ptr + 5);
14910 avg_bitrate = AML_QT_UINT32 (ptr + 9);
zengliang.li5f31ef42024-05-16 08:27:38 +000014911 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
14912 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", stream_type);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014913 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", AML_QT_UINT24 (ptr + 2));
zengliang.li5f31ef42024-05-16 08:27:38 +000014914 GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
14915 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
14916 if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
14917 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
14918 GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
14919 }
14920 if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
14921 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
14922 avg_bitrate, NULL);
14923 }
14924 ptr += 13;
14925 break;
14926 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014927 case AML_DECODER_SPECIFIC_INFO_TAG:
zengliang.li5f31ef42024-05-16 08:27:38 +000014928 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
14929 if (object_type_id == 0xe0 && len == 0x40) {
14930 guint8 *data;
14931 GstStructure *s;
14932 guint32 clut[16];
14933 gint i;
14934
14935 GST_DEBUG_OBJECT (qtdemux,
14936 "Have VOBSUB palette. Creating palette event");
14937 /* move to decConfigDescr data and read palette */
14938 data = ptr;
14939 for (i = 0; i < 16; i++) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014940 clut[i] = AML_QT_UINT32 (data);
zengliang.li5f31ef42024-05-16 08:27:38 +000014941 data += 4;
14942 }
14943
14944 s = gst_structure_new ("application/x-gst-dvd", "event",
14945 G_TYPE_STRING, "dvd-spu-clut-change",
14946 "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
14947 "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
14948 "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
14949 "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
14950 "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
14951 "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
14952 "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
14953 "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
14954 NULL);
14955
14956 /* store event and trigger custom processing */
14957 stream->pending_event =
14958 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
14959 } else {
14960 /* Generic codec_data handler puts it on the caps */
14961 data_ptr = ptr;
14962 data_len = len;
14963 }
14964
14965 ptr += len;
14966 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000014967 case AML_SL_CONFIG_DESC_TAG:
14968 GST_DEBUG_OBJECT (qtdemux, "data %02x", AML_QT_UINT8 (ptr));
zengliang.li5f31ef42024-05-16 08:27:38 +000014969 ptr += 1;
14970 break;
14971 default:
14972 GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
14973 tag);
14974 GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
14975 ptr += len;
14976 break;
14977 }
14978 }
14979
14980 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
14981 * in use, and should also be used to override some other parameters for some
14982 * codecs. */
14983 switch (object_type_id) {
14984 case 0x20: /* MPEG-4 */
14985 /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
14986 * profile_and_level_indication */
14987 if (data_ptr != NULL && data_len >= 5 &&
14988 GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
14989 gst_codec_utils_mpeg4video_caps_set_level_and_profile (entry->caps,
14990 data_ptr + 4, data_len - 4);
14991 }
14992 break; /* Nothing special needed here */
14993 case 0x21: /* H.264 */
14994 codec_name = "H.264 / AVC";
14995 caps = gst_caps_new_simple ("video/x-h264",
14996 "stream-format", G_TYPE_STRING, "avc",
14997 "alignment", G_TYPE_STRING, "au", NULL);
14998 break;
14999 case 0x40: /* AAC (any) */
15000 case 0x66: /* AAC Main */
15001 case 0x67: /* AAC LC */
15002 case 0x68: /* AAC SSR */
15003 /* Override channels and rate based on the codec_data, as it's often
15004 * wrong. */
15005 /* Only do so for basic setup without HE-AAC extension */
15006 if (data_ptr && data_len == 2) {
15007 guint channels, rate;
15008
15009 channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
15010 if (channels > 0)
15011 entry->n_channels = channels;
15012
15013 rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
15014 if (rate > 0)
15015 entry->rate = rate;
15016 }
15017
15018 /* Set level and profile if possible */
15019 if (data_ptr != NULL && data_len >= 2) {
15020 gst_codec_utils_aac_caps_set_level_and_profile (entry->caps,
15021 data_ptr, data_len);
15022 } else {
15023 const gchar *profile_str = NULL;
15024 GstBuffer *buffer;
15025 GstMapInfo map;
15026 guint8 *codec_data;
15027 gint rate_idx, profile;
15028
15029 /* No codec_data, let's invent something.
15030 * FIXME: This is wrong for SBR! */
15031
15032 GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");
15033
15034 buffer = gst_buffer_new_and_alloc (2);
15035 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
15036 codec_data = map.data;
15037
15038 rate_idx =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015039 gst_codec_utils_aac_get_index_from_sample_rate (AML_CUR_STREAM
zengliang.li5f31ef42024-05-16 08:27:38 +000015040 (stream)->rate);
15041
15042 switch (object_type_id) {
15043 case 0x66:
15044 profile_str = "main";
15045 profile = 0;
15046 break;
15047 case 0x67:
15048 profile_str = "lc";
15049 profile = 1;
15050 break;
15051 case 0x68:
15052 profile_str = "ssr";
15053 profile = 2;
15054 break;
15055 default:
15056 profile = 3;
15057 break;
15058 }
15059
15060 codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
15061 codec_data[1] =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015062 ((rate_idx & 0x1) << 7) | (AML_CUR_STREAM (stream)->n_channels << 3);
zengliang.li5f31ef42024-05-16 08:27:38 +000015063
15064 gst_buffer_unmap (buffer, &map);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015065 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "codec_data",
zengliang.li5f31ef42024-05-16 08:27:38 +000015066 GST_TYPE_BUFFER, buffer, NULL);
15067 gst_buffer_unref (buffer);
15068
15069 if (profile_str) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015070 gst_caps_set_simple (AML_CUR_STREAM (stream)->caps, "profile",
zengliang.li5f31ef42024-05-16 08:27:38 +000015071 G_TYPE_STRING, profile_str, NULL);
15072 }
15073 }
15074 break;
15075 case 0x60: /* MPEG-2, various profiles */
15076 case 0x61:
15077 case 0x62:
15078 case 0x63:
15079 case 0x64:
15080 case 0x65:
15081 codec_name = "MPEG-2 video";
15082 caps = gst_caps_new_simple ("video/mpeg",
15083 "mpegversion", G_TYPE_INT, 2,
15084 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15085 break;
15086 case 0x69: /* MPEG-2 BC audio */
15087 case 0x6B: /* MPEG-1 audio */
15088 caps = gst_caps_new_simple ("audio/mpeg",
15089 "mpegversion", G_TYPE_INT, 1, NULL);
15090 codec_name = "MPEG-1 audio";
15091 break;
15092 case 0x6A: /* MPEG-1 */
15093 codec_name = "MPEG-1 video";
15094 caps = gst_caps_new_simple ("video/mpeg",
15095 "mpegversion", G_TYPE_INT, 1,
15096 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15097 break;
15098 case 0x6C: /* MJPEG */
15099 caps =
15100 gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
15101 NULL);
15102 codec_name = "Motion-JPEG";
15103 break;
15104 case 0x6D: /* PNG */
15105 caps =
15106 gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
15107 NULL);
15108 codec_name = "PNG still images";
15109 break;
15110 case 0x6E: /* JPEG2000 */
15111 codec_name = "JPEG-2000";
15112 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15113 break;
15114 case 0xA4: /* Dirac */
15115 codec_name = "Dirac";
15116 caps = gst_caps_new_empty_simple ("video/x-dirac");
15117 break;
15118 case 0xA5: /* AC3 */
15119 codec_name = "AC-3 audio";
15120 caps = gst_caps_new_simple ("audio/x-ac3",
15121 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15122 break;
15123 case 0xA9: /* AC3 */
15124 codec_name = "DTS audio";
15125 caps = gst_caps_new_simple ("audio/x-dts",
15126 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15127 break;
15128 case 0xDD:
15129 if (stream_type == 0x05 && data_ptr) {
15130 GList *headers =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015131 aml_parse_xiph_stream_headers (qtdemux, data_ptr, data_len);
zengliang.li5f31ef42024-05-16 08:27:38 +000015132 if (headers) {
15133 GList *tmp;
15134 GValue arr_val = G_VALUE_INIT;
15135 GValue buf_val = G_VALUE_INIT;
15136 GstStructure *s;
15137
15138 /* Let's assume it's vorbis if it's an audio stream of type 0xdd and we have codec data that extracts properly */
15139 codec_name = "Vorbis";
15140 caps = gst_caps_new_empty_simple ("audio/x-vorbis");
15141 g_value_init (&arr_val, GST_TYPE_ARRAY);
15142 g_value_init (&buf_val, GST_TYPE_BUFFER);
15143 for (tmp = headers; tmp; tmp = tmp->next) {
15144 g_value_set_boxed (&buf_val, (GstBuffer *) tmp->data);
15145 gst_value_array_append_value (&arr_val, &buf_val);
15146 }
15147 s = gst_caps_get_structure (caps, 0);
15148 gst_structure_take_value (s, "streamheader", &arr_val);
15149 g_value_unset (&buf_val);
15150 g_list_free (headers);
15151
15152 data_ptr = NULL;
15153 data_len = 0;
15154 }
15155 }
15156 break;
15157 case 0xE1: /* QCELP */
15158 /* QCELP, the codec_data is a riff tag (little endian) with
15159 * 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). */
15160 caps = gst_caps_new_empty_simple ("audio/qcelp");
15161 codec_name = "QCELP";
15162 break;
15163 default:
15164 break;
15165 }
15166
15167 /* If we have a replacement caps, then change our caps for this stream */
15168 if (caps) {
15169 gst_caps_unref (entry->caps);
15170 entry->caps = caps;
15171 }
15172
15173 if (codec_name && list)
15174 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
15175 GST_TAG_AUDIO_CODEC, codec_name, NULL);
15176
15177 /* Add the codec_data attribute to caps, if we have it */
15178 if (data_ptr) {
15179 GstBuffer *buffer;
15180
15181 buffer = gst_buffer_new_and_alloc (data_len);
15182 gst_buffer_fill (buffer, 0, data_ptr, data_len);
15183
15184 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
15185 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
15186
15187 gst_caps_set_simple (entry->caps, "codec_data", GST_TYPE_BUFFER,
15188 buffer, NULL);
15189 gst_buffer_unref (buffer);
15190 }
15191
15192}
15193
15194static inline GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015195_aml_get_unknown_codec_name (const gchar * type, guint32 fourcc)
zengliang.li5f31ef42024-05-16 08:27:38 +000015196{
15197 GstCaps *caps;
15198 guint i;
15199 char *s, fourstr[5];
15200
15201 g_snprintf (fourstr, 5, "%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
15202 for (i = 0; i < 4; i++) {
15203 if (!g_ascii_isalnum (fourstr[i]))
15204 fourstr[i] = '_';
15205 }
15206 s = g_strdup_printf ("%s/x-gst-fourcc-%s", type, g_strstrip (fourstr));
15207 caps = gst_caps_new_empty_simple (s);
15208 g_free (s);
15209 return caps;
15210}
15211
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015212#define _aml_codec(name) \
zengliang.li5f31ef42024-05-16 08:27:38 +000015213 do { \
15214 if (codec_name) { \
15215 *codec_name = g_strdup (name); \
15216 } \
15217 } while (0)
15218
15219static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015220aml_qtdemux_video_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
15221 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000015222 const guint8 * stsd_entry_data, gchar ** codec_name)
15223{
15224 GstCaps *caps = NULL;
15225 GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
15226
15227 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015228 case AML_FOURCC_png:
15229 _aml_codec ("PNG still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015230 caps = gst_caps_new_empty_simple ("image/png");
15231 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015232 case AML_FOURCC_jpeg:
15233 _aml_codec ("JPEG still images");
bo.xiaof8fd4c52024-08-13 11:43:09 +080015234 GST_DEBUG_OBJECT (qtdemux, "JPEG still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015235 caps =
bo.xiao86472382024-07-23 10:24:15 +080015236 gst_caps_new_simple ("video/mjpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
zengliang.li5f31ef42024-05-16 08:27:38 +000015237 NULL);
15238 break;
15239 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'):
15240 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'):
15241 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'):
15242 case GST_MAKE_FOURCC ('d', 'm', 'b', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015243 _aml_codec ("Motion-JPEG");
bo.xiaof8fd4c52024-08-13 11:43:09 +080015244 GST_DEBUG_OBJECT (qtdemux, "Motion-JPEG");
zengliang.li5f31ef42024-05-16 08:27:38 +000015245 caps =
bo.xiao86472382024-07-23 10:24:15 +080015246 gst_caps_new_simple ("video/mjpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
zengliang.li5f31ef42024-05-16 08:27:38 +000015247 NULL);
15248 break;
15249 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015250 _aml_codec ("Motion-JPEG format B");
zengliang.li5f31ef42024-05-16 08:27:38 +000015251 caps = gst_caps_new_empty_simple ("video/x-mjpeg-b");
15252 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015253 case AML_FOURCC_mjp2:
15254 _aml_codec ("JPEG-2000");
zengliang.li5f31ef42024-05-16 08:27:38 +000015255 /* override to what it should be according to spec, avoid palette_data */
15256 entry->bits_per_sample = 24;
15257 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
15258 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015259 case AML_FOURCC_SVQ3:
15260 _aml_codec ("Sorensen video v.3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015261 caps = gst_caps_new_simple ("video/x-svq",
15262 "svqversion", G_TYPE_INT, 3, NULL);
15263 break;
15264 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'):
15265 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015266 _aml_codec ("Sorensen video v.1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015267 caps = gst_caps_new_simple ("video/x-svq",
15268 "svqversion", G_TYPE_INT, 1, NULL);
15269 break;
15270 case GST_MAKE_FOURCC ('W', 'R', 'A', 'W'):
15271 caps = gst_caps_new_empty_simple ("video/x-raw");
15272 gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015273 _aml_codec ("Windows Raw RGB");
zengliang.li5f31ef42024-05-16 08:27:38 +000015274 stream->alignment = 32;
15275 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015276 case AML_FOURCC_raw_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015277 {
15278 guint16 bps;
15279
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015280 bps = AML_QT_UINT16 (stsd_entry_data + 82);
zengliang.li5f31ef42024-05-16 08:27:38 +000015281 switch (bps) {
15282 case 15:
15283 format = GST_VIDEO_FORMAT_RGB15;
15284 break;
15285 case 16:
15286 format = GST_VIDEO_FORMAT_RGB16;
15287 break;
15288 case 24:
15289 format = GST_VIDEO_FORMAT_RGB;
15290 break;
15291 case 32:
15292 format = GST_VIDEO_FORMAT_ARGB;
15293 break;
15294 default:
15295 /* unknown */
15296 break;
15297 }
15298 break;
15299 }
15300 case GST_MAKE_FOURCC ('y', 'v', '1', '2'):
15301 format = GST_VIDEO_FORMAT_I420;
15302 break;
15303 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'):
15304 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'):
15305 format = GST_VIDEO_FORMAT_I420;
15306 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015307 case AML_FOURCC_2vuy:
zengliang.li5f31ef42024-05-16 08:27:38 +000015308 case GST_MAKE_FOURCC ('2', 'V', 'u', 'y'):
15309 format = GST_VIDEO_FORMAT_UYVY;
15310 break;
15311 case GST_MAKE_FOURCC ('v', '3', '0', '8'):
15312 format = GST_VIDEO_FORMAT_v308;
15313 break;
15314 case GST_MAKE_FOURCC ('v', '2', '1', '6'):
15315 format = GST_VIDEO_FORMAT_v216;
15316 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015317 case AML_FOURCC_v210:
zengliang.li5f31ef42024-05-16 08:27:38 +000015318 format = GST_VIDEO_FORMAT_v210;
15319 break;
15320 case GST_MAKE_FOURCC ('r', '2', '1', '0'):
15321 format = GST_VIDEO_FORMAT_r210;
15322 break;
15323 /* Packed YUV 4:4:4 10 bit in 32 bits, complex
15324 case GST_MAKE_FOURCC ('v', '4', '1', '0'):
15325 format = GST_VIDEO_FORMAT_v410;
15326 break;
15327 */
15328 /* Packed YUV 4:4:4:4 8 bit in 32 bits
15329 * but different order than AYUV
15330 case GST_MAKE_FOURCC ('v', '4', '0', '8'):
15331 format = GST_VIDEO_FORMAT_v408;
15332 break;
15333 */
15334 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'):
15335 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015336 _aml_codec ("MPEG-1 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015337 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
15338 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15339 break;
15340 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): /* HDV 720p30 */
15341 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): /* HDV 1080i60 */
15342 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): /* HDV 1080i50 */
15343 case GST_MAKE_FOURCC ('h', 'd', 'v', '4'): /* HDV 720p24 */
15344 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): /* HDV 720p25 */
15345 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): /* HDV 1080p24 */
15346 case GST_MAKE_FOURCC ('h', 'd', 'v', '7'): /* HDV 1080p25 */
15347 case GST_MAKE_FOURCC ('h', 'd', 'v', '8'): /* HDV 1080p30 */
15348 case GST_MAKE_FOURCC ('h', 'd', 'v', '9'): /* HDV 720p60 */
15349 case GST_MAKE_FOURCC ('h', 'd', 'v', 'a'): /* HDV 720p50 */
15350 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */
15351 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): /* MPEG2 IMX PAL 625/60 50mb/s produced by FCP */
15352 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */
15353 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): /* MPEG2 IMX PAL 625/60 40mb/s produced by FCP */
15354 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */
15355 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */
15356 case GST_MAKE_FOURCC ('x', 'd', 'v', '1'): /* XDCAM HD 720p30 35Mb/s */
15357 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): /* XDCAM HD 1080i60 35Mb/s */
15358 case GST_MAKE_FOURCC ('x', 'd', 'v', '3'): /* XDCAM HD 1080i50 35Mb/s */
15359 case GST_MAKE_FOURCC ('x', 'd', 'v', '4'): /* XDCAM HD 720p24 35Mb/s */
15360 case GST_MAKE_FOURCC ('x', 'd', 'v', '5'): /* XDCAM HD 720p25 35Mb/s */
15361 case GST_MAKE_FOURCC ('x', 'd', 'v', '6'): /* XDCAM HD 1080p24 35Mb/s */
15362 case GST_MAKE_FOURCC ('x', 'd', 'v', '7'): /* XDCAM HD 1080p25 35Mb/s */
15363 case GST_MAKE_FOURCC ('x', 'd', 'v', '8'): /* XDCAM HD 1080p30 35Mb/s */
15364 case GST_MAKE_FOURCC ('x', 'd', 'v', '9'): /* XDCAM HD 720p60 35Mb/s */
15365 case GST_MAKE_FOURCC ('x', 'd', 'v', 'a'): /* XDCAM HD 720p50 35Mb/s */
15366 case GST_MAKE_FOURCC ('x', 'd', 'v', 'b'): /* XDCAM EX 1080i60 50Mb/s CBR */
15367 case GST_MAKE_FOURCC ('x', 'd', 'v', 'c'): /* XDCAM EX 1080i50 50Mb/s CBR */
15368 case GST_MAKE_FOURCC ('x', 'd', 'v', 'd'): /* XDCAM HD 1080p24 50Mb/s CBR */
15369 case GST_MAKE_FOURCC ('x', 'd', 'v', 'e'): /* XDCAM HD 1080p25 50Mb/s CBR */
15370 case GST_MAKE_FOURCC ('x', 'd', 'v', 'f'): /* XDCAM HD 1080p30 50Mb/s CBR */
15371 case GST_MAKE_FOURCC ('x', 'd', '5', '1'): /* XDCAM HD422 720p30 50Mb/s CBR */
15372 case GST_MAKE_FOURCC ('x', 'd', '5', '4'): /* XDCAM HD422 720p24 50Mb/s CBR */
15373 case GST_MAKE_FOURCC ('x', 'd', '5', '5'): /* XDCAM HD422 720p25 50Mb/s CBR */
15374 case GST_MAKE_FOURCC ('x', 'd', '5', '9'): /* XDCAM HD422 720p60 50Mb/s CBR */
15375 case GST_MAKE_FOURCC ('x', 'd', '5', 'a'): /* XDCAM HD422 720p50 50Mb/s CBR */
15376 case GST_MAKE_FOURCC ('x', 'd', '5', 'b'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15377 case GST_MAKE_FOURCC ('x', 'd', '5', 'c'): /* XDCAM HD422 1080i50 50Mb/s CBR */
15378 case GST_MAKE_FOURCC ('x', 'd', '5', 'd'): /* XDCAM HD422 1080p24 50Mb/s CBR */
15379 case GST_MAKE_FOURCC ('x', 'd', '5', 'e'): /* XDCAM HD422 1080p25 50Mb/s CBR */
15380 case GST_MAKE_FOURCC ('x', 'd', '5', 'f'): /* XDCAM HD422 1080p30 50Mb/s CBR */
15381 case GST_MAKE_FOURCC ('x', 'd', 'h', 'd'): /* XDCAM HD 540p */
15382 case GST_MAKE_FOURCC ('x', 'd', 'h', '2'): /* XDCAM HD422 540p */
15383 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): /* AVID IMX PAL */
15384 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): /* AVID IMX PAL */
15385 case GST_MAKE_FOURCC ('m', 'p', '2', 'v'): /* AVID IMX PAL */
15386 case GST_MAKE_FOURCC ('m', '2', 'v', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015387 _aml_codec ("MPEG-2 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015388 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2,
15389 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15390 break;
15391 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015392 _aml_codec ("GIF still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015393 caps = gst_caps_new_empty_simple ("image/gif");
15394 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015395 case AML_FOURCC_h263:
zengliang.li5f31ef42024-05-16 08:27:38 +000015396 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015397 case AML_FOURCC_s263:
zengliang.li5f31ef42024-05-16 08:27:38 +000015398 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015399 _aml_codec ("H.263");
zengliang.li5f31ef42024-05-16 08:27:38 +000015400 /* ffmpeg uses the height/width props, don't know why */
15401 caps = gst_caps_new_simple ("video/x-h263",
15402 "variant", G_TYPE_STRING, "itu", NULL);
15403 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015404 case AML_FOURCC_mp4v:
15405 case AML_FOURCC_MP4V:
15406 _aml_codec ("MPEG-4 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015407 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15408 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15409 break;
15410 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'):
15411 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015412 _aml_codec ("Microsoft MPEG-4 4.3"); /* FIXME? */
zengliang.li5f31ef42024-05-16 08:27:38 +000015413 caps = gst_caps_new_simple ("video/x-msmpeg",
15414 "msmpegversion", G_TYPE_INT, 43, NULL);
15415 break;
15416 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015417 _aml_codec ("DivX 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015418 caps = gst_caps_new_simple ("video/x-divx",
15419 "divxversion", G_TYPE_INT, 3, NULL);
15420 break;
15421 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
15422 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015423 _aml_codec ("DivX 4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015424 caps = gst_caps_new_simple ("video/x-divx",
15425 "divxversion", G_TYPE_INT, 4, NULL);
15426 break;
15427 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015428 _aml_codec ("DivX 5");
zengliang.li5f31ef42024-05-16 08:27:38 +000015429 caps = gst_caps_new_simple ("video/x-divx",
15430 "divxversion", G_TYPE_INT, 5, NULL);
15431 break;
15432
15433 case GST_MAKE_FOURCC ('F', 'F', 'V', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015434 _aml_codec ("FFV1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015435 caps = gst_caps_new_simple ("video/x-ffv",
15436 "ffvversion", G_TYPE_INT, 1, NULL);
15437 break;
15438
15439 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'):
15440 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015441 case AML_FOURCC_XVID:
15442 case AML_FOURCC_xvid:
15443 case AML_FOURCC_FMP4:
15444 case AML_FOURCC_fmp4:
zengliang.li5f31ef42024-05-16 08:27:38 +000015445 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
15446 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
15447 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015448 _aml_codec ("MPEG-4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015449 break;
15450
15451 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015452 _aml_codec ("Cinepak");
zengliang.li5f31ef42024-05-16 08:27:38 +000015453 caps = gst_caps_new_empty_simple ("video/x-cinepak");
15454 break;
15455 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015456 _aml_codec ("Apple QuickDraw");
zengliang.li5f31ef42024-05-16 08:27:38 +000015457 caps = gst_caps_new_empty_simple ("video/x-qdrw");
15458 break;
15459 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015460 _aml_codec ("Apple video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015461 caps = gst_caps_new_empty_simple ("video/x-apple-video");
15462 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015463 case AML_FOURCC_H264:
15464 case AML_FOURCC_avc1:
15465 case AML_FOURCC_dva1:
15466 _aml_codec ("H.264 / AVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015467 caps = gst_caps_new_simple ("video/x-h264",
15468 "stream-format", G_TYPE_STRING, "avc",
15469 "alignment", G_TYPE_STRING, "au", NULL);
15470 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015471 case AML_FOURCC_avc3:
15472 case AML_FOURCC_dvav:
15473 _aml_codec ("H.264 / AVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015474 caps = gst_caps_new_simple ("video/x-h264",
15475 "stream-format", G_TYPE_STRING, "avc3",
15476 "alignment", G_TYPE_STRING, "au", NULL);
15477 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015478 case AML_FOURCC_H265:
15479 case AML_FOURCC_hvc1:
15480 case AML_FOURCC_dvh1:
15481 _aml_codec ("H.265 / HEVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015482 caps = gst_caps_new_simple ("video/x-h265",
15483 "stream-format", G_TYPE_STRING, "hvc1",
15484 "alignment", G_TYPE_STRING, "au", NULL);
15485 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015486 case AML_FOURCC_hev1:
15487 case AML_FOURCC_dvhe:
15488 _aml_codec ("H.265 / HEVC");
zengliang.li5f31ef42024-05-16 08:27:38 +000015489 caps = gst_caps_new_simple ("video/x-h265",
15490 "stream-format", G_TYPE_STRING, "hev1",
15491 "alignment", G_TYPE_STRING, "au", NULL);
15492 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015493 case AML_FOURCC_rle_:
15494 _aml_codec ("Run-length encoding");
zengliang.li5f31ef42024-05-16 08:27:38 +000015495 caps = gst_caps_new_simple ("video/x-rle",
15496 "layout", G_TYPE_STRING, "quicktime", NULL);
15497 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015498 case AML_FOURCC_WRLE:
15499 _aml_codec ("Run-length encoding");
zengliang.li5f31ef42024-05-16 08:27:38 +000015500 caps = gst_caps_new_simple ("video/x-rle",
15501 "layout", G_TYPE_STRING, "microsoft", NULL);
15502 break;
15503 case GST_MAKE_FOURCC ('I', 'V', '3', '2'):
15504 case GST_MAKE_FOURCC ('i', 'v', '3', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015505 _aml_codec ("Indeo Video 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015506 caps = gst_caps_new_simple ("video/x-indeo",
15507 "indeoversion", G_TYPE_INT, 3, NULL);
15508 break;
15509 case GST_MAKE_FOURCC ('I', 'V', '4', '1'):
15510 case GST_MAKE_FOURCC ('i', 'v', '4', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015511 _aml_codec ("Intel Video 4");
zengliang.li5f31ef42024-05-16 08:27:38 +000015512 caps = gst_caps_new_simple ("video/x-indeo",
15513 "indeoversion", G_TYPE_INT, 4, NULL);
15514 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015515 case AML_FOURCC_dvcp:
15516 case AML_FOURCC_dvc_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015517 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'):
15518 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'):
15519 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'):
15520 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'):
15521 case GST_MAKE_FOURCC ('d', 'v', '2', '5'):
15522 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015523 _aml_codec ("DV Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015524 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25,
15525 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15526 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015527 case AML_FOURCC_dv5n: /* DVCPRO50 NTSC */
15528 case AML_FOURCC_dv5p: /* DVCPRO50 PAL */
15529 _aml_codec ("DVCPro50 Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015530 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50,
15531 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15532 break;
15533 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): /* DVCPRO HD 50i produced by FCP */
15534 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): /* DVCPRO HD 60i produced by FCP */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015535 _aml_codec ("DVCProHD Video");
zengliang.li5f31ef42024-05-16 08:27:38 +000015536 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100,
15537 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
15538 break;
15539 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015540 _aml_codec ("Apple Graphics (SMC)");
zengliang.li5f31ef42024-05-16 08:27:38 +000015541 caps = gst_caps_new_empty_simple ("video/x-smc");
15542 break;
15543 case GST_MAKE_FOURCC ('V', 'P', '3', '1'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015544 _aml_codec ("VP3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015545 caps = gst_caps_new_empty_simple ("video/x-vp3");
15546 break;
15547 case GST_MAKE_FOURCC ('V', 'P', '6', 'F'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015548 _aml_codec ("VP6 Flash");
zengliang.li5f31ef42024-05-16 08:27:38 +000015549 caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
15550 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015551 case AML_FOURCC_XiTh:
15552 _aml_codec ("Theora");
zengliang.li5f31ef42024-05-16 08:27:38 +000015553 caps = gst_caps_new_empty_simple ("video/x-theora");
15554 /* theora uses one byte of padding in the data stream because it does not
15555 * allow 0 sized packets while theora does */
15556 entry->padding = 1;
15557 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015558 case AML_FOURCC_drac:
15559 _aml_codec ("Dirac");
zengliang.li5f31ef42024-05-16 08:27:38 +000015560 caps = gst_caps_new_empty_simple ("video/x-dirac");
15561 break;
15562 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015563 _aml_codec ("TIFF still images");
zengliang.li5f31ef42024-05-16 08:27:38 +000015564 caps = gst_caps_new_empty_simple ("image/tiff");
15565 break;
15566 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015567 _aml_codec ("Apple Intermediate Codec");
zengliang.li5f31ef42024-05-16 08:27:38 +000015568 caps = gst_caps_from_string ("video/x-apple-intermediate-codec");
15569 break;
15570 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015571 _aml_codec ("AVID DNxHD");
zengliang.li5f31ef42024-05-16 08:27:38 +000015572 caps = gst_caps_from_string ("video/x-dnxhd");
15573 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015574 case AML_FOURCC_VP80:
15575 case AML_FOURCC_vp08:
15576 _aml_codec ("On2 VP8");
zengliang.li5f31ef42024-05-16 08:27:38 +000015577 caps = gst_caps_from_string ("video/x-vp8");
15578 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015579 case AML_FOURCC_vp09:
15580 _aml_codec ("Google VP9");
zengliang.li5f31ef42024-05-16 08:27:38 +000015581 caps = gst_caps_from_string ("video/x-vp9");
15582 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015583 case AML_FOURCC_apcs:
15584 _aml_codec ("Apple ProRes LT");
zengliang.li5f31ef42024-05-16 08:27:38 +000015585 caps =
15586 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "lt",
15587 NULL);
15588 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015589 case AML_FOURCC_apch:
15590 _aml_codec ("Apple ProRes HQ");
zengliang.li5f31ef42024-05-16 08:27:38 +000015591 caps =
15592 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING, "hq",
15593 NULL);
15594 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015595 case AML_FOURCC_apcn:
15596 _aml_codec ("Apple ProRes");
zengliang.li5f31ef42024-05-16 08:27:38 +000015597 caps =
15598 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15599 "standard", NULL);
15600 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015601 case AML_FOURCC_apco:
15602 _aml_codec ("Apple ProRes Proxy");
zengliang.li5f31ef42024-05-16 08:27:38 +000015603 caps =
15604 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15605 "proxy", NULL);
15606 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015607 case AML_FOURCC_ap4h:
15608 _aml_codec ("Apple ProRes 4444");
zengliang.li5f31ef42024-05-16 08:27:38 +000015609 caps =
15610 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15611 "4444", NULL);
15612
15613 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15614 if (entry->bits_per_sample > 0) {
15615 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15616 NULL);
15617 }
15618 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015619 case AML_FOURCC_ap4x:
15620 _aml_codec ("Apple ProRes 4444 XQ");
zengliang.li5f31ef42024-05-16 08:27:38 +000015621 caps =
15622 gst_caps_new_simple ("video/x-prores", "variant", G_TYPE_STRING,
15623 "4444xq", NULL);
15624
15625 /* 24 bits per sample = an alpha channel is coded but image is always opaque */
15626 if (entry->bits_per_sample > 0) {
15627 gst_caps_set_simple (caps, "depth", G_TYPE_INT, entry->bits_per_sample,
15628 NULL);
15629 }
15630 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015631 case AML_FOURCC_cfhd:
15632 _aml_codec ("GoPro CineForm");
zengliang.li5f31ef42024-05-16 08:27:38 +000015633 caps = gst_caps_from_string ("video/x-cineform");
15634 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015635 case AML_FOURCC_vc_1:
15636 case AML_FOURCC_ovc1:
15637 _aml_codec ("VC-1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015638 caps = gst_caps_new_simple ("video/x-wmv",
15639 "wmvversion", G_TYPE_INT, 3, "format", G_TYPE_STRING, "WVC1", NULL);
15640 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015641 case AML_FOURCC_av01:
bo.xiao4c245f22024-08-22 11:18:25 +080015642 case AML_FOURCC_dav1:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015643 _aml_codec ("AV1");
zengliang.li5f31ef42024-05-16 08:27:38 +000015644 caps = gst_caps_new_simple ("video/x-av1",
15645 "stream-format", G_TYPE_STRING, "obu-stream",
15646 "alignment", G_TYPE_STRING, "tu", NULL);
15647 break;
15648 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
15649 default:
15650 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015651 caps = _aml_get_unknown_codec_name ("video", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000015652 break;
15653 }
15654 }
15655
15656 if (format != GST_VIDEO_FORMAT_UNKNOWN) {
15657 GstVideoInfo info;
15658
15659 gst_video_info_init (&info);
15660 gst_video_info_set_format (&info, format, entry->width, entry->height);
15661
15662 caps = gst_video_info_to_caps (&info);
15663 *codec_name = gst_pb_utils_get_codec_description (caps);
15664
15665 /* enable clipping for raw video streams */
15666 stream->need_clip = TRUE;
15667 stream->alignment = 32;
15668 }
15669
15670 return caps;
15671}
15672
15673static guint
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015674aml_round_up_pow2 (guint n)
zengliang.li5f31ef42024-05-16 08:27:38 +000015675{
15676 n = n - 1;
15677 n = n | (n >> 1);
15678 n = n | (n >> 2);
15679 n = n | (n >> 4);
15680 n = n | (n >> 8);
15681 n = n | (n >> 16);
15682 return n + 1;
15683}
15684
15685static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015686aml_qtdemux_audio_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
15687 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
zengliang.li5f31ef42024-05-16 08:27:38 +000015688 int len, gchar ** codec_name)
15689{
15690 GstCaps *caps;
15691 const GstStructure *s;
15692 const gchar *name;
15693 gint endian = 0;
15694 GstAudioFormat format = 0;
15695 gint depth;
15696
15697 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
15698
15699 depth = entry->bytes_per_packet * 8;
15700
15701 switch (fourcc) {
15702 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015703 case AML_FOURCC_raw_:
zengliang.li5f31ef42024-05-16 08:27:38 +000015704 /* 8-bit audio is unsigned */
15705 if (depth == 8)
15706 format = GST_AUDIO_FORMAT_U8;
15707 /* otherwise it's signed and big-endian just like 'twos' */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015708 case AML_FOURCC_twos:
zengliang.li5f31ef42024-05-16 08:27:38 +000015709 endian = G_BIG_ENDIAN;
15710 /* fall-through */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015711 case AML_FOURCC_sowt:
zengliang.li5f31ef42024-05-16 08:27:38 +000015712 {
15713 gchar *str;
15714
15715 if (!endian)
15716 endian = G_LITTLE_ENDIAN;
15717
15718 if (!format)
15719 format = gst_audio_format_build_integer (TRUE, endian, depth, depth);
15720
15721 str = g_strdup_printf ("Raw %d-bit PCM audio", depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015722 _aml_codec (str);
zengliang.li5f31ef42024-05-16 08:27:38 +000015723 g_free (str);
15724
15725 caps = gst_caps_new_simple ("audio/x-raw",
15726 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15727 "layout", G_TYPE_STRING, "interleaved", NULL);
15728 stream->alignment = GST_ROUND_UP_8 (depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015729 stream->alignment = aml_round_up_pow2 (stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +000015730 break;
15731 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015732 case AML_FOURCC_fl64:
15733 _aml_codec ("Raw 64-bit floating-point audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015734 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15735 * endian later */
15736 caps = gst_caps_new_simple ("audio/x-raw",
15737 "format", G_TYPE_STRING, "F64BE",
15738 "layout", G_TYPE_STRING, "interleaved", NULL);
15739 stream->alignment = 8;
15740 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015741 case AML_FOURCC_fl32:
15742 _aml_codec ("Raw 32-bit floating-point audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015743 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15744 * endian later */
15745 caps = gst_caps_new_simple ("audio/x-raw",
15746 "format", G_TYPE_STRING, "F32BE",
15747 "layout", G_TYPE_STRING, "interleaved", NULL);
15748 stream->alignment = 4;
15749 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015750 case AML_FOURCC_in24:
15751 _aml_codec ("Raw 24-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015752 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15753 * endian later */
15754 caps = gst_caps_new_simple ("audio/x-raw",
15755 "format", G_TYPE_STRING, "S24BE",
15756 "layout", G_TYPE_STRING, "interleaved", NULL);
15757 stream->alignment = 4;
15758 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015759 case AML_FOURCC_in32:
15760 _aml_codec ("Raw 32-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015761 /* we assume BIG ENDIAN, an enda box will tell us to change this to little
15762 * endian later */
15763 caps = gst_caps_new_simple ("audio/x-raw",
15764 "format", G_TYPE_STRING, "S32BE",
15765 "layout", G_TYPE_STRING, "interleaved", NULL);
15766 stream->alignment = 4;
15767 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015768 case AML_FOURCC_s16l:
15769 _aml_codec ("Raw 16-bit PCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015770 caps = gst_caps_new_simple ("audio/x-raw",
15771 "format", G_TYPE_STRING, "S16LE",
15772 "layout", G_TYPE_STRING, "interleaved", NULL);
15773 stream->alignment = 2;
15774 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015775 case AML_FOURCC_ulaw:
15776 _aml_codec ("Mu-law audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015777 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
15778 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015779 case AML_FOURCC_alaw:
15780 _aml_codec ("A-law audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015781 caps = gst_caps_new_empty_simple ("audio/x-alaw");
15782 break;
15783 case 0x0200736d:
15784 case 0x6d730002:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015785 _aml_codec ("Microsoft ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015786 /* Microsoft ADPCM-ACM code 2 */
15787 caps = gst_caps_new_simple ("audio/x-adpcm",
15788 "layout", G_TYPE_STRING, "microsoft", NULL);
15789 break;
15790 case 0x1100736d:
15791 case 0x6d730011:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015792 _aml_codec ("DVI/IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015793 caps = gst_caps_new_simple ("audio/x-adpcm",
15794 "layout", G_TYPE_STRING, "dvi", NULL);
15795 break;
15796 case 0x1700736d:
15797 case 0x6d730017:
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015798 _aml_codec ("DVI/Intel IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015799 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */
15800 caps = gst_caps_new_simple ("audio/x-adpcm",
15801 "layout", G_TYPE_STRING, "quicktime", NULL);
15802 break;
15803 case 0x5500736d:
15804 case 0x6d730055:
15805 /* MPEG layer 3, CBR only (pre QT4.1) */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015806 case AML_FOURCC__mp3:
15807 case AML_FOURCC_mp3_:
15808 _aml_codec ("MPEG-1 layer 3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015809 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */
15810 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3,
15811 "mpegversion", G_TYPE_INT, 1, NULL);
15812 break;
15813 case GST_MAKE_FOURCC ('.', 'm', 'p', '2'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015814 _aml_codec ("MPEG-1 layer 2");
zengliang.li5f31ef42024-05-16 08:27:38 +000015815 /* MPEG layer 2 */
15816 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 2,
15817 "mpegversion", G_TYPE_INT, 1, NULL);
15818 break;
15819 case 0x20736d:
15820 case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015821 _aml_codec ("EAC-3 audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015822 caps = gst_caps_new_simple ("audio/x-eac3",
15823 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15824 entry->sampled = TRUE;
15825 break;
15826 case GST_MAKE_FOURCC ('s', 'a', 'c', '3'): // Nero Recode
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015827 case AML_FOURCC_ac_3:
15828 _aml_codec ("AC-3 audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015829 caps = gst_caps_new_simple ("audio/x-ac3",
15830 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15831 entry->sampled = TRUE;
15832 break;
15833 case GST_MAKE_FOURCC ('d', 't', 's', 'c'):
15834 case GST_MAKE_FOURCC ('D', 'T', 'S', ' '):
kaiqiang.xianga582a842024-10-30 15:49:46 +080015835 case GST_MAKE_FOURCC ('d', 't', 's', 'x'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015836 _aml_codec ("DTS audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015837 caps = gst_caps_new_simple ("audio/x-dts",
15838 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15839 entry->sampled = TRUE;
15840 break;
15841 case GST_MAKE_FOURCC ('d', 't', 's', 'h'): // DTS-HD
15842 case GST_MAKE_FOURCC ('d', 't', 's', 'l'): // DTS-HD Lossless
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015843 _aml_codec ("DTS-HD audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015844 caps = gst_caps_new_simple ("audio/x-dts",
15845 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15846 entry->sampled = TRUE;
15847 break;
zengliang.li469a5ca2024-05-16 12:03:55 +000015848 case GST_MAKE_FOURCC ('d', 't', 's', 'e'): // DTS Low Bit Rate (LBR)
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015849 _aml_codec ("DTS LBR audio");
zengliang.li469a5ca2024-05-16 12:03:55 +000015850 caps = gst_caps_new_simple ("audio/x-dts",
15851 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15852 entry->sampled = TRUE;
15853 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015854 case AML_FOURCC_MAC3:
15855 _aml_codec ("MACE-3");
zengliang.li5f31ef42024-05-16 08:27:38 +000015856 caps = gst_caps_new_simple ("audio/x-mace",
15857 "maceversion", G_TYPE_INT, 3, NULL);
15858 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015859 case AML_FOURCC_MAC6:
15860 _aml_codec ("MACE-6");
zengliang.li5f31ef42024-05-16 08:27:38 +000015861 caps = gst_caps_new_simple ("audio/x-mace",
15862 "maceversion", G_TYPE_INT, 6, NULL);
15863 break;
15864 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'):
15865 /* ogg/vorbis */
15866 caps = gst_caps_new_empty_simple ("application/ogg");
15867 break;
15868 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015869 _aml_codec ("DV audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015870 caps = gst_caps_new_empty_simple ("audio/x-dv");
15871 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015872 case AML_FOURCC_mp4a:
15873 _aml_codec ("MPEG-4 AAC audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015874 caps = gst_caps_new_simple ("audio/mpeg",
15875 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
15876 "stream-format", G_TYPE_STRING, "raw", NULL);
15877 break;
15878 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015879 _aml_codec ("QDesign Music");
zengliang.li5f31ef42024-05-16 08:27:38 +000015880 caps = gst_caps_new_empty_simple ("audio/x-qdm");
15881 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015882 case AML_FOURCC_QDM2:
15883 _aml_codec ("QDesign Music v.2");
zengliang.li5f31ef42024-05-16 08:27:38 +000015884 /* FIXME: QDesign music version 2 (no constant) */
15885 if (FALSE && data) {
15886 caps = gst_caps_new_simple ("audio/x-qdm2",
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015887 "framesize", G_TYPE_INT, AML_QT_UINT32 (data + 52),
15888 "bitrate", G_TYPE_INT, AML_QT_UINT32 (data + 40),
15889 "blocksize", G_TYPE_INT, AML_QT_UINT32 (data + 44), NULL);
zengliang.li5f31ef42024-05-16 08:27:38 +000015890 } else {
15891 caps = gst_caps_new_empty_simple ("audio/x-qdm2");
15892 }
15893 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015894 case AML_FOURCC_agsm:
15895 _aml_codec ("GSM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015896 caps = gst_caps_new_empty_simple ("audio/x-gsm");
15897 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015898 case AML_FOURCC_samr:
15899 _aml_codec ("AMR audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015900 caps = gst_caps_new_empty_simple ("audio/AMR");
15901 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015902 case AML_FOURCC_sawb:
15903 _aml_codec ("AMR-WB audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015904 caps = gst_caps_new_empty_simple ("audio/AMR-WB");
15905 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015906 case AML_FOURCC_ima4:
15907 _aml_codec ("Quicktime IMA ADPCM");
zengliang.li5f31ef42024-05-16 08:27:38 +000015908 caps = gst_caps_new_simple ("audio/x-adpcm",
15909 "layout", G_TYPE_STRING, "quicktime", NULL);
15910 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015911 case AML_FOURCC_alac:
15912 _aml_codec ("Apple lossless audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015913 caps = gst_caps_new_empty_simple ("audio/x-alac");
15914 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015915 case AML_FOURCC_fLaC:
15916 _aml_codec ("Free Lossless Audio Codec");
zengliang.li5f31ef42024-05-16 08:27:38 +000015917 caps = gst_caps_new_simple ("audio/x-flac",
15918 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
15919 break;
15920 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015921 _aml_codec ("QualComm PureVoice");
zengliang.li5f31ef42024-05-16 08:27:38 +000015922 caps = gst_caps_from_string ("audio/qcelp");
15923 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015924 case AML_FOURCC_wma_:
15925 case AML_FOURCC_owma:
15926 _aml_codec ("WMA");
zengliang.li5f31ef42024-05-16 08:27:38 +000015927 caps = gst_caps_new_empty_simple ("audio/x-wma");
15928 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015929 case AML_FOURCC_opus:
15930 _aml_codec ("Opus");
zengliang.li5f31ef42024-05-16 08:27:38 +000015931 caps = gst_caps_new_empty_simple ("audio/x-opus");
15932 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015933 case AML_FOURCC_lpcm:
zengliang.li5f31ef42024-05-16 08:27:38 +000015934 {
15935 guint32 flags = 0;
15936 guint32 depth = 0;
15937 guint32 width = 0;
15938 GstAudioFormat format;
15939 enum
15940 {
15941 FLAG_IS_FLOAT = 0x1,
15942 FLAG_IS_BIG_ENDIAN = 0x2,
15943 FLAG_IS_SIGNED = 0x4,
15944 FLAG_IS_PACKED = 0x8,
15945 FLAG_IS_ALIGNED_HIGH = 0x10,
15946 FLAG_IS_NON_INTERLEAVED = 0x20
15947 };
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015948 _aml_codec ("Raw LPCM audio");
zengliang.li5f31ef42024-05-16 08:27:38 +000015949
15950 if (data && len >= 36) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015951 depth = AML_QT_UINT32 (data + 24);
15952 flags = AML_QT_UINT32 (data + 28);
15953 width = AML_QT_UINT32 (data + 32) * 8 / entry->n_channels;
zengliang.li5f31ef42024-05-16 08:27:38 +000015954 }
15955 if ((flags & FLAG_IS_FLOAT) == 0) {
15956 if (depth == 0)
15957 depth = 16;
15958 if (width == 0)
15959 width = 16;
15960 if ((flags & FLAG_IS_ALIGNED_HIGH))
15961 depth = width;
15962
15963 format = gst_audio_format_build_integer ((flags & FLAG_IS_SIGNED) ?
15964 TRUE : FALSE, (flags & FLAG_IS_BIG_ENDIAN) ?
15965 G_BIG_ENDIAN : G_LITTLE_ENDIAN, width, depth);
15966 caps = gst_caps_new_simple ("audio/x-raw",
15967 "format", G_TYPE_STRING,
15968 format !=
15969 GST_AUDIO_FORMAT_UNKNOWN ? gst_audio_format_to_string (format) :
15970 "UNKNOWN", "layout", G_TYPE_STRING,
15971 (flags & FLAG_IS_NON_INTERLEAVED) ? "non-interleaved" :
15972 "interleaved", NULL);
15973 stream->alignment = GST_ROUND_UP_8 (depth);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015974 stream->alignment = aml_round_up_pow2 (stream->alignment);
zengliang.li5f31ef42024-05-16 08:27:38 +000015975 } else {
15976 if (width == 0)
15977 width = 32;
15978 if (width == 64) {
15979 if (flags & FLAG_IS_BIG_ENDIAN)
15980 format = GST_AUDIO_FORMAT_F64BE;
15981 else
15982 format = GST_AUDIO_FORMAT_F64LE;
15983 } else {
15984 if (flags & FLAG_IS_BIG_ENDIAN)
15985 format = GST_AUDIO_FORMAT_F32BE;
15986 else
15987 format = GST_AUDIO_FORMAT_F32LE;
15988 }
15989 caps = gst_caps_new_simple ("audio/x-raw",
15990 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
15991 "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
15992 "non-interleaved" : "interleaved", NULL);
15993 stream->alignment = width / 8;
15994 }
15995 break;
15996 }
15997 case GST_MAKE_FOURCC ('a', 'c', '-', '4'):
15998 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000015999 _aml_codec ("AC4");
zengliang.li5f31ef42024-05-16 08:27:38 +000016000 caps = gst_caps_new_empty_simple ("audio/x-ac4");
bo.xiaoa6d06072024-05-30 11:04:57 +080016001 entry->sampled = TRUE;
zengliang.li5f31ef42024-05-16 08:27:38 +000016002 break;
16003 }
16004 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
16005 /* ? */
16006 default:
16007 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016008 caps = _aml_get_unknown_codec_name ("audio", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000016009 break;
16010 }
16011 }
16012
16013 if (caps) {
16014 GstCaps *templ_caps =
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016015 gst_static_pad_template_get_caps (&gst_aml_qtdemux_audiosrc_template);
zengliang.li5f31ef42024-05-16 08:27:38 +000016016 GstCaps *intersection = gst_caps_intersect (caps, templ_caps);
16017 gst_caps_unref (caps);
16018 gst_caps_unref (templ_caps);
16019 caps = intersection;
16020 }
16021
16022 /* enable clipping for raw audio streams */
16023 s = gst_caps_get_structure (caps, 0);
16024 name = gst_structure_get_name (s);
16025 if (g_str_has_prefix (name, "audio/x-raw")) {
16026 stream->need_clip = TRUE;
16027 stream->min_buffer_size = 1024 * entry->bytes_per_frame;
16028 stream->max_buffer_size = 4096 * entry->bytes_per_frame;
16029 GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
16030 stream->max_buffer_size);
16031 }
16032 return caps;
16033}
16034
16035static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016036aml_qtdemux_sub_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
16037 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000016038 const guint8 * stsd_entry_data, gchar ** codec_name)
16039{
16040 GstCaps *caps;
16041
16042 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc 0x%08x", GUINT32_TO_BE (fourcc));
16043
16044 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016045 case AML_FOURCC_mp4s:
16046 _aml_codec ("DVD subtitle");
zengliang.li5f31ef42024-05-16 08:27:38 +000016047 caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016048 stream->process_func = gst_aml_qtdemux_process_buffer_dvd;
zengliang.li5f31ef42024-05-16 08:27:38 +000016049 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016050 case AML_FOURCC_text:
16051 _aml_codec ("Quicktime timed text");
zengliang.li5f31ef42024-05-16 08:27:38 +000016052 goto text;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016053 case AML_FOURCC_tx3g:
16054 _aml_codec ("3GPP timed text");
zengliang.li5f31ef42024-05-16 08:27:38 +000016055 text:
16056 caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
16057 "utf8", NULL);
16058 /* actual text piece needs to be extracted */
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016059 stream->process_func = gst_aml_qtdemux_process_buffer_text;
zengliang.li5f31ef42024-05-16 08:27:38 +000016060 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016061 case AML_FOURCC_stpp:
16062 _aml_codec ("XML subtitles");
zengliang.li5f31ef42024-05-16 08:27:38 +000016063 caps = gst_caps_new_empty_simple ("application/ttml+xml");
16064 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016065 case AML_FOURCC_wvtt:
zengliang.li5f31ef42024-05-16 08:27:38 +000016066 {
16067 GstBuffer *buffer;
16068 const gchar *buf = "WEBVTT\n\n";
16069
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016070 _aml_codec ("WebVTT subtitles");
zengliang.li5f31ef42024-05-16 08:27:38 +000016071 caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016072 stream->process_func = gst_aml_qtdemux_process_buffer_wvtt;
zengliang.li5f31ef42024-05-16 08:27:38 +000016073
16074 /* FIXME: Parse the vttC atom and get the entire WEBVTT header */
16075 buffer = gst_buffer_new_and_alloc (8);
16076 gst_buffer_fill (buffer, 0, buf, 8);
16077 stream->buffers = g_slist_append (stream->buffers, buffer);
16078
16079 break;
16080 }
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016081 case AML_FOURCC_c608:
16082 _aml_codec ("CEA 608 Closed Caption");
zengliang.li5f31ef42024-05-16 08:27:38 +000016083 caps =
16084 gst_caps_new_simple ("closedcaption/x-cea-608", "format",
16085 G_TYPE_STRING, "s334-1a", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016086 stream->process_func = gst_aml_qtdemux_process_buffer_clcp;
zengliang.li5f31ef42024-05-16 08:27:38 +000016087 stream->need_split = TRUE;
16088 break;
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016089 case AML_FOURCC_c708:
16090 _aml_codec ("CEA 708 Closed Caption");
zengliang.li5f31ef42024-05-16 08:27:38 +000016091 caps =
16092 gst_caps_new_simple ("closedcaption/x-cea-708", "format",
16093 G_TYPE_STRING, "cdp", NULL);
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016094 stream->process_func = gst_aml_qtdemux_process_buffer_clcp;
zengliang.li5f31ef42024-05-16 08:27:38 +000016095 break;
16096
16097 default:
16098 {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016099 caps = _aml_get_unknown_codec_name ("text", fourcc);
zengliang.li5f31ef42024-05-16 08:27:38 +000016100 break;
16101 }
16102 }
16103 return caps;
16104}
16105
16106static GstCaps *
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016107aml_qtdemux_generic_caps (GstAmlQTDemux * qtdemux, AmlQtDemuxStream * stream,
16108 AmlQtDemuxStreamStsdEntry * entry, guint32 fourcc,
zengliang.li5f31ef42024-05-16 08:27:38 +000016109 const guint8 * stsd_entry_data, gchar ** codec_name)
16110{
16111 GstCaps *caps;
16112
16113 switch (fourcc) {
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016114 case AML_FOURCC_m1v:
16115 _aml_codec ("MPEG 1 video");
zengliang.li5f31ef42024-05-16 08:27:38 +000016116 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1,
16117 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
16118 break;
16119 default:
16120 caps = NULL;
16121 break;
16122 }
16123 return caps;
16124}
16125
16126static void
zengliang.lia6c0cdd2024-05-18 07:15:51 +000016127gst_aml_qtdemux_append_protection_system_id (GstAmlQTDemux * qtdemux,
zengliang.li5f31ef42024-05-16 08:27:38 +000016128 const gchar * system_id)
16129{
16130 gint i;
16131
16132 if (!qtdemux->protection_system_ids)
16133 qtdemux->protection_system_ids =
16134 g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
16135 /* Check whether we already have an entry for this system ID. */
16136 for (i = 0; i < qtdemux->protection_system_ids->len; ++i) {
16137 const gchar *id = g_ptr_array_index (qtdemux->protection_system_ids, i);
16138 if (g_ascii_strcasecmp (system_id, id) == 0) {
16139 return;
16140 }
16141 }
16142 GST_DEBUG_OBJECT (qtdemux, "Adding cenc protection system ID %s", system_id);
16143 g_ptr_array_add (qtdemux->protection_system_ids, g_ascii_strdown (system_id,
16144 -1));
16145}