Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Immersive Audio Model and Formats demuxing utils | ||
3 | * Copyright (c) 2024 James Almer <jamrial@gmail.com> | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include "libavutil/avassert.h" | ||
23 | #include "libavutil/intreadwrite.h" | ||
24 | #include "libavutil/log.h" | ||
25 | #include "libavutil/mem.h" | ||
26 | #include "libavcodec/mathops.h" | ||
27 | #include "libavcodec/packet.h" | ||
28 | #include "avformat.h" | ||
29 | #include "avio_internal.h" | ||
30 | #include "iamf.h" | ||
31 | #include "iamf_parse.h" | ||
32 | #include "iamf_reader.h" | ||
33 | |||
34 | 573 | static AVStream *find_stream_by_id(AVFormatContext *s, int id) | |
35 | { | ||
36 |
1/2✓ Branch 0 taken 1933 times.
✗ Branch 1 not taken.
|
1933 | for (int i = 0; i < s->nb_streams; i++) |
37 |
2/2✓ Branch 0 taken 573 times.
✓ Branch 1 taken 1360 times.
|
1933 | if (s->streams[i]->id == id) |
38 | 573 | return s->streams[i]; | |
39 | |||
40 | ✗ | av_log(s, AV_LOG_ERROR, "Invalid stream id %d\n", id); | |
41 | ✗ | return NULL; | |
42 | } | ||
43 | |||
44 | 573 | static int audio_frame_obu(AVFormatContext *s, const IAMFDemuxContext *c, | |
45 | AVIOContext *pb, AVPacket *pkt, | ||
46 | int len, enum IAMF_OBU_Type type, | ||
47 | unsigned skip_samples, unsigned discard_padding, | ||
48 | int id_in_bitstream) | ||
49 | { | ||
50 | AVStream *st; | ||
51 | int ret, audio_substream_id; | ||
52 | |||
53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (id_in_bitstream) { |
54 | unsigned explicit_audio_substream_id; | ||
55 | ✗ | int64_t pos = avio_tell(pb); | |
56 | ✗ | explicit_audio_substream_id = ffio_read_leb(pb); | |
57 | ✗ | len -= avio_tell(pb) - pos; | |
58 | ✗ | audio_substream_id = explicit_audio_substream_id; | |
59 | } else | ||
60 | 573 | audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0; | |
61 | |||
62 | 573 | st = find_stream_by_id(s, audio_substream_id); | |
63 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (!st) |
64 | ✗ | return AVERROR_INVALIDDATA; | |
65 | |||
66 | 573 | ret = av_get_packet(pb, pkt, len); | |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (ret < 0) |
68 | ✗ | return ret; | |
69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (ret != len) |
70 | ✗ | return AVERROR_INVALIDDATA; | |
71 | |||
72 |
4/4✓ Branch 0 taken 559 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 555 times.
|
573 | if (skip_samples || discard_padding) { |
73 | 18 | uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); | |
74 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if (!side_data) |
75 | ✗ | return AVERROR(ENOMEM); | |
76 | 18 | AV_WL32A(side_data, skip_samples); | |
77 | 18 | AV_WL32A(side_data + 4, discard_padding); | |
78 | } | ||
79 |
2/2✓ Branch 0 taken 118 times.
✓ Branch 1 taken 455 times.
|
573 | if (c->mix) { |
80 | 118 | uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size); | |
81 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
|
118 | if (!side_data) |
82 | ✗ | return AVERROR(ENOMEM); | |
83 | 118 | memcpy(side_data, c->mix, c->mix_size); | |
84 | } | ||
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (c->demix) { |
86 | ✗ | uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size); | |
87 | ✗ | if (!side_data) | |
88 | ✗ | return AVERROR(ENOMEM); | |
89 | ✗ | memcpy(side_data, c->demix, c->demix_size); | |
90 | } | ||
91 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 457 times.
|
573 | if (c->recon) { |
92 | 116 | uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size); | |
93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
|
116 | if (!side_data) |
94 | ✗ | return AVERROR(ENOMEM); | |
95 | 116 | memcpy(side_data, c->recon, c->recon_size); | |
96 | } | ||
97 | |||
98 | 573 | pkt->stream_index = st->index; | |
99 | 573 | return 0; | |
100 | } | ||
101 | |||
102 | 62 | static int parameter_block_obu(AVFormatContext *s, IAMFDemuxContext *c, | |
103 | AVIOContext *pbc, int len) | ||
104 | { | ||
105 | const IAMFParamDefinition *param_definition; | ||
106 | const AVIAMFParamDefinition *param; | ||
107 | 62 | AVIAMFParamDefinition *out_param = NULL; | |
108 | FFIOContext b; | ||
109 | AVIOContext *pb; | ||
110 | uint8_t *buf; | ||
111 | unsigned int duration, constant_subblock_duration; | ||
112 | unsigned int nb_subblocks; | ||
113 | unsigned int parameter_id; | ||
114 | size_t out_param_size; | ||
115 | int ret; | ||
116 | |||
117 | 62 | buf = av_malloc(len); | |
118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (!buf) |
119 | ✗ | return AVERROR(ENOMEM); | |
120 | |||
121 | 62 | ret = avio_read(pbc, buf, len); | |
122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret != len) { |
123 | ✗ | if (ret >= 0) | |
124 | ✗ | ret = AVERROR_INVALIDDATA; | |
125 | ✗ | goto fail; | |
126 | } | ||
127 | |||
128 | 62 | ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL); | |
129 | 62 | pb = &b.pub; | |
130 | |||
131 | 62 | parameter_id = ffio_read_leb(pb); | |
132 | 62 | param_definition = ff_iamf_get_param_definition(&c->iamf, parameter_id); | |
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (!param_definition) { |
134 | ✗ | av_log(s, AV_LOG_VERBOSE, "Non existant parameter_id %d referenced in a parameter block. Ignoring\n", | |
135 | parameter_id); | ||
136 | ✗ | ret = 0; | |
137 | ✗ | goto fail; | |
138 | } | ||
139 | |||
140 | 62 | param = param_definition->param; | |
141 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 30 times.
|
62 | if (!param_definition->mode) { |
142 | 32 | duration = ffio_read_leb(pb); | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (!duration) { |
144 | ✗ | ret = AVERROR_INVALIDDATA; | |
145 | ✗ | goto fail; | |
146 | } | ||
147 | 32 | constant_subblock_duration = ffio_read_leb(pb); | |
148 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (constant_subblock_duration == 0) |
149 | ✗ | nb_subblocks = ffio_read_leb(pb); | |
150 | else | ||
151 | 32 | nb_subblocks = duration / constant_subblock_duration; | |
152 | } else { | ||
153 | 30 | duration = param->duration; | |
154 | 30 | constant_subblock_duration = param->constant_subblock_duration; | |
155 | 30 | nb_subblocks = param->nb_subblocks; | |
156 | } | ||
157 | |||
158 | 62 | out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size); | |
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (!out_param) { |
160 | ✗ | ret = AVERROR(ENOMEM); | |
161 | ✗ | goto fail; | |
162 | } | ||
163 | |||
164 | 62 | out_param->parameter_id = param->parameter_id; | |
165 | 62 | out_param->type = param->type; | |
166 | 62 | out_param->parameter_rate = param->parameter_rate; | |
167 | 62 | out_param->duration = duration; | |
168 | 62 | out_param->constant_subblock_duration = constant_subblock_duration; | |
169 | 62 | out_param->nb_subblocks = nb_subblocks; | |
170 | |||
171 |
2/2✓ Branch 0 taken 62 times.
✓ Branch 1 taken 62 times.
|
124 | for (int i = 0; i < nb_subblocks; i++) { |
172 | 62 | void *subblock = av_iamf_param_definition_get_subblock(out_param, i); | |
173 | 62 | unsigned int subblock_duration = constant_subblock_duration; | |
174 | |||
175 |
3/4✓ Branch 0 taken 32 times.
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
|
62 | if (!param_definition->mode && !constant_subblock_duration) |
176 | ✗ | subblock_duration = ffio_read_leb(pb); | |
177 | |||
178 |
2/4✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
62 | switch (param->type) { |
179 | 32 | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { | |
180 | 32 | AVIAMFMixGain *mix = subblock; | |
181 | |||
182 | 32 | mix->animation_type = ffio_read_leb(pb); | |
183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) { |
184 | ✗ | ret = 0; | |
185 | ✗ | av_free(out_param); | |
186 | ✗ | goto fail; | |
187 | } | ||
188 | |||
189 | 32 | mix->start_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); | |
190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR) |
191 | ✗ | mix->end_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); | |
192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) { |
193 | ✗ | mix->control_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); | |
194 | ✗ | mix->control_point_relative_time = av_make_q(avio_r8(pb), 1 << 8); | |
195 | } | ||
196 | 32 | mix->subblock_duration = subblock_duration; | |
197 | 32 | break; | |
198 | } | ||
199 | ✗ | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { | |
200 | ✗ | AVIAMFDemixingInfo *demix = subblock; | |
201 | |||
202 | ✗ | demix->dmixp_mode = avio_r8(pb) >> 5; | |
203 | ✗ | demix->subblock_duration = subblock_duration; | |
204 | ✗ | break; | |
205 | } | ||
206 | 30 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { | |
207 | 30 | AVIAMFReconGain *recon = subblock; | |
208 | 30 | const IAMFAudioElement *audio_element = param_definition->audio_element; | |
209 | 30 | const AVIAMFAudioElement *element = audio_element->celement; | |
210 | |||
211 |
2/4✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30 times.
|
30 | av_assert0(audio_element && element); |
212 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 30 times.
|
90 | for (int i = 0; i < element->nb_layers; i++) { |
213 | 60 | const AVIAMFLayer *layer = element->layers[i]; | |
214 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
60 | if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) { |
215 | 30 | unsigned int recon_gain_flags = ffio_read_leb(pb); | |
216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80); |
217 | 30 | recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1); | |
218 |
2/2✓ Branch 0 taken 210 times.
✓ Branch 1 taken 30 times.
|
240 | for (int j = 0; j < bitcount; j++) { |
219 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 90 times.
|
210 | if (recon_gain_flags & (1 << j)) |
220 | 120 | recon->recon_gain[i][j] = avio_r8(pb); | |
221 | } | ||
222 | } | ||
223 | } | ||
224 | 30 | recon->subblock_duration = subblock_duration; | |
225 | 30 | break; | |
226 | } | ||
227 | ✗ | default: | |
228 | ✗ | av_assert0(0); | |
229 | } | ||
230 | } | ||
231 | |||
232 | 62 | len -= avio_tell(pb); | |
233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (len) { |
234 | ✗ | int level = (s->error_recognition & AV_EF_EXPLODE) ? AV_LOG_ERROR : AV_LOG_WARNING; | |
235 | ✗ | av_log(s, level, "Underread in parameter_block_obu. %d bytes left at the end\n", len); | |
236 | } | ||
237 | |||
238 |
2/4✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
62 | switch (param->type) { |
239 | 32 | case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: | |
240 | 32 | av_free(c->mix); | |
241 | 32 | c->mix = out_param; | |
242 | 32 | c->mix_size = out_param_size; | |
243 | 32 | break; | |
244 | ✗ | case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: | |
245 | ✗ | av_free(c->demix); | |
246 | ✗ | c->demix = out_param; | |
247 | ✗ | c->demix_size = out_param_size; | |
248 | ✗ | break; | |
249 | 30 | case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: | |
250 | 30 | av_free(c->recon); | |
251 | 30 | c->recon = out_param; | |
252 | 30 | c->recon_size = out_param_size; | |
253 | 30 | break; | |
254 | ✗ | default: | |
255 | ✗ | av_assert0(0); | |
256 | } | ||
257 | |||
258 | 62 | ret = 0; | |
259 | 62 | fail: | |
260 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) |
261 | ✗ | av_free(out_param); | |
262 | 62 | av_free(buf); | |
263 | |||
264 | 62 | return ret; | |
265 | } | ||
266 | |||
267 | 578 | int ff_iamf_read_packet(AVFormatContext *s, IAMFDemuxContext *c, | |
268 | AVIOContext *pb, int max_size, AVPacket *pkt) | ||
269 | { | ||
270 | 578 | int read = 0; | |
271 | |||
272 | 62 | while (1) { | |
273 | uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; | ||
274 | enum IAMF_OBU_Type type; | ||
275 | unsigned obu_size; | ||
276 | unsigned skip_samples, discard_padding; | ||
277 | int ret, len, size, start_pos; | ||
278 | |||
279 | 640 | ret = ffio_ensure_seekback(pb, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size)); | |
280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 640 times.
|
640 | if (ret < 0) |
281 | 578 | return ret; | |
282 | 640 | size = avio_read(pb, header, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size)); | |
283 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 635 times.
|
640 | if (size < 0) |
284 | 5 | return size; | |
285 | |||
286 | 635 | len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type, | |
287 | &skip_samples, &discard_padding); | ||
288 |
3/6✓ Branch 0 taken 635 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 635 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 635 times.
|
635 | if (len < 0 || obu_size > max_size || len > INT_MAX - read) { |
289 | ✗ | av_log(s, AV_LOG_ERROR, "Failed to read obu\n"); | |
290 | ✗ | return len < 0 ? len : AVERROR_INVALIDDATA; | |
291 | } | ||
292 | 635 | avio_seek(pb, -(size - start_pos), SEEK_CUR); | |
293 | |||
294 | 635 | read += len; | |
295 |
3/4✓ Branch 0 taken 573 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 573 times.
✗ Branch 3 not taken.
|
635 | if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) { |
296 | 573 | ret = audio_frame_obu(s, c, pb, pkt, obu_size, type, | |
297 | skip_samples, discard_padding, | ||
298 | type == IAMF_OBU_IA_AUDIO_FRAME); | ||
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 573 times.
|
573 | if (ret < 0) |
300 | ✗ | return ret; | |
301 | 573 | return read; | |
302 |
1/2✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
|
62 | } else if (type == IAMF_OBU_IA_PARAMETER_BLOCK) { |
303 | 62 | ret = parameter_block_obu(s, c, pb, obu_size); | |
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) |
305 | ✗ | return ret; | |
306 | ✗ | } else if (type == IAMF_OBU_IA_TEMPORAL_DELIMITER) { | |
307 | ✗ | av_freep(&c->mix); | |
308 | ✗ | c->mix_size = 0; | |
309 | ✗ | av_freep(&c->demix); | |
310 | ✗ | c->demix_size = 0; | |
311 | ✗ | av_freep(&c->recon); | |
312 | ✗ | c->recon_size = 0; | |
313 | } else { | ||
314 | ✗ | int64_t offset = avio_skip(pb, obu_size); | |
315 | ✗ | if (offset < 0) | |
316 | ✗ | return offset; | |
317 | } | ||
318 | 62 | max_size -= len; | |
319 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (max_size < 0) |
320 | ✗ | return AVERROR_INVALIDDATA; | |
321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (!max_size) |
322 | ✗ | break; | |
323 | } | ||
324 | |||
325 | ✗ | return read; | |
326 | } | ||
327 | |||
328 | 25 | void ff_iamf_read_deinit(IAMFDemuxContext *c) | |
329 | { | ||
330 | 25 | IAMFContext *const iamf = &c->iamf; | |
331 | |||
332 | 25 | ff_iamf_uninit_context(iamf); | |
333 | |||
334 | 25 | av_freep(&c->mix); | |
335 | 25 | c->mix_size = 0; | |
336 | 25 | av_freep(&c->demix); | |
337 | 25 | c->demix_size = 0; | |
338 | 25 | av_freep(&c->recon); | |
339 | 25 | c->recon_size = 0; | |
340 | 25 | } | |
341 |