diff options
-rw-r--r-- | DOCS/man/vf.rst | 1 | ||||
-rw-r--r-- | DOCS/man/vo.rst | 8 | ||||
-rw-r--r-- | video/csputils.c | 1 | ||||
-rw-r--r-- | video/csputils.h | 1 | ||||
-rw-r--r-- | video/out/opengl/video.c | 26 | ||||
-rw-r--r-- | video/out/opengl/video.h | 1 | ||||
-rw-r--r-- | video/out/opengl/video_shaders.c | 25 |
7 files changed, 60 insertions, 3 deletions
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst index 46384eb20f..e742bc4ada 100644 --- a/DOCS/man/vf.rst +++ b/DOCS/man/vf.rst @@ -310,6 +310,7 @@ Available filters are: :gamma2.2: Pure power curve (gamma 2.2) :gamma2.8: Pure power curve (gamma 2.8) :prophoto: ProPhoto RGB (ROMM) curve + :st2084: SMPTE ST2084 (HDR) curve ``<stereo-in>`` Set the stereo mode the video is assumed to be encoded in. Takes the diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index 7892761581..046fa7e0e6 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -1052,6 +1052,14 @@ Available video output drivers are: Pure power curve (gamma 2.8), also used for BT.470-BG prophoto ProPhoto RGB (ROMM) + st2084 + SMPTE ST2084 (HDR) curve, PQ OETF + + ``target-brightness=<1..100000>`` + Specifies the display's approximate brightness in cd/m^2. When playing + HDR content, video colors will be scaled and clipped to this + brightness. The default of 250 cd/m^2 corresponds to a typical consumer + display. ``icc-profile=<file>`` Load an ICC profile and use it to transform video RGB to screen output. diff --git a/video/csputils.c b/video/csputils.c index 69d3b80944..2637c57a94 100644 --- a/video/csputils.c +++ b/video/csputils.c @@ -77,6 +77,7 @@ const struct m_opt_choice_alternatives mp_csp_trc_names[] = { {"gamma2.2", MP_CSP_TRC_GAMMA22}, {"gamma2.8", MP_CSP_TRC_GAMMA28}, {"prophoto", MP_CSP_TRC_PRO_PHOTO}, + {"st2084", MP_CSP_TRC_SMPTE_ST2084}, {0} }; diff --git a/video/csputils.h b/video/csputils.h index 1d8d3b1d14..19dd88f145 100644 --- a/video/csputils.h +++ b/video/csputils.h @@ -78,6 +78,7 @@ enum mp_csp_trc { MP_CSP_TRC_GAMMA22, MP_CSP_TRC_GAMMA28, MP_CSP_TRC_PRO_PHOTO, + MP_CSP_TRC_SMPTE_ST2084, MP_CSP_TRC_COUNT }; diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index f7bac42e61..f23143c2b3 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -320,6 +320,7 @@ const struct gl_video_opts gl_video_opts_def = { .gamma = 1.0f, .prescale_passes = 1, .prescale_downscaling_threshold = 2.0f, + .target_brightness = 250, }; const struct gl_video_opts gl_video_opts_hq_def = { @@ -348,6 +349,7 @@ const struct gl_video_opts gl_video_opts_hq_def = { .deband = 1, .prescale_passes = 1, .prescale_downscaling_threshold = 2.0f, + .target_brightness = 250, }; static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt, @@ -376,6 +378,7 @@ const struct m_sub_options gl_video_conf = { OPT_FLAG("gamma-auto", gamma_auto, 0), OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names), OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names), + OPT_INTRANGE("target-brightness", target_brightness, 0, 1, 100000), OPT_FLAG("pbo", pbo, 0), SCALER_OPTS("scale", SCALER_SCALE), SCALER_OPTS("dscale", SCALER_DSCALE), @@ -2202,7 +2205,8 @@ static void pass_scale_main(struct gl_video *p) // Adapts the colors from the given color space to the display device's native // gamut. -static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src, +static void pass_colormanage(struct gl_video *p, bool display_scaled, + enum mp_csp_prim prim_src, enum mp_csp_trc trc_src) { GLSLF("// color management\n"); @@ -2214,6 +2218,13 @@ static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src, enum mp_csp_prim prim_orig = p->image_params.primaries; enum mp_csp_trc trc_orig = p->image_params.gamma; + // One exception: SMPTE ST.2084 is not implemented by LittleCMS + // for technical limitation reasons, so we use a gamma 2.2 input curve + // here instead. We could pick any value we want here, the difference + // is just coding efficiency. + if (trc_orig == MP_CSP_TRC_SMPTE_ST2084) + trc_orig = MP_CSP_TRC_GAMMA22; + if (gl_video_get_lut3d(p, prim_orig, trc_orig)) { prim_dst = prim_orig; trc_dst = trc_orig; @@ -2237,6 +2248,15 @@ static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src, if (need_gamma) pass_linearize(p->sc, trc_src); + // For HDR, the assumption of reference brightness = display brightness + // is discontinued. Instead, we have to rescale the brightness to match + // the display (and clip out-of-range values) + if (p->image_params.gamma == MP_CSP_TRC_SMPTE_ST2084 && !display_scaled) { + int reference_brightness = 10000; // As per SMPTE ST.2084 + GLSLF("color.rgb = clamp(%f * color.rgb, 0.0, 1.0);\n", + (float)reference_brightness / p->opts.target_brightness); + } + // Adapt to the right colorspace if necessary if (prim_src != prim_dst) { struct mp_csp_primaries csp_src = mp_get_csp_primaries(prim_src), @@ -2391,7 +2411,7 @@ static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts, } // Subtitle color management, they're assumed to be sRGB by default if (cms) - pass_colormanage(p, MP_CSP_PRIM_BT_709, MP_CSP_TRC_SRGB); + pass_colormanage(p, true, MP_CSP_PRIM_BT_709, MP_CSP_TRC_SRGB); gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd)); gl_sc_gen_shader_and_reset(p->sc); mpgl_osd_draw_part(p->osd, vp_w, vp_h, n); @@ -2514,7 +2534,7 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo) GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));) } - pass_colormanage(p, p->image_params.primaries, + pass_colormanage(p, false, p->image_params.primaries, p->use_linear ? MP_CSP_TRC_LINEAR : p->image_params.gamma); // Draw checkerboard pattern to indicate transparency diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h index 22cbaeec0a..5d8c938535 100644 --- a/video/out/opengl/video.h +++ b/video/out/opengl/video.h @@ -111,6 +111,7 @@ struct gl_video_opts { int gamma_auto; int target_prim; int target_trc; + int target_brightness; int linear_scaling; int correct_downscaling; int sigmoid_upscaling; diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c index 8956ec37b9..c63f32ca7d 100644 --- a/video/out/opengl/video_shaders.c +++ b/video/out/opengl/video_shaders.c @@ -220,6 +220,13 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler, GLSLF("}\n"); } +// Common constants for SMPTE ST.2084 (HDR) +static const float HDR_M1 = 2610./4096 * 1./4, + HDR_M2 = 2523./4096 * 128, + HDR_C1 = 3424./4096, + HDR_C2 = 2413./4096 * 32, + HDR_C3 = 2392./4096 * 32; + // Linearize (expand), given a TRC as input void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) { @@ -251,6 +258,15 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) pow(color.rgb, vec3(1.8)), lessThan(vec3(0.03125), color.rgb));) break; + case MP_CSP_TRC_SMPTE_ST2084: + GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", HDR_M2); + GLSLF("color.rgb = max(color.rgb - vec3(%f), vec3(0.0)) \n" + " / (vec3(%f) - vec3(%f) * color.rgb);\n", + HDR_C1, HDR_C2, HDR_C3); + GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", HDR_M1); + break; + default: + abort(); } } @@ -285,6 +301,15 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) pow(color.rgb, vec3(1.0/1.8)), lessThanEqual(vec3(0.001953), color.rgb));) break; + case MP_CSP_TRC_SMPTE_ST2084: + GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", HDR_M1); + GLSLF("color.rgb = (vec3(%f) + vec3(%f) * color.rgb) \n" + " / (vec3(1.0) + vec3(%f) * color.rgb);\n", + HDR_C1, HDR_C2, HDR_C3); + GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", HDR_M2); + break; + default: + abort(); } } |