refactor: add Language field to convertConfig; decouple benchmark from codec/preset; add language/hw-accel selectors to Settings Preferences

This commit is contained in:
VideoTools CI 2026-01-22 06:39:48 -05:00
parent 016d2a4f8a
commit 26a73029d4
5 changed files with 137 additions and 28 deletions

View File

@ -1,4 +1,4 @@
//go:build linux && !gstreamer
//go:build linux && !gstreamer && !gstreamer
package player

View File

@ -1,4 +1,4 @@
//go:build !gstreamer
//go:build !gstreamer && windows
package player

View File

@ -95,10 +95,12 @@ type GStreamerPlayer struct {
volume float64
queued *image.RGBA
lastErr string
backend Backend
backend BackendType
config Config
seekMu sync.Mutex
events chan busEvent
preview bool
lastSeekTarget time.Duration
mu sync.Mutex
@ -107,13 +109,10 @@ type GStreamerPlayer struct {
// Bus handling
busCh chan *C.GstMessage
// Bus loop controls
busStop chan struct{}
eos chan struct{}
busDone chan struct{}
}
// Seek coalescing
lastSeekTarget time.Duration
}
type busEvent struct {
@ -607,6 +606,72 @@ func (p *GStreamerPlayer) busLoop() {
p.mu.Unlock()
}()
for {
select {
case <-p.busQuit:
return
default:
}
p.mu.Lock()
bus := p.bus
p.mu.Unlock()
if bus == nil {
time.Sleep(100 * time.Millisecond)
continue
}
msg := C.gst_bus_timed_pop_filtered(bus, 200*1000*1000, C.vt_gst_message_mask())
if msg == nil {
continue
}
msgType := C.vt_gst_message_type(msg)
switch msgType {
case C.GST_MESSAGE_ERROR:
p.mu.Lock()
if errMsg != nil {
p.lastErr = C.GoString(errMsg)
C.vt_gst_free_error(errMsg)
} else {
p.lastErr = "gstreamer error"
}
p.mode = StateError
p.mu.Unlock()
p.pushEvent(busEvent{Kind: "error", Info: p.lastErr})
case C.GST_MESSAGE_EOS:
p.mu.Lock()
p.eos = true
p.mode = StateEOS
p.mu.Unlock()
p.pushEvent(busEvent{Kind: "eos"})
case C.GST_MESSAGE_STATE_CHANGED:
var oldState C.GstState
var newState C.GstState
var pending C.GstState
C.vt_gst_parse_state_changed(msg, &oldState, &newState, &pending)
p.mu.Lock()
p.state = newState
p.mu.Unlock()
p.pushEvent(busEvent{Kind: "state_changed", State: newState})
case C.GST_MESSAGE_DURATION_CHANGED:
p.updateDuration()
p.pushEvent(busEvent{Kind: "duration_changed"})
case C.GST_MESSAGE_CLOCK_LOST:
p.mu.Lock()
shouldRecover := !p.paused && p.pipeline != nil
p.mu.Unlock()
if shouldRecover {
C.gst_element_set_state(p.pipeline, C.GST_STATE_PAUSED)
C.gst_element_set_state(p.pipeline, C.GST_STATE_PLAYING)
}
p.pushEvent(busEvent{Kind: "clock_lost"})
}
C.gst_message_unref(msg)
}
}
p.mu.Unlock()
}()
for {
select {
case <-p.busQuit:
@ -644,7 +709,7 @@ func (p *GStreamerPlayer) busLoop() {
p.pushEvent(evt)
case C.GST_MESSAGE_EOS:
p.mu.Lock()
p.eos = true
p.eos = true
p.mode = StateEOS
p.mu.Unlock()
p.pushEvent(busEvent{Kind: "eos"})
@ -671,6 +736,12 @@ func (p *GStreamerPlayer) busLoop() {
p.pushEvent(busEvent{Kind: "clock_lost"})
}
C.gst_message_unref(msg)
default:
}
p.mu.Unlock()
p.pushEvent(busEvent{Kind: "clock_lost"})
}
C.gst_message_unref(msg)
}
}

View File

