feat(stream): GPU HDR tonemap via libplacebo
Prefer the single-pass Vulkan libplacebo filter over the CPU zscale chain for HDR->SDR tonemapping when the agent ffmpeg has it. One GPU pass does tonemap + BT.709 primaries/transfer/matrix + 8-bit yuv420p, replacing the four-stage zscale chain and its trailing format=/setparams. Higher quality, far cheaper than the CPU path, and present on builds that lack zscale. - FFmpegSupportsLibplacebo probe (cached, mirrors FFmpegSupportsZscale) - HasLibplacebo on TranscodeRuntime, wired from buildTranscodeRuntime - hls.go: videoTail picks libplacebo when present (not h264_vaapi), else keeps the zscale tonemap + format chain - test: libplacebo replaces the zscale chain, never runs alongside it
This commit is contained in:
parent
325c11c1eb
commit
005a4380dd
5 changed files with 90 additions and 11 deletions
|
|
@ -1353,26 +1353,37 @@ func buildHLSFFmpegArgsAt(cfg HLSSessionConfig, probe *StreamProbe, tmpDir strin
|
|||
hwUploadTail = ",hwupload"
|
||||
colorTail = ""
|
||||
}
|
||||
// HDR→SDR tonemap, inserted after the scale (downscale-first = fewer pixels
|
||||
// to tonemap) and before format=. Only for an HDR source on a zscale-capable
|
||||
// ffmpeg; the trailing comma in hdrTonemapChain slots it in front of format=.
|
||||
// HDR→SDR tonemap, after the scale (downscale-first = fewer pixels to map).
|
||||
// Prefer libplacebo (GPU, ONE pass — it also emits the BT.709 colorspace +
|
||||
// 8-bit format, so it REPLACES the format/setparams tail); else the zscale
|
||||
// CPU chain; else play untonemapped (desaturated, last resort). Skip
|
||||
// libplacebo on VAAPI: its Vulkan surface flow doesn't compose with our
|
||||
// nv12+hwupload path, so VAAPI keeps the zscale-or-none behaviour.
|
||||
useLibplacebo := probe.HDR != "" && cfg.Transcode.HasLibplacebo && codec != "h264_vaapi"
|
||||
tonemap := ""
|
||||
if probe.HDR != "" && cfg.Transcode.TonemapHDR {
|
||||
if probe.HDR != "" && cfg.Transcode.TonemapHDR && !useLibplacebo {
|
||||
tonemap = hdrTonemapChain
|
||||
}
|
||||
// Core video chain (scale + optional tonemap + pixel format + color metadata),
|
||||
// WITHOUT the optional hwUploadTail — that has to run last, after any subtitle
|
||||
// overlay, so it's appended separately below.
|
||||
// videoTail = everything after the scale: either libplacebo (tonemap +
|
||||
// colorspace + format in one) or the (optional zscale) tonemap then the
|
||||
// format + color-metadata tail. No leading comma — the scale chain ends in one.
|
||||
videoTail := tonemap + "format=" + pixFormat + colorTail
|
||||
if useLibplacebo {
|
||||
videoTail = libplaceboTonemapFilter
|
||||
}
|
||||
// Core video chain (scale + tonemap/format tail), WITHOUT the optional
|
||||
// hwUploadTail — that has to run last, after any subtitle overlay, so it's
|
||||
// appended separately below.
|
||||
var vchain string
|
||||
if maxH > 0 && probe.Height > maxH {
|
||||
vchain = fmt.Sprintf(
|
||||
"scale=-2:%d:force_original_aspect_ratio=decrease,scale=trunc(iw/2)*2:trunc(ih/2)*2,%sformat=%s%s",
|
||||
maxH, tonemap, pixFormat, colorTail,
|
||||
"scale=-2:%d:force_original_aspect_ratio=decrease,scale=trunc(iw/2)*2:trunc(ih/2)*2,%s",
|
||||
maxH, videoTail,
|
||||
)
|
||||
} else {
|
||||
vchain = fmt.Sprintf(
|
||||
"scale=trunc(iw/2)*2:trunc(ih/2)*2,%sformat=%s%s",
|
||||
tonemap, pixFormat, colorTail,
|
||||
"scale=trunc(iw/2)*2:trunc(ih/2)*2,%s",
|
||||
videoTail,
|
||||
)
|
||||
}
|
||||
if burnIdx >= 0 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue