diff options
-rw-r--r-- | DOCS/interface-changes.rst | 1 | ||||
-rw-r--r-- | DOCS/man/options.rst | 9 | ||||
-rw-r--r-- | video/out/gpu/video.c | 2 | ||||
-rw-r--r-- | video/out/gpu/video.h | 1 | ||||
-rw-r--r-- | video/out/vo_gpu_next.c | 30 |
5 files changed, 43 insertions, 0 deletions
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 34f3e180fb..b6fbb0c516 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -27,6 +27,7 @@ Interface changes :: --- mpv 0.36.0 --- + - add `--target-contrast` - Target luminance value is now also applied when ICC profile is used. `--icc-use-luma` has been added to use ICC profile luminance value. If target luminance and ICC luminance is not used, old behavior apply, diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 46dd39373a..a9684b2b50 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -6487,6 +6487,15 @@ them. In such a configuration, we highly recommend setting ``--tone-mapping`` to ``mobius`` or even ``clip``. +``--target-contrast=<auto|10-1000000|inf>`` + Specifies the measured contrast of the output display. ``--target-contrast`` + in conjunction with ``--target-peak`` value is used to calculate display + black point. Used in black point compensation during HDR tone-mapping. + ``auto`` is the default and assumes 1000:1 contrast as a typical SDR display + would have or an infinite contrast when HDR ``--target-trc`` is used. + ``inf`` contrast specifies display with perfect black level, in practice OLED. + (Only for ``--vo=gpu-next``) + ``--target-lut=<file>`` Specifies a custom LUT file (in Adobe .cube format) to apply to the colors before display on-screen. This LUT is fed values in normalized RGB, after diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index d961002e7b..b38c88d054 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -374,6 +374,8 @@ const struct m_sub_options gl_video_conf = { {"target-trc", OPT_CHOICE_C(target_trc, mp_csp_trc_names)}, {"target-peak", OPT_CHOICE(target_peak, {"auto", 0}), M_RANGE(10, 10000)}, + {"target-contrast", OPT_CHOICE(target_contrast, {"auto", 0}, {"inf", -1}), + M_RANGE(10, 1000000)}, {"tone-mapping", OPT_CHOICE(tone_map.curve, {"auto", TONE_MAPPING_AUTO}, {"clip", TONE_MAPPING_CLIP}, diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index de70a8f801..307f7ea2ca 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -146,6 +146,7 @@ struct gl_video_opts { int target_prim; int target_trc; int target_peak; + int target_contrast; struct gl_tone_map_opts tone_map; bool correct_downscaling; bool linear_downscaling; diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c index 93cf4f54d6..42164ecd98 100644 --- a/video/out/vo_gpu_next.c +++ b/video/out/vo_gpu_next.c @@ -18,6 +18,7 @@ */ #include <pthread.h> +#include <libplacebo/colorspace.h> #include <libplacebo/renderer.h> #include <libplacebo/shaders/lut.h> #include <libplacebo/utils/libav.h> @@ -788,6 +789,32 @@ static void update_options(struct vo *vo) p->output_levels = cparams.levels_out; } +static void apply_target_contrast(struct priv *p, struct pl_color_space *color) +{ + const struct gl_video_opts *opts = p->opts_cache->opts; + + // Auto mode, leave as is + if (!opts->target_contrast) + return; + + // Infinite contrast + if (opts->target_contrast == -1) { + color->hdr.max_luma = 1e-7; + return; + } + + // Infer max_luma for current pl_color_space + pl_color_space_nominal_luma_ex(pl_nominal_luma_params( + .color = color, + // with HDR10 meta to respect value if already set + .metadata = PL_HDR_METADATA_HDR10, + .scaling = PL_HDR_NITS, + .out_max = &color->hdr.max_luma + )); + + color->hdr.min_luma = color->hdr.max_luma / opts->target_contrast; +} + static void apply_target_options(struct priv *p, struct pl_frame *target) { @@ -806,6 +833,8 @@ static void apply_target_options(struct priv *p, struct pl_frame *target) // If swapchain returned a value use this, override is used in hint if (opts->target_peak && !target->color.hdr.max_luma) target->color.hdr.max_luma = opts->target_peak; + if (!target->color.hdr.min_luma) + apply_target_contrast(p, &target->color); if (opts->dither_depth > 0) { struct pl_bit_encoding *tbits = &target->repr.bits; tbits->color_depth += opts->dither_depth - tbits->sample_depth; @@ -897,6 +926,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) hint.transfer = mp_trc_to_pl(opts->target_trc); if (opts->target_peak) hint.hdr.max_luma = opts->target_peak; + apply_target_contrast(p, &hint); pl_swapchain_colorspace_hint(p->sw, &hint); } else if (!p->target_hint) { pl_swapchain_colorspace_hint(p->sw, NULL); |