Why FFmpeg
FFmpeg is a free, open-source audio and video processing tool, driven entirely from the command line. Open up almost any format conversion software on the market and its core is FFmpeg — so rather than installing some shady "converter," go straight to the source: free, no ads, no watermarks, and more capable to boot.
It's especially good for these situations: converting dozens of files in a batch, links with hotlink protection where you need to control request headers yourself, or simply wanting to know exactly what happens at every step. If you only convert a link once in a while and can't be bothered to set up an environment, just use the online converter on this site's homepage — paste the link in your browser, and the principle is exactly the same as what follows.
Installing FFmpeg
On Windows (Win10 and later), one command in PowerShell does it:
winget install Gyan.FFmpegNo winget? Download the essentials zip from gyan.dev, extract it, and add the bin directory to your system PATH environment variable. On Mac, use Homebrew:
brew install ffmpegAfter installing, open a new terminal window and type ffmpeg -version — if you see a version number, you're done. A package manager also makes future upgrades painless: run winget upgrade Gyan.FFmpeg on Windows or brew upgrade ffmpeg on Mac — one command either way.
The Basic Conversion Command
The one you'll use most converts an online M3U8 into a local MP4:
ffmpeg -i "https://example.com/video/index.m3u8" -c copy output.mp4-i is followed by the input address, and -c copy is the soul of the operation: stream copy, no re-encoding. The video data moves untouched from the TS wrapper into the MP4 wrapper, so the speed is basically your download speed and quality loss is zero. An hour-long video converts in two or three minutes with a decent connection, and the CPU barely moves.
If you've already downloaded the m3u8 and its pile of ts segments into the same folder, go into that folder and run:
ffmpeg -i "index.m3u8" -c copy output.mp4FFmpeg automatically finds and merges all the segments in the order recorded in the m3u8 — you don't have to manage the ts files yourself. When the input is a master playlist (with several quality variants inside), FFmpeg defaults to the highest bitrate, so no extra settings are usually needed. One more detail: when the link contains special characters like & or ?, be sure to wrap the whole address in quotes as shown above, or the shell will truncate it mid-way and report a baffling file-not-found error.
A Few Flags Worth Knowing
If the finished MP4 will be served on a web page, add -movflags +faststart to move the file's index information to the front, so browsers can start playback before the whole file has downloaded:
ffmpeg -i "https://example.com/video/index.m3u8" -c copy -movflags +faststart output.mp4When recording a live stream, there's no end marker, so FFmpeg would record forever — use -t to cap the duration, or press q to stop once you have enough:
ffmpeg -i "https://example.com/live/index.m3u8" -c copy -t 3600 output.mp4-t 3600 records for 3600 seconds (one hour). If copying the audio throws a codec-related error, add the -bsf:a aac_adtstoasc bitstream filter to convert the AAC data headers in the TS into the format MP4 expects — recent FFmpeg versions mostly handle this automatically, so add it manually only when you hit the error.
Batch Conversion Script
Dozens of course lessons, each in its own folder with an index.m3u8 — typing the command one by one would be silly. On Mac, Linux, or Git Bash on Windows, go to the directory holding all the course folders and run:
for d in */; do
ffmpeg -i "${d}index.m3u8" -c copy "${d%/}.mp4"
doneIt walks through every subfolder, converts the index.m3u8 inside into an MP4 of the same name, and puts the output in the same folder. Windows PowerShell users take this one instead:
Get-ChildItem -Directory | ForEach-Object {
ffmpeg -i (Join-Path $_.FullName "index.m3u8") -c copy (Join-Path $_.FullName ($_.Name + ".mp4"))
}A few failures along the way are normal — the log states the reason, so just handle those individually; the rest are unaffected. When running in batch you can also add -loglevel error after ffmpeg to print only real errors, otherwise the progress output from dozens of files floods the screen and the actual error becomes impossible to find.
Common Errors and Fixes
Error 403 Forbidden: the site's hotlink protection is checking where the request comes from. Give FFmpeg the source page's Referer and a normal User-Agent:
ffmpeg -headers "Referer: https://source-site.com/" \
-user_agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
-i "https://example.com/video/index.m3u8" -c copy output.mp4For Referer, use the address of the web page hosting the video (in the F12 Network panel you can see the segment requests carrying exactly this). The same command also fixes most "plays in the browser but FFmpeg can't download it" situations.
Error "Invalid data found when processing input": the input simply isn't an m3u8. Either the link was copied wrong (a ts segment or an mp4 address instead), or local segments are missing or the paths don't match. Open the m3u8 in a text editor and take a look — it should start with #EXTM3U.
The m3u8 contains #EXT-X-KEY: the stream is AES encrypted. If the key address is directly accessible, FFmpeg downloads and decrypts it automatically and the command needs no change; if the key is hotlink-protected too, you'll also need -headers. Some platforms protect their encryption very tightly and the key can't be obtained — then no tool can decrypt it; that's copyright protection.
No sound in the output: most likely the audio stream's format didn't line up during copying — retry with -bsf:a aac_adtstoasc.
Non-monotonous DTS warnings scrolling by: the timestamps are messed up, very common in spliced recorded streams. The warning itself isn't fatal, but if the result shows audio/video drift or a jumping progress bar, add -fflags +genpts to have FFmpeg regenerate timestamps and convert again.
Conversion is very slow and the CPU is maxed out: nine times out of ten you forgot -c copy, so FFmpeg is re-encoding by default. Add the flag and speed immediately returns to download speed.
One last summary: single link and convenience first — use this site's online tool; batch jobs, hotlink protection, or scripting — use FFmpeg. The files produced by either route are no different, because both do the same thing: repack the data into a new wrapper.