FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/apv.c
Date: 2025-07-28 20:30:09
Exec Total Coverage
Lines: 153 172 89.0%
Functions: 6 6 100.0%
Branches: 41 64 64.1%

Line Branch Exec Source
1 /*
2 * APV helper functions for muxers
3 * Copyright (c) 2025 Dawid Kozinski <d.kozinski@samsung.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/mem.h"
25
26 #include "apv.h"
27 #include "cbs.h"
28 #include "avformat.h"
29 #include "avio.h"
30 #include "avio_internal.h"
31 #include "libavcodec/cbs_apv.h"
32 #include "libavcodec/packet.h"
33
34 typedef struct APVDecoderFrameInfo {
35 uint8_t color_description_present_flag; // 1 bit
36
37 // The variable indicates whether the capture_time_distance value in the APV bitstream's frame header should be ignored during playback.
38 // If capture_time_distance_ignored is set to true, the capture_time_distance information will not be utilized,
39 // and timing information for playback should be calculated using an alternative method.
40 // If set to false, the capture_time_distance value will be used as is from the frame header.
41 // It is recommended to set this variable to true, allowing the use of MP4 timestamps for playback and recording,
42 // which enables the conventional compression and playback methods based on the timestamp table defined by the ISO-based file format.
43 uint8_t capture_time_distance_ignored; // 1-bit
44
45 uint8_t profile_idc; // 8 bits
46 uint8_t level_idc; // 8 bits
47 uint8_t band_idc; // 8 bits
48 uint32_t frame_width; // 32 bits
49 uint32_t frame_height; // 32 bits
50 uint8_t chroma_format_idc; // 4 bits
51 uint8_t bit_depth_minus8; // 4 bits
52 uint8_t capture_time_distance; // 8 bits
53
54 // if (color_description_present_flag)
55 uint8_t color_primaries; // 8 bits
56 uint8_t transfer_characteristics; // 8 bits
57 uint8_t matrix_coefficients; // 8 bits
58 uint8_t full_range_flag; // 1 bit
59 } APVDecoderFrameInfo;
60
61 typedef struct APVDecoderConfigurationEntry {
62 uint8_t pbu_type; // 8 bits
63 uint8_t number_of_frame_info; // 8 bits
64
65 APVDecoderFrameInfo *frame_info; // An array of size number_of_frame_info storing elements of type APVDecoderFrameInfo*
66 } APVDecoderConfigurationEntry;
67
68 // ISOBMFF binding for APV
69 // @see https://github.com/openapv/openapv/blob/main/readme/apv_isobmff.md
70 typedef struct APVDecoderConfigurationRecord {
71 uint8_t configurationVersion; // 8 bits
72 uint8_t number_of_configuration_entry; // 8 bits
73
74 APVDecoderConfigurationEntry *configuration_entry; // table of size number_of_configuration_entry
75
76 CodedBitstreamContext *cbc;
77 CodedBitstreamFragment frag;
78 } APVDecoderConfigurationRecord;
79
80 1 void ff_isom_write_apvc(AVIOContext *pb, const APVDecoderConfigurationRecord *apvc, void *logctx)
81 {
82 1 av_log(logctx, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n",
83 1 apvc->configurationVersion);
84
85 1 av_log(logctx, AV_LOG_TRACE, "number_of_configuration_entry: %"PRIu8"\n",
86 1 apvc->number_of_configuration_entry);
87
88
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = 0; i < apvc->number_of_configuration_entry; i++) {
89 1 const APVDecoderConfigurationEntry *configuration_entry = &apvc->configuration_entry[i];
90
91 1 av_log(logctx, AV_LOG_TRACE, "pbu_type: %"PRIu8"\n",
92 1 configuration_entry->pbu_type);
93
94 1 av_log(logctx, AV_LOG_TRACE, "number_of_frame_info: %"PRIu8"\n",
95 1 configuration_entry->number_of_frame_info);
96
97
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int j = 0; j < configuration_entry->number_of_frame_info; j++) {
98 1 const APVDecoderFrameInfo *frame_info = &configuration_entry->frame_info[j];
99
100 1 av_log(logctx, AV_LOG_TRACE, "color_description_present_flag: %"PRIu8"\n",
101 1 frame_info->color_description_present_flag);
102
103 1 av_log(logctx, AV_LOG_TRACE, "capture_time_distance_ignored: %"PRIu8"\n",
104 1 frame_info->capture_time_distance_ignored);
105
106 1 av_log(logctx, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n",
107 1 frame_info->profile_idc);
108
109 1 av_log(logctx, AV_LOG_TRACE, "level_idc: %"PRIu8"\n",
110 1 frame_info->level_idc);
111
112 1 av_log(logctx, AV_LOG_TRACE, "band_idc: %"PRIu8"\n",
113 1 frame_info->band_idc);
114
115 1 av_log(logctx, AV_LOG_TRACE, "frame_width: %"PRIu32"\n",
116 1 frame_info->frame_width);
117
118 1 av_log(logctx, AV_LOG_TRACE, "frame_height: %"PRIu32"\n",
119 1 frame_info->frame_height);
120
121 1 av_log(logctx, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n",
122 1 frame_info->chroma_format_idc);
123
124 1 av_log(logctx, AV_LOG_TRACE, "bit_depth_minus8: %"PRIu8"\n",
125 1 frame_info->bit_depth_minus8);
126
127 1 av_log(logctx, AV_LOG_TRACE, "capture_time_distance: %"PRIu8"\n",
128 1 frame_info->capture_time_distance);
129
130
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (frame_info->color_description_present_flag) {
131 1 av_log(logctx, AV_LOG_TRACE, "color_primaries: %"PRIu8"\n",
132 1 frame_info->color_primaries);
133
134 1 av_log(logctx, AV_LOG_TRACE, "transfer_characteristics: %"PRIu8"\n",
135 1 frame_info->transfer_characteristics);
136
137 1 av_log(logctx, AV_LOG_TRACE, "matrix_coefficients: %"PRIu8"\n",
138 1 frame_info->matrix_coefficients);
139
140 1 av_log(logctx, AV_LOG_TRACE, "full_range_flag: %"PRIu8"\n",
141 1 frame_info->full_range_flag);
142 }
143 }
144 }
145
146 /* unsigned int(8) configurationVersion = 1; */
147 1 avio_w8(pb, apvc->configurationVersion);
148
149 1 avio_w8(pb, apvc->number_of_configuration_entry);
150
151
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = 0; i < apvc->number_of_configuration_entry; i++) {
152 1 const APVDecoderConfigurationEntry *configuration_entry = &apvc->configuration_entry[i];
153
154 1 avio_w8(pb, configuration_entry->pbu_type);
155 1 avio_w8(pb, configuration_entry->number_of_frame_info);
156
157
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int j = 0; j < configuration_entry->number_of_frame_info; j++) {
158 1 const APVDecoderFrameInfo *frame_info = &configuration_entry->frame_info[j];
159
160 /* reserved_zero_6bits
161 * unsigned int(1) color_description_present_flag
162 * unsigned int(1) capture_time_distance_ignored */
163 1 avio_w8(pb, frame_info->color_description_present_flag << 1 |
164 1 frame_info->capture_time_distance_ignored);
165
166 /* unsigned int(8) profile_idc */
167 1 avio_w8(pb, frame_info->profile_idc);
168
169 /* unsigned int(8) level_idc */
170 1 avio_w8(pb, frame_info->level_idc);
171
172 /* unsigned int(8) band_idc */
173 1 avio_w8(pb, frame_info->band_idc);
174
175 /* unsigned int(32) frame_width_minus1 */
176 1 avio_wb32(pb, frame_info->frame_width);
177
178 /* unsigned int(32) frame_height_minus1 */
179 1 avio_wb32(pb, frame_info->frame_height);
180
181 /* unsigned int(4) chroma_format_idc */
182 /* unsigned int(4) bit_depth_minus8 */
183 1 avio_w8(pb, (frame_info->chroma_format_idc << 4) |
184 1 frame_info->bit_depth_minus8);
185
186 /* unsigned int(8) capture_time_distance */
187 1 avio_w8(pb, frame_info->capture_time_distance);
188
189
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (frame_info->color_description_present_flag) {
190 /* unsigned int(8) color_primaries */
191 1 avio_w8(pb, frame_info->color_primaries);
192
193 /* unsigned int(8) transfer_characteristics */
194 1 avio_w8(pb, frame_info->transfer_characteristics);
195
196 /* unsigned int(8) matrix_coefficients */
197 1 avio_w8(pb, frame_info->matrix_coefficients);
198
199 /* unsigned int(1) full_range_flag
200 * reserved_zero_7bits */
201 1 avio_w8(pb, frame_info->full_range_flag << 7);
202 }
203 }
204 }
205 1 }
206
207 static const CodedBitstreamUnitType decompose_unit_types[] = {
208 APV_PBU_PRIMARY_FRAME, APV_PBU_NON_PRIMARY_FRAME,
209 APV_PBU_PREVIEW_FRAME, APV_PBU_DEPTH_FRAME, APV_PBU_ALPHA_FRAME
210 };
211
212 1 static int apv_add_configuration_entry(APVDecoderConfigurationRecord *apvc, int pbu_type)
213 {
214 APVDecoderConfigurationEntry *temp;
215
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 av_assert0(apvc->number_of_configuration_entry < FF_ARRAY_ELEMS(decompose_unit_types));
217 1 temp = av_realloc_array(apvc->configuration_entry,
218 1 apvc->number_of_configuration_entry + 1, sizeof(*apvc->configuration_entry));
219
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!temp)
221 return AVERROR(ENOMEM);
222
223 1 apvc->configuration_entry = temp;
224 1 memset(&apvc->configuration_entry[apvc->number_of_configuration_entry], 0, sizeof(*apvc->configuration_entry));
225 1 apvc->configuration_entry[apvc->number_of_configuration_entry].pbu_type = pbu_type;
226 1 apvc->number_of_configuration_entry++;
227
228 1 return 0;
229 }
230
231 1 static int apv_add_frameinfo(APVDecoderConfigurationEntry *configuration_entry,
232 const APVDecoderFrameInfo *frame_info)
233 {
234 APVDecoderFrameInfo *temp;
235
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (configuration_entry->number_of_frame_info >= UINT8_MAX)
237 return AVERROR(EINVAL);
238
239 1 temp = av_realloc_array(configuration_entry->frame_info,
240 1 configuration_entry->number_of_frame_info + 1, sizeof(*configuration_entry->frame_info));
241
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!temp)
243 return AVERROR(ENOMEM);
244
245 1 configuration_entry->frame_info = temp;
246 1 memcpy(&configuration_entry->frame_info[configuration_entry->number_of_frame_info], frame_info, sizeof(*frame_info));
247 1 configuration_entry->number_of_frame_info++;
248
249 1 return 0;
250 }
251
252 3 int ff_isom_parse_apvc(APVDecoderConfigurationRecord *apvc,
253 const AVPacket *pkt, void *logctx)
254 {
255 APVDecoderFrameInfo frame_info;
256 int ret;
257
258
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (pkt->size < 8 || AV_RB32(pkt->data) != APV_SIGNATURE)
259 /* We can't write a valid apvC from the provided data */
260 return AVERROR_INVALIDDATA;
261
262 3 ret = ff_lavf_cbs_read(apvc->cbc, &apvc->frag, pkt->buf, pkt->data, pkt->size);
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ret < 0) {
264 av_log(logctx, AV_LOG_ERROR, "Failed to parse access unit.\n");
265 return ret;
266 }
267
268 3 memset(&frame_info, 0, sizeof(frame_info));
269 3 frame_info.capture_time_distance_ignored = 1;
270
271
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int i = 0; i < apvc->frag.nb_units; i++) {
272 6 const CodedBitstreamUnit *pbu = &apvc->frag.units[i];
273 int j;
274
275
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 switch (pbu->type) {
276 3 case APV_PBU_PRIMARY_FRAME:
277 case APV_PBU_NON_PRIMARY_FRAME:
278 case APV_PBU_PREVIEW_FRAME:
279 case APV_PBU_DEPTH_FRAME:
280 case APV_PBU_ALPHA_FRAME:
281 3 break;
282 3 default:
283 3 continue;
284 };
285
286 3 const APVRawFrame *frame = pbu->content;
287 3 const APVRawFrameHeader *header = &frame->frame_header;
288 3 const APVRawFrameInfo *info = &header->frame_info;
289 3 int bit_depth = info->bit_depth_minus8 + 8;
290
291
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 if (bit_depth < 8 || bit_depth > 16 || bit_depth % 2)
292 break;
293
294 3 frame_info.profile_idc = info->profile_idc;
295 3 frame_info.level_idc = info->level_idc;
296 3 frame_info.band_idc = info->band_idc;
297
298 3 frame_info.frame_width = info->frame_width;
299 3 frame_info.frame_height =info->frame_height;
300 3 frame_info.chroma_format_idc = info->chroma_format_idc;
301 3 frame_info.bit_depth_minus8 = info->bit_depth_minus8;
302 3 frame_info.capture_time_distance = info->capture_time_distance;
303
304 3 frame_info.color_description_present_flag = header->color_description_present_flag;
305
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (frame_info.color_description_present_flag) {
306 3 frame_info.color_primaries = header->color_primaries;
307 3 frame_info.transfer_characteristics = header->transfer_characteristics;
308 3 frame_info.matrix_coefficients = header->matrix_coefficients;
309 3 frame_info.full_range_flag = header->full_range_flag;
310 } else {
311 frame_info.color_primaries =
312 frame_info.transfer_characteristics =
313 frame_info.matrix_coefficients =
314 frame_info.full_range_flag = 0;
315 }
316
317
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (j = 0; j < apvc->number_of_configuration_entry; j++) {
318 int k;
319
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (apvc->configuration_entry[j].pbu_type != pbu->type)
321 continue;
322
323
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 for (k = 0; k < apvc->configuration_entry[j].number_of_frame_info; k++) {
324
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!memcmp(&apvc->configuration_entry[j].frame_info[k], &frame_info, sizeof(frame_info)))
325 2 break;
326 }
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (k == apvc->configuration_entry[j].number_of_frame_info) {
328 ret = apv_add_frameinfo(&apvc->configuration_entry[j], &frame_info);
329 if (ret < 0)
330 goto end;
331 }
332 2 break;
333 }
334
335
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (j == apvc->number_of_configuration_entry) {
336 1 ret = apv_add_configuration_entry(apvc, pbu->type);
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
338 goto end;
339 1 ret = apv_add_frameinfo(&apvc->configuration_entry[j], &frame_info);
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
341 goto end;
342 }
343 }
344
345 3 ret = 0;
346 3 end:
347 3 ff_lavf_cbs_fragment_reset(&apvc->frag);
348
349 3 return ret;
350 }
351
352 1 int ff_isom_init_apvc(APVDecoderConfigurationRecord **papvc, void *logctx)
353 {
354 1 APVDecoderConfigurationRecord *apvc = av_mallocz(sizeof(*apvc));
355
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!apvc)
357 return AVERROR(ENOMEM);
358
359 1 int ret = ff_lavf_cbs_init(&apvc->cbc, AV_CODEC_ID_APV, logctx);
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0) {
361 av_freep(&apvc);
362 return ret;
363 }
364
365 1 apvc->cbc->decompose_unit_types = decompose_unit_types;
366 1 apvc->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
367
368 1 apvc->configurationVersion = 1;
369
370 1 *papvc = apvc;
371
372 1 return 0;
373 }
374
375 281 void ff_isom_close_apvc(APVDecoderConfigurationRecord **papvc)
376 {
377 281 APVDecoderConfigurationRecord *apvc = *papvc;
378
379
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 1 times.
281 if (!apvc)
380 280 return;
381
382
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = 0; i < apvc->number_of_configuration_entry; i++)
383 1 av_freep(&apvc->configuration_entry[i].frame_info);
384 1 av_freep(&apvc->configuration_entry);
385
386 1 ff_lavf_cbs_fragment_free(&apvc->frag);
387 1 ff_lavf_cbs_close(&apvc->cbc);
388
389 1 av_freep(papvc);
390 }
391