unarr/internal
Deivid Soto 75dcc0f1cb feat(streaming): ffmpeg transcoding pipeline (direct play / fMP4 / HW accel)
The browser-side WebRTC reproductor needs MP4 / H.264 / AAC / yuv420p to
keep MSE happy. This package decides per request whether to:

  • direct-play  — input already MSE-compatible, just remux to fMP4
  • transcode    — re-encode video (libx264 / NVENC / QSV / VAAPI /
                   VideoToolbox) + audio (AAC), fragment to fMP4

Pieces:

- internal/streaming/transcoder.go — AnalyzeCompatibility decides the
  recipe from a parsed mediainfo. CompatibilityReport carries the reasons
  so the player UI can show "transcoding video: HEVC → H.264".

- internal/streaming/ffmpeg_args.go — BuildFFmpegArgs assembles the argv
  for ffmpeg. Direct play uses `-c copy`; transcode uses libx264 or the
  selected HW encoder. Output is always fragmented MP4 piped to stdout
  (-movflags frag_keyframe+empty_moov+default_base_moof) so the HTTP
  handler can stream straight to the browser without disk I/O.

  Quality ladder: 480p (1.5Mb), 720p (3.5Mb), 1080p (6Mb), 2160p (25Mb).
  Default 1080p when unset / unknown. -ss seek for resume / scrubbing.

- internal/streaming/hwaccel.go — DetectHWAccel runs `ffmpeg -encoders`
  once per process and caches the best available. Order: NVENC → QSV →
  VAAPI → VideoToolbox → libx264. VAAPI is the only family that wires up
  HW decode too (`-hwaccel vaapi`); the others software-decode and HW-
  encode (works fine and avoids /dev/dri permission rabbit holes).

- internal/streaming/stream.go — Transcoder facade wires Analyze + Stream
  together for the API handler in Fase 4. Captures the last 8 KiB of
  ffmpeg stderr for diagnosable errors without unbounded memory.

Tests (20 unit, all green):
- AnalyzeCompatibility: h264+aac direct, video-only direct, HEVC →
  transcode, 10-bit HDR → transcode, EAC3 audio → transcode, nil guards
- ResolveQuality: empty + unknown fallback to 1080p, 4-step ladder
- BuildFFmpegArgs: direct play -c copy, transcode libx264 + bitrate +
  scale, NVENC swaps encoder & drops preset, VAAPI injects -hwaccel +
  scale_vaapi, -ss timestamp formatting
- HWAccel: encoder-name table, VAAPI is the only one with HW decode
- formatDuration: zero, sub-second, HH:MM:SS, negative-clamped
- cappedBuffer: tail retention through multi-write and large-write paths
- NewTranscoder: rejects empty paths
2026-05-06 11:34:57 +02:00
..
agent feat(library): add server-driven file deletion with allow_delete config 2026-04-10 16:35:12 +02:00
arr feat(cli): upgrade command, rich status, and version cache 2026-03-31 22:05:43 +02:00
cmd feat(torrent): act as WebTorrent peer for browser ↔ unarr P2P streaming 2026-05-06 08:59:58 +02:00
config feat(mediainfo): ResolveFFmpeg + DownloadFFmpeg mirroring ffprobe pattern 2026-05-06 09:49:32 +02:00
engine feat(torrent): act as WebTorrent peer for browser ↔ unarr P2P streaming 2026-05-06 08:59:58 +02:00
library feat(mediainfo): ResolveFFmpeg + DownloadFFmpeg mirroring ffprobe pattern 2026-05-06 09:49:32 +02:00
mediaserver feat(cli): upgrade command, rich status, and version cache 2026-03-31 22:05:43 +02:00
parser feat: initial commit — unarr CLI 2026-03-28 11:29:42 +01:00
sentry feat(cli): upgrade command, rich status, and version cache 2026-03-31 22:05:43 +02:00
streaming feat(streaming): ffmpeg transcoding pipeline (direct play / fMP4 / HW accel) 2026-05-06 11:34:57 +02:00
ui fix(ci): fix lint errors and pin CI to Go 1.25 2026-03-31 22:15:12 +02:00
upgrade fix(upgrade): retry download on transient network errors with user feedback 2026-04-09 14:15:32 +02:00
usenet fix(ci): fix lint errors and pin CI to Go 1.25 2026-03-31 22:15:12 +02:00