FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/vvc/sei.c
Date: 2026-01-16 07:34:38
Exec Total Coverage
Lines: 46 165 27.9%
Functions: 5 12 41.7%
Branches: 14 68 20.6%

Line Branch Exec Source
1 /*
2 * VVC Supplementary Enhancement Information messages
3 *
4 * copyright (c) 2024 Wu Jianhua <toqsxw@outlook.com>
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "sei.h"
24 #include "dec.h"
25 #include "libavcodec/bytestream.h"
26 #include "libavutil/refstruct.h"
27
28 static int decode_film_grain_characteristics(H2645SEIFilmGrainCharacteristics *h, const SEIRawFilmGrainCharacteristics *s, const VVCFrameContext *fc)
29 {
30 const VVCSPS *sps = fc->ps.sps;
31
32 h->present = !s->fg_characteristics_cancel_flag;
33 if (h->present) {
34 h->model_id = s->fg_model_id;
35 h->separate_colour_description_present_flag = s->fg_separate_colour_description_present_flag;
36 if (h->separate_colour_description_present_flag) {
37 h->bit_depth_luma = s->fg_bit_depth_luma_minus8 + 8;
38 h->bit_depth_chroma = s->fg_bit_depth_chroma_minus8 + 8;
39 h->full_range = s->fg_full_range_flag;
40 h->color_primaries = s->fg_colour_primaries;
41 h->transfer_characteristics = s->fg_transfer_characteristics;
42 h->matrix_coeffs = s->fg_matrix_coeffs;
43 } else {
44 if (!sps) {
45 av_log(fc->log_ctx, AV_LOG_ERROR,
46 "No active SPS for film_grain_characteristics.\n");
47 return AVERROR_INVALIDDATA;
48 }
49 h->bit_depth_luma = sps->bit_depth;
50 h->bit_depth_chroma = sps->bit_depth;
51 h->full_range = sps->r->vui.vui_full_range_flag;
52 h->color_primaries = sps->r->vui.vui_colour_primaries;
53 h->transfer_characteristics = sps->r->vui.vui_transfer_characteristics;
54 h->matrix_coeffs = sps->r->vui.vui_matrix_coeffs ;
55 }
56
57 h->blending_mode_id = s->fg_blending_mode_id;
58 h->log2_scale_factor = s->fg_log2_scale_factor;
59
60 for (int c = 0; c < 3; c++) {
61 h->comp_model_present_flag[c] = s->fg_comp_model_present_flag[c];
62 if (h->comp_model_present_flag[c]) {
63 h->num_intensity_intervals[c] = s->fg_num_intensity_intervals_minus1[c] + 1;
64 h->num_model_values[c] = s->fg_num_model_values_minus1[c] + 1;
65
66 if (h->num_model_values[c] > 6)
67 return AVERROR_INVALIDDATA;
68
69 for (int i = 0; i < h->num_intensity_intervals[c]; i++) {
70 h->intensity_interval_lower_bound[c][i] = s->fg_intensity_interval_lower_bound[c][i];
71 h->intensity_interval_upper_bound[c][i] = s->fg_intensity_interval_upper_bound[c][i];
72 for (int j = 0; j < h->num_model_values[c]; j++)
73 h->comp_model_value[c][i][j] = s->fg_comp_model_value[c][i][j];
74 }
75 }
76 }
77
78 h->persistence_flag = s->fg_characteristics_persistence_flag;
79 }
80
81 return 0;
82 }
83
84 1008 static int decode_decoded_picture_hash(H274SEIPictureHash *h, const SEIRawDecodedPictureHash *s)
85 {
86 1008 h->present = 1;
87 1008 h->hash_type = s->dph_sei_hash_type;
88
1/2
✓ Branch 0 taken 1008 times.
✗ Branch 1 not taken.
1008 if (h->hash_type == 0)
89 1008 memcpy(h->md5, s->dph_sei_picture_md5, sizeof(h->md5));
90 else if (h->hash_type == 1)
91 memcpy(h->crc, s->dph_sei_picture_crc, sizeof(h->crc));
92 else if (h->hash_type == 2)
93 memcpy(h->checksum, s->dph_sei_picture_checksum, sizeof(h->checksum));
94
95 1008 return 0;
96 }
97
98 static int decode_display_orientation(H2645SEIDisplayOrientation *h, const SEIRawDisplayOrientation *s)
99 {
100 int degrees[] = { 0, 0x8000, 0x4000, 0xC000 };
101
102 h->present = !s->display_orientation_cancel_flag;
103 if (h->present) {
104 if (s->display_orientation_transform_type > 7)
105 return AVERROR_INVALIDDATA;
106
107 h->vflip = 0;
108 if (s->display_orientation_transform_type == 1 ||
109 s->display_orientation_transform_type == 3 ||
110 s->display_orientation_transform_type == 4 ||
111 s->display_orientation_transform_type == 6) {
112 h->hflip = 1;
113 } else {
114 h->hflip = 0;
115 }
116 h->anticlockwise_rotation = degrees[s->display_orientation_transform_type >> 1];
117 }
118
119 return 0;
120 }
121
122 static int decode_content_light_level_info(H2645SEIContentLight *h, const SEIRawContentLightLevelInfo *s)
123 {
124 h->present = 1;
125 h->max_content_light_level = s->max_content_light_level;
126 h->max_pic_average_light_level = s->max_pic_average_light_level;
127
128 return 0;
129 }
130
131 22 static int decode_frame_field_info(H274SEIFrameFieldInfo *h, const SEIRawFrameFieldInformation *s)
132 {
133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (s->ffi_source_scan_type > 3)
134 return AVERROR_INVALIDDATA;
135
136 22 h->present = 1;
137
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (s->ffi_field_pic_flag) {
138
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
22 if (s->ffi_bottom_field_flag)
139 11 h->picture_struct = AV_PICTURE_STRUCTURE_BOTTOM_FIELD;
140 else
141 11 h->picture_struct = AV_PICTURE_STRUCTURE_TOP_FIELD;
142 } else {
143 h->display_elemental_periods = s->ffi_display_elemental_periods_minus1 + 1;
144 }
145
146 22 h->source_scan_type = s->ffi_source_scan_type;
147 22 h->duplicate_flag = s->ffi_duplicate_flag;
148
149 22 return 0;
150 }
151
152 static int decode_ambient_viewing_environment(H2645SEIAmbientViewingEnvironment *h, const SEIRawAmbientViewingEnvironment *s)
153 {
154 h->present = 1;
155 h->ambient_illuminance = s->ambient_illuminance;
156 h->ambient_light_x = s->ambient_light_x;
157 h->ambient_light_y = s->ambient_light_y;
158
159 return 0;
160 }
161
162 static int decode_mastering_display_colour_volume(H2645SEIMasteringDisplay *h, const SEIRawMasteringDisplayColourVolume *s)
163 {
164 h->present = 1;
165
166 for (int c = 0; c < 3; c++) {
167 h->display_primaries[c][0] = s->display_primaries_x[c];
168 h->display_primaries[c][1] = s->display_primaries_y[c];
169 }
170
171 h->white_point[0] = s->white_point_x;
172 h->white_point[1] = s->white_point_y;
173
174 h->max_luminance = s->max_display_mastering_luminance;
175 h->min_luminance = s->min_display_mastering_luminance;
176
177 return 0;
178 }
179
180 static int decode_user_data_registered_itu_t_t35(H2645SEI *sei, const SEIRawUserDataRegistered *s,
181 const VVCFrameContext *fc)
182 {
183 GetByteContext gbc;
184 int offset = (s->itu_t_t35_country_code == 0xff) + 1;
185
186 bytestream2_init(&gbc, s->data_ref, s->data_length + offset);
187 return ff_h2645_sei_message_decode(sei, SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35,
188 AV_CODEC_ID_VVC, NULL, &gbc, fc->log_ctx);
189 }
190
191 static int decode_user_data_uregistered(H2645SEI *sei, const SEIRawUserDataUnregistered *s,
192 const VVCFrameContext *fc)
193 {
194 GetByteContext gbc;
195
196 bytestream2_init(&gbc, s->data_ref, s->data_length + 16);
197 return ff_h2645_sei_message_decode(sei, SEI_TYPE_USER_DATA_UNREGISTERED,
198 AV_CODEC_ID_VVC, NULL, &gbc, fc->log_ctx);
199 }
200
201 1098 int ff_vvc_sei_decode(VVCSEI *s, const H266RawSEI *sei, const struct VVCFrameContext *fc)
202 {
203 1098 H2645SEI *c = &s->common;
204
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1098 times.
1098 if (!sei)
206 return AVERROR_INVALIDDATA;
207
208
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 1030 times.
2128 for (int i = 0; i < sei->message_list.nb_messages; i++) {
209 1098 int ret = 0;
210 1098 SEIRawMessage *message = &sei->message_list.messages[i];
211 1098 void *payload = message->payload;
212
213
3/10
✗ Branch 0 not taken.
✓ Branch 1 taken 1008 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 68 times.
1098 switch (message->payload_type) {
214 case SEI_TYPE_FILM_GRAIN_CHARACTERISTICS:
215 av_refstruct_unref(&c->film_grain_characteristics);
216 c->film_grain_characteristics = av_refstruct_allocz(sizeof(*c->film_grain_characteristics));
217 if (!c->film_grain_characteristics)
218 return AVERROR(ENOMEM);
219 ret = decode_film_grain_characteristics(c->film_grain_characteristics, payload, fc);
220 break;
221
222 1008 case SEI_TYPE_DECODED_PICTURE_HASH:
223 1008 ret = decode_decoded_picture_hash(&s->picture_hash, payload);
224 1008 break;
225
226 case SEI_TYPE_DISPLAY_ORIENTATION:
227 ret = decode_display_orientation(&s->common.display_orientation, payload);
228 break;
229
230 case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO:
231 ret = decode_content_light_level_info(&s->common.content_light, payload);
232 break;
233
234 22 case SEI_TYPE_FRAME_FIELD_INFO:
235 22 ret = decode_frame_field_info(&s->frame_field_info, payload);
236 22 break;
237
238 case SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT:
239 ret = decode_ambient_viewing_environment(&s->common.ambient_viewing_environment, payload);
240 break;
241
242 case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
243 ret = decode_mastering_display_colour_volume(&s->common.mastering_display, payload);
244 break;
245
246 case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
247 ret = decode_user_data_registered_itu_t_t35(&s->common, payload, fc);
248 break;
249
250 case SEI_TYPE_USER_DATA_UNREGISTERED:
251 ret = decode_user_data_uregistered(&s->common, payload, fc);
252 break;
253
254 68 default:
255 68 av_log(fc->log_ctx, AV_LOG_DEBUG, "Skipped %s SEI %d\n",
256
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 sei->nal_unit_header.nal_unit_type == VVC_PREFIX_SEI_NUT ?
257 "PREFIX" : "SUFFIX", message->payload_type);
258 68 return FF_H2645_SEI_MESSAGE_UNHANDLED;
259 }
260
261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1030 times.
1030 if (ret == AVERROR(ENOMEM))
262 return ret;
263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1030 times.
1030 if (ret < 0)
264 av_log(fc->log_ctx, AV_LOG_WARNING, "Failure to parse %s SEI %d: %s\n",
265 sei->nal_unit_header.nal_unit_type == VVC_PREFIX_SEI_NUT ?
266 "PREFIX" : "SUFFIX", message->payload_type, av_err2str(ret));
267 }
268
269 1030 return 0;
270 }
271
272 972 int ff_vvc_sei_replace(VVCSEI *dst, const VVCSEI *src)
273 {
274 972 dst->picture_hash.present = 0; // drop hash
275 972 dst->frame_field_info.present = 0; // drop field info
276 972 return ff_h2645_sei_ctx_replace(&dst->common, &src->common);
277 }
278
279 846 void ff_vvc_sei_reset(VVCSEI *s)
280 {
281 846 ff_h2645_sei_reset(&s->common);
282 846 s->picture_hash.present = 0;
283 846 s->frame_field_info.present = 0;
284 846 }
285