FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/lcevc.c
Date: 2026-10-30 17:37:53
Exec Total Coverage
Lines: 130 212 61.3%
Functions: 3 3 100.0%
Branches: 42 105 40.0%

Line Branch Exec Source
1 /*
2 * LCEVC helper functions for muxers
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 "libavutil/error.h"
22 #include "libavutil/intreadwrite.h"
23 #include "libavutil/mem.h"
24 #include "libavcodec/bytestream.h"
25 #include "libavcodec/h2645_parse.h"
26 #include "libavcodec/lcevc.h"
27 #include "libavcodec/lcevctab.h"
28 #include "libavcodec/lcevc_parse.h"
29 #include "avio.h"
30 #include "avio_internal.h"
31 #include "lcevc.h"
32
33 /**
34 * Rewrite the NALu stripping the unneeded blocks.
35 * Given that length fields coded inside the NALu are not aware of any emulation_3bytes
36 * present in the bitstream, we need to keep track of the raw buffer as we navigate
37 * the stripped buffer in order to write proper NALu sizes.
38 */
39 18 static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb,
40 const H2645NAL *nal)
41 {
42 GetByteContext gbc, raw_gbc;
43 18 int64_t start = avio_tell(pb), end;
44 18 int sc = 0, gc = 0;
45 18 int skipped_byte_pos = 0, nalu_length = 3;
46
47 18 bytestream2_init(&gbc, nal->data, nal->size);
48 18 bytestream2_init(&raw_gbc, nal->raw_data, nal->raw_size);
49 18 avio_wb16(pb, 0); // size placeholder
50 18 avio_wb16(pb, bytestream2_get_be16(&gbc)); // nal_unit_header
51 18 bytestream2_skip(&raw_gbc, 2);
52
53
5/6
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 36 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 18 times.
72 while (bytestream2_get_bytes_left(&gbc) > 1 && (!sc || !gc)) {
54 GetBitContext gb;
55 uint64_t payload_size;
56 int payload_size_type, payload_type;
57 int block_size, raw_block_size, block_end;
58
59 54 init_get_bits8(&gb, gbc.buffer, bytestream2_get_bytes_left(&gbc));
60
61 54 payload_size_type = get_bits(&gb, 3);
62 54 payload_type = get_bits(&gb, 5);
63 54 payload_size = payload_size_type;
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (payload_size_type == 6)
65 ✗ return AVERROR_PATCHWELCOME;
66
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 36 times.
54 if (payload_size_type == 7)
67 18 payload_size = get_mb(&gb);
68
69
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
54 if (payload_size > INT_MAX - (get_bits_count(&gb) >> 3))
70 ✗ return AVERROR_INVALIDDATA;
71
72 54 block_size = raw_block_size = payload_size + (get_bits_count(&gb) >> 3);
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
54 if (block_size >= bytestream2_get_bytes_left(&gbc))
74 ✗ return AVERROR_INVALIDDATA;
75
76 54 block_end = bytestream2_tell(&gbc) + block_size;
77 // Take into account removed emulation 3bytes, as payload_size in
78 // the bitstream is not aware of them.
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 for (; skipped_byte_pos < nal->skipped_bytes; skipped_byte_pos++) {
80 ✗ if (nal->skipped_bytes_pos[skipped_byte_pos] >= block_end)
81 ✗ break;
82 ✗ raw_block_size++;
83 }
84
85
3/5
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
54 switch (payload_type) {
86 18 case 0:
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (sc)
88 ✗ break;
89
90 18 lvcc->profile_idc = get_bits(&gb, 4);
91 18 lvcc->level_idc = get_bits(&gb, 4);
92 18 lvcc->sublevel_idc = get_bits(&gb, 2);
93
94 18 avio_write(pb, raw_gbc.buffer, raw_block_size);
95 18 nalu_length += raw_block_size;
96 18 sc = 1;
97 18 break;
98 18 case 1: {
99 int resolution_type, bit_depth;
100
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (gc)
102 ✗ break;
103
104 18 lvcc->processed_planes_type_flag = get_bits1(&gb);
105 18 resolution_type = get_bits(&gb, 6);
106
107 18 skip_bits1(&gb);
108 18 lvcc->chroma_format_idc = get_bits(&gb, 2);
109
110 18 skip_bits(&gb, 2);
111 18 bit_depth = get_bits(&gb, 2) * 2; // enhancement_depth_type
112 18 lvcc->bit_depth_luma_minus8 = bit_depth;
113 18 lvcc->bit_depth_chroma_minus8 = bit_depth;
114
115
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (resolution_type < 63) {
116 18 lvcc->pic_width_in_luma_samples = ff_lcevc_resolution_type[resolution_type].width;
117 18 lvcc->pic_height_in_luma_samples = ff_lcevc_resolution_type[resolution_type].height;
118 } else {
119 int upsample_type, tile_dimensions_type;
120 int temporal_step_width_modifier_signalled_flag, level1_filtering_signalled_flag;
121 // Skip syntax elements until we get to the custom dimension ones
122 ✗ temporal_step_width_modifier_signalled_flag = get_bits1(&gb);
123 ✗ skip_bits(&gb, 3);
124 ✗ upsample_type = get_bits(&gb, 3);
125 ✗ level1_filtering_signalled_flag = get_bits1(&gb);
126 ✗ skip_bits(&gb, 4);
127 ✗ tile_dimensions_type = get_bits(&gb, 2);
128 ✗ skip_bits(&gb, 4);
129 ✗ if (lvcc->processed_planes_type_flag)
130 ✗ skip_bits(&gb, 4);
131 ✗ if (temporal_step_width_modifier_signalled_flag)
132 ✗ skip_bits(&gb, 8);
133 ✗ if (upsample_type)
134 ✗ skip_bits_long(&gb, 64);
135 ✗ if (level1_filtering_signalled_flag)
136 ✗ skip_bits(&gb, 8);
137 ✗ if (tile_dimensions_type) {
138 ✗ if (tile_dimensions_type == 3)
139 ✗ skip_bits_long(&gb, 32);
140 ✗ skip_bits(&gb, 8);
141 }
142
143 ✗ lvcc->pic_width_in_luma_samples = get_bits(&gb, 16);
144 ✗ lvcc->pic_height_in_luma_samples = get_bits(&gb, 16);
145 }
146
147
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (!lvcc->pic_width_in_luma_samples || !lvcc->pic_height_in_luma_samples)
148 break;
149
150 18 avio_write(pb, raw_gbc.buffer, raw_block_size);
151 18 nalu_length += raw_block_size;
152 18 gc = 1;
153 18 break;
154 }
155 ✗ case 2: {
156 ✗ int no_enhancement_bit_flag = get_bits1(&gb);
157
158 ✗ skip_bits(&gb, 4);
159 ✗ lvcc->picture_type_bit_flag = get_bits1(&gb);
160 ✗ skip_bits(&gb, 2);
161
162 ✗ if (!no_enhancement_bit_flag)
163 ✗ skip_bits(&gb, 16);
164
165 ✗ lvcc->field_type_bit_flag = lvcc->picture_type_bit_flag ? get_bits1(&gb) : 0;
166 ✗ break;
167 }
168 18 case 5:
169 18 avio_write(pb, raw_gbc.buffer, raw_block_size);
170 18 nalu_length += raw_block_size;
171 18 break;
172 ✗ default:
173 ✗ break;
174 }
175
176 54 bytestream2_skip(&gbc, block_size);
177 54 bytestream2_skip(&raw_gbc, raw_block_size);
178 }
179
180
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (!sc || !gc)
181 ✗ return AVERROR_INVALIDDATA;
182
183 18 avio_w8(pb, 0x80); // rbsp_alignment bits
184
185 18 end = avio_tell(pb);
186 18 avio_seek(pb, start, SEEK_SET);
187 18 avio_wb16(pb, nalu_length);
188 18 avio_seek(pb, end, SEEK_SET);
189
190 18 return 0;
191 }
192
193 17 int ff_lcvec_parse_config_record(LCEVCDecoderConfigurationRecord *lvcc,
194 const uint8_t *buf, int size, void *logctx)
195 {
196 17 H2645Packet h2645_pkt = { 0 };
197 AVIOContext *pb;
198 int ret;
199 int found;
200
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (size <= 0)
202 ✗ return AVERROR_INVALIDDATA;
203
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (buf[0] == 1) {
205 GetBitContext gb;
206
207 ✗ if (size < 13)
208 ✗ return AVERROR_INVALIDDATA;
209
210 ✗ ret = init_get_bits8(&gb, buf, 13);
211 ✗ if (ret < 0)
212 ✗ return ret;
213
214 ✗ memset(lvcc, 0, sizeof(*lvcc));
215
216 ✗ skip_bits(&gb, 8);
217 ✗ lvcc->profile_idc = get_bits(&gb, 8);
218 ✗ lvcc->level_idc = get_bits(&gb, 8);
219 ✗ lvcc->chroma_format_idc = get_bits(&gb, 2);
220 ✗ lvcc->bit_depth_luma_minus8 = get_bits(&gb, 3);
221 ✗ lvcc->bit_depth_chroma_minus8 = get_bits(&gb, 3);
222 ✗ skip_bits(&gb, 8);
223 ✗ lvcc->pic_width_in_luma_samples = get_bits_long(&gb, 32);
224 ✗ lvcc->pic_height_in_luma_samples = get_bits_long(&gb, 32);
225
226 ✗ return 0;
227 }
228
229 17 ret = ffio_open_null_buf(&pb);
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
231 ✗ return ret;
232
233 17 ret = ff_h2645_packet_split(&h2645_pkt, buf, size, logctx, 0, AV_CODEC_ID_LCEVC, 0);
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
235 ✗ goto fail;
236
237 /* look for IDR or NON_IDR */
238 17 found = 0;
239
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 for (int i = 0; i < h2645_pkt.nb_nals; i++) {
240 17 const H2645NAL *nal = &h2645_pkt.nals[i];
241
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (nal->type == LCEVC_IDR_NUT ||
243 ✗ nal->type == LCEVC_NON_IDR_NUT) {
244 17 ret = write_nalu(lvcc, pb, nal);
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
246 ✗ goto fail;
247 17 found = 1;
248 }
249 }
250
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!found) {
252 ✗ ret = AVERROR_INVALIDDATA;
253 ✗ goto fail;
254 }
255
256 17 ret = 0;
257 17 fail:
258 17 ffio_close_null_buf(pb);
259 17 ff_h2645_packet_uninit(&h2645_pkt);
260
261 17 return ret;
262 }
263
264 1 int ff_isom_write_lvcc(AVIOContext *pb, const uint8_t *data, int len, void *logctx)
265 {
266 1 LCEVCDecoderConfigurationRecord lvcc = { 0 };
267 1 AVIOContext *idr_pb = NULL, *nidr_pb = NULL;
268 1 H2645Packet h2645_pkt = { 0 };
269 uint8_t *idr, *nidr;
270 1 uint32_t idr_size = 0, nidr_size = 0;
271 1 int ret, nb_idr = 0, nb_nidr = 0;
272
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (len <= 6)
274 ✗ return AVERROR_INVALIDDATA;
275
276 /* check for start code */
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (AV_RB32(data) != 0x00000001 &&
278 ✗ AV_RB24(data) != 0x000001) {
279 ✗ avio_write(pb, data, len);
280 ✗ return 0;
281 }
282
283 1 ret = ff_h2645_packet_split(&h2645_pkt, data, len, logctx, 0, AV_CODEC_ID_LCEVC, 0);
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
285 ✗ return ret;
286
287 1 ret = avio_open_dyn_buf(&idr_pb);
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
289 ✗ goto fail;
290 1 ret = avio_open_dyn_buf(&nidr_pb);
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
292 ✗ goto fail;
293
294 /* look for IDR or NON_IDR */
295
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = 0; i < h2645_pkt.nb_nals; i++) {
296 1 const H2645NAL *nal = &h2645_pkt.nals[i];
297
298
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (nal->type == LCEVC_IDR_NUT) {
299 1 nb_idr++;
300
301 1 ret = write_nalu(&lvcc, idr_pb, nal);
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
303 ✗ goto fail;
304 ✗ } else if (nal->type == LCEVC_NON_IDR_NUT) {
305 ✗ nb_nidr++;
306
307 ✗ ret = write_nalu(&lvcc, nidr_pb, nal);
308 ✗ if (ret < 0)
309 ✗ goto fail;
310 }
311 }
312 1 idr_size = avio_get_dyn_buf(idr_pb, &idr);
313 1 nidr_size = avio_get_dyn_buf(nidr_pb, &nidr);
314
315
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!idr_size && !nidr_size) {
316 ✗ ret = AVERROR_INVALIDDATA;
317 ✗ goto fail;
318 }
319
320 1 avio_w8(pb, 1); /* version */
321 1 avio_w8(pb, lvcc.profile_idc);
322 1 avio_w8(pb, lvcc.level_idc);
323 1 avio_w8(pb, (lvcc.chroma_format_idc << 6) |
324 1 (lvcc.bit_depth_luma_minus8 << 3) |
325 1 lvcc.bit_depth_chroma_minus8);
326 1 avio_w8(pb, 0xff); /* 2 bits nal size length - 1 (11) + 6 bits reserved (111111)*/
327 1 avio_wb32(pb, lvcc.pic_width_in_luma_samples);
328 1 avio_wb32(pb, lvcc.pic_height_in_luma_samples);
329 1 avio_w8(pb, 0xff);
330
331 1 int nb_arrays = !!nb_idr + !!nb_nidr;
332 1 avio_w8(pb, nb_arrays);
333
334
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (nb_idr) {
335 1 avio_w8(pb, LCEVC_IDR_NUT);
336 1 avio_wb16(pb, nb_idr);
337 1 avio_write(pb, idr, idr_size);
338 }
339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (nb_nidr) {
340 ✗ avio_w8(pb, LCEVC_NON_IDR_NUT);
341 ✗ avio_wb16(pb, nb_nidr);
342 ✗ avio_write(pb, nidr, nidr_size);
343 }
344
345 1 ret = 0;
346 1 fail:
347 1 ffio_free_dyn_buf(&idr_pb);
348 1 ffio_free_dyn_buf(&nidr_pb);
349 1 ff_h2645_packet_uninit(&h2645_pkt);
350
351 1 return ret;
352 }
353