Use it

From a video file to a master playlist

The .env copy is optional. Defaults work, and a variable in the environment wins over .env, which wins over the built-in defaults.

01 Check the machine

ffmpeg
ffmpeg and ffprobe, 5.0 or newer, with the encoders you intend to use.
python3
Used to turn fractional frame rates, such as 30000/1001, into a keyframe interval.
bash
4.4 or newer. macOS ships bash 3.2, so install a current bash before running the script.

Debian and Ubuntu: sudo apt install ffmpeg python3. macOS: brew install ffmpeg python3 bash. Hardware encoding needs the matching driver. HWACCEL=none uses libx264, libx265, or SVT-AV1 and works without one.

02 Clone and encode

Subfolders are kept. video/course/lesson-1.mp4 becomes hls/course/lesson-1/master.m3u8.

shell
git clone https://github.com/CurbSoftware/video-hls.git
cd video-hls
cp .env.example .env
./run.sh --check
cp ~/Videos/talk.mp4 video/
./run.sh

One-off overrides: VIDEO_CODEC=av1 AUDIO_CODEC=opus ./run.sh, VIDEO_CODEC=h264 HWACCEL=none QUALITY=23 ./run.sh, MAX_RESOLUTION_HEIGHT=720 ./run.sh. QUALITY defaults to 28. Lower is larger and cleaner.

03 See what actually encodes

./run.sh --check encodes a throwaway frame with each candidate, so it reports what works on this machine. ./run.sh --dry-run lists the tiers it would write. ./run.sh --help prints the rest.

04 Play it over HTTP

Opening master.m3u8 from disk will not play. Serve the hls/ directory. Safari plays HLS itself. Chrome and Firefox need a player such as hls.js or Video.js.

shell
cd hls && python3 -m http.server 8000

05 Set content types on the bucket

Many buckets default to application/octet-stream, and some players refuse that. If the page and the files are on different origins, allow GET and HEAD in CORS. Published files can be cached as immutable. Republishing replaces the directory, so use a new path or purge that prefix.

.m3u8
application/vnd.apple.mpegurl
.ts
video/mp2t
.m4s
video/iso.segment
.mp4
video/mp4
.vtt
text/vtt

06 Batch encode to plain files

scripts/encode-batch.sh is a separate batch encoder. It writes playable files, checks them with ffprobe, and only then moves them into OUTPUT_DIR. It needs flock, realpath, and sha256sum as well as ffmpeg.

shell
INPUT_DIR=./raw OUTPUT_DIR=./encoded ./scripts/encode-batch.sh