Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions video/out/wayland_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -2146,10 +2146,35 @@ static void info_done(void *data, struct wp_image_description_info_v1 *image_des
struct vo_wayland_state *wl = wd->wl;
wp_image_description_info_v1_destroy(image_description_info);
if (!wd->icc_file) {
wl->preferred_csp = wd->csp;
MP_VERBOSE(wl, "Preferred surface feedback received:\n");
log_color_space(wl->log, wd);
if (wd->csp.hdr.max_luma > wd->ref_luma) {
// Wayland luminances are always in reference to the reference luminance. That is,
// if max_luma == 2*ref_luma, then there is 2x headroom above paper white. On the
// other hand, libplacebo hardcodes PL_COLOR_SDR_WHITE as the reference luminance.
// We must scale all wayland values to correspond to the libplacebo scale,
// otherwise libplacebo will assume that there is too little or too much headroom
// when ref_luma != PL_COLOR_SDR_WHITE.
float a = wd->min_luma;
float b = (PL_COLOR_SDR_WHITE - PL_COLOR_HDR_BLACK) / (wd->ref_luma - a);
Comment on lines +2157 to +2158
Copy link
Contributor Author

@mahkoh mahkoh Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just mapping primary color volume min_luma to PL_COLOR_HDR_BLACK now. I don't know if there is anything more sensible to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to test this with some dark videos to see if the blacks come out as the proper shade of black.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PL_COLOR_HDR_BLACK is effectively 0, it's just sentinel value to discriminate from "undefined" which is 0.

It shouldn't make a difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is so, but the goal of this mapping is to ensure that 0 in nominal space A maps to 0 in nominal space B. But 0 in nominal space is usually not 0 luminance. For most SDR transfer functions 0 in nominal space is 0.2 cd/m^2 and for PQ 0 in nominal space is 0.005 cd/m^2. I'd have to know what 0 is in the nominal space assumed by libplacebo.

Copy link
Member

@kasper93 kasper93 Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that it depends on the values in csp. If things are set, they will be used, if not. Default is used instead, which is 0.203 (203/1000) for SDR and 0 for PQ. Generally internal absolute black is always 0 in libplacebo and SDR is shifted according to min_luma value.

float min, max;
pl_color_space_nominal_luma_ex(pl_nominal_luma_params(
    .color    = &csp,
    .metadata = PL_HDR_METADATA_HDR10,
    .scaling  = PL_HDR_NITS,
    .out_min  = &min,
    .out_max  = &max,
));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So something like this: afaedac?

wd->csp.hdr.min_luma = (wd->csp.hdr.min_luma - a) * b + PL_COLOR_HDR_BLACK;
wd->csp.hdr.max_luma = (wd->csp.hdr.max_luma - a) * b + PL_COLOR_HDR_BLACK;
if (wd->csp.hdr.max_cll != 0)
wd->csp.hdr.max_cll = (wd->csp.hdr.max_cll - a) * b + PL_COLOR_HDR_BLACK;
if (wd->csp.hdr.max_fall != 0)
wd->csp.hdr.max_fall = (wd->csp.hdr.max_fall - a) * b + PL_COLOR_HDR_BLACK;
// Ensure that min_luma doesn't become negative.
wd->csp.hdr.min_luma = MPMAX(wd->csp.hdr.min_luma, 0.0);
// Since we want to do some exact comparisons of max_luma with PL_COLOR_SDR_WHITE,
// we need to round it.
if (fabsf(wd->csp.hdr.max_luma - PL_COLOR_SDR_WHITE) < 1e-2f) {
wd->csp.hdr.max_luma = PL_COLOR_SDR_WHITE;
if (wd->csp.hdr.max_cll != 0)
wd->csp.hdr.max_cll = MPMIN(wd->csp.hdr.max_cll, wd->csp.hdr.max_luma);
if (wd->csp.hdr.max_fall != 0)
wd->csp.hdr.max_fall = MPMIN(wd->csp.hdr.max_fall, wd->csp.hdr.max_luma);
}
wl->preferred_csp = wd->csp;
if (wd->csp.hdr.max_luma != PL_COLOR_SDR_WHITE && !pl_color_transfer_is_hdr(wd->csp.transfer)) {
MP_VERBOSE(wl, "Setting preferred transfer to PQ for HDR output.\n");
wl->preferred_csp.transfer = PL_COLOR_TRC_PQ;
}
Expand Down