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 | 5655 | int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message, | |
40 | const SEIMessageTypeDescriptor *desc) | ||
41 | { | ||
42 | void (*free_func)(FFRefStructOpaque, void*); | ||
43 | 5655 | unsigned flags = 0; | |
44 | |||
45 |
2/4✓ Branch 0 taken 5655 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5655 times.
|
5655 | av_assert0(message->payload == NULL && |
46 | message->payload_ref == NULL); | ||
47 | 5655 | message->payload_type = desc->type; | |
48 | |||
49 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 5630 times.
|
5655 | 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 5610 times.
|
5630 | else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED) |
52 | 20 | free_func = &cbs_free_user_data_unregistered; | |
53 | else { | ||
54 | 5610 | free_func = NULL; | |
55 | 5610 | flags = FF_REFSTRUCT_FLAG_NO_ZEROING; | |
56 | } | ||
57 | |||
58 | 5655 | message->payload_ref = ff_refstruct_alloc_ext(desc->size, flags, | |
59 | NULL, free_func); | ||
60 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5655 times.
|
5655 | if (!message->payload_ref) |
61 | ✗ | return AVERROR(ENOMEM); | |
62 | 5655 | message->payload = message->payload_ref; | |
63 | |||
64 | 5655 | return 0; | |
65 | } | ||
66 | |||
67 | 5787 | int ff_cbs_sei_list_add(SEIRawMessageList *list) | |
68 | { | ||
69 | void *ptr; | ||
70 | 5787 | int old_count = list->nb_messages_allocated; | |
71 | |||
72 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5787 times.
|
5787 | av_assert0(list->nb_messages <= old_count); |
73 |
2/2✓ Branch 0 taken 5778 times.
✓ Branch 1 taken 9 times.
|
5787 | if (list->nb_messages + 1 > old_count) { |
74 | 5778 | int new_count = 2 * old_count + 1; | |
75 | |||
76 | 5778 | ptr = av_realloc_array(list->messages, | |
77 | new_count, sizeof(*list->messages)); | ||
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5778 times.
|
5778 | if (!ptr) |
79 | ✗ | return AVERROR(ENOMEM); | |
80 | |||
81 | 5778 | list->messages = ptr; | |
82 | 5778 | list->nb_messages_allocated = new_count; | |
83 | |||
84 | // Zero the newly-added entries. | ||
85 | 5778 | memset(list->messages + old_count, 0, | |
86 | 5778 | (new_count - old_count) * sizeof(*list->messages)); | |
87 | } | ||
88 | 5787 | ++list->nb_messages; | |
89 | 5787 | return 0; | |
90 | } | ||
91 | |||
92 | 5767 | void ff_cbs_sei_free_message_list(SEIRawMessageList *list) | |
93 | { | ||
94 |
2/2✓ Branch 0 taken 5787 times.
✓ Branch 1 taken 5767 times.
|
11554 | for (int i = 0; i < list->nb_messages; i++) { |
95 | 5787 | SEIRawMessage *message = &list->messages[i]; | |
96 | 5787 | ff_refstruct_unref(&message->payload_ref); | |
97 | 5787 | ff_refstruct_unref(&message->extension_data); | |
98 | } | ||
99 | 5767 | av_free(list->messages); | |
100 | 5767 | } | |
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 |