/src/ffmpeg/libavutil/iamf.c
Line | Count | Source |
1 | | /* |
2 | | * Immersive Audio Model and Formats helper functions and defines |
3 | | * |
4 | | * This file is part of FFmpeg. |
5 | | * |
6 | | * FFmpeg is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * FFmpeg is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with FFmpeg; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include <limits.h> |
22 | | #include <stddef.h> |
23 | | #include <stdint.h> |
24 | | |
25 | | #include "avassert.h" |
26 | | #include "error.h" |
27 | | #include "iamf.h" |
28 | | #include "log.h" |
29 | | #include "mem.h" |
30 | | #include "opt.h" |
31 | | |
32 | | #define IAMF_ADD_FUNC_TEMPLATE(parent_type, parent_name, child_type, child_name, suffix) \ |
33 | 8.03k | child_type *av_iamf_ ## parent_name ## _add_ ## child_name(parent_type *parent_name) \ |
34 | 8.03k | { \ |
35 | 8.03k | child_type **child_name ## suffix, *child_name; \ |
36 | 8.03k | \ |
37 | 8.03k | if (parent_name->nb_## child_name ## suffix == UINT_MAX) \ |
38 | 8.03k | return NULL; \ |
39 | 8.03k | \ |
40 | 8.03k | child_name ## suffix = av_realloc_array(parent_name->child_name ## suffix, \ |
41 | 8.03k | parent_name->nb_## child_name ## suffix + 1, \ |
42 | 8.03k | sizeof(*parent_name->child_name ## suffix)); \ |
43 | 8.03k | if (!child_name ## suffix) \ |
44 | 8.03k | return NULL; \ |
45 | 8.03k | \ |
46 | 8.03k | parent_name->child_name ## suffix = child_name ## suffix; \ |
47 | 8.03k | \ |
48 | 8.03k | child_name = parent_name->child_name ## suffix[parent_name->nb_## child_name ## suffix] \ |
49 | 8.03k | = av_mallocz(sizeof(*child_name)); \ |
50 | 8.03k | if (!child_name) \ |
51 | 8.03k | return NULL; \ |
52 | 8.03k | \ |
53 | 8.03k | child_name->av_class = &child_name ## _class; \ |
54 | 8.03k | av_opt_set_defaults(child_name); \ |
55 | 8.03k | parent_name->nb_## child_name ## suffix++; \ |
56 | 8.03k | \ |
57 | 8.03k | return child_name; \ |
58 | 8.03k | } |
59 | | |
60 | | #define FLAGS AV_OPT_FLAG_ENCODING_PARAM |
61 | | |
62 | | // |
63 | | // Param Definition |
64 | | // |
65 | | #define OFFSET(x) offsetof(AVIAMFMixGain, x) |
66 | | static const AVOption mix_gain_options[] = { |
67 | | { "subblock_duration", "set subblock_duration", OFFSET(subblock_duration), AV_OPT_TYPE_UINT, {.i64 = 1 }, 1, UINT_MAX, FLAGS }, |
68 | | { "animation_type", "set animation_type", OFFSET(animation_type), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, 2, FLAGS }, |
69 | | { "start_point_value", "set start_point_value", OFFSET(start_point_value), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, -128.0, 128.0, FLAGS }, |
70 | | { "end_point_value", "set end_point_value", OFFSET(end_point_value), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, -128.0, 128.0, FLAGS }, |
71 | | { "control_point_value", "set control_point_value", OFFSET(control_point_value), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, -128.0, 128.0, FLAGS }, |
72 | | { "control_point_relative_time", "set control_point_relative_time", OFFSET(control_point_relative_time), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, 0.0, 1.0, FLAGS }, |
73 | | { NULL }, |
74 | | }; |
75 | | |
76 | | static const AVClass mix_gain_class = { |
77 | | .class_name = "AVIAMFMixGain", |
78 | | .item_name = av_default_item_name, |
79 | | .version = LIBAVUTIL_VERSION_INT, |
80 | | .option = mix_gain_options, |
81 | | }; |
82 | | |
83 | | #undef OFFSET |
84 | | #define OFFSET(x) offsetof(AVIAMFDemixingInfo, x) |
85 | | static const AVOption demixing_info_options[] = { |
86 | | { "subblock_duration", "set subblock_duration", OFFSET(subblock_duration), AV_OPT_TYPE_UINT, {.i64 = 1 }, 1, UINT_MAX, FLAGS }, |
87 | | { "dmixp_mode", "set dmixp_mode", OFFSET(dmixp_mode), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, 6, FLAGS }, |
88 | | { NULL }, |
89 | | }; |
90 | | |
91 | | static const AVClass demixing_info_class = { |
92 | | .class_name = "AVIAMFDemixingInfo", |
93 | | .item_name = av_default_item_name, |
94 | | .version = LIBAVUTIL_VERSION_INT, |
95 | | .option = demixing_info_options, |
96 | | }; |
97 | | |
98 | | #undef OFFSET |
99 | | #define OFFSET(x) offsetof(AVIAMFReconGain, x) |
100 | | static const AVOption recon_gain_options[] = { |
101 | | { "subblock_duration", "set subblock_duration", OFFSET(subblock_duration), AV_OPT_TYPE_UINT, {.i64 = 1 }, 1, UINT_MAX, FLAGS }, |
102 | | { NULL }, |
103 | | }; |
104 | | |
105 | | static const AVClass recon_gain_class = { |
106 | | .class_name = "AVIAMFReconGain", |
107 | | .item_name = av_default_item_name, |
108 | | .version = LIBAVUTIL_VERSION_INT, |
109 | | .option = recon_gain_options, |
110 | | }; |
111 | | |
112 | | #undef OFFSET |
113 | | #define OFFSET(x) offsetof(AVIAMFParamDefinition, x) |
114 | | static const AVOption param_definition_options[] = { |
115 | | { "parameter_id", "set parameter_id", OFFSET(parameter_id), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, UINT_MAX, FLAGS }, |
116 | | { "parameter_rate", "set parameter_rate", OFFSET(parameter_rate), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, UINT_MAX, FLAGS }, |
117 | | { "duration", "set duration", OFFSET(duration), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, UINT_MAX, FLAGS }, |
118 | | { "constant_subblock_duration", "set constant_subblock_duration", OFFSET(constant_subblock_duration), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, UINT_MAX, FLAGS }, |
119 | | { NULL }, |
120 | | }; |
121 | | |
122 | | static const AVClass *param_definition_child_iterate(void **opaque) |
123 | 0 | { |
124 | 0 | uintptr_t i = (uintptr_t)*opaque; |
125 | 0 | const AVClass *ret = NULL; |
126 | |
|
127 | 0 | switch(i) { |
128 | 0 | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: |
129 | 0 | ret = &mix_gain_class; |
130 | 0 | break; |
131 | 0 | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: |
132 | 0 | ret = &demixing_info_class; |
133 | 0 | break; |
134 | 0 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: |
135 | 0 | ret = &recon_gain_class; |
136 | 0 | break; |
137 | 0 | default: |
138 | 0 | break; |
139 | 0 | } |
140 | | |
141 | 0 | if (ret) |
142 | 0 | *opaque = (void*)(i + 1); |
143 | 0 | return ret; |
144 | 0 | } |
145 | | |
146 | | static const AVClass param_definition_class = { |
147 | | .class_name = "AVIAMFParamDefinition", |
148 | | .item_name = av_default_item_name, |
149 | | .version = LIBAVUTIL_VERSION_INT, |
150 | | .option = param_definition_options, |
151 | | .child_class_iterate = param_definition_child_iterate, |
152 | | }; |
153 | | |
154 | | const AVClass *av_iamf_param_definition_get_class(void) |
155 | 0 | { |
156 | 0 | return ¶m_definition_class; |
157 | 0 | } |
158 | | |
159 | | AVIAMFParamDefinition *av_iamf_param_definition_alloc(enum AVIAMFParamDefinitionType type, |
160 | | unsigned int nb_subblocks, size_t *out_size) |
161 | 16.6k | { |
162 | | |
163 | 16.6k | struct MixGainStruct { |
164 | 16.6k | AVIAMFParamDefinition p; |
165 | 16.6k | AVIAMFMixGain m; |
166 | 16.6k | }; |
167 | 16.6k | struct DemixStruct { |
168 | 16.6k | AVIAMFParamDefinition p; |
169 | 16.6k | AVIAMFDemixingInfo d; |
170 | 16.6k | }; |
171 | 16.6k | struct ReconGainStruct { |
172 | 16.6k | AVIAMFParamDefinition p; |
173 | 16.6k | AVIAMFReconGain r; |
174 | 16.6k | }; |
175 | 16.6k | size_t subblocks_offset, subblock_size; |
176 | 16.6k | size_t size; |
177 | 16.6k | AVIAMFParamDefinition *par; |
178 | | |
179 | 16.6k | switch (type) { |
180 | 15.1k | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: |
181 | 15.1k | subblocks_offset = offsetof(struct MixGainStruct, m); |
182 | 15.1k | subblock_size = sizeof(AVIAMFMixGain); |
183 | 15.1k | break; |
184 | 937 | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: |
185 | 937 | subblocks_offset = offsetof(struct DemixStruct, d); |
186 | 937 | subblock_size = sizeof(AVIAMFDemixingInfo); |
187 | 937 | break; |
188 | 629 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: |
189 | 629 | subblocks_offset = offsetof(struct ReconGainStruct, r); |
190 | 629 | subblock_size = sizeof(AVIAMFReconGain); |
191 | 629 | break; |
192 | 0 | default: |
193 | 0 | return NULL; |
194 | 16.6k | } |
195 | | |
196 | 16.6k | size = subblocks_offset; |
197 | 16.6k | if (nb_subblocks > (SIZE_MAX - size) / subblock_size) |
198 | 0 | return NULL; |
199 | 16.6k | size += subblock_size * nb_subblocks; |
200 | | |
201 | 16.6k | par = av_mallocz(size); |
202 | 16.6k | if (!par) |
203 | 20 | return NULL; |
204 | | |
205 | 16.6k | par->av_class = ¶m_definition_class; |
206 | 16.6k | av_opt_set_defaults(par); |
207 | | |
208 | 16.6k | par->type = type; |
209 | 16.6k | par->nb_subblocks = nb_subblocks; |
210 | 16.6k | par->subblock_size = subblock_size; |
211 | 16.6k | par->subblocks_offset = subblocks_offset; |
212 | | |
213 | 160M | for (int i = 0; i < nb_subblocks; i++) { |
214 | 160M | void *subblock = av_iamf_param_definition_get_subblock(par, i); |
215 | | |
216 | 160M | switch (type) { |
217 | 38.8M | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: |
218 | 38.8M | ((AVIAMFMixGain *)subblock)->av_class = &mix_gain_class; |
219 | 38.8M | break; |
220 | 37.1M | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: |
221 | 37.1M | ((AVIAMFDemixingInfo *)subblock)->av_class = &demixing_info_class; |
222 | 37.1M | break; |
223 | 84.0M | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: |
224 | 84.0M | ((AVIAMFReconGain *)subblock)->av_class = &recon_gain_class; |
225 | 84.0M | break; |
226 | 0 | default: |
227 | 0 | av_assert0(0); |
228 | 160M | } |
229 | | |
230 | 160M | av_opt_set_defaults(subblock); |
231 | 160M | } |
232 | | |
233 | 16.6k | if (out_size) |
234 | 16.6k | *out_size = size; |
235 | | |
236 | 16.6k | return par; |
237 | 16.6k | } |
238 | | |
239 | | // |
240 | | // Audio Element |
241 | | // |
242 | | #undef OFFSET |
243 | | #define OFFSET(x) offsetof(AVIAMFLayer, x) |
244 | | static const AVOption layer_options[] = { |
245 | | { "ch_layout", "set ch_layout", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL }, 0, 0, FLAGS }, |
246 | | { "flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, |
247 | | {.i64 = 0 }, 0, AV_IAMF_LAYER_FLAG_RECON_GAIN, FLAGS, .unit = "flags" }, |
248 | | {"recon_gain", "Recon gain is present", 0, AV_OPT_TYPE_CONST, |
249 | | {.i64 = AV_IAMF_LAYER_FLAG_RECON_GAIN }, INT_MIN, INT_MAX, FLAGS, .unit = "flags"}, |
250 | | { "output_gain_flags", "set output_gain_flags", OFFSET(output_gain_flags), AV_OPT_TYPE_FLAGS, |
251 | | {.i64 = 0 }, 0, (1 << 6) - 1, FLAGS, .unit = "output_gain_flags" }, |
252 | | {"FL", "Left channel", 0, AV_OPT_TYPE_CONST, |
253 | | {.i64 = 1 << 5 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
254 | | {"FR", "Right channel", 0, AV_OPT_TYPE_CONST, |
255 | | {.i64 = 1 << 4 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
256 | | {"BL", "Left surround channel", 0, AV_OPT_TYPE_CONST, |
257 | | {.i64 = 1 << 3 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
258 | | {"BR", "Right surround channel", 0, AV_OPT_TYPE_CONST, |
259 | | {.i64 = 1 << 2 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
260 | | {"TFL", "Left top front channel", 0, AV_OPT_TYPE_CONST, |
261 | | {.i64 = 1 << 1 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
262 | | {"TFR", "Right top front channel", 0, AV_OPT_TYPE_CONST, |
263 | | {.i64 = 1 << 0 }, INT_MIN, INT_MAX, FLAGS, .unit = "output_gain_flags"}, |
264 | | { "output_gain", "set output_gain", OFFSET(output_gain), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
265 | | { "ambisonics_mode", "set ambisonics_mode", OFFSET(ambisonics_mode), AV_OPT_TYPE_INT, |
266 | | { .i64 = AV_IAMF_AMBISONICS_MODE_MONO }, |
267 | | AV_IAMF_AMBISONICS_MODE_MONO, AV_IAMF_AMBISONICS_MODE_PROJECTION, FLAGS, .unit = "ambisonics_mode" }, |
268 | | { "mono", NULL, 0, AV_OPT_TYPE_CONST, |
269 | | { .i64 = AV_IAMF_AMBISONICS_MODE_MONO }, .unit = "ambisonics_mode" }, |
270 | | { "projection", NULL, 0, AV_OPT_TYPE_CONST, |
271 | | { .i64 = AV_IAMF_AMBISONICS_MODE_PROJECTION }, .unit = "ambisonics_mode" }, |
272 | | { NULL }, |
273 | | }; |
274 | | |
275 | | static const AVClass layer_class = { |
276 | | .class_name = "AVIAMFLayer", |
277 | | .item_name = av_default_item_name, |
278 | | .version = LIBAVUTIL_VERSION_INT, |
279 | | .option = layer_options, |
280 | | }; |
281 | | |
282 | | #undef OFFSET |
283 | | #define OFFSET(x) offsetof(AVIAMFAudioElement, x) |
284 | | static const AVOption audio_element_options[] = { |
285 | | { "audio_element_type", "set audio_element_type", OFFSET(audio_element_type), AV_OPT_TYPE_INT, |
286 | | {.i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL }, |
287 | | AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL, AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE, FLAGS, .unit = "audio_element_type" }, |
288 | | { "channel", NULL, 0, AV_OPT_TYPE_CONST, |
289 | | { .i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL }, .unit = "audio_element_type" }, |
290 | | { "scene", NULL, 0, AV_OPT_TYPE_CONST, |
291 | | { .i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE }, .unit = "audio_element_type" }, |
292 | | { "default_w", "set default_w", OFFSET(default_w), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, 10, FLAGS }, |
293 | | { NULL }, |
294 | | }; |
295 | | |
296 | | static const AVClass *audio_element_child_iterate(void **opaque) |
297 | 0 | { |
298 | 0 | uintptr_t i = (uintptr_t)*opaque; |
299 | 0 | const AVClass *ret = NULL; |
300 | |
|
301 | 0 | if (i) |
302 | 0 | ret = &layer_class; |
303 | |
|
304 | 0 | if (ret) |
305 | 0 | *opaque = (void*)(i + 1); |
306 | 0 | return ret; |
307 | 0 | } |
308 | | |
309 | | static const AVClass audio_element_class = { |
310 | | .class_name = "AVIAMFAudioElement", |
311 | | .item_name = av_default_item_name, |
312 | | .version = LIBAVUTIL_VERSION_INT, |
313 | | .option = audio_element_options, |
314 | | .child_class_iterate = audio_element_child_iterate, |
315 | | }; |
316 | | |
317 | | const AVClass *av_iamf_audio_element_get_class(void) |
318 | 0 | { |
319 | 0 | return &audio_element_class; |
320 | 0 | } |
321 | | |
322 | | AVIAMFAudioElement *av_iamf_audio_element_alloc(void) |
323 | 3.61k | { |
324 | 3.61k | AVIAMFAudioElement *audio_element = av_mallocz(sizeof(*audio_element)); |
325 | | |
326 | 3.61k | if (audio_element) { |
327 | 3.61k | audio_element->av_class = &audio_element_class; |
328 | 3.61k | av_opt_set_defaults(audio_element); |
329 | 3.61k | } |
330 | | |
331 | 3.61k | return audio_element; |
332 | 3.61k | } |
333 | | |
334 | 2.88k | IAMF_ADD_FUNC_TEMPLATE(AVIAMFAudioElement, audio_element, AVIAMFLayer, layer, s) |
335 | | |
336 | | void av_iamf_audio_element_free(AVIAMFAudioElement **paudio_element) |
337 | 5.24k | { |
338 | 5.24k | AVIAMFAudioElement *audio_element = *paudio_element; |
339 | | |
340 | 5.24k | if (!audio_element) |
341 | 1.63k | return; |
342 | | |
343 | 6.49k | for (int i = 0; i < audio_element->nb_layers; i++) { |
344 | 2.88k | AVIAMFLayer *layer = audio_element->layers[i]; |
345 | 2.88k | av_opt_free(layer); |
346 | 2.88k | av_free(layer->demixing_matrix); |
347 | 2.88k | av_free(layer); |
348 | 2.88k | } |
349 | 3.61k | av_free(audio_element->layers); |
350 | | |
351 | 3.61k | av_free(audio_element->demixing_info); |
352 | 3.61k | av_free(audio_element->recon_gain_info); |
353 | 3.61k | av_freep(paudio_element); |
354 | 3.61k | } |
355 | | |
356 | | // |
357 | | // Mix Presentation |
358 | | // |
359 | | #undef OFFSET |
360 | | #define OFFSET(x) offsetof(AVIAMFSubmixElement, x) |
361 | | static const AVOption submix_element_options[] = { |
362 | | { "headphones_rendering_mode", "Headphones rendering mode", OFFSET(headphones_rendering_mode), AV_OPT_TYPE_INT, |
363 | | { .i64 = AV_IAMF_HEADPHONES_MODE_STEREO }, |
364 | | AV_IAMF_HEADPHONES_MODE_STEREO, AV_IAMF_HEADPHONES_MODE_BINAURAL, FLAGS, .unit = "headphones_rendering_mode" }, |
365 | | { "stereo", NULL, 0, AV_OPT_TYPE_CONST, |
366 | | { .i64 = AV_IAMF_HEADPHONES_MODE_STEREO }, .unit = "headphones_rendering_mode" }, |
367 | | { "binaural", NULL, 0, AV_OPT_TYPE_CONST, |
368 | | { .i64 = AV_IAMF_HEADPHONES_MODE_BINAURAL }, .unit = "headphones_rendering_mode" }, |
369 | | { "default_mix_gain", "Default mix gain", OFFSET(default_mix_gain), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
370 | | { "annotations", "Annotations", OFFSET(annotations), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS }, |
371 | | { NULL }, |
372 | | }; |
373 | | |
374 | | static void *submix_element_child_next(void *obj, void *prev) |
375 | 0 | { |
376 | 0 | AVIAMFSubmixElement *submix_element = obj; |
377 | 0 | if (!prev) |
378 | 0 | return submix_element->element_mix_config; |
379 | | |
380 | 0 | return NULL; |
381 | 0 | } |
382 | | |
383 | | static const AVClass *submix_element_child_iterate(void **opaque) |
384 | 0 | { |
385 | 0 | uintptr_t i = (uintptr_t)*opaque; |
386 | 0 | const AVClass *ret = NULL; |
387 | |
|
388 | 0 | if (i) |
389 | 0 | ret = ¶m_definition_class; |
390 | |
|
391 | 0 | if (ret) |
392 | 0 | *opaque = (void*)(i + 1); |
393 | 0 | return ret; |
394 | 0 | } |
395 | | |
396 | | static const AVClass element_class = { |
397 | | .class_name = "AVIAMFSubmixElement", |
398 | | .item_name = av_default_item_name, |
399 | | .version = LIBAVUTIL_VERSION_INT, |
400 | | .option = submix_element_options, |
401 | | .child_next = submix_element_child_next, |
402 | | .child_class_iterate = submix_element_child_iterate, |
403 | | }; |
404 | | |
405 | 247 | IAMF_ADD_FUNC_TEMPLATE(AVIAMFSubmix, submix, AVIAMFSubmixElement, element, s) |
406 | | |
407 | | #undef OFFSET |
408 | | #define OFFSET(x) offsetof(AVIAMFSubmixLayout, x) |
409 | | static const AVOption submix_layout_options[] = { |
410 | | { "layout_type", "Layout type", OFFSET(layout_type), AV_OPT_TYPE_INT, |
411 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS }, |
412 | | AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS, AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL, FLAGS, .unit = "layout_type" }, |
413 | | { "loudspeakers", NULL, 0, AV_OPT_TYPE_CONST, |
414 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS }, .unit = "layout_type" }, |
415 | | { "binaural", NULL, 0, AV_OPT_TYPE_CONST, |
416 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL }, .unit = "layout_type" }, |
417 | | { "sound_system", "Sound System", OFFSET(sound_system), AV_OPT_TYPE_CHLAYOUT, { .str = NULL }, 0, 0, FLAGS }, |
418 | | { "integrated_loudness", "Integrated loudness", OFFSET(integrated_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
419 | | { "digital_peak", "Digital peak", OFFSET(digital_peak), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
420 | | { "true_peak", "True peak", OFFSET(true_peak), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
421 | | { "dialog_anchored_loudness", "Anchored loudness (Dialog)", OFFSET(dialogue_anchored_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
422 | | { "album_anchored_loudness", "Anchored loudness (Album)", OFFSET(album_anchored_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
423 | | { NULL }, |
424 | | }; |
425 | | |
426 | | static const AVClass layout_class = { |
427 | | .class_name = "AVIAMFSubmixLayout", |
428 | | .item_name = av_default_item_name, |
429 | | .version = LIBAVUTIL_VERSION_INT, |
430 | | .option = submix_layout_options, |
431 | | }; |
432 | | |
433 | 2.85k | IAMF_ADD_FUNC_TEMPLATE(AVIAMFSubmix, submix, AVIAMFSubmixLayout, layout, s) |
434 | | |
435 | | #undef OFFSET |
436 | | #define OFFSET(x) offsetof(AVIAMFSubmix, x) |
437 | | static const AVOption submix_presentation_options[] = { |
438 | | { "default_mix_gain", "Default mix gain", OFFSET(default_mix_gain), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
439 | | { NULL }, |
440 | | }; |
441 | | |
442 | | static void *submix_presentation_child_next(void *obj, void *prev) |
443 | 0 | { |
444 | 0 | AVIAMFSubmix *sub_mix = obj; |
445 | 0 | if (!prev) |
446 | 0 | return sub_mix->output_mix_config; |
447 | | |
448 | 0 | return NULL; |
449 | 0 | } |
450 | | |
451 | | static const AVClass *submix_presentation_child_iterate(void **opaque) |
452 | 0 | { |
453 | 0 | uintptr_t i = (uintptr_t)*opaque; |
454 | 0 | const AVClass *ret = NULL; |
455 | |
|
456 | 0 | switch(i) { |
457 | 0 | case 0: |
458 | 0 | ret = &element_class; |
459 | 0 | break; |
460 | 0 | case 1: |
461 | 0 | ret = &layout_class; |
462 | 0 | break; |
463 | 0 | case 2: |
464 | 0 | ret = ¶m_definition_class; |
465 | 0 | break; |
466 | 0 | default: |
467 | 0 | break; |
468 | 0 | } |
469 | | |
470 | 0 | if (ret) |
471 | 0 | *opaque = (void*)(i + 1); |
472 | 0 | return ret; |
473 | 0 | } |
474 | | |
475 | | static const AVClass submix_class = { |
476 | | .class_name = "AVIAMFSubmix", |
477 | | .item_name = av_default_item_name, |
478 | | .version = LIBAVUTIL_VERSION_INT, |
479 | | .option = submix_presentation_options, |
480 | | .child_next = submix_presentation_child_next, |
481 | | .child_class_iterate = submix_presentation_child_iterate, |
482 | | }; |
483 | | |
484 | | #undef OFFSET |
485 | | #define OFFSET(x) offsetof(AVIAMFMixPresentation, x) |
486 | | static const AVOption mix_presentation_options[] = { |
487 | | { "annotations", "set annotations", OFFSET(annotations), AV_OPT_TYPE_DICT, {.str = NULL }, 0, 0, FLAGS }, |
488 | | { NULL }, |
489 | | }; |
490 | | |
491 | | #undef OFFSET |
492 | | #undef FLAGS |
493 | | |
494 | | static const AVClass *mix_presentation_child_iterate(void **opaque) |
495 | 0 | { |
496 | 0 | uintptr_t i = (uintptr_t)*opaque; |
497 | 0 | const AVClass *ret = NULL; |
498 | |
|
499 | 0 | if (i) |
500 | 0 | ret = &submix_class; |
501 | |
|
502 | 0 | if (ret) |
503 | 0 | *opaque = (void*)(i + 1); |
504 | 0 | return ret; |
505 | 0 | } |
506 | | |
507 | | static const AVClass mix_presentation_class = { |
508 | | .class_name = "AVIAMFMixPresentation", |
509 | | .item_name = av_default_item_name, |
510 | | .version = LIBAVUTIL_VERSION_INT, |
511 | | .option = mix_presentation_options, |
512 | | .child_class_iterate = mix_presentation_child_iterate, |
513 | | }; |
514 | | |
515 | | const AVClass *av_iamf_mix_presentation_get_class(void) |
516 | 0 | { |
517 | 0 | return &mix_presentation_class; |
518 | 0 | } |
519 | | |
520 | | AVIAMFMixPresentation *av_iamf_mix_presentation_alloc(void) |
521 | 1.80k | { |
522 | 1.80k | AVIAMFMixPresentation *mix_presentation = av_mallocz(sizeof(*mix_presentation)); |
523 | | |
524 | 1.80k | if (mix_presentation) { |
525 | 1.80k | mix_presentation->av_class = &mix_presentation_class; |
526 | 1.80k | av_opt_set_defaults(mix_presentation); |
527 | 1.80k | } |
528 | | |
529 | 1.80k | return mix_presentation; |
530 | 1.80k | } |
531 | | |
532 | 2.03k | IAMF_ADD_FUNC_TEMPLATE(AVIAMFMixPresentation, mix_presentation, AVIAMFSubmix, submix, es) |
533 | | |
534 | | void av_iamf_mix_presentation_free(AVIAMFMixPresentation **pmix_presentation) |
535 | 2.19k | { |
536 | 2.19k | AVIAMFMixPresentation *mix_presentation = *pmix_presentation; |
537 | | |
538 | 2.19k | if (!mix_presentation) |
539 | 391 | return; |
540 | | |
541 | 3.84k | for (int i = 0; i < mix_presentation->nb_submixes; i++) { |
542 | 2.03k | AVIAMFSubmix *sub_mix = mix_presentation->submixes[i]; |
543 | 2.28k | for (int j = 0; j < sub_mix->nb_elements; j++) { |
544 | 247 | AVIAMFSubmixElement *submix_element = sub_mix->elements[j]; |
545 | 247 | av_opt_free(submix_element); |
546 | 247 | av_free(submix_element->element_mix_config); |
547 | 247 | av_free(submix_element); |
548 | 247 | } |
549 | 2.03k | av_free(sub_mix->elements); |
550 | 4.89k | for (int j = 0; j < sub_mix->nb_layouts; j++) { |
551 | 2.85k | AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[j]; |
552 | 2.85k | av_opt_free(submix_layout); |
553 | 2.85k | av_free(submix_layout); |
554 | 2.85k | } |
555 | 2.03k | av_free(sub_mix->layouts); |
556 | 2.03k | av_free(sub_mix->output_mix_config); |
557 | 2.03k | av_free(sub_mix); |
558 | 2.03k | } |
559 | 1.80k | av_opt_free(mix_presentation); |
560 | 1.80k | av_free(mix_presentation->submixes); |
561 | | |
562 | 1.80k | av_freep(pmix_presentation); |
563 | 1.80k | } |