Improve GStreamer seek responsiveness and allow disable toggle

This commit is contained in:
VideoTools CI 2026-01-18 06:43:36 -05:00
parent d14ca93560
commit 016d2a4f8a
3 changed files with 49 additions and 24 deletions

View File

@ -9,8 +9,16 @@ import (
"git.leaktechnologies.dev/stu/VideoTools/internal/logging" "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 // newController creates a GStreamer-based controller for embedded video playback
func newController() Controller { func newController() Controller {
if DisableGStreamer {
logging.Info(logging.CatPlayer, "GStreamer disabled by settings; using stub controller")
return &stubController{}
}
config := Config{ config := Config{
Backend: BackendAuto, Backend: BackendAuto,
WindowWidth: 640, WindowWidth: 640,
@ -132,5 +140,5 @@ func (s *stubController) SetVolume(level float64) error {
return fmt.Errorf("GStreamer player not available") return fmt.Errorf("GStreamer player not available")
} }
func (s *stubController) FullScreen() 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) Stop() error { return fmt.Errorf("GStreamer player not available") }
func (s *stubController) Close() {} func (s *stubController) Close() {}

View File

@ -2,6 +2,11 @@
package player package player
import "fmt"
func newFramePlayer(config Config) (framePlayer, error) { func newFramePlayer(config Config) (framePlayer, error) {
if DisableGStreamer {
return nil, fmt.Errorf("gstreamer disabled by settings")
}
return NewGStreamerPlayer(config) return NewGStreamerPlayer(config)
} }

View File

@ -83,26 +83,37 @@ import (
var gstInitOnce sync.Once var gstInitOnce sync.Once
type GStreamerPlayer struct { type GStreamerPlayer struct {
mu sync.Mutex
seekMu sync.Mutex
pipeline *C.GstElement pipeline *C.GstElement
appsink *C.GstElement
bus *C.GstBus bus *C.GstBus
busQuit chan struct{} appsink *C.GstElement
busDone chan struct{}
events chan busEvent width int
paused bool height int
volume float64 fps float64
preview bool mode PlayerState
width int paused bool
height int volume float64
fps float64 queued *image.RGBA
queued *image.RGBA lastErr string
lastErr string backend Backend
eos bool config Config
state C.GstState seekMu sync.Mutex
events chan busEvent
mu sync.Mutex
// Cached duration
duration time.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 { type busEvent struct {
@ -163,9 +174,9 @@ func (p *GStreamerPlayer) Load(path string, offset time.Duration) error {
caps := C.gst_caps_from_string(capsStr) caps := C.gst_caps_from_string(capsStr)
C.free(unsafe.Pointer(capsStr)) C.free(unsafe.Pointer(capsStr))
if caps != nil { if caps != nil {
capsName := C.CString("caps") capsName := C.CString("caps")
C.vt_gst_set_obj(appsink, capsName, C.gpointer(caps)) C.vt_gst_set_obj(appsink, capsName, C.gpointer(caps))
C.free(unsafe.Pointer(capsName)) C.free(unsafe.Pointer(capsName))
C.gst_caps_unref(caps) C.gst_caps_unref(caps)
} }
emitSignals := C.CString("emit-signals") emitSignals := C.CString("emit-signals")
@ -309,7 +320,8 @@ func (p *GStreamerPlayer) Pause() error {
func (p *GStreamerPlayer) SeekToTime(offset time.Duration) error { func (p *GStreamerPlayer) SeekToTime(offset time.Duration) error {
p.seekMu.Lock() p.seekMu.Lock()
defer p.seekMu.Unlock() p.lastSeekTarget = offset
p.seekMu.Unlock()
p.mu.Lock() p.mu.Lock()
prevMode := p.mode prevMode := p.mode
@ -471,7 +483,7 @@ func (p *GStreamerPlayer) primeAfterSeekLocked() {
return return
} }
p.drainPendingLocked() 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 { if err != nil || frame == nil {
return return
} }