summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/vf.rst1
-rw-r--r--DOCS/man/vo.rst8
-rw-r--r--video/csputils.c1
-rw-r--r--video/csputils.h1
-rw-r--r--video/out/opengl/video.c26
-rw-r--r--video/out/opengl/video.h1
-rw-r--r--video/out/opengl/video_shaders.c25
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();
}
}