Perform hardware codec checks on separate go routine (#6414)

Warn if tests are taking a long time
Add WaitDelay to try to kill process if hanging
This commit is contained in:
WithoutPants
2025-12-15 14:57:00 +11:00
committed by GitHub
parent 1691280d1b
commit b23c3cd618
3 changed files with 45 additions and 7 deletions

View File

@@ -313,6 +313,7 @@ func (s *Manager) RefreshFFMpeg(ctx context.Context) {
s.FFMpeg = ffmpeg.NewEncoder(ffmpegPath) s.FFMpeg = ffmpeg.NewEncoder(ffmpegPath)
s.FFProbe = ffmpeg.NewFFProbe(ffprobePath) s.FFProbe = ffmpeg.NewFFProbe(ffprobePath)
s.FFMpeg.InitHWSupport(ctx) // initialise hardware support with background context
s.FFMpeg.InitHWSupport(context.Background())
} }
} }

View File

@@ -36,6 +36,32 @@ const minHeight int = 480
// Tests all (given) hardware codec's // Tests all (given) hardware codec's
func (f *FFMpeg) InitHWSupport(ctx context.Context) { func (f *FFMpeg) InitHWSupport(ctx context.Context) {
// do the hardware codec tests in a separate goroutine to avoid blocking
done := make(chan struct{})
go func() {
f.initHWSupport(ctx)
close(done)
}()
// log if the initialization takes too long
const hwInitLogTimeoutSecondsDefault = 5
hwInitLogTimeoutSeconds := hwInitLogTimeoutSecondsDefault * time.Second
timer := time.NewTimer(hwInitLogTimeoutSeconds)
go func() {
select {
case <-timer.C:
logger.Warnf("[InitHWSupport] Hardware codec initialization is taking longer than %s...", hwInitLogTimeoutSeconds)
logger.Info("[InitHWSupport] Hardware encoding will not be available until initialization is complete.")
case <-done:
if !timer.Stop() {
<-timer.C
}
}
}()
}
func (f *FFMpeg) initHWSupport(ctx context.Context) {
var hwCodecSupport []VideoCodec var hwCodecSupport []VideoCodec
// Note that the first compatible codec is returned, so order is important // Note that the first compatible codec is returned, so order is important
@@ -83,6 +109,7 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
defer cancel() defer cancel()
cmd := f.Command(testCtx, args) cmd := f.Command(testCtx, args)
cmd.WaitDelay = time.Second
logger.Tracef("[InitHWSupport] Testing codec %s: %v", codec, cmd.Args) logger.Tracef("[InitHWSupport] Testing codec %s: %v", codec, cmd.Args)
var stderr bytes.Buffer var stderr bytes.Buffer
@@ -112,6 +139,8 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
} }
logger.Info(outstr) logger.Info(outstr)
f.hwCodecSupportMutex.Lock()
defer f.hwCodecSupportMutex.Unlock()
f.hwCodecSupport = hwCodecSupport f.hwCodecSupport = hwCodecSupport
} }
@@ -411,7 +440,7 @@ func (f *FFMpeg) hwMaxResFilter(toCodec VideoCodec, vf *models.VideoFile, reqHei
// Return if a hardware accelerated for HLS is available // Return if a hardware accelerated for HLS is available
func (f *FFMpeg) hwCodecHLSCompatible() *VideoCodec { func (f *FFMpeg) hwCodecHLSCompatible() *VideoCodec {
for _, element := range f.hwCodecSupport { for _, element := range f.getHWCodecSupport() {
switch element { switch element {
case VideoCodecN264, case VideoCodecN264,
VideoCodecN264H, VideoCodecN264H,
@@ -429,7 +458,7 @@ func (f *FFMpeg) hwCodecHLSCompatible() *VideoCodec {
// Return if a hardware accelerated codec for MP4 is available // Return if a hardware accelerated codec for MP4 is available
func (f *FFMpeg) hwCodecMP4Compatible() *VideoCodec { func (f *FFMpeg) hwCodecMP4Compatible() *VideoCodec {
for _, element := range f.hwCodecSupport { for _, element := range f.getHWCodecSupport() {
switch element { switch element {
case VideoCodecN264, case VideoCodecN264,
VideoCodecN264H, VideoCodecN264H,
@@ -445,7 +474,7 @@ func (f *FFMpeg) hwCodecMP4Compatible() *VideoCodec {
// Return if a hardware accelerated codec for WebM is available // Return if a hardware accelerated codec for WebM is available
func (f *FFMpeg) hwCodecWEBMCompatible() *VideoCodec { func (f *FFMpeg) hwCodecWEBMCompatible() *VideoCodec {
for _, element := range f.hwCodecSupport { for _, element := range f.getHWCodecSupport() {
switch element { switch element {
case VideoCodecIVP9, case VideoCodecIVP9,
VideoCodecVVP9: VideoCodecVVP9:

View File

@@ -10,6 +10,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
stashExec "github.com/stashapp/stash/pkg/exec" stashExec "github.com/stashapp/stash/pkg/exec"
"github.com/stashapp/stash/pkg/fsutil" "github.com/stashapp/stash/pkg/fsutil"
@@ -216,9 +217,10 @@ func (v Version) String() string {
// FFMpeg provides an interface to ffmpeg. // FFMpeg provides an interface to ffmpeg.
type FFMpeg struct { type FFMpeg struct {
ffmpeg string ffmpeg string
version Version version Version
hwCodecSupport []VideoCodec hwCodecSupport []VideoCodec
hwCodecSupportMutex sync.RWMutex
} }
// Creates a new FFMpeg encoder // Creates a new FFMpeg encoder
@@ -241,3 +243,9 @@ func (f *FFMpeg) Command(ctx context.Context, args []string) *exec.Cmd {
func (f *FFMpeg) Path() string { func (f *FFMpeg) Path() string {
return f.ffmpeg return f.ffmpeg
} }
func (f *FFMpeg) getHWCodecSupport() []VideoCodec {
f.hwCodecSupportMutex.RLock()
defer f.hwCodecSupportMutex.RUnlock()
return f.hwCodecSupport
}