@ -0,0 +1,51 @@
//go:build !gstreamer
package player
import (
"errors"
"image"
"time"
)
// Reuse types from vtplayer.go to avoid redeclaration conflicts.
type busEvent struct {
Type int
Info string
State PlayerState
}
// GStreamerPlayer is a stub used when the gstreamer build tag is not enabled.
type GStreamerPlayer struct{}
// NewGStreamerPlayer returns an error because GStreamer is not available in this build.
func NewGStreamerPlayer(config Config) (*GStreamerPlayer, error) {
return nil, errors.New("gstreamer not available; build with -tags gstreamer")
}
func (p *GStreamerPlayer) Load(path string, offset time.Duration) error {
return errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) Play() error { return errors.New("gstreamer not available") }
func (p *GStreamerPlayer) Pause() error { return errors.New("gstreamer not available") }
func (p *GStreamerPlayer) SeekToTime(offset time.Duration) error {
return errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) SeekToFrame(frame int64) error {
return errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) GetCurrentTime() time.Duration { return 0 }
func (p *GStreamerPlayer) GetFrameImage() (*image.RGBA, error) {
return nil, errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) SetVolume(level float64) error {
return errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) SetWindow(x, y, w, h int) {}
func (p *GStreamerPlayer) SetFullScreen(fullscreen bool) error {
return errors.New("gstreamer not available")
}
func (p *GStreamerPlayer) Stop() error { return nil }
func (p *GStreamerPlayer) Close() {}
func (p *GStreamerPlayer) Events() <-chan busEvent { return nil }
func (p *GStreamerPlayer) State() PlayerState { return StateStopped }

25
main.go
View File

@ -768,6 +768,7 @@ type convertConfig struct {
AspectUserSet bool // Tracks if user explicitly set OutputAspect
ForceAspect bool // Force DAR/SAR metadata even when no aspect conversion
TempDir string // Optional temp/cache directory override
Language string // UI language preference ("System" or BCP47 tag)
}
func (c convertConfig) OutputFile() string {
@ -837,6 +838,7 @@ func defaultConvertConfig() convertConfig {
AspectUserSet: false,
ForceAspect: true,
TempDir: "",
Language: "System",
}
}
@ -2930,8 +2932,8 @@ func (s *appState) saveBenchmarkRun(results []benchmark.Result, encoder, preset
func (s *appState) applyBenchmarkRecommendation(encoder, preset string) {
logging.Debug(logging.CatSystem, "applied benchmark recommendation: encoder=%s preset=%s", encoder, preset)
// Map encoder to hardware acceleration setting
hwAccel := "none"
// Map encoder to hardware acceleration setting only; do not touch codec/preset.
hwAccel := s.convert.HardwareAccel
switch {
case strings.Contains(encoder, "nvenc"):
hwAccel = "nvenc"
@ -2943,26 +2945,11 @@ func (s *appState) applyBenchmarkRecommendation(encoder, preset string) {
hwAccel = "videotoolbox"
}
// Map encoder to friendly codec to align Convert defaults
if codec := friendlyCodecFromPreset(encoder); codec != "" {
s.convert.VideoCodec = codec
}
// Respect user's quality preference: if they have slow/slower set, upgrade the preset
currentPreset := strings.ToLower(s.convert.EncoderPreset)
if currentPreset == "slow" || currentPreset == "slower" {
// User prefers quality over speed - upgrade benchmark preset to slower
preset = "slow"
logging.Debug(logging.CatSystem, "user prefers quality - upgraded preset to 'slow'")
}
s.convert.EncoderPreset = preset
s.convert.HardwareAccel = hwAccel
s.persistConvertConfig()
dialog.ShowInformation("Benchmark Settings Applied",
fmt.Sprintf("Applied recommended defaults:\n\nEncoder: %s\nPreset: %s\nHardware Accel: %s\n\nThese are now set as your Convert defaults.",
encoder, preset, hwAccel), s.window)
// Minimal notice without confirmation loops.
logging.Info(logging.CatSystem, "benchmark applied hardware acceleration: %s", hwAccel)
}
func (s *appState) showBenchmarkHistory() {