summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2016-05-15 20:16:12 +0200
committerNiklas Haas <git@nand.wakku.to>2016-05-16 02:45:39 +0200
commitf81f486c68ca55c1e6175e41b0473d9568a6fe5b (patch)
tree647dcc25bdcc21e6b47ced6509470f827357e0c0
parent965031ccd5acb04dcff2bbbfea0ac35a3c035ecb (diff)
downloadmpv-f81f486c68ca55c1e6175e41b0473d9568a6fe5b.tar.bz2
mpv-f81f486c68ca55c1e6175e41b0473d9568a6fe5b.tar.xz
vo_opengl: implement HDR (SMPTE ST2084)
Currently, this relies on the user manually entering their display brightness (since we have no way to detect this at runtime or from ICC metadata). The default value of 250 was picked by looking at ~10 reviews on tftcentral.co.uk and realizing they all come with around 250 cd/m^2 out of the box. (In addition, ITU-R Rec. BT.2022 supports this) Since there is no metadata in FFmpeg to indicate usage of this TRC, the only way to actually play HDR content currently is to set ``--vf=format=gamma=st2084``. (It could be guessed based on SEI, but this is not implemented yet) Incidentally, since SEI is ignored, it's currently assumed that all content is scaled to 10,000 cd/m^2 (and hard-clipped where out of range). I don't see this assumption changing much, though. As an unfortunate consequence of the fact that we don't know the display brightness, mixed with the fact that LittleCMS' parametric tone curves are not flexible enough to support PQ, we have to build the 3DLUT against gamma 2.2 if it's used. This might be a good thing, though, consdering the PQ source space is probably not fantastic for interpolation either way. Partially addresses #2572.
-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();
}
}