feat(streaming): seek-restart, single-session, idle sweeper, probe.json

Follow-ups on the daemon HLS pipeline (0fc0e1c):

- engine/hls.go HLSSession.Register now closes every other active
  session in the registry. Modeled as "one viewer == one transcode" so
  repeated quality switches or page reloads don't leave orphan ffmpegs
  saturating the CPU until the idle sweeper reaps them 30 min later.
- engine/hls.go restartFromSegment kills + respawns ffmpeg with
  -ss / -output_ts_offset / -start_number when the browser asks for a
  segment far ahead of the writer head. Segments already on disk stay
  cached. Without this, a user dragging the scrubber to minute 30 of a
  fresh stream blocks until the encoder reaches minute 30 in real time.
- engine/hls.go subtitle disambiguation: never set DEFAULT=YES on any
  rendition (anime forced "signs only" tracks were autoselected and
  rendered nothing during opening dialogue, looking broken). Names get
  numeric suffixes when language is duplicated; FORCED tracks get a
  "(forzados)" suffix.
- engine/hls.go ProbeInfo() exposes codec / audio / subtitle metadata
  to the new GET /hls/<id>/probe.json endpoint for the player's info
  badge + bandwidth logic.
- engine/hls.go scale chain fix: chains a trunc(iw/2)*2 scale after
  the height cap so libx264 stops rejecting odd widths (853x480 etc.).
- engine/hls.go HW encoder tuning: NVENC -preset p4 -rc vbr -tune hq,
  QSV -preset medium.
- engine/stream_server.go routes /hls/<id>/probe.json to the session.
- cmd/daemon.go runs an idle sweeper goroutine every 5 min, reaping
  sessions whose last segment fetch was >30 min ago.
This commit is contained in:
Deivid Soto 2026-05-07 23:55:05 +02:00
parent 0fc0e1c21a
commit eb2548f9a6
3 changed files with 295 additions and 25 deletions

View file

@ -317,6 +317,10 @@ func (ss *StreamServer) hlsHandler(w http.ResponseWriter, r *http.Request) {
switch {
case resource == "master.m3u8":
session.ServeMaster(w, r)
case resource == "probe.json":
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-cache")
_ = json.NewEncoder(w).Encode(session.ProbeInfo())
case resource == "video/index.m3u8":
session.ServeVideoPlaylist(w, r)
case resource == "video/init.mp4":