VideoTools/internal/player/factory.go
Stu Leak e9608c6085 Implement VT_Player module with frame-accurate video playback
- Add VTPlayer interface with microsecond precision seeking
- Implement MPV controller for frame-accurate playback
- Add VLC backend support for cross-platform compatibility
- Create FFplay wrapper to bridge existing controller
- Add factory pattern for automatic backend selection
- Implement Fyne UI wrapper with real-time controls
- Add frame extraction capabilities for preview system
- Support preview mode for trim/upscale/filter modules
- Include working demo and implementation documentation
2025-12-21 16:31:44 -05:00

166 lines
3.8 KiB
Go

package player
import (
"fmt"
"os/exec"
"runtime"
)
// Factory creates VTPlayer instances based on backend preference
type Factory struct {
config *Config
}
// NewFactory creates a new player factory with the given configuration
func NewFactory(config *Config) *Factory {
return &Factory{
config: config,
}
}
// CreatePlayer creates a new VTPlayer instance based on the configured backend
func (f *Factory) CreatePlayer() (VTPlayer, error) {
if f.config == nil {
f.config = &Config{
Backend: BackendAuto,
Volume: 100.0,
}
}
backend := f.config.Backend
// Auto-select backend if needed
if backend == BackendAuto {
backend = f.selectBestBackend()
}
switch backend {
case BackendMPV:
return f.createMPVPlayer()
case BackendVLC:
return f.createVLCPlayer()
case BackendFFplay:
return f.createFFplayPlayer()
default:
return nil, fmt.Errorf("unsupported backend: %v", backend)
}
}
// selectBestBackend automatically chooses the best available backend
func (f *Factory) selectBestBackend() BackendType {
// Try MPV first (best for frame accuracy)
if f.isMPVAvailable() {
return BackendMPV
}
// Try VLC next (good cross-platform support)
if f.isVLCAvailable() {
return BackendVLC
}
// Fall back to FFplay (always available with ffmpeg)
if f.isFFplayAvailable() {
return BackendFFplay
}
// Default to MPV and let it fail with a helpful error
return BackendMPV
}
// isMPVAvailable checks if MPV is available on the system
func (f *Factory) isMPVAvailable() bool {
// Check for mpv executable
_, err := exec.LookPath("mpv")
if err != nil {
return false
}
// Additional platform-specific checks could be added here
// For example, checking for libmpv libraries on Linux/Windows
return true
}
// isVLCAvailable checks if VLC is available on the system
func (f *Factory) isVLCAvailable() bool {
_, err := exec.LookPath("vlc")
if err != nil {
return false
}
// Check for libvlc libraries
// This would be platform-specific
switch runtime.GOOS {
case "linux":
// Check for libvlc.so
_, err := exec.LookPath("libvlc.so.5")
if err != nil {
// Try other common library names
_, err := exec.LookPath("libvlc.so")
return err == nil
}
return true
case "windows":
// Check for VLC installation directory
_, err := exec.LookPath("libvlc.dll")
return err == nil
case "darwin":
// Check for VLC app or framework
_, err := exec.LookPath("/Applications/VLC.app/Contents/MacOS/VLC")
return err == nil
}
return false
}
// isFFplayAvailable checks if FFplay is available on the system
func (f *Factory) isFFplayAvailable() bool {
_, err := exec.LookPath("ffplay")
return err == nil
}
// createMPVPlayer creates an MPV-based player
func (f *Factory) createMPVPlayer() (VTPlayer, error) {
// Use the existing MPV controller
return NewMPVController(f.config)
}
// createVLCPlayer creates a VLC-based player
func (f *Factory) createVLCPlayer() (VTPlayer, error) {
// Use the existing VLC controller
return NewVLCController(f.config)
}
// createFFplayPlayer creates an FFplay-based player
func (f *Factory) createFFplayPlayer() (VTPlayer, error) {
// Wrap the existing FFplay controller to implement VTPlayer interface
return NewFFplayWrapper(f.config)
}
// GetAvailableBackends returns a list of available backends
func (f *Factory) GetAvailableBackends() []BackendType {
var backends []BackendType
if f.isMPVAvailable() {
backends = append(backends, BackendMPV)
}
if f.isVLCAvailable() {
backends = append(backends, BackendVLC)
}
if f.isFFplayAvailable() {
backends = append(backends, BackendFFplay)
}
return backends
}
// SetConfig updates the factory configuration
func (f *Factory) SetConfig(config *Config) {
f.config = config
}
// GetConfig returns the current factory configuration
func (f *Factory) GetConfig() *Config {
return f.config
}