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:
Deivid Soto 2026-06-03 09:29:55 +02:00
parent 325c11c1eb
commit 005a4380dd
5 changed files with 90 additions and 11 deletions

View file

@ -66,6 +66,25 @@ func TestTonemap_AppliedInNoDownscaleBranch(t *testing.T) {
}
}
func TestTonemap_LibplaceboPreferredOverZscale(t *testing.T) {
// HDR source + an ffmpeg with libplacebo → the single GPU filter replaces
// the whole CPU zscale chain (and the trailing format=/setparams it folds in).
cfg := HLSSessionConfig{
SessionID: "test",
SourcePath: "/movies/x.mkv",
Quality: "720p",
Transcode: TranscodeRuntime{FFmpegPath: "/usr/bin/ffmpeg", HWAccel: HWAccelNone, TonemapHDR: true, HasLibplacebo: true},
}
probe := &StreamProbe{Width: 3840, Height: 2160, BitDepth: 10, HDR: "HDR10", DurationSec: 100}
vf := vfChain(strings.Join(buildHLSFFmpegArgsAt(cfg, probe, "/tmp/t", 0, 0), " "))
if !strings.Contains(vf, "libplacebo") {
t.Fatalf("libplacebo-capable ffmpeg: expected libplacebo filter, got %q", vf)
}
if strings.Contains(vf, "zscale=t=linear") || strings.Contains(vf, "tonemap=tonemap=hable") {
t.Errorf("libplacebo must replace the zscale chain, not run alongside it: %q", vf)
}
}
func TestTonemap_SkippedWhenFFmpegLacksZscale(t *testing.T) {
vf := vfChain(hlsArgsFor("HDR10", false, HWAccelNone))
if strings.Contains(vf, "zscale") || strings.Contains(vf, "tonemap") {