/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 | 0 | child_type *av_iamf_ ## parent_name ## _add_ ## child_name(parent_type *parent_name) \ |
34 | 0 | { \ |
35 | 0 | child_type **child_name ## suffix, *child_name; \ |
36 | 0 | \ |
37 | 0 | if (parent_name->nb_## child_name ## suffix == UINT_MAX) \ |
38 | 0 | return NULL; \ |
39 | 0 | \ |
40 | 0 | child_name ## suffix = av_realloc_array(parent_name->child_name ## suffix, \ |
41 | 0 | parent_name->nb_## child_name ## suffix + 1, \ |
42 | 0 | sizeof(*parent_name->child_name ## suffix)); \ |
43 | 0 | if (!child_name ## suffix) \ |
44 | 0 | return NULL; \ |
45 | 0 | \ |
46 | 0 | parent_name->child_name ## suffix = child_name ## suffix; \ |
47 | 0 | \ |
48 | 0 | child_name = parent_name->child_name ## suffix[parent_name->nb_## child_name ## suffix] \ |
49 | 0 | = av_mallocz(sizeof(*child_name)); \ |
50 | 0 | if (!child_name) \ |
51 | 0 | return NULL; \ |
52 | 0 | \ |
53 | 0 | child_name->av_class = &child_name ## _class; \ |
54 | 0 | av_opt_set_defaults(child_name); \ |
55 | 0 | parent_name->nb_## child_name ## suffix++; \ |
56 | 0 | \ |
57 | 0 | return child_name; \ |
58 | 0 | } |
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 | 0 | { |
162 | |
|
163 | 0 | struct MixGainStruct { |
164 | 0 | AVIAMFParamDefinition p; |
165 | 0 | AVIAMFMixGain m; |
166 | 0 | }; |
167 | 0 | struct DemixStruct { |
168 | 0 | AVIAMFParamDefinition p; |
169 | 0 | AVIAMFDemixingInfo d; |
170 | 0 | }; |
171 | 0 | struct ReconGainStruct { |
172 | 0 | AVIAMFParamDefinition p; |
173 | 0 | AVIAMFReconGain r; |
174 | 0 | }; |
175 | 0 | size_t subblocks_offset, subblock_size; |
176 | 0 | size_t size; |
177 | 0 | AVIAMFParamDefinition *par; |
178 | |
|
179 | 0 | switch (type) { |
180 | 0 | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: |
181 | 0 | subblocks_offset = offsetof(struct MixGainStruct, m); |
182 | 0 | subblock_size = sizeof(AVIAMFMixGain); |
183 | 0 | break; |
184 | 0 | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: |
185 | 0 | subblocks_offset = offsetof(struct DemixStruct, d); |
186 | 0 | subblock_size = sizeof(AVIAMFDemixingInfo); |
187 | 0 | break; |
188 | 0 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: |
189 | 0 | subblocks_offset = offsetof(struct ReconGainStruct, r); |
190 | 0 | subblock_size = sizeof(AVIAMFReconGain); |
191 | 0 | break; |
192 | 0 | default: |
193 | 0 | return NULL; |
194 | 0 | } |
195 | | |
196 | 0 | size = subblocks_offset; |
197 | 0 | if (nb_subblocks > (SIZE_MAX - size) / subblock_size) |
198 | 0 | return NULL; |
199 | 0 | size += subblock_size * nb_subblocks; |
200 | |
|
201 | 0 | par = av_mallocz(size); |
202 | 0 | if (!par) |
203 | 0 | return NULL; |
204 | | |
205 | 0 | par->av_class = ¶m_definition_class; |
206 | 0 | av_opt_set_defaults(par); |
207 | |
|
208 | 0 | par->type = type; |
209 | 0 | par->nb_subblocks = nb_subblocks; |
210 | 0 | par->subblock_size = subblock_size; |
211 | 0 | par->subblocks_offset = subblocks_offset; |
212 | |
|
213 | 0 | for (int i = 0; i < nb_subblocks; i++) { |
214 | 0 | void *subblock = av_iamf_param_definition_get_subblock(par, i); |
215 | |
|
216 | 0 | switch (type) { |
217 | 0 | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: |
218 | 0 | ((AVIAMFMixGain *)subblock)->av_class = &mix_gain_class; |
219 | 0 | break; |
220 | 0 | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: |
221 | 0 | ((AVIAMFDemixingInfo *)subblock)->av_class = &demixing_info_class; |
222 | 0 | break; |
223 | 0 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: |
224 | 0 | ((AVIAMFReconGain *)subblock)->av_class = &recon_gain_class; |
225 | 0 | break; |
226 | 0 | default: |
227 | 0 | av_assert0(0); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | 0 | if (out_size) |
232 | 0 | *out_size = size; |
233 | |
|
234 | 0 | return par; |
235 | 0 | } |
236 | | |
237 | | // |
238 | | // Audio Element |
239 | | // |
240 | | static const AVOptionArrayDef demixing_matrix_def = { .size_max = (255 + 255) * 255, .sep = '|' }; |
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 | | { "demixing_matrix", "set demixing_matrix", OFFSET(demixing_matrix), AV_OPT_TYPE_RATIONAL | AV_OPT_TYPE_FLAG_ARRAY, |
273 | | { .arr = &demixing_matrix_def }, -1.0, 1.0, FLAGS }, |
274 | | { NULL }, |
275 | | }; |
276 | | |
277 | | static const AVClass layer_class = { |
278 | | .class_name = "AVIAMFLayer", |
279 | | .item_name = av_default_item_name, |
280 | | .version = LIBAVUTIL_VERSION_INT, |
281 | | .option = layer_options, |
282 | | }; |
283 | | |
284 | | #undef OFFSET |
285 | | #define OFFSET(x) offsetof(AVIAMFAudioElement, x) |
286 | | static const AVOption audio_element_options[] = { |
287 | | { "audio_element_type", "set audio_element_type", OFFSET(audio_element_type), AV_OPT_TYPE_INT, |
288 | | {.i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL }, |
289 | | AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL, AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE, FLAGS, .unit = "audio_element_type" }, |
290 | | { "channel", NULL, 0, AV_OPT_TYPE_CONST, |
291 | | { .i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL }, .unit = "audio_element_type" }, |
292 | | { "scene", NULL, 0, AV_OPT_TYPE_CONST, |
293 | | { .i64 = AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE }, .unit = "audio_element_type" }, |
294 | | { "default_w", "set default_w", OFFSET(default_w), AV_OPT_TYPE_UINT, {.i64 = 0 }, 0, 10, FLAGS }, |
295 | | { NULL }, |
296 | | }; |
297 | | |
298 | | static const AVClass *audio_element_child_iterate(void **opaque) |
299 | 0 | { |
300 | 0 | uintptr_t i = (uintptr_t)*opaque; |
301 | 0 | const AVClass *ret = NULL; |
302 | |
|
303 | 0 | if (i) |
304 | 0 | ret = &layer_class; |
305 | |
|
306 | 0 | if (ret) |
307 | 0 | *opaque = (void*)(i + 1); |
308 | 0 | return ret; |
309 | 0 | } |
310 | | |
311 | | static const AVClass audio_element_class = { |
312 | | .class_name = "AVIAMFAudioElement", |
313 | | .item_name = av_default_item_name, |
314 | | .version = LIBAVUTIL_VERSION_INT, |
315 | | .option = audio_element_options, |
316 | | .child_class_iterate = audio_element_child_iterate, |
317 | | }; |
318 | | |
319 | | const AVClass *av_iamf_audio_element_get_class(void) |
320 | 0 | { |
321 | 0 | return &audio_element_class; |
322 | 0 | } |
323 | | |
324 | | AVIAMFAudioElement *av_iamf_audio_element_alloc(void) |
325 | 0 | { |
326 | 0 | AVIAMFAudioElement *audio_element = av_mallocz(sizeof(*audio_element)); |
327 | |
|
328 | 0 | if (audio_element) { |
329 | 0 | audio_element->av_class = &audio_element_class; |
330 | 0 | av_opt_set_defaults(audio_element); |
331 | 0 | } |
332 | |
|
333 | 0 | return audio_element; |
334 | 0 | } |
335 | | |
336 | 0 | IAMF_ADD_FUNC_TEMPLATE(AVIAMFAudioElement, audio_element, AVIAMFLayer, layer, s) |
337 | | |
338 | | void av_iamf_audio_element_free(AVIAMFAudioElement **paudio_element) |
339 | 0 | { |
340 | 0 | AVIAMFAudioElement *audio_element = *paudio_element; |
341 | |
|
342 | 0 | if (!audio_element) |
343 | 0 | return; |
344 | | |
345 | 0 | for (int i = 0; i < audio_element->nb_layers; i++) { |
346 | 0 | AVIAMFLayer *layer = audio_element->layers[i]; |
347 | 0 | av_opt_free(layer); |
348 | 0 | av_free(layer->demixing_matrix); |
349 | 0 | av_free(layer); |
350 | 0 | } |
351 | 0 | av_free(audio_element->layers); |
352 | |
|
353 | 0 | av_free(audio_element->demixing_info); |
354 | 0 | av_free(audio_element->recon_gain_info); |
355 | 0 | av_freep(paudio_element); |
356 | 0 | } |
357 | | |
358 | | // |
359 | | // Mix Presentation |
360 | | // |
361 | | #undef OFFSET |
362 | | #define OFFSET(x) offsetof(AVIAMFSubmixElement, x) |
363 | | static const AVOption submix_element_options[] = { |
364 | | { "headphones_rendering_mode", "Headphones rendering mode", OFFSET(headphones_rendering_mode), AV_OPT_TYPE_INT, |
365 | | { .i64 = AV_IAMF_HEADPHONES_MODE_STEREO }, |
366 | | AV_IAMF_HEADPHONES_MODE_STEREO, AV_IAMF_HEADPHONES_MODE_BINAURAL, FLAGS, .unit = "headphones_rendering_mode" }, |
367 | | { "stereo", NULL, 0, AV_OPT_TYPE_CONST, |
368 | | { .i64 = AV_IAMF_HEADPHONES_MODE_STEREO }, .unit = "headphones_rendering_mode" }, |
369 | | { "binaural", NULL, 0, AV_OPT_TYPE_CONST, |
370 | | { .i64 = AV_IAMF_HEADPHONES_MODE_BINAURAL }, .unit = "headphones_rendering_mode" }, |
371 | | { "default_mix_gain", "Default mix gain", OFFSET(default_mix_gain), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
372 | | { "annotations", "Annotations", OFFSET(annotations), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS }, |
373 | | { NULL }, |
374 | | }; |
375 | | |
376 | | static void *submix_element_child_next(void *obj, void *prev) |
377 | 0 | { |
378 | 0 | AVIAMFSubmixElement *submix_element = obj; |
379 | 0 | if (!prev) |
380 | 0 | return submix_element->element_mix_config; |
381 | | |
382 | 0 | return NULL; |
383 | 0 | } |
384 | | |
385 | | static const AVClass *submix_element_child_iterate(void **opaque) |
386 | 0 | { |
387 | 0 | uintptr_t i = (uintptr_t)*opaque; |
388 | 0 | const AVClass *ret = NULL; |
389 | |
|
390 | 0 | if (i) |
391 | 0 | ret = ¶m_definition_class; |
392 | |
|
393 | 0 | if (ret) |
394 | 0 | *opaque = (void*)(i + 1); |
395 | 0 | return ret; |
396 | 0 | } |
397 | | |
398 | | static const AVClass element_class = { |
399 | | .class_name = "AVIAMFSubmixElement", |
400 | | .item_name = av_default_item_name, |
401 | | .version = LIBAVUTIL_VERSION_INT, |
402 | | .option = submix_element_options, |
403 | | .child_next = submix_element_child_next, |
404 | | .child_class_iterate = submix_element_child_iterate, |
405 | | }; |
406 | | |
407 | 0 | IAMF_ADD_FUNC_TEMPLATE(AVIAMFSubmix, submix, AVIAMFSubmixElement, element, s) |
408 | | |
409 | | #undef OFFSET |
410 | | #define OFFSET(x) offsetof(AVIAMFSubmixLayout, x) |
411 | | static const AVOption submix_layout_options[] = { |
412 | | { "layout_type", "Layout type", OFFSET(layout_type), AV_OPT_TYPE_INT, |
413 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS }, |
414 | | AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS, AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL, FLAGS, .unit = "layout_type" }, |
415 | | { "loudspeakers", NULL, 0, AV_OPT_TYPE_CONST, |
416 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS }, .unit = "layout_type" }, |
417 | | { "binaural", NULL, 0, AV_OPT_TYPE_CONST, |
418 | | { .i64 = AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL }, .unit = "layout_type" }, |
419 | | { "sound_system", "Sound System", OFFSET(sound_system), AV_OPT_TYPE_CHLAYOUT, { .str = NULL }, 0, 0, FLAGS }, |
420 | | { "integrated_loudness", "Integrated loudness", OFFSET(integrated_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
421 | | { "digital_peak", "Digital peak", OFFSET(digital_peak), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
422 | | { "true_peak", "True peak", OFFSET(true_peak), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
423 | | { "dialog_anchored_loudness", "Anchored loudness (Dialog)", OFFSET(dialogue_anchored_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
424 | | { "album_anchored_loudness", "Anchored loudness (Album)", OFFSET(album_anchored_loudness), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
425 | | { NULL }, |
426 | | }; |
427 | | |
428 | | static const AVClass layout_class = { |
429 | | .class_name = "AVIAMFSubmixLayout", |
430 | | .item_name = av_default_item_name, |
431 | | .version = LIBAVUTIL_VERSION_INT, |
432 | | .option = submix_layout_options, |
433 | | }; |
434 | | |
435 | 0 | IAMF_ADD_FUNC_TEMPLATE(AVIAMFSubmix, submix, AVIAMFSubmixLayout, layout, s) |
436 | | |
437 | | #undef OFFSET |
438 | | #define OFFSET(x) offsetof(AVIAMFSubmix, x) |
439 | | static const AVOption submix_presentation_options[] = { |
440 | | { "default_mix_gain", "Default mix gain", OFFSET(default_mix_gain), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, -128.0, 128.0, FLAGS }, |
441 | | { NULL }, |
442 | | }; |
443 | | |
444 | | static void *submix_presentation_child_next(void *obj, void *prev) |
445 | 0 | { |
446 | 0 | AVIAMFSubmix *sub_mix = obj; |
447 | 0 | if (!prev) |
448 | 0 | return sub_mix->output_mix_config; |
449 | | |
450 | 0 | return NULL; |
451 | 0 | } |
452 | | |
453 | | static const AVClass *submix_presentation_child_iterate(void **opaque) |
454 | 0 | { |
455 | 0 | uintptr_t i = (uintptr_t)*opaque; |
456 | 0 | const AVClass *ret = NULL; |
457 | |
|
458 | 0 | switch(i) { |
459 | 0 | case 0: |
460 | 0 | ret = &element_class; |
461 | 0 | break; |
462 | 0 | case 1: |
463 | 0 | ret = &layout_class; |
464 | 0 | break; |
465 | 0 | case 2: |
466 | 0 | ret = ¶m_definition_class; |
467 | 0 | break; |
468 | 0 | default: |
469 | 0 | break; |
470 | 0 | } |
471 | | |
472 | 0 | if (ret) |
473 | 0 | *opaque = (void*)(i + 1); |
474 | 0 | return ret; |
475 | 0 | } |
476 | | |
477 | | static const AVClass submix_class = { |
478 | | .class_name = "AVIAMFSubmix", |
479 | | .item_name = av_default_item_name, |
480 | | .version = LIBAVUTIL_VERSION_INT, |
481 | | .option = submix_presentation_options, |
482 | | .child_next = submix_presentation_child_next, |
483 | | .child_class_iterate = submix_presentation_child_iterate, |
484 | | }; |
485 | | |
486 | | #undef OFFSET |
487 | | #define OFFSET(x) offsetof(AVIAMFMixPresentation, x) |
488 | | static const AVOption mix_presentation_options[] = { |
489 | | { "annotations", "set annotations", OFFSET(annotations), AV_OPT_TYPE_DICT, {.str = NULL }, 0, 0, FLAGS }, |
490 | | { NULL }, |
491 | | }; |
492 | | |
493 | | #undef OFFSET |
494 | | #undef FLAGS |
495 | | |
496 | | static const AVClass *mix_presentation_child_iterate(void **opaque) |
497 | 0 | { |
498 | 0 | uintptr_t i = (uintptr_t)*opaque; |
499 | 0 | const AVClass *ret = NULL; |
500 | |
|
501 | 0 | if (i) |
502 | 0 | ret = &submix_class; |
503 | |
|
504 | 0 | if (ret) |
505 | 0 | *opaque = (void*)(i + 1); |
506 | 0 | return ret; |
507 | 0 | } |
508 | | |
509 | | static const AVClass mix_presentation_class = { |
510 | | .class_name = "AVIAMFMixPresentation", |
511 | | .item_name = av_default_item_name, |
512 | | .version = LIBAVUTIL_VERSION_INT, |
513 | | .option = mix_presentation_options, |
514 | | .child_class_iterate = mix_presentation_child_iterate, |
515 | | }; |
516 | | |
517 | | const AVClass *av_iamf_mix_presentation_get_class(void) |
518 | 0 | { |
519 | 0 | return &mix_presentation_class; |
520 | 0 | } |
521 | | |
522 | | AVIAMFMixPresentation *av_iamf_mix_presentation_alloc(void) |
523 | 0 | { |
524 | 0 | AVIAMFMixPresentation *mix_presentation = av_mallocz(sizeof(*mix_presentation)); |
525 | |
|
526 | 0 | if (mix_presentation) { |
527 | 0 | mix_presentation->av_class = &mix_presentation_class; |
528 | 0 | av_opt_set_defaults(mix_presentation); |
529 | 0 | } |
530 | |
|
531 | 0 | return mix_presentation; |
532 | 0 | } |
533 | | |
534 | 0 | IAMF_ADD_FUNC_TEMPLATE(AVIAMFMixPresentation, mix_presentation, AVIAMFSubmix, submix, es) |
535 | | |
536 | | void av_iamf_mix_presentation_free(AVIAMFMixPresentation **pmix_presentation) |
537 | 0 | { |
538 | 0 | AVIAMFMixPresentation *mix_presentation = *pmix_presentation; |
539 | |
|
540 | 0 | if (!mix_presentation) |
541 | 0 | return; |
542 | | |
543 | 0 | for (int i = 0; i < mix_presentation->nb_submixes; i++) { |
544 | 0 | AVIAMFSubmix *sub_mix = mix_presentation->submixes[i]; |
545 | 0 | for (int j = 0; j < sub_mix->nb_elements; j++) { |
546 | 0 | AVIAMFSubmixElement *submix_element = sub_mix->elements[j]; |
547 | 0 | av_opt_free(submix_element); |
548 | 0 | av_free(submix_element->element_mix_config); |
549 | 0 | av_free(submix_element); |
550 | 0 | } |
551 | 0 | av_free(sub_mix->elements); |
552 | 0 | for (int j = 0; j < sub_mix->nb_layouts; j++) { |
553 | 0 | AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[j]; |
554 | 0 | av_opt_free(submix_layout); |
555 | 0 | av_free(submix_layout); |
556 | 0 | } |
557 | 0 | av_free(sub_mix->layouts); |
558 | 0 | av_free(sub_mix->output_mix_config); |
559 | 0 | av_free(sub_mix); |
560 | 0 | } |
561 | 0 | av_opt_free(mix_presentation); |
562 | 0 | av_free(mix_presentation->submixes); |
563 | |
|
564 | 0 | av_freep(pmix_presentation); |
565 | 0 | } |