Convert HLS to MP4
A segment sink writes HLS to object storage — a playlist plus .ts or .m4s segments — not a single MP4. To get a file you can drop into an editor or send to someone, remux the finalized playlist locally with ffmpeg.
H.264 + AAC (the recommended encoding for this sink) copies into MP4 without re-encoding.
1. Install ffmpeg
Section titled “1. Install ffmpeg”Pick one, then confirm with ffmpeg -version.
macOS (Homebrew):
brew install ffmpegWindows (WinGet), or download a build from ffmpeg.org:
winget install ffmpegDebian / Ubuntu:
sudo apt update && sudo apt install ffmpeg2. Wait for the playlist to finalize
Section titled “2. Wait for the playlist to finalize”The playlist is live until the job stops. Finalize writes #EXT-X-ENDLIST and, if you used a sliding window, puts every segment back in. Convert after a terminal status — or a job.completed webhook — not while the job is still running. See Record a room.
3. Remux the playlist
Section titled “3. Remux the playlist”-c copy remuxes. It does not transcode, so it is fast and does not change quality.
If the objects are public, or you have a signed URL that can also fetch the sibling segments:
ffmpeg -i "https://<bucket>/<pathPrefix>/index.m3u8" -c copy recording.mp4filename defaults to index → index.m3u8. If you set filename in the sink config, use that basename instead.
If the bucket is private, download the whole prefix (playlist + segments) and remux on disk. Relative URIs in the playlist resolve next to the .m3u8:
aws s3 sync "s3://recordings/rooms/my-room/2026-08-19/" ./hls/ffmpeg -i ./hls/index.m3u8 -c copy recording.mp4The same idea works with gsutil -m rsync or az storage blob download-batch. You need the playlist and every seg_*.ts / seg_*.m4s it lists. fMP4 recordings also have init_*.mp4 — the playlist’s EXT-X-MAP points at them.
Captions
Section titled “Captions”When asr is wired into the sink, the canonical {filename}.m3u8 is a master playlist:
| Object | Role |
|---|---|
{filename}.m3u8 | Master (playable entry) |
{filename}_media.m3u8 | Audio / video |
{filename}_subs.m3u8 + {filename}_subs.vtt | WebVTT, uploaded on finalize |
ffmpeg on the master usually picks the A/V variant. To mux the VTT into the MP4 as a text track:
ffmpeg -i ./hls/recording.m3u8 -i ./hls/recording_subs.vtt \ -c copy -c:s mov_text recording.mp4Use {filename}_media.m3u8 if you only want audio and video.
If remux fails
Section titled “If remux fails”A discontinuity (late audio or video, a codec change) or an unusual codec pair can make -c copy refuse the MP4 container. Re-encode only then — it is slower:
ffmpeg -i ./hls/index.m3u8 -c:v libx264 -c:a aac recording.mp4