| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Dolby Vision RPU encoder | ||
| 3 | * | ||
| 4 | * Copyright (C) 2024 Niklas Haas | ||
| 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 "libavutil/attributes.h" | ||
| 24 | #include "libavutil/avassert.h" | ||
| 25 | #include "libavutil/crc.h" | ||
| 26 | #include "libavutil/mem.h" | ||
| 27 | #include "libavutil/refstruct.h" | ||
| 28 | |||
| 29 | #include "avcodec.h" | ||
| 30 | #include "dovi_rpu.h" | ||
| 31 | #include "itut35.h" | ||
| 32 | #include "put_bits.h" | ||
| 33 | #include "put_golomb.h" | ||
| 34 | |||
| 35 | static const struct { | ||
| 36 | uint64_t pps; // maximum pixels per second | ||
| 37 | int width; // maximum width | ||
| 38 | int main; // maximum bitrate in main tier | ||
| 39 | int high; // maximum bitrate in high tier | ||
| 40 | } dv_levels[] = { | ||
| 41 | [1] = {1280*720*24, 1280, 20, 50}, | ||
| 42 | [2] = {1280*720*30, 1280, 20, 50}, | ||
| 43 | [3] = {1920*1080*24, 1920, 20, 70}, | ||
| 44 | [4] = {1920*1080*30, 2560, 20, 70}, | ||
| 45 | [5] = {1920*1080*60, 3840, 20, 70}, | ||
| 46 | [6] = {3840*2160*24, 3840, 25, 130}, | ||
| 47 | [7] = {3840*2160*30, 3840, 25, 130}, | ||
| 48 | [8] = {3840*2160*48, 3840, 40, 130}, | ||
| 49 | [9] = {3840*2160*60, 3840, 40, 130}, | ||
| 50 | [10] = {3840*2160*120, 3840, 60, 240}, | ||
| 51 | [11] = {3840*2160*120, 7680, 60, 240}, | ||
| 52 | [12] = {7680*4320*60, 7680, 120, 450}, | ||
| 53 | [13] = {7680*4320*120u, 7680, 240, 800}, | ||
| 54 | }; | ||
| 55 | |||
| 56 | ✗ | static av_cold int dovi_configure_ext(DOVIContext *s, enum AVCodecID codec_id, | |
| 57 | const AVDOVIMetadata *metadata, | ||
| 58 | enum AVDOVICompression compression, | ||
| 59 | int strict_std_compliance, | ||
| 60 | int width, int height, | ||
| 61 | AVRational framerate, | ||
| 62 | enum AVPixelFormat pix_format, | ||
| 63 | enum AVColorSpace color_space, | ||
| 64 | enum AVColorPrimaries color_primaries, | ||
| 65 | enum AVColorTransferCharacteristic color_trc, | ||
| 66 | AVPacketSideData **coded_side_data, | ||
| 67 | int *nb_coded_side_data) | ||
| 68 | { | ||
| 69 | AVDOVIDecoderConfigurationRecord *cfg; | ||
| 70 | ✗ | const AVDOVIRpuDataHeader *hdr = NULL; | |
| 71 | ✗ | int dv_profile, dv_level, bl_compat_id = -1; | |
| 72 | size_t cfg_size; | ||
| 73 | uint64_t pps; | ||
| 74 | |||
| 75 | ✗ | if (!s->enable) | |
| 76 | ✗ | goto skip; | |
| 77 | |||
| 78 | ✗ | if (metadata) | |
| 79 | ✗ | hdr = av_dovi_get_header(metadata); | |
| 80 | |||
| 81 | ✗ | if (s->enable == FF_DOVI_AUTOMATIC && !hdr) | |
| 82 | ✗ | goto skip; | |
| 83 | |||
| 84 | ✗ | if (compression == AV_DOVI_COMPRESSION_RESERVED || | |
| 85 | compression > AV_DOVI_COMPRESSION_EXTENDED) | ||
| 86 | ✗ | return AVERROR(EINVAL); | |
| 87 | |||
| 88 | ✗ | switch (codec_id) { | |
| 89 | ✗ | case AV_CODEC_ID_AV1: dv_profile = 10; break; | |
| 90 | ✗ | case AV_CODEC_ID_H264: dv_profile = 9; break; | |
| 91 | ✗ | case AV_CODEC_ID_HEVC: | |
| 92 | ✗ | if (hdr) { | |
| 93 | ✗ | dv_profile = ff_dovi_guess_profile_hevc(hdr); | |
| 94 | ✗ | break; | |
| 95 | } | ||
| 96 | |||
| 97 | /* This is likely to be proprietary IPTPQc2 */ | ||
| 98 | ✗ | if (color_space == AVCOL_SPC_IPT_C2 || | |
| 99 | ✗ | (color_space == AVCOL_SPC_UNSPECIFIED && | |
| 100 | color_trc == AVCOL_TRC_UNSPECIFIED)) | ||
| 101 | ✗ | dv_profile = 5; | |
| 102 | else | ||
| 103 | ✗ | dv_profile = 8; | |
| 104 | ✗ | break; | |
| 105 | ✗ | default: | |
| 106 | ✗ | av_unreachable("ff_dovi_configure only used with AV1, H.264 and HEVC"); | |
| 107 | } | ||
| 108 | |||
| 109 | ✗ | if (strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { | |
| 110 | ✗ | if (dv_profile == 9) { | |
| 111 | ✗ | if (pix_format != AV_PIX_FMT_YUV420P) | |
| 112 | ✗ | dv_profile = 0; | |
| 113 | } else { | ||
| 114 | ✗ | if (pix_format != AV_PIX_FMT_YUV420P10) | |
| 115 | ✗ | dv_profile = 0; | |
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | ✗ | switch (dv_profile) { | |
| 120 | ✗ | case 4: /* HEVC with enhancement layer */ | |
| 121 | case 7: | ||
| 122 | ✗ | if (s->enable > 0) { | |
| 123 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Coding of Dolby Vision enhancement " | |
| 124 | "layers is currently unsupported."); | ||
| 125 | ✗ | return AVERROR_PATCHWELCOME; | |
| 126 | } else { | ||
| 127 | ✗ | goto skip; | |
| 128 | } | ||
| 129 | ✗ | case 5: /* HEVC with proprietary IPTPQc2 */ | |
| 130 | ✗ | bl_compat_id = 0; | |
| 131 | ✗ | break; | |
| 132 | ✗ | case 10: | |
| 133 | /* FIXME: check for proper H.273 tags once those are added */ | ||
| 134 | ✗ | if (hdr && hdr->bl_video_full_range_flag) { | |
| 135 | /* AV1 with proprietary IPTPQc2 */ | ||
| 136 | ✗ | bl_compat_id = 0; | |
| 137 | ✗ | break; | |
| 138 | } | ||
| 139 | /* fall through */ | ||
| 140 | case 8: /* HEVC (or AV1) with BL compatibility */ | ||
| 141 | ✗ | if (color_space == AVCOL_SPC_BT2020_NCL && | |
| 142 | ✗ | color_primaries == AVCOL_PRI_BT2020 && | |
| 143 | color_trc == AVCOL_TRC_SMPTE2084) { | ||
| 144 | ✗ | bl_compat_id = 1; | |
| 145 | ✗ | } else if (color_space == AVCOL_SPC_BT2020_NCL && | |
| 146 | ✗ | color_primaries == AVCOL_PRI_BT2020 && | |
| 147 | color_trc == AVCOL_TRC_ARIB_STD_B67) { | ||
| 148 | ✗ | bl_compat_id = 4; | |
| 149 | ✗ | } else if (color_space == AVCOL_SPC_BT709 && | |
| 150 | ✗ | color_primaries == AVCOL_PRI_BT709 && | |
| 151 | color_trc == AVCOL_TRC_BT709) { | ||
| 152 | ✗ | bl_compat_id = 2; | |
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | ✗ | if (!dv_profile || bl_compat_id < 0) { | |
| 157 | ✗ | if (s->enable > 0) { | |
| 158 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision enabled, but could " | |
| 159 | "not determine profile and compatibility mode. Double-check " | ||
| 160 | "colorspace and format settings for compatibility?\n"); | ||
| 161 | ✗ | return AVERROR(EINVAL); | |
| 162 | } | ||
| 163 | ✗ | goto skip; | |
| 164 | } | ||
| 165 | |||
| 166 | ✗ | if (compression != AV_DOVI_COMPRESSION_NONE) { | |
| 167 | ✗ | if (dv_profile < 8 && strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { | |
| 168 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision metadata compression " | |
| 169 | "is not permitted for profiles 7 and earlier. (dv_profile: %d, " | ||
| 170 | "compression: %d)\n", dv_profile, compression); | ||
| 171 | ✗ | return AVERROR(EINVAL); | |
| 172 | ✗ | } else if (compression == AV_DOVI_COMPRESSION_EXTENDED && | |
| 173 | strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { | ||
| 174 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision extended metadata " | |
| 175 | "compression is experimental and not supported by " | ||
| 176 | "devices."); | ||
| 177 | ✗ | return AVERROR(EINVAL); | |
| 178 | ✗ | } else if (dv_profile == 8) { | |
| 179 | ✗ | av_log(s->logctx, AV_LOG_WARNING, "Dolby Vision metadata compression " | |
| 180 | "for profile 8 is known to be unsupported by many devices, " | ||
| 181 | "use with caution.\n"); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | ✗ | pps = width * height; | |
| 186 | ✗ | if (framerate.num) { | |
| 187 | ✗ | pps = pps * framerate.num / framerate.den; | |
| 188 | } else { | ||
| 189 | ✗ | pps *= 25; /* sanity fallback */ | |
| 190 | } | ||
| 191 | |||
| 192 | ✗ | dv_level = 0; | |
| 193 | ✗ | for (int i = 1; i < FF_ARRAY_ELEMS(dv_levels); i++) { | |
| 194 | ✗ | if (pps > dv_levels[i].pps) | |
| 195 | ✗ | continue; | |
| 196 | ✗ | if (width > dv_levels[i].width) | |
| 197 | ✗ | continue; | |
| 198 | /* In theory, we should also test the bitrate when known, and | ||
| 199 | * distinguish between main and high tier. In practice, just ignore | ||
| 200 | * the bitrate constraints and hope they work out. This would ideally | ||
| 201 | * be handled by either the encoder or muxer directly. */ | ||
| 202 | ✗ | dv_level = i; | |
| 203 | ✗ | break; | |
| 204 | } | ||
| 205 | |||
| 206 | ✗ | if (!dv_level) { | |
| 207 | ✗ | if (strict_std_compliance >= FF_COMPLIANCE_STRICT) { | |
| 208 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Coded PPS (%"PRIu64") and width (%d) " | |
| 209 | "exceed Dolby Vision limitations\n", pps, width); | ||
| 210 | ✗ | return AVERROR(EINVAL); | |
| 211 | } else { | ||
| 212 | ✗ | av_log(s->logctx, AV_LOG_WARNING, "Coded PPS (%"PRIu64") and width (%d) " | |
| 213 | "exceed Dolby Vision limitations. Ignoring, resulting file " | ||
| 214 | "may be non-conforming.\n", pps, width); | ||
| 215 | ✗ | dv_level = FF_ARRAY_ELEMS(dv_levels) - 1; | |
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | ✗ | cfg = av_dovi_alloc(&cfg_size); | |
| 220 | ✗ | if (!cfg) | |
| 221 | ✗ | return AVERROR(ENOMEM); | |
| 222 | |||
| 223 | ✗ | if (!av_packet_side_data_add(coded_side_data, | |
| 224 | nb_coded_side_data, | ||
| 225 | AV_PKT_DATA_DOVI_CONF, cfg, cfg_size, 0)) { | ||
| 226 | ✗ | av_free(cfg); | |
| 227 | ✗ | return AVERROR(ENOMEM); | |
| 228 | } | ||
| 229 | |||
| 230 | ✗ | cfg->dv_version_major = 1; | |
| 231 | ✗ | cfg->dv_version_minor = 0; | |
| 232 | ✗ | cfg->dv_profile = dv_profile; | |
| 233 | ✗ | cfg->dv_level = dv_level; | |
| 234 | ✗ | cfg->rpu_present_flag = 1; | |
| 235 | ✗ | cfg->el_present_flag = 0; | |
| 236 | ✗ | cfg->bl_present_flag = 1; | |
| 237 | ✗ | cfg->dv_bl_signal_compatibility_id = bl_compat_id; | |
| 238 | ✗ | cfg->dv_md_compression = compression; | |
| 239 | |||
| 240 | ✗ | s->cfg = *cfg; | |
| 241 | ✗ | return 0; | |
| 242 | |||
| 243 | ✗ | skip: | |
| 244 | ✗ | s->cfg = (AVDOVIDecoderConfigurationRecord) {0}; | |
| 245 | ✗ | return 0; | |
| 246 | } | ||
| 247 | |||
| 248 | ✗ | av_cold int ff_dovi_configure_from_codedpar(DOVIContext *s, AVCodecParameters *par, | |
| 249 | const AVDOVIMetadata *metadata, | ||
| 250 | enum AVDOVICompression compression, | ||
| 251 | int strict_std_compliance) | ||
| 252 | { | ||
| 253 | ✗ | return dovi_configure_ext(s, par->codec_id, metadata, compression, | |
| 254 | strict_std_compliance, par->width, par->height, | ||
| 255 | ✗ | par->framerate, par->format, par->color_space, | |
| 256 | par->color_primaries, par->color_trc, | ||
| 257 | &par->coded_side_data, &par->nb_coded_side_data); | ||
| 258 | } | ||
| 259 | |||
| 260 | ✗ | av_cold int ff_dovi_configure(DOVIContext *s, AVCodecContext *avctx) | |
| 261 | { | ||
| 262 | ✗ | const AVDOVIMetadata *metadata = NULL; | |
| 263 | const AVFrameSideData *sd; | ||
| 264 | ✗ | sd = av_frame_side_data_get(avctx->decoded_side_data, | |
| 265 | avctx->nb_decoded_side_data, | ||
| 266 | AV_FRAME_DATA_DOVI_METADATA); | ||
| 267 | ✗ | if (sd) | |
| 268 | ✗ | metadata = (const AVDOVIMetadata *) sd->data; | |
| 269 | |||
| 270 | /* Current encoders cannot handle metadata compression during encoding */ | ||
| 271 | ✗ | return dovi_configure_ext(s, avctx->codec_id, metadata, AV_DOVI_COMPRESSION_NONE, | |
| 272 | avctx->strict_std_compliance, avctx->width, | ||
| 273 | avctx->height, avctx->framerate, avctx->pix_fmt, | ||
| 274 | avctx->colorspace, avctx->color_primaries, avctx->color_trc, | ||
| 275 | &avctx->coded_side_data, &avctx->nb_coded_side_data); | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Compares only the static DM metadata parts of AVDOVIColorMetadata (excluding | ||
| 279 | * dm_metadata_id and scene_refresh_flag) */ | ||
| 280 | ✗ | static int cmp_dm_level0(const AVDOVIColorMetadata *dm1, | |
| 281 | const AVDOVIColorMetadata *dm2) | ||
| 282 | { | ||
| 283 | int ret; | ||
| 284 | ✗ | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->ycc_to_rgb_matrix); i++) { | |
| 285 | ✗ | if ((ret = av_cmp_q(dm1->ycc_to_rgb_matrix[i], dm2->ycc_to_rgb_matrix[i]))) | |
| 286 | ✗ | return ret; | |
| 287 | } | ||
| 288 | |||
| 289 | ✗ | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->ycc_to_rgb_offset); i++) { | |
| 290 | ✗ | if ((ret = av_cmp_q(dm1->ycc_to_rgb_offset[i], dm2->ycc_to_rgb_offset[i]))) | |
| 291 | ✗ | return ret; | |
| 292 | } | ||
| 293 | |||
| 294 | ✗ | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->rgb_to_lms_matrix); i++) { | |
| 295 | ✗ | if ((ret = av_cmp_q(dm1->rgb_to_lms_matrix[i], dm2->rgb_to_lms_matrix[i]))) | |
| 296 | ✗ | return ret; | |
| 297 | } | ||
| 298 | |||
| 299 | ✗ | return memcmp(&dm1->signal_eotf, &dm2->signal_eotf, | |
| 300 | sizeof(AVDOVIColorMetadata) -offsetof(AVDOVIColorMetadata, signal_eotf)); | ||
| 301 | } | ||
| 302 | |||
| 303 | /* Tries to reuse the static ext blocks. May reorder `ext->dm_static` */ | ||
| 304 | ✗ | static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) | |
| 305 | { | ||
| 306 | ✗ | int i, j, idx = 0; | |
| 307 | |||
| 308 | ✗ | for (i = 0; i < metadata->num_ext_blocks; i++) { | |
| 309 | ✗ | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); | |
| 310 | ✗ | if (!ff_dovi_rpu_extension_is_static(dm->level)) | |
| 311 | ✗ | continue; | |
| 312 | |||
| 313 | /* Find the first matching ext block and move it to [idx] */ | ||
| 314 | ✗ | for (j = idx; j < ext->num_static; j++) { | |
| 315 | ✗ | if (!memcmp(&ext->dm_static[j], dm, sizeof(*dm))) { | |
| 316 | ✗ | if (j != idx) | |
| 317 | ✗ | FFSWAP(AVDOVIDmData, ext->dm_static[j], ext->dm_static[idx]); | |
| 318 | ✗ | idx++; | |
| 319 | ✗ | break; | |
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | ✗ | if (j == ext->num_static) { | |
| 324 | /* Found no matching ext block */ | ||
| 325 | ✗ | return 0; | |
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | /* If idx is less than ext->num_static, then there are extra unmatched | ||
| 330 | * ext blocks inside ext->dm_static */ | ||
| 331 | ✗ | return idx == ext->num_static; | |
| 332 | } | ||
| 333 | |||
| 334 | ✗ | static inline void put_ue_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr, | |
| 335 | uint64_t coef) | ||
| 336 | { | ||
| 337 | union { uint32_t u32; float f32; } fpart; | ||
| 338 | |||
| 339 | ✗ | switch (hdr->coef_data_type) { | |
| 340 | ✗ | case RPU_COEFF_FIXED: | |
| 341 | ✗ | set_ue_golomb(pb, coef >> hdr->coef_log2_denom); | |
| 342 | ✗ | put_bits63(pb, hdr->coef_log2_denom, | |
| 343 | ✗ | coef & ((1LL << hdr->coef_log2_denom) - 1)); | |
| 344 | ✗ | break; | |
| 345 | ✗ | case RPU_COEFF_FLOAT: | |
| 346 | ✗ | fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom); | |
| 347 | ✗ | put_bits63(pb, hdr->coef_log2_denom, fpart.u32); | |
| 348 | ✗ | break; | |
| 349 | } | ||
| 350 | ✗ | } | |
| 351 | |||
| 352 | ✗ | static inline void put_se_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr, | |
| 353 | uint64_t coef) | ||
| 354 | { | ||
| 355 | union { uint32_t u32; float f32; } fpart; | ||
| 356 | |||
| 357 | ✗ | switch (hdr->coef_data_type) { | |
| 358 | ✗ | case RPU_COEFF_FIXED: | |
| 359 | ✗ | set_se_golomb(pb, coef >> hdr->coef_log2_denom); | |
| 360 | ✗ | put_bits63(pb, hdr->coef_log2_denom, | |
| 361 | ✗ | coef & ((1LL << hdr->coef_log2_denom) - 1)); | |
| 362 | ✗ | break; | |
| 363 | ✗ | case RPU_COEFF_FLOAT: | |
| 364 | ✗ | fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom); | |
| 365 | ✗ | put_bits63(pb, hdr->coef_log2_denom, fpart.u32); | |
| 366 | ✗ | break; | |
| 367 | } | ||
| 368 | ✗ | } | |
| 369 | |||
| 370 | ✗ | static int av_q2den(AVRational q, int den) | |
| 371 | { | ||
| 372 | ✗ | if (!q.den || q.den == den) | |
| 373 | ✗ | return q.num; | |
| 374 | ✗ | q = av_mul_q(q, av_make_q(den, 1)); | |
| 375 | ✗ | return (q.num + (q.den >> 1)) / q.den; | |
| 376 | } | ||
| 377 | |||
| 378 | ✗ | static void generate_ext_v1(PutBitContext *pb, const AVDOVIDmData *dm) | |
| 379 | { | ||
| 380 | int ext_block_length, start_pos, pad_bits; | ||
| 381 | |||
| 382 | ✗ | switch (dm->level) { | |
| 383 | ✗ | case 1: ext_block_length = 5; break; | |
| 384 | ✗ | case 2: ext_block_length = 11; break; | |
| 385 | ✗ | case 4: ext_block_length = 3; break; | |
| 386 | ✗ | case 5: ext_block_length = 7; break; | |
| 387 | ✗ | case 6: ext_block_length = 8; break; | |
| 388 | ✗ | case 255: ext_block_length = 6; break; | |
| 389 | ✗ | default: return; | |
| 390 | } | ||
| 391 | |||
| 392 | ✗ | set_ue_golomb(pb, ext_block_length); | |
| 393 | ✗ | put_bits(pb, 8, dm->level); | |
| 394 | ✗ | start_pos = put_bits_count(pb); | |
| 395 | |||
| 396 | ✗ | switch (dm->level) { | |
| 397 | ✗ | case 1: | |
| 398 | ✗ | put_bits(pb, 12, dm->l1.min_pq); | |
| 399 | ✗ | put_bits(pb, 12, dm->l1.max_pq); | |
| 400 | ✗ | put_bits(pb, 12, dm->l1.avg_pq); | |
| 401 | ✗ | break; | |
| 402 | ✗ | case 2: | |
| 403 | ✗ | put_bits(pb, 12, dm->l2.target_max_pq); | |
| 404 | ✗ | put_bits(pb, 12, dm->l2.trim_slope); | |
| 405 | ✗ | put_bits(pb, 12, dm->l2.trim_offset); | |
| 406 | ✗ | put_bits(pb, 12, dm->l2.trim_power); | |
| 407 | ✗ | put_bits(pb, 12, dm->l2.trim_chroma_weight); | |
| 408 | ✗ | put_bits(pb, 12, dm->l2.trim_saturation_gain); | |
| 409 | ✗ | put_sbits(pb, 13, dm->l2.ms_weight); | |
| 410 | ✗ | break; | |
| 411 | ✗ | case 4: | |
| 412 | ✗ | put_bits(pb, 12, dm->l4.anchor_pq); | |
| 413 | ✗ | put_bits(pb, 12, dm->l4.anchor_power); | |
| 414 | ✗ | break; | |
| 415 | ✗ | case 5: | |
| 416 | ✗ | put_bits(pb, 13, dm->l5.left_offset); | |
| 417 | ✗ | put_bits(pb, 13, dm->l5.right_offset); | |
| 418 | ✗ | put_bits(pb, 13, dm->l5.top_offset); | |
| 419 | ✗ | put_bits(pb, 13, dm->l5.bottom_offset); | |
| 420 | ✗ | break; | |
| 421 | ✗ | case 6: | |
| 422 | ✗ | put_bits(pb, 16, dm->l6.max_luminance); | |
| 423 | ✗ | put_bits(pb, 16, dm->l6.min_luminance); | |
| 424 | ✗ | put_bits(pb, 16, dm->l6.max_cll); | |
| 425 | ✗ | put_bits(pb, 16, dm->l6.max_fall); | |
| 426 | ✗ | break; | |
| 427 | ✗ | case 255: | |
| 428 | ✗ | put_bits(pb, 8, dm->l255.dm_run_mode); | |
| 429 | ✗ | put_bits(pb, 8, dm->l255.dm_run_version); | |
| 430 | ✗ | for (int i = 0; i < 4; i++) | |
| 431 | ✗ | put_bits(pb, 8, dm->l255.dm_debug[i]); | |
| 432 | ✗ | break; | |
| 433 | } | ||
| 434 | |||
| 435 | ✗ | pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos); | |
| 436 | av_assert1(pad_bits >= 0); | ||
| 437 | ✗ | put_bits(pb, pad_bits, 0); | |
| 438 | } | ||
| 439 | |||
| 440 | ✗ | static void put_cie_xy(PutBitContext *pb, AVCIExy xy) | |
| 441 | { | ||
| 442 | ✗ | const int denom = 32767; | |
| 443 | ✗ | put_sbits(pb, 16, av_q2den(xy.x, denom)); | |
| 444 | ✗ | put_sbits(pb, 16, av_q2den(xy.y, denom)); | |
| 445 | ✗ | } | |
| 446 | |||
| 447 | #define ANY6(arr) (arr[0] || arr[1] || arr[2] || arr[3] || arr[4] || arr[5]) | ||
| 448 | #define ANY_XY(xy) (xy.x.num || xy.y.num) | ||
| 449 | #define ANY_CSP(csp) (ANY_XY(csp.prim.r) || ANY_XY(csp.prim.g) || \ | ||
| 450 | ANY_XY(csp.prim.b) || ANY_XY(csp.wp)) | ||
| 451 | |||
| 452 | ✗ | static void generate_ext_v2(PutBitContext *pb, const AVDOVIDmData *dm) | |
| 453 | { | ||
| 454 | int ext_block_length, start_pos, pad_bits; | ||
| 455 | |||
| 456 | ✗ | switch (dm->level) { | |
| 457 | ✗ | case 3: ext_block_length = 5; break; | |
| 458 | ✗ | case 8: | |
| 459 | ✗ | if (ANY6(dm->l8.hue_vector_field)) { | |
| 460 | ✗ | ext_block_length = 25; | |
| 461 | ✗ | } else if (ANY6(dm->l8.saturation_vector_field)) { | |
| 462 | ✗ | ext_block_length = 19; | |
| 463 | ✗ | } else if (dm->l8.clip_trim) { | |
| 464 | ✗ | ext_block_length = 13; | |
| 465 | ✗ | } else if (dm->l8.target_mid_contrast) { | |
| 466 | ✗ | ext_block_length = 12; | |
| 467 | } else { | ||
| 468 | ✗ | ext_block_length = 10; | |
| 469 | } | ||
| 470 | ✗ | break; | |
| 471 | ✗ | case 9: | |
| 472 | ✗ | if (ANY_CSP(dm->l9.source_display_primaries)) { | |
| 473 | ✗ | ext_block_length = 17; | |
| 474 | } else { | ||
| 475 | ✗ | ext_block_length = 1; | |
| 476 | } | ||
| 477 | ✗ | break; | |
| 478 | ✗ | case 10: | |
| 479 | ✗ | if (ANY_CSP(dm->l10.target_display_primaries)) { | |
| 480 | ✗ | ext_block_length = 21; | |
| 481 | } else { | ||
| 482 | ✗ | ext_block_length = 5; | |
| 483 | } | ||
| 484 | ✗ | break; | |
| 485 | ✗ | case 11: ext_block_length = 4; break; | |
| 486 | ✗ | case 254: ext_block_length = 2; break; | |
| 487 | ✗ | default: return; | |
| 488 | } | ||
| 489 | |||
| 490 | ✗ | set_ue_golomb(pb, ext_block_length); | |
| 491 | ✗ | put_bits(pb, 8, dm->level); | |
| 492 | ✗ | start_pos = put_bits_count(pb); | |
| 493 | |||
| 494 | ✗ | switch (dm->level) { | |
| 495 | ✗ | case 3: | |
| 496 | ✗ | put_bits(pb, 12, dm->l3.min_pq_offset); | |
| 497 | ✗ | put_bits(pb, 12, dm->l3.max_pq_offset); | |
| 498 | ✗ | put_bits(pb, 12, dm->l3.avg_pq_offset); | |
| 499 | ✗ | break; | |
| 500 | ✗ | case 8: | |
| 501 | ✗ | put_bits(pb, 8, dm->l8.target_display_index); | |
| 502 | ✗ | put_bits(pb, 12, dm->l8.trim_slope); | |
| 503 | ✗ | put_bits(pb, 12, dm->l8.trim_offset); | |
| 504 | ✗ | put_bits(pb, 12, dm->l8.trim_power); | |
| 505 | ✗ | put_bits(pb, 12, dm->l8.trim_chroma_weight); | |
| 506 | ✗ | put_bits(pb, 12, dm->l8.trim_saturation_gain); | |
| 507 | ✗ | put_bits(pb, 12, dm->l8.ms_weight); | |
| 508 | ✗ | if (ext_block_length < 12) | |
| 509 | ✗ | break; | |
| 510 | ✗ | put_bits(pb, 12, dm->l8.target_mid_contrast); | |
| 511 | ✗ | if (ext_block_length < 13) | |
| 512 | ✗ | break; | |
| 513 | ✗ | put_bits(pb, 12, dm->l8.clip_trim); | |
| 514 | ✗ | if (ext_block_length < 19) | |
| 515 | ✗ | break; | |
| 516 | ✗ | for (int i = 0; i < 6; i++) | |
| 517 | ✗ | put_bits(pb, 8, dm->l8.saturation_vector_field[i]); | |
| 518 | ✗ | if (ext_block_length < 25) | |
| 519 | ✗ | break; | |
| 520 | ✗ | for (int i = 0; i < 6; i++) | |
| 521 | ✗ | put_bits(pb, 8, dm->l8.hue_vector_field[i]); | |
| 522 | ✗ | break; | |
| 523 | ✗ | case 9: | |
| 524 | ✗ | put_bits(pb, 8, dm->l9.source_primary_index); | |
| 525 | ✗ | if (ext_block_length < 17) | |
| 526 | ✗ | break; | |
| 527 | ✗ | put_cie_xy(pb, dm->l9.source_display_primaries.prim.r); | |
| 528 | ✗ | put_cie_xy(pb, dm->l9.source_display_primaries.prim.g); | |
| 529 | ✗ | put_cie_xy(pb, dm->l9.source_display_primaries.prim.b); | |
| 530 | ✗ | put_cie_xy(pb, dm->l9.source_display_primaries.wp); | |
| 531 | ✗ | break; | |
| 532 | ✗ | case 10: | |
| 533 | ✗ | put_bits(pb, 8, dm->l10.target_display_index); | |
| 534 | ✗ | put_bits(pb, 12, dm->l10.target_max_pq); | |
| 535 | ✗ | put_bits(pb, 12, dm->l10.target_min_pq); | |
| 536 | ✗ | put_bits(pb, 8, dm->l10.target_primary_index); | |
| 537 | ✗ | if (ext_block_length < 21) | |
| 538 | ✗ | break; | |
| 539 | ✗ | put_cie_xy(pb, dm->l10.target_display_primaries.prim.r); | |
| 540 | ✗ | put_cie_xy(pb, dm->l10.target_display_primaries.prim.g); | |
| 541 | ✗ | put_cie_xy(pb, dm->l10.target_display_primaries.prim.b); | |
| 542 | ✗ | put_cie_xy(pb, dm->l10.target_display_primaries.wp); | |
| 543 | ✗ | break; | |
| 544 | ✗ | case 11: | |
| 545 | ✗ | put_bits(pb, 8, dm->l11.content_type); | |
| 546 | ✗ | put_bits(pb, 4, dm->l11.whitepoint); | |
| 547 | ✗ | put_bits(pb, 1, dm->l11.reference_mode_flag); | |
| 548 | ✗ | put_bits(pb, 3, 0); /* reserved */ | |
| 549 | ✗ | put_bits(pb, 2, dm->l11.sharpness); | |
| 550 | ✗ | put_bits(pb, 2, dm->l11.noise_reduction); | |
| 551 | ✗ | put_bits(pb, 2, dm->l11.mpeg_noise_reduction); | |
| 552 | ✗ | put_bits(pb, 2, dm->l11.frame_rate_conversion); | |
| 553 | ✗ | put_bits(pb, 2, dm->l11.brightness); | |
| 554 | ✗ | put_bits(pb, 2, dm->l11.color); | |
| 555 | ✗ | break; | |
| 556 | ✗ | case 254: | |
| 557 | ✗ | put_bits(pb, 8, dm->l254.dm_mode); | |
| 558 | ✗ | put_bits(pb, 8, dm->l254.dm_version_index); | |
| 559 | ✗ | break; | |
| 560 | } | ||
| 561 | |||
| 562 | ✗ | pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos); | |
| 563 | av_assert1(pad_bits >= 0); | ||
| 564 | ✗ | put_bits(pb, pad_bits, 0); | |
| 565 | } | ||
| 566 | |||
| 567 | ✗ | int ff_dovi_rpu_generate(DOVIContext *s, const AVDOVIMetadata *metadata, | |
| 568 | int flags, uint8_t **out_rpu, int *out_size) | ||
| 569 | { | ||
| 570 | ✗ | PutBitContext *pb = &(PutBitContext){0}; | |
| 571 | const AVDOVIRpuDataHeader *hdr; | ||
| 572 | const AVDOVIDataMapping *mapping; | ||
| 573 | const AVDOVIColorMetadata *color; | ||
| 574 | int vdr_dm_metadata_present, vdr_rpu_id, use_prev_vdr_rpu, profile, | ||
| 575 | buffer_size, rpu_size, pad, zero_run, dm_compression; | ||
| 576 | int num_ext_blocks_v1, num_ext_blocks_v2; | ||
| 577 | ✗ | int dv_md_compression = s->cfg.dv_md_compression; | |
| 578 | uint32_t crc; | ||
| 579 | uint8_t *dst; | ||
| 580 | ✗ | if (!metadata) { | |
| 581 | ✗ | *out_rpu = NULL; | |
| 582 | ✗ | *out_size = 0; | |
| 583 | ✗ | return 0; | |
| 584 | } | ||
| 585 | |||
| 586 | ✗ | hdr = av_dovi_get_header(metadata); | |
| 587 | ✗ | mapping = av_dovi_get_mapping(metadata); | |
| 588 | ✗ | color = av_dovi_get_color(metadata); | |
| 589 | ✗ | av_assert0(s->cfg.dv_profile); | |
| 590 | |||
| 591 | ✗ | if (!(flags & FF_DOVI_COMPRESS_RPU)) | |
| 592 | ✗ | dv_md_compression = AV_DOVI_COMPRESSION_NONE; | |
| 593 | ✗ | else if (dv_md_compression == AV_DOVI_COMPRESSION_RESERVED) | |
| 594 | ✗ | return AVERROR(EINVAL); | |
| 595 | |||
| 596 | ✗ | if (hdr->rpu_type != 2) { | |
| 597 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Unhandled RPU type %"PRIu8"\n", | |
| 598 | ✗ | hdr->rpu_type); | |
| 599 | ✗ | return AVERROR_INVALIDDATA; | |
| 600 | } | ||
| 601 | |||
| 602 | ✗ | if (!(flags & FF_DOVI_COMPRESS_RPU)) | |
| 603 | ✗ | dv_md_compression = AV_DOVI_COMPRESSION_NONE; | |
| 604 | |||
| 605 | ✗ | vdr_rpu_id = mapping->vdr_rpu_id; | |
| 606 | ✗ | use_prev_vdr_rpu = 0; | |
| 607 | |||
| 608 | ✗ | if (!s->vdr[vdr_rpu_id]) { | |
| 609 | ✗ | s->vdr[vdr_rpu_id] = av_refstruct_allocz(sizeof(AVDOVIDataMapping)); | |
| 610 | ✗ | if (!s->vdr[vdr_rpu_id]) | |
| 611 | ✗ | return AVERROR(ENOMEM); | |
| 612 | } | ||
| 613 | |||
| 614 | ✗ | switch (dv_md_compression) { | |
| 615 | ✗ | case AV_DOVI_COMPRESSION_LIMITED: | |
| 616 | /* Limited metadata compression requires vdr_rpi_id == 0 */ | ||
| 617 | ✗ | if (vdr_rpu_id != 0) | |
| 618 | ✗ | break; | |
| 619 | /* fall through */ | ||
| 620 | case AV_DOVI_COMPRESSION_EXTENDED: | ||
| 621 | ✗ | if (s->vdr[vdr_rpu_id]) | |
| 622 | ✗ | use_prev_vdr_rpu = !memcmp(s->vdr[vdr_rpu_id], mapping, sizeof(*mapping)); | |
| 623 | ✗ | break; | |
| 624 | ✗ | case AV_DOVI_COMPRESSION_RESERVED: | |
| 625 | ✗ | return AVERROR(EINVAL); | |
| 626 | } | ||
| 627 | |||
| 628 | ✗ | if (s->cfg.dv_md_compression != AV_DOVI_COMPRESSION_EXTENDED) { | |
| 629 | /* Flush VDRs to avoid leaking old state; maintaining multiple VDR | ||
| 630 | * references requires extended compression */ | ||
| 631 | ✗ | for (int i = 0; i <= DOVI_MAX_DM_ID; i++) { | |
| 632 | ✗ | if (i != vdr_rpu_id) | |
| 633 | ✗ | av_refstruct_unref(&s->vdr[i]); | |
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | ✗ | if (metadata->num_ext_blocks && !s->ext_blocks) { | |
| 638 | ✗ | s->ext_blocks = av_refstruct_allocz(sizeof(*s->ext_blocks)); | |
| 639 | ✗ | if (!s->ext_blocks) | |
| 640 | ✗ | return AVERROR(ENOMEM); | |
| 641 | } | ||
| 642 | |||
| 643 | ✗ | vdr_dm_metadata_present = memcmp(color, &ff_dovi_color_default, sizeof(*color)); | |
| 644 | ✗ | if (metadata->num_ext_blocks) | |
| 645 | ✗ | vdr_dm_metadata_present = 1; | |
| 646 | |||
| 647 | ✗ | if (vdr_dm_metadata_present && !s->dm) { | |
| 648 | ✗ | s->dm = av_refstruct_allocz(sizeof(AVDOVIColorMetadata)); | |
| 649 | ✗ | if (!s->dm) | |
| 650 | ✗ | return AVERROR(ENOMEM); | |
| 651 | } | ||
| 652 | |||
| 653 | ✗ | dm_compression = 0; | |
| 654 | ✗ | if (dv_md_compression != AV_DOVI_COMPRESSION_NONE) { | |
| 655 | ✗ | if (!cmp_dm_level0(s->dm, color) && try_reuse_ext(s->ext_blocks, metadata)) | |
| 656 | ✗ | dm_compression = 1; | |
| 657 | } | ||
| 658 | |||
| 659 | ✗ | num_ext_blocks_v1 = num_ext_blocks_v2 = 0; | |
| 660 | ✗ | for (int i = 0; i < metadata->num_ext_blocks; i++) { | |
| 661 | ✗ | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); | |
| 662 | ✗ | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) | |
| 663 | ✗ | continue; | |
| 664 | |||
| 665 | ✗ | switch (dm->level) { | |
| 666 | ✗ | case 1: | |
| 667 | case 2: | ||
| 668 | case 4: | ||
| 669 | case 5: | ||
| 670 | case 6: | ||
| 671 | case 255: | ||
| 672 | ✗ | num_ext_blocks_v1++; | |
| 673 | ✗ | break; | |
| 674 | ✗ | case 3: | |
| 675 | case 8: | ||
| 676 | case 9: | ||
| 677 | case 10: | ||
| 678 | case 11: | ||
| 679 | case 254: | ||
| 680 | ✗ | num_ext_blocks_v2++; | |
| 681 | ✗ | break; | |
| 682 | ✗ | default: | |
| 683 | ✗ | av_log(s->logctx, AV_LOG_ERROR, "Invalid ext block level %d\n", | |
| 684 | ✗ | dm->level); | |
| 685 | ✗ | return AVERROR_INVALIDDATA; | |
| 686 | } | ||
| 687 | } | ||
| 688 | |||
| 689 | ✗ | buffer_size = 12 /* vdr seq info */ + 5 /* CRC32 + terminator */; | |
| 690 | ✗ | buffer_size += num_ext_blocks_v1 * 13; | |
| 691 | ✗ | buffer_size += num_ext_blocks_v2 * 28; | |
| 692 | ✗ | if (!use_prev_vdr_rpu) { | |
| 693 | ✗ | buffer_size += 160; | |
| 694 | ✗ | for (int c = 0; c < 3; c++) { | |
| 695 | ✗ | for (int i = 0; i < mapping->curves[c].num_pivots - 1; i++) { | |
| 696 | ✗ | switch (mapping->curves[c].mapping_idc[i]) { | |
| 697 | ✗ | case AV_DOVI_MAPPING_POLYNOMIAL: buffer_size += 26; break; | |
| 698 | ✗ | case AV_DOVI_MAPPING_MMR: buffer_size += 177; break; | |
| 699 | } | ||
| 700 | } | ||
| 701 | } | ||
| 702 | } | ||
| 703 | ✗ | if (vdr_dm_metadata_present) | |
| 704 | ✗ | buffer_size += 67; | |
| 705 | |||
| 706 | ✗ | av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, buffer_size); | |
| 707 | ✗ | if (!s->rpu_buf) | |
| 708 | ✗ | return AVERROR(ENOMEM); | |
| 709 | ✗ | init_put_bits(pb, s->rpu_buf, s->rpu_buf_sz); | |
| 710 | |||
| 711 | /* RPU header */ | ||
| 712 | ✗ | put_bits(pb, 6, hdr->rpu_type); | |
| 713 | ✗ | put_bits(pb, 11, hdr->rpu_format); | |
| 714 | ✗ | put_bits(pb, 4, hdr->vdr_rpu_profile); | |
| 715 | ✗ | put_bits(pb, 4, hdr->vdr_rpu_level); | |
| 716 | ✗ | put_bits(pb, 1, 1); /* vdr_seq_info_present */ | |
| 717 | ✗ | put_bits(pb, 1, hdr->chroma_resampling_explicit_filter_flag); | |
| 718 | ✗ | put_bits(pb, 2, hdr->coef_data_type); | |
| 719 | ✗ | if (hdr->coef_data_type == RPU_COEFF_FIXED) | |
| 720 | ✗ | set_ue_golomb(pb, hdr->coef_log2_denom); | |
| 721 | ✗ | put_bits(pb, 2, hdr->vdr_rpu_normalized_idc); | |
| 722 | ✗ | put_bits(pb, 1, hdr->bl_video_full_range_flag); | |
| 723 | ✗ | if ((hdr->rpu_format & 0x700) == 0) { | |
| 724 | ✗ | int ext_mapping_idc = (hdr->ext_mapping_idc_5_7 << 5) | hdr->ext_mapping_idc_0_4; | |
| 725 | ✗ | set_ue_golomb(pb, hdr->bl_bit_depth - 8); | |
| 726 | ✗ | set_ue_golomb(pb, (ext_mapping_idc << 8) | (hdr->el_bit_depth - 8)); | |
| 727 | ✗ | set_ue_golomb(pb, hdr->vdr_bit_depth - 8); | |
| 728 | ✗ | put_bits(pb, 1, hdr->spatial_resampling_filter_flag); | |
| 729 | ✗ | put_bits(pb, 3, dm_compression); | |
| 730 | ✗ | put_bits(pb, 1, hdr->el_spatial_resampling_filter_flag); | |
| 731 | ✗ | put_bits(pb, 1, hdr->disable_residual_flag); | |
| 732 | } | ||
| 733 | ✗ | s->header = *hdr; | |
| 734 | |||
| 735 | ✗ | put_bits(pb, 1, vdr_dm_metadata_present); | |
| 736 | ✗ | put_bits(pb, 1, use_prev_vdr_rpu); | |
| 737 | ✗ | set_ue_golomb(pb, vdr_rpu_id); | |
| 738 | ✗ | s->mapping = s->vdr[vdr_rpu_id]; | |
| 739 | |||
| 740 | ✗ | profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr); | |
| 741 | |||
| 742 | ✗ | if (!use_prev_vdr_rpu) { | |
| 743 | ✗ | set_ue_golomb(pb, mapping->mapping_color_space); | |
| 744 | ✗ | set_ue_golomb(pb, mapping->mapping_chroma_format_idc); | |
| 745 | ✗ | for (int c = 0; c < 3; c++) { | |
| 746 | ✗ | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; | |
| 747 | ✗ | int prev = 0; | |
| 748 | ✗ | set_ue_golomb(pb, curve->num_pivots - 2); | |
| 749 | ✗ | for (int i = 0; i < curve->num_pivots; i++) { | |
| 750 | ✗ | put_bits(pb, hdr->bl_bit_depth, curve->pivots[i] - prev); | |
| 751 | ✗ | prev = curve->pivots[i]; | |
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | ✗ | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { | |
| 756 | ✗ | put_bits(pb, 3, mapping->nlq_method_idc); | |
| 757 | ✗ | put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[0]); | |
| 758 | ✗ | put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[1] - mapping->nlq_pivots[0]); | |
| 759 | } | ||
| 760 | |||
| 761 | ✗ | set_ue_golomb(pb, mapping->num_x_partitions - 1); | |
| 762 | ✗ | set_ue_golomb(pb, mapping->num_y_partitions - 1); | |
| 763 | |||
| 764 | ✗ | for (int c = 0; c < 3; c++) { | |
| 765 | ✗ | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; | |
| 766 | ✗ | for (int i = 0; i < curve->num_pivots - 1; i++) { | |
| 767 | ✗ | set_ue_golomb(pb, curve->mapping_idc[i]); | |
| 768 | ✗ | switch (curve->mapping_idc[i]) { | |
| 769 | ✗ | case AV_DOVI_MAPPING_POLYNOMIAL: { | |
| 770 | ✗ | set_ue_golomb(pb, curve->poly_order[i] - 1); | |
| 771 | ✗ | if (curve->poly_order[i] == 1) | |
| 772 | ✗ | put_bits(pb, 1, 0); /* linear_interp_flag */ | |
| 773 | ✗ | for (int k = 0; k <= curve->poly_order[i]; k++) | |
| 774 | ✗ | put_se_coef(pb, hdr, curve->poly_coef[i][k]); | |
| 775 | ✗ | break; | |
| 776 | } | ||
| 777 | ✗ | case AV_DOVI_MAPPING_MMR: { | |
| 778 | ✗ | put_bits(pb, 2, curve->mmr_order[i] - 1); | |
| 779 | ✗ | put_se_coef(pb, hdr, curve->mmr_constant[i]); | |
| 780 | ✗ | for (int j = 0; j < curve->mmr_order[i]; j++) { | |
| 781 | ✗ | for (int k = 0; k < 7; k++) | |
| 782 | ✗ | put_se_coef(pb, hdr, curve->mmr_coef[i][j][k]); | |
| 783 | } | ||
| 784 | ✗ | break; | |
| 785 | } | ||
| 786 | } | ||
| 787 | } | ||
| 788 | } | ||
| 789 | |||
| 790 | ✗ | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { | |
| 791 | ✗ | for (int c = 0; c < 3; c++) { | |
| 792 | ✗ | const AVDOVINLQParams *nlq = &mapping->nlq[c]; | |
| 793 | ✗ | put_bits(pb, hdr->el_bit_depth, nlq->nlq_offset); | |
| 794 | ✗ | put_ue_coef(pb, hdr, nlq->vdr_in_max); | |
| 795 | ✗ | switch (mapping->nlq_method_idc) { | |
| 796 | ✗ | case AV_DOVI_NLQ_LINEAR_DZ: | |
| 797 | ✗ | put_ue_coef(pb, hdr, nlq->linear_deadzone_slope); | |
| 798 | ✗ | put_ue_coef(pb, hdr, nlq->linear_deadzone_threshold); | |
| 799 | ✗ | break; | |
| 800 | } | ||
| 801 | } | ||
| 802 | } | ||
| 803 | |||
| 804 | ✗ | memcpy(s->vdr[vdr_rpu_id], mapping, sizeof(*mapping)); | |
| 805 | } | ||
| 806 | |||
| 807 | ✗ | if (vdr_dm_metadata_present) { | |
| 808 | ✗ | DOVIExt *ext = s->ext_blocks; | |
| 809 | ✗ | const int denom = profile == 4 ? (1 << 30) : (1 << 28); | |
| 810 | ✗ | set_ue_golomb(pb, color->dm_metadata_id); /* affected_dm_id */ | |
| 811 | ✗ | set_ue_golomb(pb, color->dm_metadata_id); /* current_dm_id */ | |
| 812 | ✗ | set_ue_golomb(pb, color->scene_refresh_flag); | |
| 813 | ✗ | if (!dm_compression) { | |
| 814 | ✗ | for (int i = 0; i < 9; i++) | |
| 815 | ✗ | put_sbits(pb, 16, av_q2den(color->ycc_to_rgb_matrix[i], 1 << 13)); | |
| 816 | ✗ | for (int i = 0; i < 3; i++) | |
| 817 | ✗ | put_bits32(pb, av_q2den(color->ycc_to_rgb_offset[i], denom)); | |
| 818 | ✗ | for (int i = 0; i < 9; i++) | |
| 819 | ✗ | put_sbits(pb, 16, av_q2den(color->rgb_to_lms_matrix[i], 1 << 14)); | |
| 820 | ✗ | put_bits(pb, 16, color->signal_eotf); | |
| 821 | ✗ | put_bits(pb, 16, color->signal_eotf_param0); | |
| 822 | ✗ | put_bits(pb, 16, color->signal_eotf_param1); | |
| 823 | ✗ | put_bits32(pb, color->signal_eotf_param2); | |
| 824 | ✗ | put_bits(pb, 5, color->signal_bit_depth); | |
| 825 | ✗ | put_bits(pb, 2, color->signal_color_space); | |
| 826 | ✗ | put_bits(pb, 2, color->signal_chroma_format); | |
| 827 | ✗ | put_bits(pb, 2, color->signal_full_range_flag); | |
| 828 | ✗ | put_bits(pb, 12, color->source_min_pq); | |
| 829 | ✗ | put_bits(pb, 12, color->source_max_pq); | |
| 830 | ✗ | put_bits(pb, 10, color->source_diagonal); | |
| 831 | } | ||
| 832 | |||
| 833 | ✗ | memcpy(s->dm, color, sizeof(*color)); | |
| 834 | ✗ | s->color = s->dm; | |
| 835 | |||
| 836 | /* Extension blocks */ | ||
| 837 | ✗ | set_ue_golomb(pb, num_ext_blocks_v1); | |
| 838 | ✗ | align_put_bits(pb); | |
| 839 | ✗ | for (int i = 0; i < metadata->num_ext_blocks; i++) { | |
| 840 | ✗ | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); | |
| 841 | ✗ | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) | |
| 842 | ✗ | continue; | |
| 843 | ✗ | generate_ext_v1(pb, dm); | |
| 844 | } | ||
| 845 | |||
| 846 | ✗ | if (num_ext_blocks_v2) { | |
| 847 | ✗ | set_ue_golomb(pb, num_ext_blocks_v2); | |
| 848 | ✗ | align_put_bits(pb); | |
| 849 | ✗ | for (int i = 0; i < metadata->num_ext_blocks; i++) { | |
| 850 | ✗ | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); | |
| 851 | ✗ | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) | |
| 852 | ✗ | continue; | |
| 853 | ✗ | generate_ext_v2(pb, av_dovi_get_ext(metadata, i)); | |
| 854 | } | ||
| 855 | } | ||
| 856 | |||
| 857 | ✗ | if (ext) { | |
| 858 | ✗ | size_t ext_sz = FFMIN(sizeof(AVDOVIDmData), metadata->ext_block_size); | |
| 859 | ✗ | ext->num_dynamic = 0; | |
| 860 | ✗ | if (!dm_compression) | |
| 861 | ✗ | ext->num_static = 0; | |
| 862 | ✗ | for (int i = 0; i < metadata->num_ext_blocks; i++) { | |
| 863 | ✗ | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); | |
| 864 | ✗ | if (!ff_dovi_rpu_extension_is_static(dm->level)) | |
| 865 | ✗ | memcpy(&ext->dm_dynamic[ext->num_dynamic++], dm, ext_sz); | |
| 866 | ✗ | else if (!dm_compression) | |
| 867 | ✗ | memcpy(&ext->dm_static[ext->num_static++], dm, ext_sz); | |
| 868 | } | ||
| 869 | } | ||
| 870 | } else { | ||
| 871 | ✗ | s->color = &ff_dovi_color_default; | |
| 872 | ✗ | av_refstruct_unref(&s->ext_blocks); | |
| 873 | } | ||
| 874 | |||
| 875 | ✗ | flush_put_bits(pb); | |
| 876 | ✗ | crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, | |
| 877 | ✗ | s->rpu_buf, put_bytes_output(pb))); | |
| 878 | ✗ | put_bits32(pb, crc); | |
| 879 | ✗ | put_bits(pb, 8, 0x80); /* terminator */ | |
| 880 | ✗ | flush_put_bits(pb); | |
| 881 | |||
| 882 | ✗ | rpu_size = put_bytes_output(pb); | |
| 883 | ✗ | if (flags & FF_DOVI_WRAP_T35) { | |
| 884 | ✗ | *out_rpu = av_malloc(rpu_size + 15); | |
| 885 | ✗ | if (!*out_rpu) | |
| 886 | ✗ | return AVERROR(ENOMEM); | |
| 887 | ✗ | init_put_bits(pb, *out_rpu, rpu_size + 15); | |
| 888 | ✗ | put_bits(pb, 8, ITU_T_T35_COUNTRY_CODE_US); | |
| 889 | ✗ | put_bits(pb, 16, ITU_T_T35_PROVIDER_CODE_DOLBY); | |
| 890 | ✗ | put_bits32(pb, 0x800); /* provider_oriented_code */ | |
| 891 | ✗ | put_bits(pb, 27, 0x01be6841u); /* fixed EMDF header, see above */ | |
| 892 | ✗ | if (rpu_size > 0xFF) { | |
| 893 | av_assert2(rpu_size <= 0x10000); | ||
| 894 | ✗ | put_bits(pb, 8, (rpu_size >> 8) - 1); | |
| 895 | ✗ | put_bits(pb, 1, 1); /* read_more */ | |
| 896 | ✗ | put_bits(pb, 8, rpu_size & 0xFF); | |
| 897 | ✗ | put_bits(pb, 1, 0); | |
| 898 | } else { | ||
| 899 | ✗ | put_bits(pb, 8, rpu_size); | |
| 900 | ✗ | put_bits(pb, 1, 0); | |
| 901 | } | ||
| 902 | ✗ | ff_copy_bits(pb, s->rpu_buf, rpu_size * 8); | |
| 903 | ✗ | put_bits(pb, 17, 0x400); /* emdf payload id + emdf_protection */ | |
| 904 | |||
| 905 | ✗ | pad = pb->bit_left & 7; | |
| 906 | ✗ | put_bits(pb, pad, (1 << pad) - 1); /* pad to next byte with 1 bits */ | |
| 907 | ✗ | flush_put_bits(pb); | |
| 908 | ✗ | *out_size = put_bytes_output(pb); | |
| 909 | ✗ | return 0; | |
| 910 | ✗ | } else if (flags & FF_DOVI_WRAP_NAL) { | |
| 911 | ✗ | *out_rpu = dst = av_malloc(4 + rpu_size * 3 / 2); /* worst case */ | |
| 912 | ✗ | if (!*out_rpu) | |
| 913 | ✗ | return AVERROR(ENOMEM); | |
| 914 | ✗ | *dst++ = 25; /* NAL prefix */ | |
| 915 | ✗ | zero_run = 0; | |
| 916 | ✗ | for (int i = 0; i < rpu_size; i++) { | |
| 917 | ✗ | if (zero_run < 2) { | |
| 918 | ✗ | if (s->rpu_buf[i] == 0) { | |
| 919 | ✗ | zero_run++; | |
| 920 | } else { | ||
| 921 | ✗ | zero_run = 0; | |
| 922 | } | ||
| 923 | } else { | ||
| 924 | ✗ | if ((s->rpu_buf[i] & ~3) == 0) { | |
| 925 | /* emulation prevention */ | ||
| 926 | ✗ | *dst++ = 3; | |
| 927 | } | ||
| 928 | ✗ | zero_run = s->rpu_buf[i] == 0; | |
| 929 | } | ||
| 930 | ✗ | *dst++ = s->rpu_buf[i]; | |
| 931 | } | ||
| 932 | ✗ | *out_size = dst - *out_rpu; | |
| 933 | ✗ | return 0; | |
| 934 | } else { | ||
| 935 | /* Return intermediate buffer directly */ | ||
| 936 | ✗ | *out_rpu = s->rpu_buf; | |
| 937 | ✗ | *out_size = rpu_size; | |
| 938 | ✗ | s->rpu_buf = NULL; | |
| 939 | ✗ | s->rpu_buf_sz = 0; | |
| 940 | ✗ | return 0; | |
| 941 | } | ||
| 942 | } | ||
| 943 |