FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_apv.c
Date: 2025-08-19 23:55:23
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 ub(width, name) \
71 xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, )
72 #define us(width, name, range_min, range_max, subs, ...) \
73 xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__)
74 #define ubs(width, name, subs, ...) \
75 xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__)
76
77 #define fixed(width, name, value) do { \
78 av_unused uint32_t fixed_value = value; \
79 xu(width, name, fixed_value, value, value, 0, ); \
80 } while (0)
81
82
83 #if CBS_READ
84 #define READ
85 #define READWRITE read
86 #define RWContext GetBitContext
87 #define FUNC(name) cbs_apv_read_ ## name
88
89 #define xu(width, name, var, range_min, range_max, subs, ...) do { \
90 uint32_t value; \
91 CHECK(CBS_FUNC(read_unsigned)(ctx, rw, width, #name, \
92 SUBSCRIPTS(subs, __VA_ARGS__), \
93 &value, range_min, range_max)); \
94 var = value; \
95 } while (0)
96
97 #define infer(name, value) do { \
98 current->name = value; \
99 } while (0)
100
101 #define byte_alignment(rw) (get_bits_count(rw) % 8)
102
103 #include "cbs_apv_syntax_template.c"
104
105 #undef READ
106 #undef READWRITE
107 #undef RWContext
108 #undef FUNC
109 #undef xu
110 #undef infer
111 #undef byte_alignment
112 #endif // CBS_READ
113
114 #if CBS_WRITE
115 #define WRITE
116 #define READWRITE write
117 #define RWContext PutBitContext
118 #define FUNC(name) cbs_apv_write_ ## name
119
120 #define xu(width, name, var, range_min, range_max, subs, ...) do { \
121 uint32_t value = var; \
122 CHECK(CBS_FUNC(write_unsigned)(ctx, rw, width, #name, \
123 SUBSCRIPTS(subs, __VA_ARGS__), \
124 value, range_min, range_max)); \
125 } while (0)
126
127 #define infer(name, value) do { \
128 if (current->name != (value)) { \
129 av_log(ctx->log_ctx, AV_LOG_ERROR, \
130 "%s does not match inferred value: " \
131 "%"PRId64", but should be %"PRId64".\n", \
132 #name, (int64_t)current->name, (int64_t)(value)); \
133 return AVERROR_INVALIDDATA; \
134 } \
135 } while (0)
136
137 #define byte_alignment(rw) (put_bits_count(rw) % 8)
138
139 #include "cbs_apv_syntax_template.c"
140
141 #undef WRITE
142 #undef READWRITE
143 #undef RWContext
144 #undef FUNC
145 #undef xu
146 #undef infer
147 #undef byte_alignment
148 #endif // CBS_WRITE
149
150
151 45 static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
152 CodedBitstreamFragment *frag,
153 int header)
154 {
155 #if CBS_READ
156 45 uint8_t *data = frag->data;
157 45 size_t size = frag->data_size;
158 uint32_t signature;
159 int err, trace;
160
161
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) {
162 // Ignore empty or extradata fragments.
163 6 return 0;
164 }
165
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (frag->data_size < 4) {
167 // Too small to be a valid fragment.
168 return AVERROR_INVALIDDATA;
169 }
170
171 // Don't include parsing here in trace output.
172 39 trace = ctx->trace_enable;
173 39 ctx->trace_enable = 0;
174
175 39 signature = AV_RB32(data);
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (signature != APV_SIGNATURE) {
177 av_log(ctx->log_ctx, AV_LOG_ERROR,
178 "Invalid APV access unit: bad signature %08x.\n",
179 signature);
180 err = AVERROR_INVALIDDATA;
181 goto fail;
182 }
183 39 data += 4;
184 39 size -= 4;
185
186
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 39 times.
117 while (size > 0) {
187 GetBitContext gbc;
188 uint32_t pbu_size;
189 APVRawPBUHeader pbu_header;
190
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (size < 8) {
192 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
193 "fragment too short (%"SIZE_SPECIFIER" bytes).\n",
194 size);
195 err = AVERROR_INVALIDDATA;
196 goto fail;
197 }
198
199 78 pbu_size = AV_RB32(data);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (pbu_size < 8) {
201 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
202 "pbu_size too small (%"PRIu32" bytes).\n",
203 pbu_size);
204 err = AVERROR_INVALIDDATA;
205 goto fail;
206 }
207
208 78 data += 4;
209 78 size -= 4;
210
211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (pbu_size > size) {
212 av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
213 "pbu_size too large (%"PRIu32" bytes).\n",
214 pbu_size);
215 err = AVERROR_INVALIDDATA;
216 goto fail;
217 }
218
219 78 init_get_bits(&gbc, data, 8 * pbu_size);
220
221 78 err = cbs_apv_read_pbu_header(ctx, &gbc, &pbu_header);
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (err < 0)
223 goto fail;
224
225 // Could select/skip frames based on type/group_id here.
226
227 78 err = CBS_FUNC(append_unit_data)(frag, pbu_header.pbu_type,
228 data, pbu_size, frag->data_ref);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (err < 0)
230 goto fail;
231
232 78 data += pbu_size;
233 78 size -= pbu_size;
234 }
235
236 39 err = 0;
237 39 fail:
238 39 ctx->trace_enable = trace;
239 39 return err;
240 #else
241 return AVERROR(ENOSYS);
242 #endif
243 }
244
245 57 static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
246 CodedBitstreamUnit *unit)
247 {
248 #if CBS_READ
249 GetBitContext gbc;
250 int err;
251
252 57 err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (err < 0)
254 return err;
255
256 57 err = CBS_FUNC(alloc_unit_content)(ctx, unit);
257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (err < 0)
258 return err;
259
260
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) {
261 39 case APV_PBU_PRIMARY_FRAME:
262 case APV_PBU_NON_PRIMARY_FRAME:
263 case APV_PBU_PREVIEW_FRAME:
264 case APV_PBU_DEPTH_FRAME:
265 case APV_PBU_ALPHA_FRAME:
266 {
267 39 APVRawFrame *frame = unit->content;
268
269 39 err = cbs_apv_read_frame(ctx, &gbc, frame);
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (err < 0)
271 return err;
272
273 // Each tile inside the frame has pointers into the unit
274 // data buffer; make a single reference here for all of
275 // them together.
276 39 frame->tile_data_ref = av_buffer_ref(unit->data_ref);
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (!frame->tile_data_ref)
278 return AVERROR(ENOMEM);
279 }
280 39 break;
281 case APV_PBU_ACCESS_UNIT_INFORMATION:
282 {
283 err = cbs_apv_read_au_info(ctx, &gbc, unit->content);
284 if (err < 0)
285 return err;
286 }
287 break;
288 18 case APV_PBU_METADATA:
289 {
290 18 err = cbs_apv_read_metadata(ctx, &gbc, unit->content);
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (err < 0)
292 return err;
293 }
294 18 break;
295 case APV_PBU_FILLER:
296 {
297 err = cbs_apv_read_filler(ctx, &gbc, unit->content);
298 if (err < 0)
299 return err;
300 }
301 break;
302 default:
303 return AVERROR(ENOSYS);
304 }
305
306 57 return 0;
307 #else
308 return AVERROR(ENOSYS);
309 #endif
310 }
311
312 12 static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
313 CodedBitstreamUnit *unit,
314 PutBitContext *pbc)
315 {
316 #if CBS_WRITE
317 int err;
318
319
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) {
320 6 case APV_PBU_PRIMARY_FRAME:
321 case APV_PBU_NON_PRIMARY_FRAME:
322 case APV_PBU_PREVIEW_FRAME:
323 case APV_PBU_DEPTH_FRAME:
324 case APV_PBU_ALPHA_FRAME:
325 {
326 6 APVRawFrame *frame = unit->content;
327
328 6 err = cbs_apv_write_frame(ctx, pbc, frame);
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (err < 0)
330 return err;
331 }
332 6 break;
333 case APV_PBU_ACCESS_UNIT_INFORMATION:
334 {
335 err = cbs_apv_write_au_info(ctx, pbc, unit->content);
336 if (err < 0)
337 return err;
338 }
339 break;
340 6 case APV_PBU_METADATA:
341 {
342 6 err = cbs_apv_write_metadata(ctx, pbc, unit->content);
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (err < 0)
344 return err;
345 }
346 6 break;
347 case APV_PBU_FILLER:
348 {
349 err = cbs_apv_write_filler(ctx, pbc, unit->content);
350 if (err < 0)
351 return err;
352 }
353 break;
354 default:
355 return AVERROR(ENOSYS);
356 }
357
358 12 return 0;
359 #else
360 return AVERROR(ENOSYS);
361 #endif
362 }
363
364 6 static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx,
365 CodedBitstreamFragment *frag)
366 {
367 #if CBS_WRITE
368 6 size_t size = 4, pos;
369
370
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (int i = 0; i < frag->nb_units; i++)
371 12 size += frag->units[i].data_size + 4;
372
373 6 frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
374
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!frag->data_ref)
375 return AVERROR(ENOMEM);
376 6 frag->data = frag->data_ref->data;
377 6 memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
378
379 6 AV_WB32(frag->data, APV_SIGNATURE);
380 6 pos = 4;
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 AV_WB32(frag->data + pos, frag->units[i].data_size);
383 12 pos += 4;
384
385 12 memcpy(frag->data + pos, frag->units[i].data,
386 12 frag->units[i].data_size);
387 12 pos += frag->units[i].data_size;
388 }
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 av_assert0(pos == size);
390 6 frag->data_size = size;
391
392 6 return 0;
393 #else
394 return AVERROR(ENOSYS);
395 #endif
396 }
397
398
399 18 static void cbs_apv_free_metadata(AVRefStructOpaque unused, void *content)
400 {
401 18 APVRawMetadata *md = content;
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 av_assert0(md->pbu_header.pbu_type == APV_PBU_METADATA);
403
404
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 18 times.
54 for (int i = 0; i < md->metadata_count; i++) {
405 36 APVRawMetadataPayload *pl = &md->payloads[i];
406
407
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 switch (pl->payload_type) {
408 case APV_METADATA_MDCV:
409 case APV_METADATA_CLL:
410 case APV_METADATA_FILLER:
411 break;
412 case APV_METADATA_ITU_T_T35:
413 av_buffer_unref(&pl->itu_t_t35.data_ref);
414 break;
415 36 case APV_METADATA_USER_DEFINED:
416 36 av_buffer_unref(&pl->user_defined.data_ref);
417 36 break;
418 default:
419 av_buffer_unref(&pl->undefined.data_ref);
420 }
421 }
422 18 }
423
424 static const CodedBitstreamUnitTypeDescriptor cbs_apv_unit_types[] = {
425 {
426 .nb_unit_types = CBS_UNIT_TYPE_RANGE,
427 .unit_type.range = {
428 .start = APV_PBU_PRIMARY_FRAME,
429 .end = APV_PBU_ALPHA_FRAME,
430 },
431 .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS,
432 .content_size = sizeof(APVRawFrame),
433 .type.ref = {
434 .nb_offsets = 1,
435 .offsets = { offsetof(APVRawFrame, tile_data_ref) -
436 sizeof(void*) },
437 },
438 },
439
440 CBS_UNIT_TYPE_COMPLEX(APV_PBU_METADATA, APVRawMetadata,
441 &cbs_apv_free_metadata),
442
443 CBS_UNIT_TYPE_POD(APV_PBU_ACCESS_UNIT_INFORMATION, APVRawAUInfo),
444 CBS_UNIT_TYPE_POD(APV_PBU_FILLER, APVRawFiller),
445
446 CBS_UNIT_TYPE_END_OF_LIST
447 };
448
449 const CodedBitstreamType CBS_FUNC(type_apv) = {
450 .codec_id = AV_CODEC_ID_APV,
451
452 .priv_data_size = sizeof(CodedBitstreamAPVContext),
453
454 .unit_types = cbs_apv_unit_types,
455
456 .split_fragment = &cbs_apv_split_fragment,
457 .read_unit = &cbs_apv_read_unit,
458 .write_unit = &cbs_apv_write_unit,
459 .assemble_fragment = &cbs_apv_assemble_fragment,
460 };
461