vd_lavc: expose mastering display side data reference peak

This greatly improves the result when decoding typical (ST.2084) HDR
content, since the job of tone mapping gets significantly easier when
you're only mapping from 1000 to 250, rather than 10000 to 250.

The difference is so drastic that we can now even reasonably use
`hdr-tone-mapping=linear` and get a very perceptually uniform result
that is only slightly darker than normal. (To compensate for the extra
dynamic range)

Due to weird implementation details, this only seems to be present on
keyframes (or something like that), so we have to cache the last seen
value for the frames in between.

Also, in some files the metadata is just completely broken /
nonsensical, so I decided to apply a simple heuristic to detect
completely broken metadata.
This commit is contained in:
Niklas Haas 2016-06-29 09:43:55 +02:00 committed by wm4
parent 923e3c7b20
commit 5b6cce2b73
3 changed files with 32 additions and 1 deletions

View File

@ -26,7 +26,7 @@ typedef struct lavc_ctx {
bool hwdec_notified;
// For HDR side-data caching
int cached_hdr_peak;
double cached_hdr_peak;
struct mp_image **delay_queue;
int num_delay_queue;

View File

@ -47,6 +47,10 @@
#include "video/csputils.h"
#include "video/sws_utils.h"
#if HAVE_AVUTIL_MASTERING_METADATA
#include <libavutil/mastering_display_metadata.h>
#endif
#include "lavc.h"
#if AVPALETTE_SIZE != MP_PALETTE_SIZE
@ -572,6 +576,26 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
vd_ffmpeg_ctx *ctx = vd->priv;
struct MPOpts *opts = ctx->opts;
#if HAVE_AVUTIL_MASTERING_METADATA
// Get the reference peak (for HDR) if available. This is cached into ctx
// when it's found, since it's not available on every frame (and seems to
// be only available for keyframes)
AVFrameSideData *sd = av_frame_get_side_data(frame,
AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
if (sd) {
AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data;
if (mdm->has_luminance) {
double peak = av_q2d(mdm->max_luminance);
if (!isnormal(peak) || peak < 10 || peak > 100000) {
// Invalid data, ignore it. Sadly necessary
MP_WARN(vd, "Invalid HDR reference peak in stream: %f\n", peak);
} else {
ctx->cached_hdr_peak = peak;
}
}
}
#endif
*out_params = (struct mp_image_params) {
.imgfmt = pixfmt2imgfmt(frame->format),
.w = frame->width,
@ -583,6 +607,7 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
.levels = avcol_range_to_mp_csp_levels(ctx->avctx->color_range),
.primaries = avcol_pri_to_mp_csp_prim(ctx->avctx->color_primaries),
.gamma = avcol_trc_to_mp_csp_trc(ctx->avctx->color_trc),
.sig_peak = ctx->cached_hdr_peak,
},
.chroma_location =
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),

View File

@ -504,6 +504,12 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
'AVCOL_TRC_SMPTEST2084,'
'AVCOL_TRC_ARIB_STD_B67',
use='libav'),
}, {
'name': 'avutil-mastering-metadata',
'desc': 'libavutil mastering display metadata struct',
'func': check_statement('libavutil/mastering_display_metadata.h',
'AV_FRAME_DATA_MASTERING_DISPLAY_METADATA',
use='libav'),
}
]