fix(playback): probe real VideoToolbox modes, fix chapterthumbs extraction

Address adversarial review: the VideoToolbox probe now smoke-encodes
both encoders in the exact constant-quality (-q:v) mode the uncapped
transcode path emits, so Intel Macs without qscale support resolve
auto to software instead of approving unrunnable commands; and
chapter-thumbnail extraction gains a videotoolbox case (hardware
decode, software filters) instead of misclassifying CPU args as a
hardware attempt — HDR thumbnails on macOS now tone-map via the shared
CPU chain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Quick104
2026-08-11 12:23:53 -04:00
co-authored by Claude Fable 5
parent f701702a55
commit 2489c3160b
2 changed files with 59 additions and 15 deletions
+27 -15
View File
@@ -202,9 +202,12 @@ func ffmpegSupportsVideoToolbox(ffmpegPath string) (bool, string) {
}
// probeFFmpegVideoToolbox verifies the configured FFmpeg exposes VideoToolbox
// decode plus both encoders, then smoke-encodes one frame. No filter probes:
// the videotoolbox pipeline keeps decoded frames in system memory, so the
// regular software filter graph applies (see appendHWAccelArgs).
// decode plus both encoders, then smoke-encodes one frame per encoder in the
// same constant-quality mode the uncapped transcode path emits (-q:v needs an
// Apple Silicon compression session; Intel Macs must fail here so auto
// resolves to software instead of approving commands that cannot run). No
// filter probes: the videotoolbox pipeline keeps decoded frames in system
// memory, so the regular software filter graph applies (see appendHWAccelArgs).
func probeFFmpegVideoToolbox(ffmpegPath string) nvencProbeResult {
if output, err := runFFmpegProbe(ffmpegPath, "-hide_banner", "-hwaccels"); err != nil {
return nvencProbeResult{reason: "hwaccels probe failed: " + probeFailure(err, output)}
@@ -220,18 +223,27 @@ func probeFFmpegVideoToolbox(ffmpegPath string) nvencProbeResult {
return nvencProbeResult{reason: "hevc_videotoolbox encoder unavailable"}
}
if output, err := runFFmpegProbe(ffmpegPath,
"-hide_banner",
"-loglevel", "error",
"-f", "lavfi",
"-i", "testsrc2=size=640x360:rate=1",
"-frames:v", "1",
"-an",
"-c:v", "h264_videotoolbox",
"-f", "null",
"-",
); err != nil {
return nvencProbeResult{reason: "h264_videotoolbox smoke encode failed: " + probeFailure(err, output)}
for _, smoke := range []struct {
encoder string
quality string
}{
{"h264_videotoolbox", "65"},
{"hevc_videotoolbox", "60"},
} {
if output, err := runFFmpegProbe(ffmpegPath,
"-hide_banner",
"-loglevel", "error",
"-f", "lavfi",
"-i", "testsrc2=size=640x360:rate=1",
"-frames:v", "1",
"-an",
"-c:v", smoke.encoder,
"-q:v", smoke.quality,
"-f", "null",
"-",
); err != nil {
return nvencProbeResult{reason: smoke.encoder + " smoke encode failed: " + probeFailure(err, output)}
}
}
return nvencProbeResult{available: true}
+32
View File
@@ -376,6 +376,38 @@ func TestResolveHWAccelWithFFmpegDarwinRequiresBothVTEncoders(t *testing.T) {
}
}
func TestResolveHWAccelWithFFmpegDarwinFallsBackToNoneWhenSmokeEncodeFails(t *testing.T) {
setupHWAccelTest(t)
currentGOOS = "darwin"
ffmpeg := writeFakeFFmpeg(t, fakeFFmpegProbe{videotoolbox: true, h264VT: true, hevcVT: true})
if got := ResolveHWAccelWithFFmpeg("auto", ffmpeg.path); got != "none" {
t.Fatalf("ResolveHWAccelWithFFmpeg() = %q, want none when smoke encode fails", got)
}
}
func TestVideoToolboxProbeSmokesBothEncodersInConstantQualityMode(t *testing.T) {
setupHWAccelTest(t)
currentGOOS = "darwin"
ffmpeg := writeFakeFFmpeg(t, successfulVideoToolboxProbe())
if got := ResolveHWAccelWithFFmpeg("auto", ffmpeg.path); got != "videotoolbox" {
t.Fatalf("ResolveHWAccelWithFFmpeg() = %q, want videotoolbox", got)
}
log, err := os.ReadFile(ffmpeg.logPath)
if err != nil {
t.Fatalf("read probe log: %v", err)
}
// The smoke encodes must exercise the exact uncapped (-q:v) mode the
// transcode arg builder emits — Intel Macs fail that mode at session
// creation, so probing without it would approve unrunnable commands.
for _, want := range []string{"-c:v h264_videotoolbox -q:v 65", "-c:v hevc_videotoolbox -q:v 60"} {
if !strings.Contains(string(log), want) {
t.Fatalf("probe log missing %q:\n%s", want, log)
}
}
}
func TestExplicitVideoToolboxBypassesFFmpegProbe(t *testing.T) {
setupHWAccelTest(t)