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.
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.