FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_sei.c
Date: 2023-09-24 13:02:57
Exec Total Coverage
Lines: 65 202 32.2%
Functions: 7 11 63.6%
Branches: 26 112 23.2%

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