feat(stream): HLS-copy — reemplazo resiliente del remux progresivo

Nuevo modo VideoCopy en el engine HLS: ffmpeg -c:v copy (el vídeo jamás se
re-encodea — I/O puro, funciona en un NAS sin GPU), audio copy si ya es AAC
o AAC 192k si no, muxeado a segmentos fMP4 con ffmpeg escribiendo SU PROPIO
playlist (EVENT mientras corre, ENDLIST al acabar, EXTINF exactos en los
keyframes del source). Sustituye al remux growing-fMP4 servido por HTTP
Range artesanal, cuya fragilidad estructural produjo tres incidentes en un
día (init malformado/delay_moov, loop de re-seek por total inventado, iOS
rechazando total desconocido).

Diferencias deliberadas respecto al modo encode:
- playlist de ffmpeg servido desde disco (los cortes van a keyframe del
  source → duraciones imposibles de pre-renderizar; medido: probar
  keyframes antes cuesta 8-24s, inviable para TTFF)
- sin seek-restart ni auto-restart (la copia va a velocidad de disco y
  adelanta a cualquier viewer; el -ss de segmentos uniformes corrompería
  la timeline de cortes variables)
- sin caché HLS (regenerar no cuesta encode; cachear solo quema disco)
- resume vía -ss (snap a keyframe) + -output_ts_offset
- master playlist sin CODECS (un string hardcodeado equivocado hace que
  iOS rechace la variante; omitirlo es legal y universal)

Validación: TTFB seg-0 510ms sobre el MKV real del incidente (HEVC Main10
+ EAC3, 6.7GB). Suite de integración con ffmpeg real (tag smoke): h264+aac
(copy total), h264+ac3 (re-encode de audio con priming dts — la clase
delay_moov), hevc10+eac3 (la forma exacta del incidente, tag hvc1), resume
con StartSec, y serving del playlist; asserts de codecs vía ffprobe sobre
el playlist servido, suma EXTINF ≈ duración, segmentos completos en disco
(+temp_file = rename atómico).

El wiring web (plan remux→hls+videoCopy con gate de versión ≥1.0.10) va en
el repo web. Plan: docs/plans/hls-copy-remux-replacement.md (web).
This commit is contained in:
Deivid Soto 2026-06-10 23:06:21 +02:00
parent 3fcfaaf234
commit 5a92df1e14
4 changed files with 499 additions and 12 deletions

View file

@ -522,6 +522,12 @@ type StreamSession struct {
// the raw file over /stream (HTTP Range, no ffmpeg) instead of
// transcoding to HLS. See hueco #3 phase 3a in the roadmap.
PlayMethod string `json:"playMethod,omitempty"`
// VideoCopy (playMethod "hls" only): serve via HLS-copy — ffmpeg -c:v copy
// into fMP4 segments, audio to AAC when needed. The robust replacement for
// the progressive-remux path: same near-zero CPU (video never re-encoded,
// works on a GPU-less NAS), but in the segmented transport every player
// handles. Set by webs that know this agent supports it (≥1.0.10).
VideoCopy bool `json:"videoCopy,omitempty"`
// DirectURL, when set, is an HTTPS link to the media resolved server-side
// from the user's debrid account (hueco #2 / 2a). The source has no local
// file: the daemon streams /stream from this URL via ranged GETs