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
|
|
@ -28,11 +28,53 @@ import (
|
|||
// possible follow-up if HLG/DV-only sources become common.
|
||||
const hdrTonemapChain = "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,"
|
||||
|
||||
// libplaceboTonemapFilter maps an HDR source to SDR BT.709 in a SINGLE GPU pass
|
||||
// (Vulkan): tone-map the HDR curve, convert primaries/transfer/matrix to BT.709
|
||||
// limited range, and output 8-bit yuv420p — so it REPLACES the zscale chain AND
|
||||
// the trailing `format=yuv420p,setparams=bt709` (it does both). Higher quality
|
||||
// and far cheaper than the CPU zscale chain, and the agent's ffmpeg has it where
|
||||
// zscale is missing. It does NOT scale here — the CPU scale chain runs first
|
||||
// (it owns the even-dimension rounding libx264/nvenc require). No trailing comma:
|
||||
// it's the last filter in the chain.
|
||||
const libplaceboTonemapFilter = "libplacebo=colorspace=bt709:color_primaries=bt709:color_trc=bt709:range=tv:format=yuv420p:tonemapping=bt.2390"
|
||||
|
||||
var (
|
||||
zscaleCacheMu sync.Mutex
|
||||
zscaleCache = map[string]bool{}
|
||||
|
||||
libplaceboCacheMu sync.Mutex
|
||||
libplaceboCache = map[string]bool{}
|
||||
)
|
||||
|
||||
// FFmpegSupportsLibplacebo reports whether the ffmpeg binary has the libplacebo
|
||||
// filter (Vulkan GPU HDR tonemap + colorspace). Preferred over zscale when both
|
||||
// exist. Cached per path; a probe failure is treated as "no". Mirrors
|
||||
// FFmpegSupportsZscale.
|
||||
func FFmpegSupportsLibplacebo(ffmpegPath string) bool {
|
||||
if ffmpegPath == "" {
|
||||
return false
|
||||
}
|
||||
libplaceboCacheMu.Lock()
|
||||
if v, ok := libplaceboCache[ffmpegPath]; ok {
|
||||
libplaceboCacheMu.Unlock()
|
||||
return v
|
||||
}
|
||||
libplaceboCacheMu.Unlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
out, err := exec.CommandContext(ctx, ffmpegPath, "-hide_banner", "-filters").Output()
|
||||
supported := err == nil && bytes.Contains(out, []byte("libplacebo"))
|
||||
|
||||
libplaceboCacheMu.Lock()
|
||||
libplaceboCache[ffmpegPath] = supported
|
||||
libplaceboCacheMu.Unlock()
|
||||
if supported {
|
||||
log.Printf("[tonemap] ffmpeg has libplacebo — HDR sources tonemapped on the GPU (preferred)")
|
||||
}
|
||||
return supported
|
||||
}
|
||||
|
||||
// FFmpegSupportsZscale reports whether the ffmpeg binary at path was built with
|
||||
// the zscale filter (libzimg), required for HDR→SDR tonemapping. Cached per
|
||||
// path. A detection failure (binary missing, exec error) is treated as "no" so
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue