FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_apv.c
Date: 2025-12-07 22:02:57
Exec Total Coverage
Lines: 104 164 63.4%
Functions: 7 7 100.0%
Branches: 37 76 48.7%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libavutil/mem.h"
20 #include "cbs.h"
21 #include "cbs_internal.h"
22 #include "cbs_apv.h"
23
24
25 45 static int cbs_apv_get_num_comp(const APVRawFrameHeader *fh)
26 {
27
2/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
45 switch (fh->frame_info.chroma_format_idc) {
28 17 case APV_CHROMA_FORMAT_400:
29 17 return 1;
30 28 case APV_CHROMA_FORMAT_422:
31 case APV_CHROMA_FORMAT_444:
32 28 return 3;
33 case APV_CHROMA_FORMAT_4444:
34 return 4;
35 default:
36 av_assert0(0 && "Invalid chroma_format_idc");
37 }
38 }
39
40 45 static void cbs_apv_derive_tile_info(CodedBitstreamContext *ctx,
41 const APVRawFrameHeader *fh)
42 {
43 45 CodedBitstreamAPVContext *priv = ctx->priv_data;
44 45 int frame_width_in_mbs = (fh->frame_info.frame_width + 15) / 16;
45 45 int frame_height_in_mbs = (fh->frame_info.frame_height + 15) / 16;
46 45 int tile_cols = (frame_width_in_mbs + fh->tile_info.tile_width_in_mbs - 1) / fh->tile_info.tile_width_in_mbs;
47 45 int tile_rows = (frame_height_in_mbs + fh->tile_info.tile_height_in_mbs - 1) / fh->tile_info.tile_height_in_mbs;
48
49
2/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
45 av_assert0(tile_cols <= APV_MAX_TILE_COLS && tile_rows <= APV_MAX_TILE_ROWS);
50
51 45 priv->num_tiles = tile_cols * tile_rows;
52 45 }
53
54
55 #define HEADER(name) do { \
56 CBS_FUNC(trace_header)(ctx, name); \
57 } while (0)
58
59 #define CHECK(call) do { \
60 err = (call); \
61 if (err < 0) \
62 return err; \
63 } while (0)
64
65 #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
66
67
68 #define u(width, name, range_min, range_max) \
69 xu(width, name, current->name, range_min, range_max, 0, )
70 #define us(width, name, range_min, range_max, subs, ...) \
71 xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__)
72 #define ubs(width, name, subs, ...) \
73 xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__)
74
75 #define fixed(width, name, value) do { \
76 av_unused uint32_t fixed_value = value; \
77 xu(width, name, fixed_value, value, value, 0, ); \
78 } while (0)
79
80
81 #if CBS_READ
82 #define READ
83 #define READWRITE read
84 #define RWContext GetBitContext
85 #define FUNC(name) cbs_apv_read_ ## name
86
87 #define ub(width, name) do { \
88 uint32_t value; \
89 CHECK(CBS_FUNC(read_simple_unsigned)(ctx, rw, width, #name, \
90 &value)); \
91 current->name = value; \
92 } while (0)
93 #define xu(width, name, var, range_min, range_max, subs, ...) do { \
94 uint32_t value; \
95 CHECK(CBS_FUNC(read_unsigned)(ctx, rw, width, #name, \
96 SUBSCRIPTS(subs, __VA_ARGS__), \
97 &value, range_min, range_max)); \
98 var = value; \
99 } while (0)
100
101 #define infer(name, value) do { \
102 current->name = value; \
103 } while (0)
104
105 #define byte_alignment(rw) (get_bits_count(rw) % 8)
106
107 #include "cbs_apv_syntax_template.c"
108
109 #undef READ
110 #undef READWRITE
111 #undef RWContext
112 #undef FUNC
113 #undef ub
114 #undef xu
115 #undef infer
116 #undef byte_alignment
117 #endif // CBS_READ
118
119 #if CBS_WRITE
120 #define WRITE
121 #define READWRITE write
122 #define RWContext PutBitContext
123 #define FUNC(name) cbs_apv_write_ ## name
124
125 #define ub(width, name) do { \
126 uint32_t value = current->name; \
127 CHECK(CBS_FUNC(write_simple_unsigned)(ctx, rw, width, #name, \
128 value)); \
129 } while (0)
130 #define xu(width, name, var, range_min, range_max, subs, ...) do { \
131 uint32_t value = var; \
132 CHECK(CBS_FUNC(write_unsigned)(ctx, rw, width, #name, \
133 SUBSCRIPTS(subs, __VA_ARGS__), \
134 value, range_min, range_max)); \
135 } while (0)
136
137 #define infer(name, value) do { \
138 if (current->name != (value)) { \
139 av_log(ctx->log_ctx, AV_LOG_ERROR, \
140 "%s does not match inferred value: " \
141 "%"PRId64", but should be %"PRId64".\n", \
142 #name, (int64_t)current->name, (int64_t)(value)); \
143 return AVERROR_INVALIDDATA; \
144 } \
145 } while (0)
146
147 #define byte_alignment(rw) (put_bits_count(rw) % 8)
148
149 #include "cbs_apv_syntax_template.c"
150
151 #undef WRITE
152 #undef READWRITE
153 #undef RWContext
154 #undef FUNC
155 #undef ub
156 #undef xu
157 #undef infer
158 #undef byte_alignment
159 #endif // CBS_WRITE
160
161
162 45 static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
163 CodedBitstreamFragment *frag,
164 int header)
165 {
166 #if CBS_READ
167 45 uint8_t *data = frag->data;
168 45 size_t size = frag->data_size;
169 uint32_t signature;
170 int err, trace;
171
172
3/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 39 times.
45 if (header || !frag->data_size) {
173 // Ignore empty or extradata fragments.
174 6 return 0;
175 }
176
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (frag->data_size < 4) {
178 // Too small to be a valid fragment.
179 return AVERROR_INVALIDDATA;
180 }
181
182 // Don't include parsing here in trace output.
183 39 trace = ctx->trace_enable;
184 39 ctx->trace_enable = 0;
185
186 39 signature = AV_RB32(data);
187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (signature != APV_SIGNATURE) {
188 av_log(ctx->log_ctx, AV_LOG_ERROR,
189 "Invalid APV access unit: bad signature %08x.\n",
190 signature);
191 err = AVERROR_INVALIDDATA;
192 goto fail;
193 }
194 39 data += 4;
195 39 size -= 4;
196
197
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 39 times.
117 while (size > 0) {
198 GetBitContext gbc;
199 uint32_t pbu_size;
200 APVRawPBUHeader pbu_header;
201
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (size < 8) {
203 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
204 "fragment too short (%zu bytes).\n",
205 size);
206 err = AVERROR_INVALIDDATA;
207 goto fail;
208 }
209
210 78 pbu_size = AV_RB32(data);
211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (pbu_size < 8) {
212 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
213 "pbu_size too small (%"PRIu32" bytes).\n",
214 pbu_size);
215 err = AVERROR_INVALIDDATA;
216 goto fail;
217 }
218
219 78 data += 4;
220 78 size -= 4;
221
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (pbu_size > size) {
223 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
224 "pbu_size too large (%"PRIu32" bytes).\n",
225 pbu_size);
226 err = AVERROR_INVALIDDATA;
227 goto fail;
228 }
229
230 78 init_get_bits(&gbc, data, 8 * pbu_size);
231
232 78 err = cbs_apv_read_pbu_header(ctx, &gbc, &pbu_header);
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (err < 0)
234 goto fail;
235
236 // Could select/skip frames based on type/group_id here.
237
238 78 err = CBS_FUNC(append_unit_data)(frag, pbu_header.pbu_type,
239 data, pbu_size, frag->data_ref);
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (err < 0)
241 goto fail;
242
243 78 data += pbu_size;
244 78 size -= pbu_size;
245 }
246
247 39 err = 0;
248 39 fail:
249 39 ctx->trace_enable = trace;
250 39 return err;
251 #else
252 return AVERROR(ENOSYS);
253 #endif
254 }
255
256 57 static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
257 CodedBitstreamUnit *unit)
258 {
259 #if CBS_READ
260 GetBitContext gbc;
261 int err;
262
263 57 err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (err < 0)
265 return err;
266
267 57 err = CBS_FUNC(alloc_unit_content)(ctx, unit);
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (err < 0)
269 return err;
270
271
2/5
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
57 switch (unit->type) {
272 39 case APV_PBU_PRIMARY_FRAME:
273 case APV_PBU_NON_PRIMARY_FRAME:
274 case APV_PBU_PREVIEW_FRAME:
275 case APV_PBU_DEPTH_FRAME:
276 case APV_PBU_ALPHA_FRAME:
277 {
278 39 APVRawFrame *frame = unit->content;
279
280 39 err = cbs_apv_read_frame(ctx, &gbc, frame);
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (err < 0)
282 return err;
283
284 // Each tile inside the frame has pointers into the unit
285 // data buffer; make a single reference here for all of
286 // them together.
287 39 frame->tile_data_ref = av_buffer_ref(unit->data_ref);
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (!frame->tile_data_ref)
289 return AVERROR(ENOMEM);
290 }
291 39 break;
292 case APV_PBU_ACCESS_UNIT_INFORMATION:
293 {
294 err = cbs_apv_read_au_info(ctx, &gbc, unit->content);
295 if (err < 0)
296 return err;
297 }
298 break;
299 18 case APV_PBU_METADATA:
300 {
301 18 err = cbs_apv_read_metadata(ctx, &gbc, unit->content);
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (err < 0)
303 return err;
304 }
305 18 break;
306 case APV_PBU_FILLER:
307 {
308 err = cbs_apv_read_filler(ctx, &gbc, unit->content);
309 if (err < 0)
310 return err;
311 }
312 break;
313 default:
314 return AVERROR(ENOSYS);
315 }
316
317 57 return 0;
318 #else
319 return AVERROR(ENOSYS);
320 #endif
321 }
322
323 12 static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
324 CodedBitstreamUnit *unit,
325 PutBitContext *pbc)
326 {
327 #if CBS_WRITE
328 int err;
329
330
2/5
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 switch (unit->type) {
331 6 case APV_PBU_PRIMARY_FRAME:
332 case APV_PBU_NON_PRIMARY_FRAME:
333 case APV_PBU_PREVIEW_FRAME:
334 case APV_PBU_DEPTH_FRAME:
335 case APV_PBU_ALPHA_FRAME:
336 {
337 6 APVRawFrame *frame = unit->content;
338
339 6 err = cbs_apv_write_frame(ctx, pbc, frame);
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (err < 0)
341 return err;
342 }
343 6 break;
344 case APV_PBU_ACCESS_UNIT_INFORMATION:
345 {
346 err = cbs_apv_write_au_info(ctx, pbc, unit->content);
347 if (err < 0)
348 return err;
349 }
350 break;
351 6 case APV_PBU_METADATA:
352 {
353 6 err = cbs_apv_write_metadata(ctx, pbc, unit->content);
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (err < 0)
355 return err;
356 }
357 6 break;
358 case APV_PBU_FILLER:
359 {
360 err = cbs_apv_write_filler(ctx, pbc, unit->content);
361 if (err < 0)
362 return err;
363 }
364 break;
365 default:
366 return AVERROR(ENOSYS);
367 }
368
369 12 return 0;
370 #else
371 return AVERROR(ENOSYS);
372 #endif
373 }
374
375 6 static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx,
376 CodedBitstreamFragment *frag)
377 {
378 #if CBS_WRITE
379 6 size_t size = 4, pos;
380
381
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (int i = 0; i < frag->nb_units; i++)
382 12 size += frag->units[i].data_size + 4;
383
384 6 frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!frag->data_ref)
386 return AVERROR(ENOMEM);
387 6 frag->data = frag->data_ref->data;
388 6 memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
389
390 6 AV_WB32(frag->data, APV_SIGNATURE);
391 6 pos = 4;
392
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (int i = 0; i < frag->nb_units; i++) {
393 12 AV_WB32(frag->data + pos, frag->units[i].data_size);
394 12 pos += 4;
395
396 12 memcpy(frag->data + pos, frag->units[i].data,
397 12 frag->units[i].data_size);
398 12 pos += frag->units[i].data_size;
399 }
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 av_assert0(pos == size);
401 6 frag->data_size = size;
402
403 6 return 0;
404 #else
405 return AVERROR(ENOSYS);
406 #endif
407 }
408
409
410 18 static void cbs_apv_free_metadata(AVRefStructOpaque unused, void *content)
411 {
412 18 APVRawMetadata *md = content;
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 av_assert0(md->pbu_header.pbu_type == APV_PBU_METADATA);
414
415
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 18 times.
54 for (int i = 0; i < md->metadata_count; i++) {
416 36 APVRawMetadataPayload *pl = &md->payloads[i];
417
418
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 switch (pl->payload_type) {
419 case APV_METADATA_MDCV:
420 case APV_METADATA_CLL:
421 case APV_METADATA_FILLER:
422 break;
423 case APV_METADATA_ITU_T_T35:
424 av_buffer_unref(&pl->itu_t_t35.data_ref);
425 break;
426 36 case APV_METADATA_USER_DEFINED:
427 36 av_buffer_unref(&pl->user_defined.data_ref);
428 36 break;
429 default:
430 av_buffer_unref(&pl->undefined.data_ref);
431 }
432 }
433 18 }
434
435 static CodedBitstreamUnitTypeDescriptor cbs_apv_unit_types[] = {
436 {
437 .nb_unit_types = CBS_UNIT_TYPE_RANGE,
438 .unit_type.range = {
439 .start = APV_PBU_PRIMARY_FRAME,
440 .end = APV_PBU_ALPHA_FRAME,
441 },
442 .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS,
443 .content_size = sizeof(APVRawFrame),
444 .type.ref = {
445 .nb_offsets = 1,
446 .offsets = { offsetof(APVRawFrame, tile_data_ref) -
447 sizeof(void*) },
448 },
449 },
450
451 CBS_UNIT_TYPE_COMPLEX(APV_PBU_METADATA, APVRawMetadata,
452 &cbs_apv_free_metadata),
453
454 CBS_UNIT_TYPE_POD(APV_PBU_ACCESS_UNIT_INFORMATION, APVRawAUInfo),
455 CBS_UNIT_TYPE_POD(APV_PBU_FILLER, APVRawFiller),
456
457 CBS_UNIT_TYPE_END_OF_LIST
458 };
459
460 const CodedBitstreamType CBS_FUNC(type_apv) = {
461 .codec_id = AV_CODEC_ID_APV,
462
463 .priv_data_size = sizeof(CodedBitstreamAPVContext),
464
465 .unit_types = cbs_apv_unit_types,
466
467 .split_fragment = &cbs_apv_split_fragment,
468 .read_unit = &cbs_apv_read_unit,
469 .write_unit = &cbs_apv_write_unit,
470 .assemble_fragment = &cbs_apv_assemble_fragment,
471 };
472