vo_opengl: implement ARIB STD-B68 (HLG) HDR TRC

This HDR function is unique in that it's still display-referred, it just
allows for values above the reference peak (super-highlights). The
official standard doesn't actually document this very well, but the
nominal peak turns out to be exactly 12.0 - so we normalize to this
value internally in mpv. (This lets us preserve the property that the
textures are encoded in the range [0,1], preventing clipping and making
the best use of an integer texture's range)

This was grouped together with SMPTE ST2084 when checking libavutil
compatibility since they were added in the same release window, in a
similar timeframe.
This commit is contained in:
Niklas Haas 2016-06-26 19:04:36 +02:00 committed by wm4
parent 4ce53025cb
commit 9278ce98f7
7 changed files with 49 additions and 10 deletions

View File

@ -311,6 +311,7 @@ Available filters are:
:gamma2.8: Pure power curve (gamma 2.8)
:prophoto: ProPhoto RGB (ROMM) curve
:st2084: SMPTE ST2084 (HDR) curve
:std-b67: ARIB STD-B67 (Hybrid Log-gamma) curve
``<peak>``
Reference peak illumination for the video file. This is mostly

View File

@ -1003,6 +1003,8 @@ Available video output drivers are:
ProPhoto RGB (ROMM)
st2084
SMPTE ST2084 (HDR) curve, PQ OETF
std-b67
ARIB STD-B67 (Hybrid Log-gamma) curve, also known as BBC/NHK HDR
``target-brightness=<1..100000>``
Specifies the display's approximate brightness in cd/m^2. When playing

View File

@ -78,6 +78,7 @@ const struct m_opt_choice_alternatives mp_csp_trc_names[] = {
{"gamma2.8", MP_CSP_TRC_GAMMA28},
{"prophoto", MP_CSP_TRC_PRO_PHOTO},
{"st2084", MP_CSP_TRC_SMPTE_ST2084},
{"std-b67", MP_CSP_TRC_ARIB_STD_B67},
{0}
};
@ -171,8 +172,9 @@ enum mp_csp_trc avcol_trc_to_mp_csp_trc(int avtrc)
case AVCOL_TRC_LINEAR: return MP_CSP_TRC_LINEAR;
case AVCOL_TRC_GAMMA22: return MP_CSP_TRC_GAMMA22;
case AVCOL_TRC_GAMMA28: return MP_CSP_TRC_GAMMA28;
#if HAVE_AVUTIL_ST2084
#if HAVE_AVUTIL_HDR
case AVCOL_TRC_SMPTEST2084: return MP_CSP_TRC_SMPTE_ST2084;
case AVCOL_TRC_ARIB_STD_B67: return MP_CSP_TRC_ARIB_STD_B67;
#endif
default: return MP_CSP_TRC_AUTO;
}
@ -222,8 +224,9 @@ int mp_csp_trc_to_avcol_trc(enum mp_csp_trc trc)
case MP_CSP_TRC_LINEAR: return AVCOL_TRC_LINEAR;
case MP_CSP_TRC_GAMMA22: return AVCOL_TRC_GAMMA22;
case MP_CSP_TRC_GAMMA28: return AVCOL_TRC_GAMMA28;
#if HAVE_AVUTIL_ST2084
#if HAVE_AVUTIL_HDR
case MP_CSP_TRC_SMPTE_ST2084: return AVCOL_TRC_SMPTEST2084;
case MP_CSP_TRC_ARIB_STD_B67: return AVCOL_TRC_ARIB_STD_B67;
#endif
default: return AVCOL_TRC_UNSPECIFIED;
}

View File

@ -79,6 +79,7 @@ enum mp_csp_trc {
MP_CSP_TRC_GAMMA28,
MP_CSP_TRC_PRO_PHOTO,
MP_CSP_TRC_SMPTE_ST2084,
MP_CSP_TRC_ARIB_STD_B67,
MP_CSP_TRC_COUNT
};

View File

@ -2172,12 +2172,15 @@ static void pass_colormanage(struct gl_video *p, float peak_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)
// One exception: HDR 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_ARIB_STD_B67)
{
trc_orig = MP_CSP_TRC_GAMMA22;
}
if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
prim_dst = prim_orig;
@ -2216,6 +2219,11 @@ static void pass_colormanage(struct gl_video *p, float peak_src,
// If the source has no information known, it's display-referred
// (and should be treated relative to the specified desired peak_dst)
peak_src = peak_dst;
// Exception: ARIB STD-B67's nominal peak is exactly 12 times the
// target's reference peak
if (trc_src == MP_CSP_TRC_ARIB_STD_B67)
peak_src = 12 * peak_dst;
}
// All operations from here on require linear light as a starting point,

View File

@ -227,6 +227,11 @@ static const float HDR_M1 = 2610./4096 * 1./4,
HDR_C2 = 2413./4096 * 32,
HDR_C3 = 2392./4096 * 32;
// Common constants for ARIB STD-B67 (Hybrid Log-gamma)
static const float B67_A = 0.17883277,
B67_B = 0.28466892,
B67_C = 0.55991073;
// Linearize (expand), given a TRC as input
void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
{
@ -265,6 +270,17 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
HDR_C1, HDR_C2, HDR_C3);
GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", HDR_M1);
break;
case MP_CSP_TRC_ARIB_STD_B67:
GLSLF("color.rgb = mix(vec3(4.0) * color.rgb * color.rgb,\n"
" exp((color.rgb - vec3(%f)) / vec3(%f)) + vec3(%f),\n"
" lessThan(vec3(0.5), color.rgb));\n",
B67_C, B67_A, B67_B);
// Since the ARIB function's signal value of 1.0 corresponds to
// a peak of 12.0, we need to renormalize to prevent GL textures
// from clipping. (In general, mpv's internal conversions always
// assume 1.0 is the maximum brightness, not the reference peak)
GLSL(color.rgb /= vec3(12.0);)
break;
default:
abort();
}
@ -308,6 +324,13 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
HDR_C1, HDR_C2, HDR_C3);
GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", HDR_M2);
break;
case MP_CSP_TRC_ARIB_STD_B67:
GLSL(color.rgb *= vec3(12.0);)
GLSLF("color.rgb = mix(vec3(0.5) * sqrt(color.rgb),\n"
" vec3(%f) * log(color.rgb - vec3(%f)) + vec3(%f),\n"
" lessThan(vec3(1.0), color.rgb));\n",
B67_A, B67_B, B67_C);
break;
default:
abort();
}

View File

@ -498,10 +498,11 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
'(void)offsetof(AVFrame, hw_frames_ctx)',
use='libav'),
}, {
'name': 'avutil-st2084',
'desc': 'libavutil AVCOL_TRC_SMPTEST2084',
'name': 'avutil-hdr',
'desc': 'libavutil HDR TRCs',
'func': check_statement('libavutil/pixfmt.h',
'AVCOL_TRC_SMPTEST2084',
'AVCOL_TRC_SMPTEST2084,'
'AVCOL_TRC_ARIB_STD_B67',
use='libav'),
}
]