mirror of https://github.com/mpv-player/mpv
ALL: use pl_hdr_metadata and nuke sig_peak
This commit replaces all uses of sig_peak and maps all HDR metadata. Form notable changes mixed usage of maxCLL and max_luma is resolved and not always max_luma is used which makes vo_gpu and vo_gpu_next behave the same way.
This commit is contained in:
parent
d9a483cb17
commit
73fbe09a49
|
@ -594,7 +594,6 @@ static void parse_trackcolour(struct demuxer *demuxer, struct mkv_track *track,
|
|||
}
|
||||
if (colour->n_max_cll) {
|
||||
track->color.hdr.max_cll = colour->max_cll;
|
||||
track->color.sig_peak = track->color.hdr.max_cll / MP_REF_WHITE;
|
||||
MP_DBG(demuxer, "| + MaxCLL: %"PRIu64"\n", colour->max_cll);
|
||||
}
|
||||
if (colour->n_max_fall) {
|
||||
|
@ -642,8 +641,6 @@ static void parse_trackcolour(struct demuxer *demuxer, struct mkv_track *track,
|
|||
}
|
||||
if (mastering->n_luminance_max) {
|
||||
track->color.hdr.max_luma = mastering->luminance_max;
|
||||
if (!track->color.sig_peak)
|
||||
track->color.sig_peak = track->color.hdr.max_luma / MP_REF_WHITE;
|
||||
MP_DBG(demuxer, "| + LuminanceMax: %f\n", track->color.hdr.max_luma);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -548,11 +548,6 @@ void mp_decoder_wrapper_set_play_dir(struct mp_decoder_wrapper *d, int dir)
|
|||
thread_unlock(p);
|
||||
}
|
||||
|
||||
static bool is_valid_peak(float sig_peak)
|
||||
{
|
||||
return !sig_peak || (sig_peak >= 1 && sig_peak <= 100);
|
||||
}
|
||||
|
||||
static void fix_image_params(struct priv *p,
|
||||
struct mp_image_params *params)
|
||||
{
|
||||
|
@ -623,12 +618,6 @@ static void fix_image_params(struct priv *p,
|
|||
|
||||
mp_colorspace_merge(&m.color, &c->color);
|
||||
|
||||
// Sanitize the HDR peak. Sadly necessary
|
||||
if (!is_valid_peak(m.color.sig_peak)) {
|
||||
MP_WARN(p, "Invalid HDR peak in stream: %f\n", m.color.sig_peak);
|
||||
m.color.sig_peak = 0.0;
|
||||
}
|
||||
|
||||
// Guess missing colorspace fields from metadata. This guarantees all
|
||||
// fields are at least set to legal values afterwards.
|
||||
mp_image_params_guess_csp(&m);
|
||||
|
|
|
@ -2323,7 +2323,7 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
|
|||
SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.color.primaries))},
|
||||
{"gamma",
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.color.gamma))},
|
||||
{"sig-peak", SUB_PROP_FLOAT(p.color.sig_peak)},
|
||||
{"sig-peak", SUB_PROP_FLOAT(p.color.hdr.max_luma * MP_REF_WHITE)},
|
||||
{"light",
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_light_names, p.color.light))},
|
||||
{"chroma-location",
|
||||
|
|
|
@ -933,11 +933,6 @@ local function add_video(s)
|
|||
append_fps(s, "container-fps", "estimated-vf-fps")
|
||||
end
|
||||
append_img_params(s, r, ro)
|
||||
|
||||
if not ro["max-cll"] then
|
||||
ro["max-cll"] = math.floor((ro["sig-peak"] or 0) * 203)
|
||||
end
|
||||
|
||||
append_hdr(s, ro)
|
||||
append_property(s, "packet-video-bitrate", {prefix="Bitrate:", suffix=" kbps"})
|
||||
append_filters(s, "vf", "Filters:")
|
||||
|
|
|
@ -64,6 +64,7 @@ img_utils_files = [
|
|||
'video/fmt-conversion.c',
|
||||
'video/img_format.c',
|
||||
'video/mp_image.c',
|
||||
'video/out/placebo/utils.c',
|
||||
'video/sws_utils.c'
|
||||
]
|
||||
if features['zimg']
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <libavutil/common.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "options/m_option.h"
|
||||
#include "options/path.h"
|
||||
#include "osdep/subprocess.h"
|
||||
|
@ -98,7 +99,7 @@ void assert_memcmp_impl(const char *file, int line,
|
|||
}
|
||||
|
||||
/* Stubs: see test_utils.h */
|
||||
struct mp_log *mp_null_log;
|
||||
struct mp_log *const mp_null_log;
|
||||
const char *mp_help_text;
|
||||
|
||||
void mp_msg(struct mp_log *log, int lev, const char *format, ...) {};
|
||||
|
@ -106,3 +107,5 @@ int mp_msg_find_level(const char *s) {return 0;};
|
|||
int mp_msg_level(struct mp_log *log) {return 0;};
|
||||
void mp_write_console_ansi(void) {};
|
||||
void mp_set_avdict(AVDictionary **dict, char **kv) {};
|
||||
struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent,
|
||||
const char *name) { return NULL; };
|
||||
|
|
|
@ -129,8 +129,6 @@ void mp_colorspace_merge(struct mp_colorspace *orig, struct mp_colorspace *new)
|
|||
orig->primaries = new->primaries;
|
||||
if (!orig->gamma)
|
||||
orig->gamma = new->gamma;
|
||||
if (!orig->sig_peak)
|
||||
orig->sig_peak = new->sig_peak;
|
||||
if (!orig->light)
|
||||
orig->light = new->light;
|
||||
pl_hdr_metadata_merge(&orig->hdr, &new->hdr);
|
||||
|
@ -913,7 +911,6 @@ bool mp_colorspace_equal(struct mp_colorspace c1, struct mp_colorspace c2)
|
|||
c1.primaries == c2.primaries &&
|
||||
c1.gamma == c2.gamma &&
|
||||
c1.light == c2.light &&
|
||||
c1.sig_peak == c2.sig_peak &&
|
||||
pl_hdr_metadata_equal(&c1.hdr, &c2.hdr);
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,6 @@ struct mp_colorspace {
|
|||
enum mp_csp_prim primaries;
|
||||
enum mp_csp_trc gamma;
|
||||
enum mp_csp_light light;
|
||||
float sig_peak; // highest relative value in signal. 0 = unknown/auto (deprecated)
|
||||
struct pl_hdr_metadata hdr;
|
||||
};
|
||||
|
||||
|
|
|
@ -77,14 +77,12 @@ static void set_params(struct vf_format_opts *p, struct mp_image_params *out,
|
|||
// When changing the gamma function explicitly, also reset stuff
|
||||
// related to the gamma function since that information will almost
|
||||
// surely be false now and have to be re-inferred
|
||||
out->color.sig_peak = 0.0;
|
||||
out->color.hdr = (struct pl_hdr_metadata){0};
|
||||
out->color.light = MP_CSP_LIGHT_AUTO;
|
||||
}
|
||||
}
|
||||
if (p->sig_peak) {
|
||||
out->color.sig_peak = p->sig_peak;
|
||||
out->color.hdr.max_cll = p->sig_peak;
|
||||
}
|
||||
if (p->sig_peak)
|
||||
out->color.hdr = (struct pl_hdr_metadata){ .max_luma = p->sig_peak * MP_REF_WHITE };
|
||||
if (p->light)
|
||||
out->color.light = p->light;
|
||||
if (p->chroma_location)
|
||||
|
|
|
@ -637,7 +637,7 @@ static struct mp_image *convert_image(struct mp_image *image, int destfmt,
|
|||
p.color.primaries = MP_CSP_PRIM_BT_709;
|
||||
p.color.gamma = MP_CSP_TRC_AUTO;
|
||||
p.color.light = MP_CSP_LIGHT_DISPLAY;
|
||||
p.color.sig_peak = 0;
|
||||
p.color.hdr = (struct pl_hdr_metadata){0};
|
||||
if (p.color.space != MP_CSP_RGB) {
|
||||
p.color.levels = yuv_levels;
|
||||
p.color.space = MP_CSP_BT_601;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <libavutil/rational.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/mastering_display_metadata.h>
|
||||
#include <libplacebo/utils/libav.h>
|
||||
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 16, 100)
|
||||
# include <libavutil/dovi_meta.h>
|
||||
|
@ -41,6 +42,7 @@
|
|||
#include "mp_image.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "sws_utils.h"
|
||||
#include "out/placebo/utils.h"
|
||||
|
||||
// Determine strides, plane sizes, and total required size for an image
|
||||
// allocation. Returns total size on success, <0 on error. Unused planes
|
||||
|
@ -776,8 +778,6 @@ char *mp_image_params_to_str_buf(char *b, size_t bs,
|
|||
m_opt_choice_str(mp_csp_trc_names, p->color.gamma),
|
||||
m_opt_choice_str(mp_csp_levels_names, p->color.levels),
|
||||
m_opt_choice_str(mp_csp_light_names, p->color.light));
|
||||
if (p->color.sig_peak)
|
||||
mp_snprintf_cat(b, bs, " SP=%f", p->color.sig_peak);
|
||||
mp_snprintf_cat(b, bs, " CL=%s",
|
||||
m_opt_choice_str(mp_chroma_names, p->chroma_location));
|
||||
if (mp_image_crop_valid(p)) {
|
||||
|
@ -955,20 +955,20 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
|
|||
params->color.gamma = MP_CSP_TRC_AUTO;
|
||||
}
|
||||
|
||||
if (!params->color.sig_peak) {
|
||||
if (!params->color.hdr.max_luma) {
|
||||
if (params->color.gamma == MP_CSP_TRC_HLG) {
|
||||
params->color.sig_peak = 1000 / MP_REF_WHITE; // reference display
|
||||
params->color.hdr.max_luma = 1000; // reference display
|
||||
} else {
|
||||
// If the signal peak is unknown, we're forced to pick the TRC's
|
||||
// nominal range as the signal peak to prevent clipping
|
||||
params->color.sig_peak = mp_trc_nom_peak(params->color.gamma);
|
||||
params->color.hdr.max_luma = mp_trc_nom_peak(params->color.gamma) * MP_REF_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mp_trc_is_hdr(params->color.gamma)) {
|
||||
// Some clips have leftover HDR metadata after conversion to SDR, so to
|
||||
// avoid blowing up the tone mapping code, strip/sanitize it
|
||||
params->color.sig_peak = 1.0;
|
||||
params->color.hdr = pl_hdr_metadata_empty;
|
||||
}
|
||||
|
||||
if (params->chroma_location == MP_CHROMA_AUTO) {
|
||||
|
@ -1061,20 +1061,14 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src)
|
|||
if (sd)
|
||||
dst->icc_profile = sd->buf;
|
||||
|
||||
// Get the content light metadata if available
|
||||
sd = av_frame_get_side_data(src, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
|
||||
if (sd) {
|
||||
AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data;
|
||||
dst->params.color.sig_peak = clm->MaxCLL / MP_REF_WHITE;
|
||||
}
|
||||
|
||||
// Otherwise, try getting the mastering metadata if available
|
||||
sd = av_frame_get_side_data(src, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
|
||||
if (!dst->params.color.sig_peak && sd) {
|
||||
AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data;
|
||||
if (mdm->has_luminance)
|
||||
dst->params.color.sig_peak = av_q2d(mdm->max_luminance) / MP_REF_WHITE;
|
||||
}
|
||||
AVFrameSideData *mdm = av_frame_get_side_data(src, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
|
||||
AVFrameSideData *clm = av_frame_get_side_data(src, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
|
||||
AVFrameSideData *dhp = av_frame_get_side_data(src, AV_FRAME_DATA_DYNAMIC_HDR_PLUS);
|
||||
pl_map_hdr_metadata(&dst->params.color.hdr, &(struct pl_av_hdr_metadata) {
|
||||
.mdm = (void *)(mdm ? mdm->data : NULL),
|
||||
.clm = (void *)(clm ? clm->data : NULL),
|
||||
.dhp = (void *)(dhp ? dhp->data : NULL),
|
||||
});
|
||||
|
||||
sd = av_frame_get_side_data(src, AV_FRAME_DATA_A53_CC);
|
||||
if (sd)
|
||||
|
@ -1189,12 +1183,11 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src)
|
|||
new_ref->icc_profile = NULL;
|
||||
}
|
||||
|
||||
if (src->params.color.sig_peak) {
|
||||
AVContentLightMetadata *clm =
|
||||
av_content_light_metadata_create_side_data(dst);
|
||||
MP_HANDLE_OOM(clm);
|
||||
clm->MaxCLL = src->params.color.sig_peak * MP_REF_WHITE;
|
||||
}
|
||||
pl_avframe_set_color(dst, (struct pl_color_space){
|
||||
.primaries = mp_prim_to_pl(src->params.color.primaries),
|
||||
.transfer = mp_trc_to_pl(src->params.color.gamma),
|
||||
.hdr = src->params.color.hdr,
|
||||
});
|
||||
|
||||
{
|
||||
AVFrameSideData *sd = av_frame_new_side_data(dst,
|
||||
|
|
|
@ -609,7 +609,7 @@ struct mp_colorspace gl_video_get_output_colorspace(struct gl_video *p)
|
|||
return (struct mp_colorspace) {
|
||||
.primaries = p->opts.target_prim,
|
||||
.gamma = p->opts.target_trc,
|
||||
.sig_peak = p->opts.target_peak / MP_REF_WHITE,
|
||||
.hdr.max_luma = p->opts.target_peak,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2565,8 +2565,8 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
|
|||
.primaries = p->opts.target_prim == MP_CSP_PRIM_AUTO ?
|
||||
fbo_csp.primaries : p->opts.target_prim,
|
||||
.light = MP_CSP_LIGHT_DISPLAY,
|
||||
.sig_peak = !p->opts.target_peak ?
|
||||
fbo_csp.sig_peak : p->opts.target_peak / MP_REF_WHITE,
|
||||
.hdr.max_luma = !p->opts.target_peak ?
|
||||
fbo_csp.hdr.max_luma : p->opts.target_peak,
|
||||
};
|
||||
|
||||
if (!p->colorspace_override_warned &&
|
||||
|
@ -2643,10 +2643,10 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
|
|||
// If there's no specific signal peak known for the output display, infer
|
||||
// it from the chosen transfer function. Also normalize the src peak, in
|
||||
// case it was unknown
|
||||
if (!dst.sig_peak)
|
||||
dst.sig_peak = mp_trc_nom_peak(dst.gamma);
|
||||
if (!src.sig_peak)
|
||||
src.sig_peak = mp_trc_nom_peak(src.gamma);
|
||||
if (!dst.hdr.max_luma)
|
||||
dst.hdr.max_luma = mp_trc_nom_peak(dst.gamma) * MP_REF_WHITE;
|
||||
if (!src.hdr.max_luma)
|
||||
src.hdr.max_luma = mp_trc_nom_peak(src.gamma) * MP_REF_WHITE;
|
||||
|
||||
// Whitelist supported modes
|
||||
switch (p->opts.tone_map.curve) {
|
||||
|
@ -2679,7 +2679,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
|
|||
|
||||
struct gl_tone_map_opts tone_map = p->opts.tone_map;
|
||||
bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma)
|
||||
&& src.sig_peak > dst.sig_peak;
|
||||
&& src.hdr.max_luma > dst.hdr.max_luma;
|
||||
|
||||
if (detect_peak && !p->hdr_peak_ssbo) {
|
||||
struct {
|
||||
|
|
|
@ -848,7 +848,7 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
|||
gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz[1]);
|
||||
|
||||
bool need_ootf = src.light != dst.light;
|
||||
if (src.light == MP_CSP_LIGHT_SCENE_HLG && src.sig_peak != dst.sig_peak)
|
||||
if (src.light == MP_CSP_LIGHT_SCENE_HLG && src.hdr.max_luma != dst.hdr.max_luma)
|
||||
need_ootf = true;
|
||||
|
||||
// All operations from here on require linear light as a starting point,
|
||||
|
@ -856,7 +856,7 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
|||
// operations needs it
|
||||
bool need_linear = src.gamma != dst.gamma ||
|
||||
src.primaries != dst.primaries ||
|
||||
src.sig_peak != dst.sig_peak ||
|
||||
src.hdr.max_luma != dst.hdr.max_luma ||
|
||||
need_ootf;
|
||||
|
||||
if (need_linear && !is_linear) {
|
||||
|
@ -869,11 +869,13 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
|||
GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(src.gamma));
|
||||
|
||||
if (need_ootf)
|
||||
pass_ootf(sc, src.light, src.sig_peak);
|
||||
pass_ootf(sc, src.light, src.hdr.max_luma / MP_REF_WHITE);
|
||||
|
||||
// Tone map to prevent clipping due to excessive brightness
|
||||
if (src.sig_peak > dst.sig_peak)
|
||||
pass_tone_map(sc, src.sig_peak, dst.sig_peak, opts);
|
||||
if (src.hdr.max_luma > dst.hdr.max_luma) {
|
||||
pass_tone_map(sc, src.hdr.max_luma / MP_REF_WHITE,
|
||||
dst.hdr.max_luma / MP_REF_WHITE, opts);
|
||||
}
|
||||
|
||||
// Adapt to the right colorspace if necessary
|
||||
if (src.primaries != dst.primaries) {
|
||||
|
@ -892,18 +894,18 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
|||
color.rgb = mix(color.rgb, vec3(luma), coeff);
|
||||
})
|
||||
GLSLF("float cmax = 1.0/%f * max(max(color.r, color.g), color.b);\n",
|
||||
dst.sig_peak);
|
||||
dst.hdr.max_luma / MP_REF_WHITE);
|
||||
GLSL(if (cmax > 1.0) color.rgb /= cmax;)
|
||||
}
|
||||
}
|
||||
|
||||
if (need_ootf)
|
||||
pass_inverse_ootf(sc, dst.light, dst.sig_peak);
|
||||
pass_inverse_ootf(sc, dst.light, dst.hdr.max_luma / MP_REF_WHITE);
|
||||
|
||||
// Post-scale the outgoing values from absolute scale to normalized.
|
||||
// For SDR, we normalize to the chosen signal peak. For HDR, we normalize
|
||||
// to the encoding range of the transfer function.
|
||||
float dst_range = dst.sig_peak;
|
||||
float dst_range = dst.hdr.max_luma / MP_REF_WHITE;
|
||||
if (mp_trc_is_hdr(dst.gamma))
|
||||
dst_range = mp_trc_nom_peak(dst.gamma);
|
||||
|
||||
|
|
|
@ -439,30 +439,13 @@ static int plane_data_from_imgfmt(struct pl_plane_data out_data[4],
|
|||
return desc.num_planes;
|
||||
}
|
||||
|
||||
static inline void *get_side_data(const struct mp_image *mpi,
|
||||
enum AVFrameSideDataType type)
|
||||
{
|
||||
for (int i = 0; i <mpi->num_ff_side_data; i++) {
|
||||
if (mpi->ff_side_data[i].type == type)
|
||||
return (void *) mpi->ff_side_data[i].buf->data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi)
|
||||
{
|
||||
struct pl_color_space csp = {
|
||||
.primaries = mp_prim_to_pl(mpi->params.color.primaries),
|
||||
.transfer = mp_trc_to_pl(mpi->params.color.gamma),
|
||||
.hdr.max_luma = mpi->params.color.sig_peak * MP_REF_WHITE,
|
||||
.hdr = mpi->params.color.hdr,
|
||||
};
|
||||
|
||||
pl_map_hdr_metadata(&csp.hdr, &(struct pl_av_hdr_metadata) {
|
||||
.mdm = get_side_data(mpi, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA),
|
||||
.clm = get_side_data(mpi, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL),
|
||||
.dhp = get_side_data(mpi, AV_FRAME_DATA_DYNAMIC_HDR_PLUS),
|
||||
});
|
||||
return csp;
|
||||
}
|
||||
|
||||
|
@ -1346,7 +1329,7 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
|
|||
args->res->params.color.primaries = mp_prim_from_pl(target.color.primaries);
|
||||
args->res->params.color.gamma = mp_trc_from_pl(target.color.transfer);
|
||||
args->res->params.color.levels = mp_levels_from_pl(target.repr.levels);
|
||||
args->res->params.color.sig_peak = target.color.hdr.max_luma / MP_REF_WHITE;
|
||||
args->res->params.color.hdr = target.color.hdr;
|
||||
if (args->scaled)
|
||||
args->res->params.p_w = args->res->params.p_h = 1;
|
||||
|
||||
|
|
|
@ -548,8 +548,8 @@ static bool mp_zimg_state_init(struct mp_zimg_context *ctx,
|
|||
params.allow_approximate_gamma = 1;
|
||||
|
||||
// leave at default for SDR, which means 100 cd/m^2 for zimg
|
||||
if (ctx->dst.color.sig_peak > 0 && mp_trc_is_hdr(ctx->dst.color.gamma))
|
||||
params.nominal_peak_luminance = ctx->dst.color.sig_peak * MP_REF_WHITE;
|
||||
if (ctx->dst.color.hdr.max_luma > 0 && mp_trc_is_hdr(ctx->dst.color.gamma))
|
||||
params.nominal_peak_luminance = ctx->dst.color.hdr.max_luma;
|
||||
|
||||
st->graph = zimg_filter_graph_build(&src_fmt, &dst_fmt, ¶ms);
|
||||
if (!st->graph) {
|
||||
|
|
Loading…
Reference in New Issue