1/* 2 * Dolby Vision RPU decoder 3 * 4 * Copyright (C) 2021 Jan Ekström 5 * Copyright (C) 2021 Niklas Haas 6 * 7 * This file is part of FFmpeg. 8 * 9 * FFmpeg is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * FFmpeg is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with FFmpeg; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#include "libavutil/buffer.h" 25 26#include "dovi_rpu.h" 27#include "golomb.h" 28#include "get_bits.h" 29 30enum { 31 RPU_COEFF_FIXED = 0, 32 RPU_COEFF_FLOAT = 1, 33}; 34 35/** 36 * Private contents of vdr_ref. 37 */ 38typedef struct DOVIVdrRef { 39 AVDOVIDataMapping mapping; 40 AVDOVIColorMetadata color; 41} DOVIVdrRef; 42 43void ff_dovi_ctx_unref(DOVIContext *s) 44{ 45 for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++) 46 av_buffer_unref(&s->vdr_ref[i]); 47 48 *s = (DOVIContext) { 49 .logctx = s->logctx, 50 }; 51} 52 53void ff_dovi_ctx_flush(DOVIContext *s) 54{ 55 for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++) 56 av_buffer_unref(&s->vdr_ref[i]); 57 58 *s = (DOVIContext) { 59 .logctx = s->logctx, 60 .dv_profile = s->dv_profile, 61 }; 62} 63 64int ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0) 65{ 66 int ret; 67 s->logctx = s0->logctx; 68 s->mapping = s0->mapping; 69 s->color = s0->color; 70 s->dv_profile = s0->dv_profile; 71 for (int i = 0; i < DOVI_MAX_DM_ID; i++) { 72 if ((ret = av_buffer_replace(&s->vdr_ref[i], s0->vdr_ref[i])) < 0) 73 goto fail; 74 } 75 76 return 0; 77 78fail: 79 ff_dovi_ctx_unref(s); 80 return ret; 81} 82 83void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg) 84{ 85 if (!cfg) 86 return; 87 88 s->dv_profile = cfg->dv_profile; 89} 90 91int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame) 92{ 93 AVFrameSideData *sd; 94 AVBufferRef *buf; 95 AVDOVIMetadata *dovi; 96 size_t dovi_size; 97 98 if (!s->mapping || !s->color) 99 return 0; /* incomplete dovi metadata */ 100 101 dovi = av_dovi_metadata_alloc(&dovi_size); 102 if (!dovi) 103 return AVERROR(ENOMEM); 104 105 buf = av_buffer_create((uint8_t *) dovi, dovi_size, NULL, NULL, 0); 106 if (!buf) { 107 av_free(dovi); 108 return AVERROR(ENOMEM); 109 } 110 111 sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf); 112 if (!sd) { 113 av_buffer_unref(&buf); 114 return AVERROR(ENOMEM); 115 } 116 117 /* Copy only the parts of these structs known to us at compiler-time. */ 118#define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last)) 119 COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, disable_residual_flag); 120 COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq[2].linear_deadzone_threshold); 121 COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal); 122 return 0; 123} 124 125static int guess_profile(const AVDOVIRpuDataHeader *hdr) 126{ 127 switch (hdr->vdr_rpu_profile) { 128 case 0: 129 if (hdr->bl_video_full_range_flag) 130 return 5; 131 break; 132 case 1: 133 if (hdr->el_spatial_resampling_filter_flag && !hdr->disable_residual_flag) { 134 if (hdr->vdr_bit_depth == 12) { 135 return 7; 136 } else { 137 return 4; 138 } 139 } else { 140 return 8; 141 } 142 } 143 144 return 0; /* unknown */ 145} 146 147static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr) 148{ 149 uint64_t ipart; 150 union { uint32_t u32; float f32; } fpart; 151 152 switch (hdr->coef_data_type) { 153 case RPU_COEFF_FIXED: 154 ipart = get_ue_golomb_long(gb); 155 fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom); 156 return (ipart << hdr->coef_log2_denom) + fpart.u32; 157 158 case RPU_COEFF_FLOAT: 159 fpart.u32 = get_bits_long(gb, 32); 160 return fpart.f32 * (1LL << hdr->coef_log2_denom); 161 } 162 163 return 0; /* unreachable */ 164} 165 166static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr) 167{ 168 int64_t ipart; 169 union { uint32_t u32; float f32; } fpart; 170 171 switch (hdr->coef_data_type) { 172 case RPU_COEFF_FIXED: 173 ipart = get_se_golomb_long(gb); 174 fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom); 175 return ipart * (1LL << hdr->coef_log2_denom) + fpart.u32; 176 177 case RPU_COEFF_FLOAT: 178 fpart.u32 = get_bits_long(gb, 32); 179 return fpart.f32 * (1LL << hdr->coef_log2_denom); 180 } 181 182 return 0; /* unreachable */ 183} 184 185#define VALIDATE(VAR, MIN, MAX) \ 186 do { \ 187 if (VAR < MIN || VAR > MAX) { \ 188 av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: " \ 189 #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR); \ 190 goto fail; \ 191 } \ 192 } while (0) 193 194int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size) 195{ 196 AVDOVIRpuDataHeader *hdr = &s->header; 197 GetBitContext *gb = &(GetBitContext){0}; 198 DOVIVdrRef *vdr; 199 int ret; 200 201 uint8_t nal_prefix; 202 uint8_t rpu_type; 203 uint8_t vdr_seq_info_present; 204 uint8_t vdr_dm_metadata_present; 205 uint8_t use_prev_vdr_rpu; 206 uint8_t use_nlq; 207 uint8_t profile; 208 if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0) 209 return ret; 210 211 /* RPU header, common values */ 212 nal_prefix = get_bits(gb, 8); 213 VALIDATE(nal_prefix, 25, 25); 214 rpu_type = get_bits(gb, 6); 215 if (rpu_type != 2) { 216 av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type " 217 "%"PRIu8", ignoring\n", rpu_type); 218 return 0; 219 } 220 221 hdr->rpu_type = rpu_type; 222 hdr->rpu_format = get_bits(gb, 11); 223 224 /* Values specific to RPU type 2 */ 225 hdr->vdr_rpu_profile = get_bits(gb, 4); 226 hdr->vdr_rpu_level = get_bits(gb, 4); 227 228 vdr_seq_info_present = get_bits1(gb); 229 if (vdr_seq_info_present) { 230 hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb); 231 hdr->coef_data_type = get_bits(gb, 2); 232 VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT); 233 switch (hdr->coef_data_type) { 234 case RPU_COEFF_FIXED: 235 hdr->coef_log2_denom = get_ue_golomb(gb); 236 VALIDATE(hdr->coef_log2_denom, 13, 32); 237 break; 238 case RPU_COEFF_FLOAT: 239 hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */ 240 break; 241 } 242 243 hdr->vdr_rpu_normalized_idc = get_bits(gb, 2); 244 hdr->bl_video_full_range_flag = get_bits1(gb); 245 246 if ((hdr->rpu_format & 0x700) == 0) { 247 int bl_bit_depth_minus8 = get_ue_golomb_31(gb); 248 int el_bit_depth_minus8 = get_ue_golomb_31(gb); 249 int vdr_bit_depth_minus8 = get_ue_golomb_31(gb); 250 VALIDATE(bl_bit_depth_minus8, 0, 8); 251 VALIDATE(el_bit_depth_minus8, 0, 8); 252 VALIDATE(vdr_bit_depth_minus8, 0, 8); 253 hdr->bl_bit_depth = bl_bit_depth_minus8 + 8; 254 hdr->el_bit_depth = el_bit_depth_minus8 + 8; 255 hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8; 256 hdr->spatial_resampling_filter_flag = get_bits1(gb); 257 skip_bits(gb, 3); /* reserved_zero_3bits */ 258 hdr->el_spatial_resampling_filter_flag = get_bits1(gb); 259 hdr->disable_residual_flag = get_bits1(gb); 260 } 261 } 262 263 if (!hdr->bl_bit_depth) { 264 av_log(s->logctx, AV_LOG_ERROR, "Missing RPU VDR sequence info?\n"); 265 goto fail; 266 } 267 268 vdr_dm_metadata_present = get_bits1(gb); 269 use_prev_vdr_rpu = get_bits1(gb); 270 use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag; 271 272 profile = s->dv_profile ? s->dv_profile : guess_profile(hdr); 273 if (profile == 5 && use_nlq) { 274 av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n"); 275 goto fail; 276 } 277 278 if (use_prev_vdr_rpu) { 279 int prev_vdr_rpu_id = get_ue_golomb_31(gb); 280 VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID); 281 if (!s->vdr_ref[prev_vdr_rpu_id]) { 282 av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n", 283 prev_vdr_rpu_id); 284 goto fail; 285 } 286 vdr = (DOVIVdrRef *) s->vdr_ref[prev_vdr_rpu_id]->data; 287 s->mapping = &vdr->mapping; 288 } else { 289 int vdr_rpu_id = get_ue_golomb_31(gb); 290 VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID); 291 if (!s->vdr_ref[vdr_rpu_id]) { 292 s->vdr_ref[vdr_rpu_id] = av_buffer_allocz(sizeof(DOVIVdrRef)); 293 if (!s->vdr_ref[vdr_rpu_id]) 294 return AVERROR(ENOMEM); 295 } 296 297 vdr = (DOVIVdrRef *) s->vdr_ref[vdr_rpu_id]->data; 298 s->mapping = &vdr->mapping; 299 300 vdr->mapping.vdr_rpu_id = vdr_rpu_id; 301 vdr->mapping.mapping_color_space = get_ue_golomb_31(gb); 302 vdr->mapping.mapping_chroma_format_idc = get_ue_golomb_31(gb); 303 304 for (int c = 0; c < 3; c++) { 305 AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c]; 306 int num_pivots_minus_2 = get_ue_golomb_31(gb); 307 int pivot = 0; 308 309 VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1); 310 curve->num_pivots = num_pivots_minus_2 + 2; 311 for (int i = 0; i < curve->num_pivots; i++) { 312 pivot += get_bits(gb, hdr->bl_bit_depth); 313 curve->pivots[i] = av_clip_uint16(pivot); 314 } 315 } 316 317 if (use_nlq) { 318 vdr->mapping.nlq_method_idc = get_bits(gb, 3); 319 /** 320 * The patent mentions another legal value, NLQ_MU_LAW, but it's 321 * not documented anywhere how to parse or apply that type of NLQ. 322 */ 323 VALIDATE(vdr->mapping.nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ); 324 } else { 325 vdr->mapping.nlq_method_idc = AV_DOVI_NLQ_NONE; 326 } 327 328 vdr->mapping.num_x_partitions = get_ue_golomb_long(gb) + 1; 329 vdr->mapping.num_y_partitions = get_ue_golomb_long(gb) + 1; 330 /* End of rpu_data_header(), start of vdr_rpu_data_payload() */ 331 332 for (int c = 0; c < 3; c++) { 333 AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c]; 334 for (int i = 0; i < curve->num_pivots - 1; i++) { 335 int mapping_idc = get_ue_golomb_31(gb); 336 VALIDATE(mapping_idc, 0, 1); 337 curve->mapping_idc[i] = mapping_idc; 338 switch (mapping_idc) { 339 case AV_DOVI_MAPPING_POLYNOMIAL: { 340 int poly_order_minus1 = get_ue_golomb_31(gb); 341 VALIDATE(poly_order_minus1, 0, 1); 342 curve->poly_order[i] = poly_order_minus1 + 1; 343 if (poly_order_minus1 == 0) { 344 int linear_interp_flag = get_bits1(gb); 345 if (linear_interp_flag) { 346 /* lack of documentation/samples */ 347 avpriv_request_sample(s->logctx, "Dolby Vision " 348 "linear interpolation"); 349 ff_dovi_ctx_unref(s); 350 return AVERROR_PATCHWELCOME; 351 } 352 } 353 for (int k = 0; k <= curve->poly_order[i]; k++) 354 curve->poly_coef[i][k] = get_se_coef(gb, hdr); 355 break; 356 } 357 case AV_DOVI_MAPPING_MMR: { 358 int mmr_order_minus1 = get_bits(gb, 2); 359 VALIDATE(mmr_order_minus1, 0, 2); 360 curve->mmr_order[i] = mmr_order_minus1 + 1; 361 curve->mmr_constant[i] = get_se_coef(gb, hdr); 362 for (int j = 0; j < curve->mmr_order[i]; j++) { 363 for (int k = 0; k < 7; k++) 364 curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr); 365 } 366 break; 367 } 368 } 369 } 370 } 371 372 if (use_nlq) { 373 for (int c = 0; c < 3; c++) { 374 AVDOVINLQParams *nlq = &vdr->mapping.nlq[c]; 375 nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth); 376 nlq->vdr_in_max = get_ue_coef(gb, hdr); 377 switch (vdr->mapping.nlq_method_idc) { 378 case AV_DOVI_NLQ_LINEAR_DZ: 379 nlq->linear_deadzone_slope = get_ue_coef(gb, hdr); 380 nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr); 381 break; 382 } 383 } 384 } 385 } 386 387 if (vdr_dm_metadata_present) { 388 AVDOVIColorMetadata *color; 389 int affected_dm_id = get_ue_golomb_31(gb); 390 int current_dm_id = get_ue_golomb_31(gb); 391 VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID); 392 VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID); 393 if (!s->vdr_ref[affected_dm_id]) { 394 s->vdr_ref[affected_dm_id] = av_buffer_allocz(sizeof(DOVIVdrRef)); 395 if (!s->vdr_ref[affected_dm_id]) 396 return AVERROR(ENOMEM); 397 } 398 399 if (!s->vdr_ref[current_dm_id]) { 400 av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n", 401 current_dm_id); 402 goto fail; 403 } 404 405 /* Update current pointer based on current_dm_id */ 406 vdr = (DOVIVdrRef *) s->vdr_ref[current_dm_id]->data; 407 s->color = &vdr->color; 408 409 /* Update values of affected_dm_id */ 410 vdr = (DOVIVdrRef *) s->vdr_ref[affected_dm_id]->data; 411 color = &vdr->color; 412 color->dm_metadata_id = affected_dm_id; 413 color->scene_refresh_flag = get_ue_golomb_31(gb); 414 for (int i = 0; i < 9; i++) 415 color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13); 416 for (int i = 0; i < 3; i++) { 417 int denom = profile == 4 ? (1 << 30) : (1 << 28); 418 unsigned offset = get_bits_long(gb, 32); 419 if (offset > INT_MAX) { 420 /* Ensure the result fits inside AVRational */ 421 offset >>= 1; 422 denom >>= 1; 423 } 424 color->ycc_to_rgb_offset[i] = av_make_q(offset, denom); 425 } 426 for (int i = 0; i < 9; i++) 427 color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14); 428 429 color->signal_eotf = get_bits(gb, 16); 430 color->signal_eotf_param0 = get_bits(gb, 16); 431 color->signal_eotf_param1 = get_bits(gb, 16); 432 color->signal_eotf_param2 = get_bits_long(gb, 32); 433 color->signal_bit_depth = get_bits(gb, 5); 434 VALIDATE(color->signal_bit_depth, 8, 16); 435 color->signal_color_space = get_bits(gb, 2); 436 color->signal_chroma_format = get_bits(gb, 2); 437 color->signal_full_range_flag = get_bits(gb, 2); 438 color->source_min_pq = get_bits(gb, 12); 439 color->source_max_pq = get_bits(gb, 12); 440 color->source_diagonal = get_bits(gb, 10); 441 } 442 443 /* FIXME: verify CRC32, requires implementation of AV_CRC_32_MPEG_2 */ 444 return 0; 445 446fail: 447 ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */ 448 return AVERROR(EINVAL); 449} 450