Convert M3U8 to MP4
.m3u8 is the playlist file for HLS streaming - a text file listing the small video segments that make up a full playback. Wave stitches the segments you have on disk into a single MP4, in your browser. This is the sibling of HLS to MP4 for the extension people usually type.
What actually changes
A .m3u8 file is a UTF-8-encoded text playlist for HLS. Its format is a minor extension of the M3U playlist format from the WinAmp era - each line is either metadata (segment duration, encryption key, codec info) or a URL pointing at a video segment. A working HLS stream reference chain is: master .m3u8 (lists the available bitrate variants) → variant .m3u8 (lists the segments for one bitrate) → .ts or .m4s segments (the actual video data).
When you capture an HLS stream with yt-dlp (`yt-dlp -f best <stream_url>`), N_m3u8DL_DASH, or the browser dev-tools resource-download workflow, the tool downloads the playlist and every segment it references and writes them all to a folder next to each other. The .m3u8 file on your disk then has the segment references rewritten as local relative paths - Wave reads that file and follows the local segment paths to concatenate them into MP4.
Wave's concat is stream-level, not sample-level. Each .ts segment already contains H.264 (or HEVC) NAL units and AAC audio; Wave copies those bytes into an MP4 container without re-encoding a frame. A ten-minute HLS stream (typically ~120 segments) finishes in under a minute on modern hardware. No decode step, no re-encode step, no quality loss.
Where this fails: when the .m3u8 still references remote URLs and browser CORS blocks the fetches, when the segments are encrypted with AES-128 SAMPLE-AES without a key you can supply, or when the segments are non-standard container formats. In practice, yt-dlp's local-rewrite of the playlist handles the first case (segments are local paths), most legitimate use cases don't have DRM (dev-tools captures of streams you had access to), and non-standard segments are rare.
Three steps.
-
01
Step 1
Download the HLS stream to disk using yt-dlp: `yt-dlp -f best -o "video.%(ext)s" <stream_url>`. You end up with an .m3u8 file and its segments.
-
02
Step 2
Drop the .m3u8 file (or the folder of segments) onto Wave. Wave reads the playlist and concatenates the segments.
-
03
Step 3
Save the output MP4. Same video and audio, single file, no re-encoding.
Answers, specifically.
What is the difference between HLS to MP4 and M3U8 to MP4?
Nothing - they are the same operation. HLS is the streaming spec name; .m3u8 is the file extension. Both pages exist because both terms are what people type into search.
Can I paste an .m3u8 URL and have Wave download it?
No. Browser CORS policy blocks arbitrary origin fetches. Capture the stream with yt-dlp on the command line first (which doesn't have CORS constraints), then feed the local .m3u8 and segments to Wave.
The .m3u8 file references remote HTTPS URLs, not local paths.
Then it hasn't been captured yet. Run `yt-dlp -f best <stream_url>` to download the segments and rewrite the playlist to local paths, then feed the local .m3u8 to Wave.
My yt-dlp output is a single .mp4 - do I need Wave?
No. yt-dlp combines segments into an MP4 by default when ffmpeg is on your PATH. If it produced a single MP4, that's already what you wanted. Wave is for the case where yt-dlp downloaded segments separately (`--no-merge` or missing ffmpeg) or where you captured segments via a browser workflow.
Segments are .m4s not .ts - handled?
Yes. HLS 2.0 uses fragmented MP4 (fMP4) segments with .m4s extension. Wave handles both TS and fMP4 segment types; the concat mechanics are the same.
The master playlist has multiple bitrates - which one does Wave pick?
yt-dlp picks the bitrate at download time (via `-f best` or `-f worst` or a specific format ID). Once the segments are on disk, Wave concatenates whatever bitrate was captured. To include multiple bitrates in the output, you'd need a re-encode workflow that Wave doesn't currently support.
Discontinuity markers in the playlist (EXT-X-DISCONTINUITY) - handled?
Yes. Discontinuities usually mean ad breaks in live streams or codec/resolution changes. Wave handles them at the container level - the output MP4 will have discontinuity boundaries but a single container. Some players show a brief pause at each discontinuity boundary.