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 |