refactor: add Language field to convertConfig; decouple benchmark from codec/preset; add language/hw-accel selectors to Settings Preferences
This commit is contained in:
parent
016d2a4f8a
commit
26a73029d4
|
|
@ -1,4 +1,4 @@
|
|||
//go:build linux && !gstreamer
|
||||
//go:build linux && !gstreamer && !gstreamer
|
||||
|
||||
package player
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !gstreamer
|
||||
//go:build !gstreamer && windows
|
||||
|
||||
package player
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
51
internal/player/gstreamer_player_stub.go
Normal file
51
internal/player/gstreamer_player_stub.go
Normal 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
25
main.go
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user