VideoTools/internal/player/controller_gstreamer.go
Stu Leak 501e2622dc feat(player): integrate GStreamer for stable video playback
- Add GStreamer as mandatory core dependency in install.sh
- Create controller_gstreamer.go wrapping GStreamerPlayer
- Add missing methods to GStreamerPlayer (SetWindow, Stop, SetFullScreen)
- Fix GstSeekFlags type casting issue
- Update build scripts to always use -tags gstreamer
- Update controller_linux.go build tag to exclude when gstreamer enabled
- Add comprehensive migration documentation

GStreamer replaces the broken FFmpeg pipe-based UnifiedPlayer.
GStreamer 1.26+ provides frame-accurate seeking and reliable A/V sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 03:43:34 -05:00

137 lines
3.7 KiB
Go

//go:build gstreamer
package player
import (
"fmt"
"time"
"git.leaktechnologies.dev/stu/VideoTools/internal/logging"
)
// newController creates a GStreamer-based controller for embedded video playback
func newController() Controller {
config := Config{
Backend: BackendAuto,
WindowWidth: 640,
WindowHeight: 360,
Volume: 1.0,
Muted: false,
AutoPlay: false,
HardwareAccel: false,
PreviewMode: false, // Full playback mode
AudioOutput: "auto",
VideoOutput: "rgb24",
CacheEnabled: true,
CacheSize: 64 * 1024 * 1024,
LogLevel: LogInfo,
}
player, err := NewGStreamerPlayer(config)
if err != nil {
logging.Error(logging.CatPlayer, "Failed to create GStreamer player: %v", err)
return &stubController{}
}
logging.Info(logging.CatPlayer, "GStreamer controller initialized (GStreamer %s)", "1.26+")
return &gstreamerController{
player: player,
}
}
// gstreamerController wraps GStreamerPlayer to implement the Controller interface
type gstreamerController struct {
player *GStreamerPlayer
}
func (c *gstreamerController) Load(path string, offset float64) error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
offsetDuration := time.Duration(offset * float64(time.Second))
logging.Debug(logging.CatPlayer, "Loading video: path=%s offset=%.3fs", path, offset)
return c.player.Load(path, offsetDuration)
}
func (c *gstreamerController) SetWindow(x, y, w, h int) {
if c.player == nil {
return
}
c.player.SetWindow(x, y, w, h)
}
func (c *gstreamerController) Play() error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
return c.player.Play()
}
func (c *gstreamerController) Pause() error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
return c.player.Pause()
}
func (c *gstreamerController) Seek(offset float64) error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
offsetDuration := time.Duration(offset * float64(time.Second))
return c.player.SeekToTime(offsetDuration)
}
func (c *gstreamerController) SetVolume(level float64) error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
// Controller uses 0-100 scale, GStreamer uses 0.0-1.0
normalizedLevel := level / 100.0
return c.player.SetVolume(normalizedLevel)
}
func (c *gstreamerController) FullScreen() error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
return c.player.SetFullScreen(true)
}
func (c *gstreamerController) Stop() error {
if c.player == nil {
return fmt.Errorf("GStreamer player not initialized")
}
return c.player.Stop()
}
func (c *gstreamerController) Close() {
if c.player != nil {
c.player.Close()
}
}
// stubController provides a no-op implementation when GStreamer fails to initialize
type stubController struct{}
func (s *stubController) Load(path string, offset float64) error {
return fmt.Errorf("GStreamer player not available")
}
func (s *stubController) SetWindow(x, y, w, h int) {}
func (s *stubController) Play() error { return fmt.Errorf("GStreamer player not available") }
func (s *stubController) Pause() error { return fmt.Errorf("GStreamer player not available") }
func (s *stubController) Seek(offset float64) error {
return fmt.Errorf("GStreamer player not available")
}
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() {}