mirror of
https://github.com/mpv-player/mpv.git
synced 2024-09-20 03:52:22 +02:00
video: Support BT.2020 constant luminance system
Signed-off-by: wm4 <wm4@nowhere>
This commit is contained in:
parent
ef6db24366
commit
204fed4d5b
@ -537,7 +537,8 @@ OPTIONS
|
||||
:auto: automatic selection (default)
|
||||
:BT.601: ITU-R BT.601 (SD)
|
||||
:BT.709: ITU-R BT.709 (HD)
|
||||
:BT.2020-NC: ITU-R BT.2020 non-constant luminance system
|
||||
:BT.2020-NCL: ITU-R BT.2020 non-constant luminance system
|
||||
:BT.2020-CL: ITU-R BT.2020 constant luminance system
|
||||
:SMPTE-240M: SMPTE-240M
|
||||
|
||||
``--colormatrix-input-range=<color-range>``
|
||||
|
@ -389,7 +389,8 @@ const m_option_t mp_opts[] = {
|
||||
{"BT.601", MP_CSP_BT_601},
|
||||
{"BT.709", MP_CSP_BT_709},
|
||||
{"SMPTE-240M", MP_CSP_SMPTE_240M},
|
||||
{"BT.2020-NC", MP_CSP_BT_2020_NC},
|
||||
{"BT.2020-NCL", MP_CSP_BT_2020_NC},
|
||||
{"BT.2020-CL", MP_CSP_BT_2020_C},
|
||||
{"YCgCo", MP_CSP_YCGCO})),
|
||||
OPT_CHOICE("colormatrix-input-range", requested_input_range, 0,
|
||||
({"auto", MP_CSP_LEVELS_AUTO},
|
||||
|
@ -42,7 +42,8 @@ const char *const mp_csp_names[MP_CSP_COUNT] = {
|
||||
"BT.601 (SD)",
|
||||
"BT.709 (HD)",
|
||||
"SMPTE-240M",
|
||||
"BT.2020-NC (UHD)",
|
||||
"BT.2020-NCL (UHD)",
|
||||
"BT.2020-CL (UHD)",
|
||||
"RGB",
|
||||
"XYZ",
|
||||
"YCgCo",
|
||||
@ -83,6 +84,7 @@ enum mp_csp avcol_spc_to_mp_csp(int avcolorspace)
|
||||
case AVCOL_SPC_BT470BG: return MP_CSP_BT_601;
|
||||
#if HAVE_AVCOL_SPC_BT2020
|
||||
case AVCOL_SPC_BT2020_NCL: return MP_CSP_BT_2020_NC;
|
||||
case AVCOL_SPC_BT2020_CL: return MP_CSP_BT_2020_C;
|
||||
#endif
|
||||
case AVCOL_SPC_SMPTE170M: return MP_CSP_BT_601;
|
||||
case AVCOL_SPC_SMPTE240M: return MP_CSP_SMPTE_240M;
|
||||
@ -122,6 +124,7 @@ int mp_csp_to_avcol_spc(enum mp_csp colorspace)
|
||||
case MP_CSP_BT_601: return AVCOL_SPC_BT470BG;
|
||||
#if HAVE_AVCOL_SPC_BT2020
|
||||
case MP_CSP_BT_2020_NC: return AVCOL_SPC_BT2020_NCL;
|
||||
case MP_CSP_BT_2020_C: return AVCOL_SPC_BT2020_CL;
|
||||
#endif
|
||||
case MP_CSP_SMPTE_240M: return AVCOL_SPC_SMPTE240M;
|
||||
case MP_CSP_RGB: return AVCOL_SPC_RGB;
|
||||
@ -310,6 +313,15 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
|
||||
case MP_CSP_BT_709: luma_coeffs(m, 0.2126, 0.7152, 0.0722); break;
|
||||
case MP_CSP_SMPTE_240M: luma_coeffs(m, 0.2122, 0.7013, 0.0865); break;
|
||||
case MP_CSP_BT_2020_NC: luma_coeffs(m, 0.2627, 0.6780, 0.0593); break;
|
||||
case MP_CSP_BT_2020_C: {
|
||||
// Note: This outputs into the [-0.5,0.5] range for chroma information.
|
||||
// If this clips on any VO, a constant 0.5 coefficient can be added
|
||||
// to the chroma channels to normalize them into [0,1]. This is not
|
||||
// currently needed by anything, though.
|
||||
static const float ycbcr_to_crycb[3][4] = {{0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
|
||||
memcpy(m, ycbcr_to_crycb, sizeof(ycbcr_to_crycb));
|
||||
break;
|
||||
}
|
||||
case MP_CSP_RGB: {
|
||||
static const float ident[3][4] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
||||
memcpy(m, ident, sizeof(ident));
|
||||
|
@ -38,6 +38,7 @@ enum mp_csp {
|
||||
MP_CSP_BT_709,
|
||||
MP_CSP_SMPTE_240M,
|
||||
MP_CSP_BT_2020_NC,
|
||||
MP_CSP_BT_2020_C,
|
||||
MP_CSP_RGB,
|
||||
MP_CSP_XYZ,
|
||||
MP_CSP_YCGCO,
|
||||
|
@ -524,6 +524,7 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
|
||||
if (params->colorspace != MP_CSP_BT_601 &&
|
||||
params->colorspace != MP_CSP_BT_709 &&
|
||||
params->colorspace != MP_CSP_BT_2020_NC &&
|
||||
params->colorspace != MP_CSP_BT_2020_C &&
|
||||
params->colorspace != MP_CSP_SMPTE_240M &&
|
||||
params->colorspace != MP_CSP_YCGCO)
|
||||
{
|
||||
@ -539,7 +540,8 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
|
||||
// We assume BT.709 primaries for all untagged BT.609/BT.709
|
||||
// content, because it offers the minimal deviation from all three,
|
||||
// including both NTSC and PAL/SECAM.
|
||||
if (params->colorspace == MP_CSP_BT_2020_NC) {
|
||||
if (params->colorspace == MP_CSP_BT_2020_NC ||
|
||||
params->colorspace == MP_CSP_BT_2020_C) {
|
||||
params->primaries = MP_CSP_PRIM_BT_2020;
|
||||
} else {
|
||||
params->primaries = MP_CSP_PRIM_BT_709;
|
||||
|
@ -894,11 +894,12 @@ static void compile_shaders(struct gl_video *p)
|
||||
|
||||
bool use_input_gamma = p->input_gamma != 1.0;
|
||||
bool use_conv_gamma = p->conv_gamma != 1.0;
|
||||
bool use_const_luma = p->image_params.colorspace == MP_CSP_BT_2020_C;
|
||||
|
||||
// Linear light scaling is only enabled when either color correction
|
||||
// option (3dlut or srgb) is enabled, otherwise scaling is done in the
|
||||
// source space.
|
||||
bool convert_to_linear_gamma = !p->is_linear_rgb && use_cms;
|
||||
// source space. We also need to linearize for constant luminance systems.
|
||||
bool convert_to_linear_gamma = !p->is_linear_rgb && use_cms || use_const_luma;
|
||||
|
||||
if (p->image_format == IMGFMT_NV12 || p->image_format == IMGFMT_NV21) {
|
||||
shader_def(&header_conv, "USE_CONV", "CONV_NV12");
|
||||
@ -914,8 +915,11 @@ static void compile_shaders(struct gl_video *p)
|
||||
shader_def_opt(&header_conv, "USE_INPUT_GAMMA", use_input_gamma);
|
||||
shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb);
|
||||
shader_def_opt(&header_conv, "USE_CONV_GAMMA", use_conv_gamma);
|
||||
shader_def_opt(&header_conv, "USE_LINEAR_LIGHT", convert_to_linear_gamma);
|
||||
shader_def_opt(&header_conv, "USE_APPROX_GAMMA", p->opts.approx_gamma);
|
||||
shader_def_opt(&header_conv, "USE_CONST_LUMA", use_const_luma);
|
||||
shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_APPROX",
|
||||
convert_to_linear_gamma && p->opts.approx_gamma);
|
||||
shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_BT2020",
|
||||
convert_to_linear_gamma && !p->opts.approx_gamma);
|
||||
if (p->opts.alpha_mode > 0 && p->has_alpha && p->plane_count > 3)
|
||||
shader_def(&header_conv, "USE_ALPHA_PLANE", "3");
|
||||
if (p->opts.alpha_mode == 2 && p->has_alpha)
|
||||
@ -926,6 +930,10 @@ static void compile_shaders(struct gl_video *p)
|
||||
shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
|
||||
// 3DLUT overrides SRGB
|
||||
shader_def_opt(&header_final, "USE_SRGB", p->opts.srgb && !p->use_lut_3d);
|
||||
shader_def_opt(&header_final, "USE_CONST_LUMA_INV_APPROX",
|
||||
use_const_luma && !use_cms && p->opts.approx_gamma);
|
||||
shader_def_opt(&header_final, "USE_CONST_LUMA_INV_BT2020",
|
||||
use_const_luma && !use_cms && !p->opts.approx_gamma);
|
||||
shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0);
|
||||
shader_def_opt(&header_final, "USE_TEMPORAL_DITHER", p->opts.temporal_dither);
|
||||
|
||||
@ -1339,7 +1347,7 @@ static void init_video(struct gl_video *p, const struct mp_image_params *params)
|
||||
}
|
||||
|
||||
int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
|
||||
if (p->is_yuv)
|
||||
if (p->is_yuv && p->image_params.colorspace != MP_CSP_BT_2020_C)
|
||||
eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX;
|
||||
p->video_eq.capabilities = eq_caps;
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
#if __VERSION__ >= 130
|
||||
vec3 srgb_compand(vec3 v)
|
||||
{
|
||||
return mix(v * 12.92, 1.055 * pow(v, vec3(1.0/2.4)) - vec3(0.055),
|
||||
return mix(v * 12.92, 1.055 * pow(v, vec3(1.0/2.4)) - 0.055,
|
||||
lessThanEqual(vec3(0.0031308), v));
|
||||
}
|
||||
|
||||
@ -54,6 +54,12 @@ vec3 bt2020_expand(vec3 v)
|
||||
return mix(v / 4.5, pow((v + vec3(0.0993))/1.0993, vec3(1/0.45)),
|
||||
lessThanEqual(vec3(0.08145), v));
|
||||
}
|
||||
|
||||
vec3 bt2020_compand(vec3 v)
|
||||
{
|
||||
return mix(v * 4.5, 1.0993 * pow(v, vec3(0.45)) - vec3(0.0993),
|
||||
lessThanEqual(vec3(0.0181), v));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Constant matrix for conversion from BT.2020 to sRGB
|
||||
@ -393,13 +399,30 @@ void main() {
|
||||
#ifdef USE_COLORMATRIX
|
||||
// Conversion from Y'CbCr or other spaces to RGB
|
||||
color = mat3(colormatrix) * color + colormatrix[3];
|
||||
color = clamp(color, 0, 1);
|
||||
#endif
|
||||
#ifdef USE_CONV_GAMMA
|
||||
// Post-colormatrix converted gamma correction (eg. for MP_IMGFLAG_XYZ)
|
||||
color = pow(color, vec3(conv_gamma));
|
||||
#endif
|
||||
#ifdef USE_LINEAR_LIGHT
|
||||
#ifdef USE_CONST_LUMA
|
||||
// Conversion from C'rcY'cC'bc to R'Y'cB' via the BT.2020 CL system:
|
||||
// C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0
|
||||
// = (B'-Y'c) / 1.5816 | C'bc > 0
|
||||
//
|
||||
// C'rc = (R'-Y'c) / 1.7184 | C'rc <= 0
|
||||
// = (R'-Y'c) / 0.9936 | C'rc > 0
|
||||
//
|
||||
// as per the BT.2020 specification, table 4. This is a non-linear
|
||||
// transformation because (constant) luminance receives non-equal
|
||||
// contributions from the three different channels.
|
||||
color.br = color.br * mix(vec2(1.5816, 0.9936), vec2(1.9404, 1.7184),
|
||||
lessThanEqual(color.br, vec2(0))) + color.gg;
|
||||
#endif
|
||||
#ifdef USE_COLORMATRIX
|
||||
// Clamp down here to avoid clipping CbCr details before CONST_LUMA
|
||||
// has a chance to convert them.
|
||||
color = clamp(color, 0, 1);
|
||||
#endif
|
||||
// If we are scaling in linear light (SRGB or 3DLUT option enabled), we
|
||||
// expand our source colors before scaling. This shader currently just
|
||||
// assumes everything uses the BT.2020 12-bit gamma function, since the
|
||||
@ -407,13 +430,18 @@ void main() {
|
||||
// below the rounding error threshold for both 8-bit and even 10-bit
|
||||
// content. It only makes a difference for 12-bit sources, so it should be
|
||||
// fine to use here.
|
||||
#ifdef USE_APPROX_GAMMA
|
||||
#ifdef USE_LINEAR_LIGHT_APPROX
|
||||
// We differentiate between approximate BT.2020 (gamma 1.95) ...
|
||||
color = pow(color, vec3(1.95));
|
||||
#else
|
||||
#endif
|
||||
#ifdef USE_LINEAR_LIGHT_BT2020
|
||||
// ... and actual BT.2020 (two-part function)
|
||||
color = bt2020_expand(color);
|
||||
#endif
|
||||
#ifdef USE_CONST_LUMA
|
||||
// Calculate the green channel from the expanded RYcB
|
||||
// The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B
|
||||
color.g = (color.g - 0.2627*color.r - 0.0593*color.b)/0.6780;
|
||||
#endif
|
||||
// Image upscaling happens roughly here
|
||||
#ifdef USE_GAMMA_POW
|
||||
@ -442,6 +470,16 @@ void main() {
|
||||
#ifdef USE_SRGB
|
||||
// Adapt and compand from the linear BT2020 source to the sRGB output
|
||||
color = srgb_compand(clamp(srgb_matrix * color, 0, 1));
|
||||
#endif
|
||||
// If none of these options took care of companding again, we have to do
|
||||
// it manually here for the previously-expanded channels. This again
|
||||
// comes in two flavours, one for the approximate gamma system and one
|
||||
// for the actual gamma system.
|
||||
#ifdef USE_CONST_LUMA_INV_APPROX
|
||||
color = pow(color, vec3(1/1.95));
|
||||
#endif
|
||||
#ifdef USE_CONST_LUMA_INV_BT2020
|
||||
color = bt2020_compand(color);
|
||||
#endif
|
||||
#ifdef USE_DITHER
|
||||
vec2 dither_pos = gl_FragCoord.xy / dither_size;
|
||||
|
Loading…
Reference in New Issue
Block a user