Skip to content

Commit 5256fa6

Browse files
committed
Probe VAAPI device initialization
1 parent 3040426 commit 5256fa6

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

internal/transcode/ffmpeg_args_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package transcode
22

33
import (
44
"errors"
5+
"fmt"
6+
"os"
57
"path/filepath"
68
"slices"
9+
"strings"
710
"testing"
811
)
912

@@ -172,6 +175,52 @@ func TestResolveHardwareDecodeFallsBackToSoftwareWhenProbeFails(t *testing.T) {
172175
}
173176
}
174177

178+
func TestDefaultHardwareProbeRejectsVAAPIWhenDeviceInitializationFails(t *testing.T) {
179+
tempDir := t.TempDir()
180+
ffmpegPath := filepath.Join(tempDir, "ffmpeg")
181+
callsPath := filepath.Join(tempDir, "calls")
182+
script := fmt.Sprintf(`#!/bin/sh
183+
echo "$@" >> %q
184+
case " $* " in
185+
*" -hwaccels "*)
186+
printf 'Hardware acceleration methods:\nvaapi\n'
187+
exit 0
188+
;;
189+
*" -init_hw_device "*)
190+
echo 'Failed to initialise VAAPI connection' >&2
191+
exit 1
192+
;;
193+
esac
194+
exit 0
195+
`, callsPath)
196+
if err := os.WriteFile(ffmpegPath, []byte(script), 0o755); err != nil {
197+
t.Fatal(err)
198+
}
199+
devicePath := filepath.Join(tempDir, "renderD128")
200+
if err := os.WriteFile(devicePath, nil, 0o600); err != nil {
201+
t.Fatal(err)
202+
}
203+
204+
err := defaultHardwareProbe(ffmpegPath, FFmpegOptions{
205+
HardwareDecode: "vaapi",
206+
HardwareDevice: devicePath,
207+
})
208+
209+
if err == nil {
210+
t.Fatal("expected VAAPI device initialization failure")
211+
}
212+
if !strings.Contains(err.Error(), "vaapi device init probe failed") {
213+
t.Fatalf("error = %v", err)
214+
}
215+
calls, err := os.ReadFile(callsPath)
216+
if err != nil {
217+
t.Fatal(err)
218+
}
219+
if !strings.Contains(string(calls), "-init_hw_device vaapi=probe:"+devicePath) {
220+
t.Fatalf("ffmpeg calls should initialize the configured VAAPI device, calls=%s", calls)
221+
}
222+
}
223+
175224
func TestNewManagerFallsBackToSoftwareDecodeWhenHardwareProbeFails(t *testing.T) {
176225
manager := NewManager(Options{
177226
FFmpegPath: "/usr/bin/ffmpeg",

internal/transcode/manager.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,10 @@ func defaultHardwareProbe(ffmpegPath string, options FFmpegOptions) error {
751751
if err := probeFFmpegHWAccel(ffmpegPath, "vaapi"); err != nil {
752752
return err
753753
}
754-
return probeDevice(options.HardwareDevice)
754+
if err := probeDevice(options.HardwareDevice); err != nil {
755+
return err
756+
}
757+
return probeFFmpegVAAPIDeviceInit(ffmpegPath, options.HardwareDevice)
755758
default:
756759
return fmt.Errorf("unsupported hardware decode mode %q", options.HardwareDecode)
757760
}
@@ -776,6 +779,32 @@ func probeFFmpegHWAccel(ffmpegPath, name string) error {
776779
return fmt.Errorf("ffmpeg does not list %s hwaccel", name)
777780
}
778781

782+
func probeFFmpegVAAPIDeviceInit(ffmpegPath, device string) error {
783+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
784+
defer cancel()
785+
786+
output, err := exec.CommandContext(ctx, ffmpegPath,
787+
"-hide_banner",
788+
"-loglevel", "error",
789+
"-init_hw_device", "vaapi=probe:"+device,
790+
"-f", "lavfi",
791+
"-i", "nullsrc=s=16x16:d=0.1",
792+
"-frames:v", "1",
793+
"-f", "null",
794+
"-",
795+
).CombinedOutput()
796+
if ctx.Err() != nil {
797+
return fmt.Errorf("vaapi device init probe timed out")
798+
}
799+
if err != nil {
800+
if message := strings.TrimSpace(string(output)); message != "" {
801+
return fmt.Errorf("vaapi device init probe failed: %w: %s", err, message)
802+
}
803+
return fmt.Errorf("vaapi device init probe failed: %w", err)
804+
}
805+
return nil
806+
}
807+
779808
func probeDevice(device string) error {
780809
if strings.TrimSpace(device) == "" {
781810
return errors.New("hardware device is required")

0 commit comments

Comments
 (0)