summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2014-03-26 23:00:09 +0100
committerwm4 <wm4@nowhere>2014-06-22 19:02:00 +0200
commit204fed4d5b4aa20b5a6b5824f5d4e71ccbaf87fb (patch)
tree60605dd0ae0de7bb2a3b5ad45203a8d68fba293f
parentef6db24366da2974cdee1d9578cf91910b5faa9c (diff)
downloadmpv-204fed4d5b4aa20b5a6b5824f5d4e71ccbaf87fb.tar.bz2
mpv-204fed4d5b4aa20b5a6b5824f5d4e71ccbaf87fb.tar.xz
video: Support BT.2020 constant luminance system
Signed-off-by: wm4 <wm4@nowhere>
-rw-r--r--DOCS/man/options.rst3
-rw-r--r--options/options.c3
-rw-r--r--video/csputils.c14
-rw-r--r--video/csputils.h1
-rw-r--r--video/mp_image.c4
-rw-r--r--video/out/gl_video.c18
-rw-r--r--video/out/gl_video_shaders.glsl48
7 files changed, 77 insertions, 14 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 92dd35d0cf..018be5919d 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -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>``
diff --git a/options/options.c b/options/options.c
index d5f751b9cf..0c2f8257df 100644
--- a/options/options.c
+++ b/options/options.c
@@ -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},
diff --git a/video/csputils.c b/video/csputils.c
index 32461697c0..43dd17511a 100644
--- a/video/csputils.c
+++ b/video/csputils.c
@@ -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));
diff --git a/video/csputils.h b/video/csputils.h
index d7d052a753..796dfd7422 100644
--- a/video/csputils.h
+++ b/video/csputils.h
@@ -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,
diff --git a/video/mp_image.c b/video/mp_image.c
index 1c686e5193..521b3d8e90 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -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;
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index e50cef3686..f0981887c6 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -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;
diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl
index a6ecc2eeeb..196e81e5e3 100644
--- a/video/out/gl_video_shaders.glsl
+++ b/video/out/gl_video_shaders.glsl
@@ -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
@@ -443,6 +471,16 @@ void main() {
// 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;
#ifdef USE_TEMPORAL_DITHER