FFmpeg coverage


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