Whether you are archiving a VOD asset, debugging stream packaging, or trying to save a live broadcast, FFmpeg provides the most robust set of commands for interacting with HLS streams. This guide will walk you through the basic conversion process, advanced audio handling, decryption, and protocol-specific configurations to ensure a lossless extraction.
Why Use FFmpeg?
Most "M3U8 downloaders" found online are essentially graphical wrappers around FFmpeg. Using FFmpeg directly gives you total control over the download process, allowing you to bypass aggressive rate limiting, specify custom HTTP headers, choose precise audio tracks, and remux without unnecessary quality loss.
The Basic M3U8 to MP4 Command
The simplest way to convert an HLS stream into an MP4 file is to let FFmpeg read the manifest URL directly. Assuming you have FFmpeg installed, open your terminal and run:
ffmpeg -i "https://example.com/stream/playlist.m3u8" -c copy output.mp4
Let's break down exactly what this command does:
-i "url"specifies the input. The quotes are important if your URL contains query parameters (like?token=123) which might break the shell command.-c copyinstructs FFmpeg to copy the video and audio codecs directly (remuxing). It does not re-encode the video. This guarantees zero quality loss and makes the process incredibly fast—limited only by your download speed.output.mp4is the resulting file. The.mp4extension tells FFmpeg to wrap the downloaded H264/AAC data into an MP4 container.
Handling Live Streams and Timeouts
If you execute the basic command on a live stream, FFmpeg will continuously download segments indefinitely. To record a specific duration of a live broadcast, use the -t flag to specify the time in seconds or HH:MM:SS format.
ffmpeg -i "https://live-server.com/live.m3u8" -t 00:30:00 -c copy live_capture.mp4
Avoid Disconnections
Live streams can drop segments or pause. If you want FFmpeg to keep trying instead of failing immediately upon a network hiccup, add the -reconnect flags.
To make your live stream download highly resilient against network errors, use this advanced command:
ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 5 -i "https://live-server.com/live.m3u8" -c copy output.mp4
Decrypting AES-128 Encrypted Streams
HLS natively supports AES-128 encryption. If the playlist contains an #EXT-X-KEY:METHOD=AES-128,URI="key.bin" tag, FFmpeg handles decryption automatically, provided it can access the key URI.
If the key is protected behind authentication or requires specific cookies, FFmpeg will fail with an HTTP 403 Forbidden error. You can pass HTTP headers and cookies directly to FFmpeg using the -headers argument.
ffmpeg -headers "Cookie: session_id=abc123xyz" -i "https://protected-site.com/video.m3u8" -c copy output.mp4
Note: FFmpeg cannot bypass DRM schemes like Widevine, FairPlay, or PlayReady (indicated by SAMPLE-AES). It only decrypts standard clear-key AES-128.
Fixing Audio Sync Issues
Sometimes, the raw TS (Transport Stream) segments have slightly misaligned Presentation Time Stamps (PTS). When remuxing into an MP4 container, this can lead to the audio slowly drifting out of sync with the video.
To force FFmpeg to rebuild the timestamps correctly during the demuxing stage, use the -bsf:a aac_adtstoasc bitstream filter. This converts ADTS AAC audio into MPEG-4 Audio Specific Configuration, which often fixes playback issues in QuickTime and standard browsers.
ffmpeg -i "https://example.com/playlist.m3u8" -c copy -bsf:a aac_adtstoasc output.mp4
Selecting Specific Video/Audio Tracks
A master M3U8 playlist often contains multiple resolutions (360p, 720p, 1080p). If you pass a master playlist directly to FFmpeg, it will usually pick the first available or the highest quality stream. However, to guarantee you get the exact version you want, you should use the -map function.
To see all available streams without downloading the file, run:
ffmpeg -i "https://example.com/master.m3u8"
Look for lines indicating Program 1, Program 2, etc. If you want to download Program 2 (perhaps the 1080p track with English audio), specify the map:
ffmpeg -i "https://example.com/master.m3u8" -map p:2 -c copy output.mp4
Alternatively, the most robust method is to inspect the master manifest in a text editor (or browser DevTools), find the URL of the specific media playlist (e.g., 1080p.m3u8), and feed that direct URL into FFmpeg instead of the master URL.
Batch Processing Local M3U8 Files
If you have already downloaded the .m3u8 manifest and the .ts segments to a local folder, you can still use FFmpeg to combine them. Navigate to the directory containing the local .m3u8 file and run:
ffmpeg -allowed_extensions ALL -i local_playlist.m3u8 -c copy output.mp4
The -allowed_extensions ALL flag is crucial when reading local playlists, as FFmpeg's default security model restricts it from reading arbitrary local files referenced inside text documents.