feat(hls): resume-aware first spawn + capped-CRF/CQ rate control
- HLSSessionConfig.StartSec (sync StreamSession.startSec): el primer ffmpeg arranca ya seekeado en el punto de resume (-ss + -output_ts_offset + -start_number, misma maquinaria que el seek-restart) en vez de encodear desde seg-0 para morir en el seek-restart inmediato del player (doble spawn, resume lento). readyMax se pre-siembra al índice de arranque; el ready-watcher compara ReadyCount() > WriterStartIdx() para no marcar "ready" antes del primer segmento real. startSec >= duración → arranque desde 0 (resume obsoleto de un fichero reemplazado). - Rate control: capped constant-quality donde el encoder lo hace bien — libx264 -crf 23, NVENC -cq 23 -b:v 0 — con el mismo -maxrate de siempre y -bufsize 2x (antes 1x estrangulaba picos). Escenas fáciles emiten muchos menos bits (menos stalls vía funnel/LTE); el peor caso no cambia. QSV/VideoToolbox/VAAPI conservan el triple de bitrate fijo probado (sus knobs de calidad tienen gotchas de vendor). - Limpieza: wrapper buildHLSFFmpegArgs y guard startIdx<0 muertos.
This commit is contained in:
parent
f7ca282ca0
commit
9b97aedfe4
5 changed files with 259 additions and 16 deletions
|
|
@ -1,5 +1,10 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TranscodeRuntime carries the resolved ffmpeg/ffprobe paths + tunables so
|
||||
// each session can decide whether to passthrough or pipe through ffmpeg.
|
||||
type TranscodeRuntime struct {
|
||||
|
|
@ -48,6 +53,35 @@ func resolveQualityCap(label string) qualityCap {
|
|||
}
|
||||
}
|
||||
|
||||
// doubleBitrate returns an ffmpeg bitrate string with twice the value of the
|
||||
// input ("6000k" → "12000k", "1.5M" → "3M", "5M" → "10M"). Used to size
|
||||
// `-bufsize` at the standard 2× of `-maxrate` for capped-CRF/CQ rate control.
|
||||
// An unparseable string falls back to the input unchanged (1× bufsize — the
|
||||
// pre-CRF behaviour, safe just suboptimal). The doubled CPB stays far below
|
||||
// every H.264 level's limit for the (level, maxrate) pairs this package emits
|
||||
// (worst case: 1080p level 4.1 → 12000k bufsize vs 62500k allowed).
|
||||
func doubleBitrate(b string) string {
|
||||
if b == "" {
|
||||
return b
|
||||
}
|
||||
num := b
|
||||
suffix := ""
|
||||
switch b[len(b)-1] {
|
||||
case 'k', 'K', 'm', 'M':
|
||||
num = b[:len(b)-1]
|
||||
suffix = string(b[len(b)-1])
|
||||
}
|
||||
v, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil || v <= 0 {
|
||||
return b
|
||||
}
|
||||
d := v * 2
|
||||
if d == math.Trunc(d) {
|
||||
return strconv.FormatFloat(d, 'f', 0, 64) + suffix
|
||||
}
|
||||
return strconv.FormatFloat(d, 'f', -1, 64) + suffix
|
||||
}
|
||||
|
||||
// capForHeight returns the bitrate-cap pair appropriate for an effective
|
||||
// output height. Used after clamping outputHeight to the source's resolution:
|
||||
// asking ffmpeg for "2160p" bitrate (25 Mbps) on a 1080p source overshoots
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue