fix(video): stop stream-copying oversized sources into HLS #166

Merged
cameron merged 6 commits from fix/hls-copy-limits into master 2026-10-13 15:09:38 +00:00
6 Commits
Author SHA1 Message Date
Cameron CordesandClaude Opus 5.5 84843c1c25 docs: run tests with an in-memory DATABASE_URL
The docs said tests need BASE_PATH in .env. The full suite (lib, bin and
integration tests) passes with no .env and no BASE_PATH; the only thing
it needs is DATABASE_URL, and ":memory:" works. Without it, DB-backed
tests such as reels::tests::reel_capture_preserves_owner_defaults panic
with "DATABASE_URL must be set".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:09:14 -04:00
Cameron CordesandClaude Opus 5.5 985f366bc2 chore(video): drop unused Ffmpeg::_generate_playlist, fix reels comment
_generate_playlist was a private, never-called predecessor of the HLS
path in video/actors.rs. Its leftover args (-hls_list_size 100, width
1080 scale, libx264 only) no longer described how playlists are built.
Removing it also drops the now-unused std::process::Output import.

The reels encoder comment said its NVENC settings matched the HLS
transcode; reels use preset p4, HLS uses p1. Only the quality target
(cq 23) is shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:07:05 -04:00
Cameron CordesandClaude Opus 5.5 0b87320ef7 fix(video): make NVENC honour forced HLS keyframes
-force_key_frames asks for a keyframe every 3 s, but h264_nvenc inserts
those as plain I-frames unless -forced-idr is set. The HLS muxer only
splits on IDR frames, so NVENC transcodes fell back to the encoder's
default 250-frame GOP: 8.34 s segments at 29.97 fps, ~10 s at 25 fps.
On prod ~480 playlists have a target duration above 4 s. Long segments
mean more data before playback starts and coarser seeks.

Verified on a 10 s 29.97 fps clip: segments went from 8.34 + 1.67 s to
3.003 x 3 + 1.001 s, matching the libx264 path, which was already
correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:03:18 -04:00
Cameron CordesandClaude Opus 5.5 83b1923e25 fix(video): keep source frame timing when transcoding to HLS
Variable-frame-rate sources (phone clips that change capture rate,
screen recordings) report r_frame_rate as their 1/90000 timebase. The
HLS transcode used the mpegts muxer's default CFR mode, so ffmpeg
duplicated frames to fill 90000 fps: on prod, three 2.6-5 s phone clips
became 3-second "segments" of up to 446 MB / 250k frames. Players
download the whole thing before starting and can't seek within it.

-fps_mode vfr passes source timestamps through. Verified on
20220208_164356_01.mp4: 233 frames over 2.578 s in, 233 frames over
2.578 s out, 5.9 MB vs 221 MB. The copy path already keeps timestamps
and is unchanged. Requires ffmpeg >= 5.1 (prod runs 8.0.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:58:13 -04:00
Cameron CordesandClaude Opus 5.5 052609d604 docs: document HLS_COPY_MAX_BITRATE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:58:12 -04:00
Cameron CordesandClaude Opus 5.5 1279cb8430 fix(video): stop stream-copying oversized sources into HLS
HLS generation stream-copied any h264 source with no rotation and a
GOP within one segment, which keeps the source's resolution and
bitrate. Mirrorless footage (4K at 100-160 Mbps) came through as-is,
giving ~50 MB 3-second segments; playback start and seeks stalled on
anything slower than wired gigabit. On prod, 16 playlists run above
50 Mbps, versus ~10 Mbps typical for phone clips.

The stream probe now also reads width, height and bit_rate (stream
level, falling back to format level; all header fields, so the probe
stays header-only). Copy additionally requires the short side to be
<= 1080 and the bitrate <= HLS_COPY_MAX_BITRATE (default 20 Mbps).
Unknown size or bitrate transcodes, matching the GOP probe's fallback.

The transcode scale filter capped width at 1080, so landscape sources
came out at 1080x608. It now caps the short side instead: 3840x2160
-> 1920x1080, 2160x3840 -> 1080x1920, smaller sources unchanged
(verified with ffmpeg).

Existing oversized playlists need regenerating after deploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:46:46 -04:00