diff --git a/internal/player/controller_gstreamer.go b/internal/player/controller_gstreamer.go index df3df62..4263e91 100644 --- a/internal/player/controller_gstreamer.go +++ b/internal/player/controller_gstreamer.go @@ -9,8 +9,16 @@ import ( "git.leaktechnologies.dev/stu/VideoTools/internal/logging" ) +// DisableGStreamer forces fallback to the stub controller even when the gstreamer build tag is set. +var DisableGStreamer bool + // newController creates a GStreamer-based controller for embedded video playback func newController() Controller { + if DisableGStreamer { + logging.Info(logging.CatPlayer, "GStreamer disabled by settings; using stub controller") + return &stubController{} + } + config := Config{ Backend: BackendAuto, WindowWidth: 640, @@ -132,5 +140,5 @@ func (s *stubController) SetVolume(level float64) error { return fmt.Errorf("GStreamer player not available") } func (s *stubController) FullScreen() error { return fmt.Errorf("GStreamer player not available") } -func (s *stubController) Stop() error { return fmt.Errorf("GStreamer player not available") } -func (s *stubController) Close() {} +func (s *stubController) Stop() error { return fmt.Errorf("GStreamer player not available") } +func (s *stubController) Close() {} diff --git a/internal/player/frame_player_gstreamer.go b/internal/player/frame_player_gstreamer.go index d47a984..e99ba06 100644 --- a/internal/player/frame_player_gstreamer.go +++ b/internal/player/frame_player_gstreamer.go @@ -2,6 +2,11 @@ package player +import "fmt" + func newFramePlayer(config Config) (framePlayer, error) { + if DisableGStreamer { + return nil, fmt.Errorf("gstreamer disabled by settings") + } return NewGStreamerPlayer(config) } diff --git a/internal/player/gstreamer_player.go b/internal/player/gstreamer_player.go index 298086c..5871803 100644 --- a/internal/player/gstreamer_player.go +++ b/internal/player/gstreamer_player.go @@ -83,26 +83,37 @@ import ( var gstInitOnce sync.Once type GStreamerPlayer struct { - mu sync.Mutex - seekMu sync.Mutex pipeline *C.GstElement - appsink *C.GstElement bus *C.GstBus - busQuit chan struct{} - busDone chan struct{} - events chan busEvent - paused bool - volume float64 - preview bool - width int - height int - fps float64 - queued *image.RGBA - lastErr string - eos bool - state C.GstState + appsink *C.GstElement + + width int + height int + fps float64 + mode PlayerState + paused bool + volume float64 + queued *image.RGBA + lastErr string + backend Backend + config Config + seekMu sync.Mutex + events chan busEvent + + mu sync.Mutex + + // Cached duration duration time.Duration - mode PlayerState + + // Bus handling + busCh chan *C.GstMessage + + // Bus loop controls + busStop chan struct{} + busDone chan struct{} + + // Seek coalescing + lastSeekTarget time.Duration } type busEvent struct { @@ -163,9 +174,9 @@ func (p *GStreamerPlayer) Load(path string, offset time.Duration) error { caps := C.gst_caps_from_string(capsStr) C.free(unsafe.Pointer(capsStr)) if caps != nil { - capsName := C.CString("caps") - C.vt_gst_set_obj(appsink, capsName, C.gpointer(caps)) - C.free(unsafe.Pointer(capsName)) + capsName := C.CString("caps") + C.vt_gst_set_obj(appsink, capsName, C.gpointer(caps)) + C.free(unsafe.Pointer(capsName)) C.gst_caps_unref(caps) } emitSignals := C.CString("emit-signals") @@ -309,7 +320,8 @@ func (p *GStreamerPlayer) Pause() error { func (p *GStreamerPlayer) SeekToTime(offset time.Duration) error { p.seekMu.Lock() - defer p.seekMu.Unlock() + p.lastSeekTarget = offset + p.seekMu.Unlock() p.mu.Lock() prevMode := p.mode @@ -471,7 +483,7 @@ func (p *GStreamerPlayer) primeAfterSeekLocked() { return } p.drainPendingLocked() - frame, err := p.readFrameLocked(C.GstClockTime(200 * 1000 * 1000)) + frame, err := p.readFrameLocked(C.GstClockTime(120 * 1000 * 1000)) if err != nil || frame == nil { return }