Fix log viewer crash and improve bitrate controls
- Fix panic when closing log viewer (duplicate channel close) - Improve CBR: Set bufsize to 2x bitrate for better encoder handling - Improve VBR: Increase maxrate cap from 1.5x to 2x target bitrate - Add bufsize to VBR at 4x target (2x maxrate) to enforce caps - Update VBR hint to reflect 2x peak cap and 2-pass encoding This eliminates runaway bitrates while maintaining quality peaks.
This commit is contained in:
parent
0ebd442479
commit
3511d598f4
68
main.go
68
main.go
|
|
@ -349,15 +349,18 @@ func (s *appState) openLogViewer(title, path string, live bool) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var d dialog.Dialog
|
||||||
closeBtn := widget.NewButton("Close", func() {
|
closeBtn := widget.NewButton("Close", func() {
|
||||||
close(stop)
|
if d != nil {
|
||||||
|
d.Hide()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
copyBtn := widget.NewButton("Copy All", func() {
|
copyBtn := widget.NewButton("Copy All", func() {
|
||||||
s.window.Clipboard().SetContent(text.Text)
|
s.window.Clipboard().SetContent(text.Text)
|
||||||
})
|
})
|
||||||
buttons := container.NewHBox(copyBtn, layout.NewSpacer(), closeBtn)
|
buttons := container.NewHBox(copyBtn, layout.NewSpacer(), closeBtn)
|
||||||
content := container.NewBorder(nil, buttons, nil, nil, scroll)
|
content := container.NewBorder(nil, buttons, nil, nil, scroll)
|
||||||
d := dialog.NewCustom(title, "Close", content, s.window)
|
d = dialog.NewCustom(title, "Close", content, s.window)
|
||||||
d.SetOnClosed(func() { close(stop) })
|
d.SetOnClosed(func() { close(stop) })
|
||||||
d.Show()
|
d.Show()
|
||||||
}
|
}
|
||||||
|
|
@ -863,17 +866,6 @@ Config:
|
||||||
detailsLabel := widget.NewLabel(details)
|
detailsLabel := widget.NewLabel(details)
|
||||||
detailsLabel.Wrapping = fyne.TextWrapWord
|
detailsLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
// FFmpeg Command section
|
|
||||||
var ffmpegSection fyne.CanvasObject
|
|
||||||
if entry.FFmpegCmd != "" {
|
|
||||||
cmdWidget := ui.NewFFmpegCommandWidget(entry.FFmpegCmd, s.window)
|
|
||||||
ffmpegSection = container.NewVBox(
|
|
||||||
widget.NewSeparator(),
|
|
||||||
widget.NewLabel("FFmpeg Command:"),
|
|
||||||
cmdWidget,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
var buttons []fyne.CanvasObject
|
var buttons []fyne.CanvasObject
|
||||||
|
|
||||||
|
|
@ -899,23 +891,36 @@ Config:
|
||||||
closeBtn := widget.NewButton("Close", nil)
|
closeBtn := widget.NewButton("Close", nil)
|
||||||
buttons = append(buttons, layout.NewSpacer(), closeBtn)
|
buttons = append(buttons, layout.NewSpacer(), closeBtn)
|
||||||
|
|
||||||
// Layout
|
// Job details in scrollable area
|
||||||
contentVBox := container.NewVBox(
|
detailsScroll := container.NewVScroll(detailsLabel)
|
||||||
container.NewVScroll(detailsLabel),
|
detailsScroll.SetMinSize(fyne.NewSize(650, 250))
|
||||||
)
|
|
||||||
if ffmpegSection != nil {
|
// FFmpeg Command section at bottom
|
||||||
contentVBox.Add(ffmpegSection)
|
var ffmpegSection fyne.CanvasObject
|
||||||
|
if entry.FFmpegCmd != "" {
|
||||||
|
cmdWidget := ui.NewFFmpegCommandWidget(entry.FFmpegCmd, s.window)
|
||||||
|
cmdLabel := widget.NewLabel("FFmpeg Command:")
|
||||||
|
cmdLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
ffmpegSection = container.NewVBox(
|
||||||
|
widget.NewSeparator(),
|
||||||
|
cmdLabel,
|
||||||
|
cmdWidget,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Layout: details at top (scrollable), FFmpeg at bottom (fixed)
|
||||||
content := container.NewBorder(
|
content := container.NewBorder(
|
||||||
nil,
|
detailsScroll, // Top: job details (scrollable, takes priority)
|
||||||
container.NewHBox(buttons...),
|
container.NewVBox( // Bottom: FFmpeg command (fixed)
|
||||||
|
ffmpegSection,
|
||||||
|
container.NewHBox(buttons...),
|
||||||
|
),
|
||||||
nil, nil,
|
nil, nil,
|
||||||
contentVBox,
|
nil, // No center content - top and bottom fill the space
|
||||||
)
|
)
|
||||||
|
|
||||||
d := dialog.NewCustom("Job Details", "Close", content, s.window)
|
d := dialog.NewCustom("Job Details", "Close", content, s.window)
|
||||||
d.Resize(fyne.NewSize(700, 600))
|
d.Resize(fyne.NewSize(750, 650))
|
||||||
closeBtn.OnTapped = func() { d.Hide() }
|
closeBtn.OnTapped = func() { d.Hide() }
|
||||||
d.Show()
|
d.Show()
|
||||||
}
|
}
|
||||||
|
|
@ -5951,7 +5956,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
crfContainer.Hide()
|
crfContainer.Hide()
|
||||||
bitrateContainer.Show()
|
bitrateContainer.Show()
|
||||||
targetSizeContainer.Hide()
|
targetSizeContainer.Hide()
|
||||||
hint = "VBR mode: Variable bitrate - targets average bitrate. More efficient than CBR. Use 2-pass for accuracy."
|
hint = "VBR mode: Variable bitrate - targets average bitrate with 2x peak cap. Efficient quality. Uses 2-pass encoding."
|
||||||
case "Target Size":
|
case "Target Size":
|
||||||
// Show only target size controls
|
// Show only target size controls
|
||||||
crfContainer.Hide()
|
crfContainer.Hide()
|
||||||
|
|
@ -9398,7 +9403,13 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But
|
||||||
if vb == "" {
|
if vb == "" {
|
||||||
vb = defaultBitrate(cfg.VideoCodec, src.Width, sourceBitrate)
|
vb = defaultBitrate(cfg.VideoCodec, src.Width, sourceBitrate)
|
||||||
}
|
}
|
||||||
args = append(args, "-b:v", vb, "-minrate", vb, "-maxrate", vb, "-bufsize", vb)
|
// Set bufsize to 2x target for better encoder handling
|
||||||
|
bitrateVal, err := utils.ParseInt(strings.TrimSuffix(vb, "k"))
|
||||||
|
bufsize := vb
|
||||||
|
if err == nil {
|
||||||
|
bufsize = fmt.Sprintf("%dk", bitrateVal*2)
|
||||||
|
}
|
||||||
|
args = append(args, "-b:v", vb, "-minrate", vb, "-maxrate", vb, "-bufsize", bufsize)
|
||||||
} else if cfg.BitrateMode == "VBR" {
|
} else if cfg.BitrateMode == "VBR" {
|
||||||
// Variable bitrate - use 2-pass for accuracy
|
// Variable bitrate - use 2-pass for accuracy
|
||||||
vb := cfg.VideoBitrate
|
vb := cfg.VideoBitrate
|
||||||
|
|
@ -9406,11 +9417,12 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But
|
||||||
vb = defaultBitrate(cfg.VideoCodec, src.Width, sourceBitrate)
|
vb = defaultBitrate(cfg.VideoCodec, src.Width, sourceBitrate)
|
||||||
}
|
}
|
||||||
args = append(args, "-b:v", vb)
|
args = append(args, "-b:v", vb)
|
||||||
// VBR uses maxrate at ~1.5x target for quality peaks
|
// VBR uses maxrate at 2x target for quality peaks, bufsize at 2x maxrate to enforce cap
|
||||||
bitrateVal, err := utils.ParseInt(strings.TrimSuffix(vb, "k"))
|
bitrateVal, err := utils.ParseInt(strings.TrimSuffix(vb, "k"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
maxBitrate := fmt.Sprintf("%dk", int(float64(bitrateVal)*1.5))
|
maxBitrate := fmt.Sprintf("%dk", bitrateVal*2)
|
||||||
args = append(args, "-maxrate", maxBitrate)
|
bufsize := fmt.Sprintf("%dk", bitrateVal*4)
|
||||||
|
args = append(args, "-maxrate", maxBitrate, "-bufsize", bufsize)
|
||||||
}
|
}
|
||||||
// Force 2-pass for VBR accuracy
|
// Force 2-pass for VBR accuracy
|
||||||
if !cfg.TwoPass {
|
if !cfg.TwoPass {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user