FFmpeg coverage


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