@@ -88,6 +88,7 @@ type Session struct {
8888 PositionTicks int64
8989 Paused bool
9090 bufferPaused bool
91+ HardwarePipeline string
9192
9293 cancel context.CancelFunc
9394 process Process
@@ -102,10 +103,11 @@ type Runner interface {
102103}
103104
104105type Manager struct {
105- mu sync.Mutex
106- options Options
107- sessions map [string ]* Session
108- media map [string ]MediaInfo
106+ mu sync.Mutex
107+ options Options
108+ sessions map [string ]* Session
109+ media map [string ]MediaInfo
110+ vaapiEncodeFallback map [string ]bool
109111}
110112
111113func NewManager (options Options ) * Manager {
@@ -120,7 +122,7 @@ func NewManager(options Options) *Manager {
120122 Options : ffmpegOptions ,
121123 }
122124 }
123- return & Manager {options : options , sessions : map [string ]* Session {}, media : map [string ]MediaInfo {}}
125+ return & Manager {options : options , sessions : map [string ]* Session {}, media : map [string ]MediaInfo {}, vaapiEncodeFallback : map [ string ] bool {} }
124126}
125127
126128func NewManagerStrict (options Options ) (* Manager , error ) {
@@ -141,7 +143,7 @@ func NewManagerStrict(options Options) (*Manager, error) {
141143 Options : ffmpegOptions ,
142144 }
143145 }
144- return & Manager {options : options , sessions : map [string ]* Session {}, media : map [string ]MediaInfo {}}, nil
146+ return & Manager {options : options , sessions : map [string ]* Session {}, media : map [string ]MediaInfo {}, vaapiEncodeFallback : map [ string ] bool {} }, nil
145147}
146148
147149func normalizeManagerOptions (options Options ) Options {
@@ -346,6 +348,10 @@ func (m *Manager) Ensure(id string, request Request) (*Session, error) {
346348 if existing , ok := m .sessions [id ]; ok {
347349 if sessionProcessDone (existing ) {
348350 delete (m .sessions , id )
351+ if shouldFallbackToVAAPIEncode (existing , m .options .Runner ) {
352+ m .vaapiEncodeFallback [id ] = true
353+ logging .Infof ("hardware transcode fallback id=%s from=vaapi-full to=vaapi-encode reason=process_done" , id )
354+ }
349355 stale = existing
350356 traceSwitch ("manager_restart_done id=%s input=%s" , id , redactURLString (existing .InputURL ))
351357 } else if shouldRestart (existing , request ) {
@@ -386,6 +392,12 @@ func (m *Manager) Ensure(id string, request Request) (*Session, error) {
386392 }
387393 ctx , cancel := context .WithCancel (context .Background ())
388394 session := & Session {ID : id , Dir : dir , InputURL : request .InputURL , LastAccess : now , LastMediaAccess : now , cancel : cancel }
395+ if runner , ok := m .options .Runner .(FFmpegRunner ); ok {
396+ session .HardwarePipeline = effectiveFFmpegOptions (session , runner .Options ).HardwarePipeline
397+ }
398+ if m .vaapiEncodeFallback [id ] {
399+ session .HardwarePipeline = "vaapi-encode"
400+ }
389401 session .SegmentTicks = m .segmentTicks ()
390402 touchSession (session , request , now , true )
391403 if session .Media .IsZero () {
@@ -837,6 +849,20 @@ func sessionProcessDone(session *Session) bool {
837849 return ok && process .Done ()
838850}
839851
852+ func shouldFallbackToVAAPIEncode (session * Session , runner Runner ) bool {
853+ if session == nil {
854+ return false
855+ }
856+ if strings .EqualFold (strings .TrimSpace (session .HardwarePipeline ), "vaapi-encode" ) {
857+ return false
858+ }
859+ ffmpegRunner , ok := runner .(FFmpegRunner )
860+ if ! ok {
861+ return false
862+ }
863+ return hardwarePipeline (ffmpegRunner .Options ) == "vaapi-full"
864+ }
865+
840866func formatTicks (ticks int64 ) string {
841867 if ticks <= 0 {
842868 return "0s"
@@ -1101,10 +1127,11 @@ func (r FFmpegRunner) Start(ctx context.Context, session *Session, request Reque
11011127 return nil , errors .New ("ffmpeg path is required" )
11021128 }
11031129
1104- args := buildFFmpegArgs (session , request , r .Options )
1130+ options := effectiveFFmpegOptions (session , r .Options )
1131+ args := buildFFmpegArgs (session , request , options )
11051132 playlist := filepath .Join (session .Dir , "master.m3u8" )
11061133 logPath := filepath .Join (session .Dir , "ffmpeg.log" )
1107- logging .Infof ("transcode start id=%s segment=%d decode=%s audio_stream_index=%d audio_map=%s audio=optional-aac log=%s" , session .ID , session .SegmentStartIndex , ffmpegOptionsSummary (r . Options ), request .AudioStreamIndex , audioMapArg (session , request ), logPath )
1134+ logging .Infof ("transcode start id=%s segment=%d decode=%s audio_stream_index=%d audio_map=%s audio=optional-aac log=%s" , session .ID , session .SegmentStartIndex , ffmpegOptionsSummary (options ), request .AudioStreamIndex , audioMapArg (session , request ), logPath )
11081135 logging .Debugf ("ffmpeg start id=%s item=%s media_source=%s start_ticks=%d segment_start=%d path=%s input=%s playlist=%s media=%s args=%s" , session .ID , session .ItemID , session .MediaSourceID , session .StartTimeTicks , session .SegmentStartIndex , r .Path , redactURLString (request .InputURL ), playlist , session .Media .Summary (), redactFFmpegArgs (args ))
11091136 cmd := exec .CommandContext (ctx , r .Path , args ... )
11101137 stdin , err := cmd .StdinPipe ()
@@ -1121,7 +1148,7 @@ func (r FFmpegRunner) Start(ctx context.Context, session *Session, request Reque
11211148 _ = logFile .Close ()
11221149 return nil , fmt .Errorf ("start ffmpeg: %w" , err )
11231150 }
1124- logging .Infof ("ffmpeg started id=%s pid=%d decode=%s" , session .ID , cmd .Process .Pid , ffmpegOptionsSummary (r . Options ))
1151+ logging .Infof ("ffmpeg started id=%s pid=%d decode=%s" , session .ID , cmd .Process .Pid , ffmpegOptionsSummary (options ))
11251152 process := & execProcess {cmd : cmd , logFile : logFile , stdin : stdin , doneCh : make (chan struct {})}
11261153 go func () {
11271154 err := cmd .Wait ()
@@ -1144,6 +1171,16 @@ func (r FFmpegRunner) Start(ctx context.Context, session *Session, request Reque
11441171 return process , nil
11451172}
11461173
1174+ func effectiveFFmpegOptions (session * Session , options FFmpegOptions ) FFmpegOptions {
1175+ if session == nil {
1176+ return options
1177+ }
1178+ if pipeline := strings .TrimSpace (session .HardwarePipeline ); pipeline != "" {
1179+ options .HardwarePipeline = pipeline
1180+ }
1181+ return options
1182+ }
1183+
11471184func archiveFailedTranscodeLog (session * Session , logPath string ) (string , error ) {
11481185 if session == nil {
11491186 return "" , errors .New ("session is required" )
0 commit comments