FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_sei.c
Date: 2024-02-16 17:37:06
Exec Total Coverage
Lines: 60 192 31.2%
Functions: 7 11 63.6%
Branches: 23 106 21.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 "cbs.h"
20 #include "cbs_internal.h"
21 #include "cbs_h264.h"
22 #include "cbs_h265.h"
23 #include "cbs_h266.h"
24 #include "cbs_sei.h"
25 #include "refstruct.h"
26
27 25 static void cbs_free_user_data_registered(FFRefStructOpaque unused, void *obj)
28 {
29 25 SEIRawUserDataRegistered *udr = obj;
30 25 ff_refstruct_unref(&udr->data);
31 25 }
32
33 20 static void cbs_free_user_data_unregistered(FFRefStructOpaque unused, void *obj)
34 {
35 20 SEIRawUserDataUnregistered *udu = obj;
36 20 ff_refstruct_unref(&udu->data);
37 20 }
38
39 5680 int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
40 const SEIMessageTypeDescriptor *desc)
41 {
42 void (*free_func)(FFRefStructOpaque, void*);
43 5680 unsigned flags = 0;
44
45
2/4
✓ Branch 0 taken 5680 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5680 times.
5680 av_assert0(message->payload == NULL &&
46 message->payload_ref == NULL);
47 5680 message->payload_type = desc->type;
48
49
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 5655 times.
5680 if (desc->type == SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35)
50 25 free_func = &cbs_free_user_data_registered;
51
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5635 times.
5655 else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED)
52 20 free_func = &cbs_free_user_data_unregistered;
53 else {
54 5635 free_func = NULL;
55 5635 flags = FF_REFSTRUCT_FLAG_NO_ZEROING;
56 }
57
58 5680 message->payload_ref = ff_refstruct_alloc_ext(desc->size, flags,
59 NULL, free_func);
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5680 times.
5680 if (!message->payload_ref)
61 return AVERROR(ENOMEM);
62 5680 message->payload = message->payload_ref;
63
64 5680 return 0;
65 }
66
67 5815 int ff_cbs_sei_list_add(SEIRawMessageList *list)
68 {
69 void *ptr;
70 5815 int old_count = list->nb_messages_allocated;
71
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5815 times.
5815 av_assert0(list->nb_messages <= old_count);
73
2/2
✓ Branch 0 taken 5806 times.
✓ Branch 1 taken 9 times.
5815 if (list->nb_messages + 1 > old_count) {
74 5806 int new_count = 2 * old_count + 1;
75
76 5806 ptr = av_realloc_array(list->messages,
77 new_count, sizeof(*list->messages));
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5806 times.
5806 if (!ptr)
79 return AVERROR(ENOMEM);
80
81 5806 list->messages = ptr;
82 5806 list->nb_messages_allocated = new_count;
83
84 // Zero the newly-added entries.
85 5806 memset(list->messages + old_count, 0,
86 5806 (new_count - old_count) * sizeof(*list->messages));
87 }
88 5815 ++list->nb_messages;
89 5815 return 0;
90 }
91
92 5795 void ff_cbs_sei_free_message_list(SEIRawMessageList *list)
93 {
94
2/2
✓ Branch 0 taken 5815 times.
✓ Branch 1 taken 5795 times.
11610 for (int i = 0; i < list->nb_messages; i++) {
95 5815 SEIRawMessage *message = &list->messages[i];
96 5815 ff_refstruct_unref(&message->payload_ref);
97 5815 ff_refstruct_unref(&message->extension_data);
98 }
99 5795 av_free(list->messages);
100 5795 }
101
102 static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
103 CodedBitstreamFragment *au,
104 int prefix,
105 CodedBitstreamUnit **sei_unit)
106 {
107 CodedBitstreamUnit *unit;
108 int sei_type, highest_vcl_type, err, i, position;
109
110 switch (ctx->codec->codec_id) {
111 case AV_CODEC_ID_H264:
112 // (We can ignore auxiliary slices because we only have prefix
113 // SEI in H.264 and an auxiliary picture must always follow a
114 // primary picture.)
115 highest_vcl_type = H264_NAL_IDR_SLICE;
116 if (prefix)
117 sei_type = H264_NAL_SEI;
118 else
119 return AVERROR(EINVAL);
120 break;
121 case AV_CODEC_ID_H265:
122 highest_vcl_type = HEVC_NAL_RSV_VCL31;
123 if (prefix)
124 sei_type = HEVC_NAL_SEI_PREFIX;
125 else
126 sei_type = HEVC_NAL_SEI_SUFFIX;
127 break;
128 case AV_CODEC_ID_H266:
129 highest_vcl_type = VVC_RSV_IRAP_11;
130 if (prefix)
131 sei_type = VVC_PREFIX_SEI_NUT;
132 else
133 sei_type = VVC_SUFFIX_SEI_NUT;
134 break;
135 default:
136 return AVERROR(EINVAL);
137 }
138
139 // Find an existing SEI NAL unit of the right type.
140 unit = NULL;
141 for (i = 0; i < au->nb_units; i++) {
142 if (au->units[i].type == sei_type) {
143 unit = &au->units[i];
144 break;
145 }
146 }
147
148 if (unit) {
149 *sei_unit = unit;
150 return 0;
151 }
152
153 // Need to add a new SEI NAL unit ...
154 if (prefix) {
155 // ... before the first VCL NAL unit.
156 for (i = 0; i < au->nb_units; i++) {
157 if (au->units[i].type < highest_vcl_type)
158 break;
159 }
160 position = i;
161 } else {
162 // ... after the last VCL NAL unit.
163 for (i = au->nb_units - 1; i >= 0; i--) {
164 if (au->units[i].type < highest_vcl_type)
165 break;
166 }
167 if (i < 0) {
168 // No VCL units; just put it at the end.
169 position = au->nb_units;
170 } else {
171 position = i + 1;
172 }
173 }
174
175 err = ff_cbs_insert_unit_content(au, position, sei_type,
176 NULL, NULL);
177 if (err < 0)
178 return err;
179 unit = &au->units[position];
180 unit->type = sei_type;
181
182 err = ff_cbs_alloc_unit_content(ctx, unit);
183 if (err < 0)
184 return err;
185
186 switch (ctx->codec->codec_id) {
187 case AV_CODEC_ID_H264:
188 {
189 H264RawSEI sei = {
190 .nal_unit_header = {
191 .nal_ref_idc = 0,
192 .nal_unit_type = sei_type,
193 },
194 };
195 memcpy(unit->content, &sei, sizeof(sei));
196 }
197 break;
198 case AV_CODEC_ID_H265:
199 {
200 H265RawSEI sei = {
201 .nal_unit_header = {
202 .nal_unit_type = sei_type,
203 .nuh_layer_id = 0,
204 .nuh_temporal_id_plus1 = 1,
205 },
206 };
207 memcpy(unit->content, &sei, sizeof(sei));
208 }
209 break;
210 case AV_CODEC_ID_H266:
211 {
212 H266RawSEI sei = {
213 .nal_unit_header = {
214 .nal_unit_type = sei_type,
215 .nuh_layer_id = 0,
216 .nuh_temporal_id_plus1 = 1,
217 },
218 };
219 memcpy(unit->content, &sei, sizeof(sei));
220 }
221 break;
222 default:
223 av_assert0(0);
224 }
225
226 *sei_unit = unit;
227 return 0;
228 }
229
230 104 static int cbs_sei_get_message_list(CodedBitstreamContext *ctx,
231 CodedBitstreamUnit *unit,
232 SEIRawMessageList **list)
233 {
234
1/4
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
104 switch (ctx->codec->codec_id) {
235 104 case AV_CODEC_ID_H264:
236 {
237 104 H264RawSEI *sei = unit->content;
238
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 61 times.
104 if (unit->type != H264_NAL_SEI)
239 43 return AVERROR(EINVAL);
240 61 *list = &sei->message_list;
241 }
242 61 break;
243 case AV_CODEC_ID_H265:
244 {
245 H265RawSEI *sei = unit->content;
246 if (unit->type != HEVC_NAL_SEI_PREFIX &&
247 unit->type != HEVC_NAL_SEI_SUFFIX)
248 return AVERROR(EINVAL);
249 *list = &sei->message_list;
250 }
251 break;
252 case AV_CODEC_ID_H266:
253 {
254 H266RawSEI *sei = unit->content;
255 if (unit->type != VVC_PREFIX_SEI_NUT &&
256 unit->type != VVC_SUFFIX_SEI_NUT)
257 return AVERROR(EINVAL);
258 *list = &sei->message_list;
259 }
260 break;
261 default:
262 return AVERROR(EINVAL);
263 }
264
265 61 return 0;
266 }
267
268 int ff_cbs_sei_add_message(CodedBitstreamContext *ctx,
269 CodedBitstreamFragment *au,
270 int prefix,
271 uint32_t payload_type,
272 void *payload_data,
273 void *payload_ref)
274 {
275 const SEIMessageTypeDescriptor *desc;
276 CodedBitstreamUnit *unit;
277 SEIRawMessageList *list;
278 SEIRawMessage *message;
279 int err;
280
281 desc = ff_cbs_sei_find_type(ctx, payload_type);
282 if (!desc)
283 return AVERROR(EINVAL);
284
285 // Find an existing SEI unit or make a new one to add to.
286 err = cbs_sei_get_unit(ctx, au, prefix, &unit);
287 if (err < 0)
288 return err;
289
290 // Find the message list inside the codec-dependent unit.
291 err = cbs_sei_get_message_list(ctx, unit, &list);
292 if (err < 0)
293 return err;
294
295 // Add a new message to the message list.
296 err = ff_cbs_sei_list_add(list);
297 if (err < 0)
298 return err;
299
300 if (payload_ref) {
301 /* The following just increments payload_ref's refcount,
302 * so that payload_ref is now owned by us. */
303 payload_ref = ff_refstruct_ref(payload_ref);
304 }
305
306 message = &list->messages[list->nb_messages - 1];
307
308 message->payload_type = payload_type;
309 message->payload = payload_data;
310 message->payload_ref = payload_ref;
311
312 return 0;
313 }
314
315 int ff_cbs_sei_find_message(CodedBitstreamContext *ctx,
316 CodedBitstreamFragment *au,
317 uint32_t payload_type,
318 SEIRawMessage **iter)
319 {
320 int err, i, j, found;
321
322 found = 0;
323 for (i = 0; i < au->nb_units; i++) {
324 CodedBitstreamUnit *unit = &au->units[i];
325 SEIRawMessageList *list;
326
327 err = cbs_sei_get_message_list(ctx, unit, &list);
328 if (err < 0)
329 continue;
330
331 for (j = 0; j < list->nb_messages; j++) {
332 SEIRawMessage *message = &list->messages[j];
333
334 if (message->payload_type == payload_type) {
335 if (!*iter || found) {
336 *iter = message;
337 return 0;
338 }
339 if (message == *iter)
340 found = 1;
341 }
342 }
343 }
344
345 return AVERROR(ENOENT);
346 }
347
348 static void cbs_sei_delete_message(SEIRawMessageList *list,
349 int position)
350 {
351 SEIRawMessage *message;
352
353 av_assert0(0 <= position && position < list->nb_messages);
354
355 message = &list->messages[position];
356 ff_refstruct_unref(&message->payload_ref);
357 ff_refstruct_unref(&message->extension_data);
358
359 --list->nb_messages;
360
361 if (list->nb_messages > 0) {
362 memmove(list->messages + position,
363 list->messages + position + 1,
364 (list->nb_messages - position) * sizeof(*list->messages));
365 }
366 }
367
368 40 void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
369 CodedBitstreamFragment *au,
370 uint32_t payload_type)
371 {
372 int err, i, j;
373
374
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 40 times.
144 for (i = 0; i < au->nb_units; i++) {
375 104 CodedBitstreamUnit *unit = &au->units[i];
376 SEIRawMessageList *list;
377
378 104 err = cbs_sei_get_message_list(ctx, unit, &list);
379
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 61 times.
104 if (err < 0)
380 43 continue;
381
382
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 61 times.
122 for (j = list->nb_messages - 1; j >= 0; j--) {
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (list->messages[j].payload_type == payload_type)
384 cbs_sei_delete_message(list, j);
385 }
386 }
387 40 }
388