5 min read
How to convert M3U8 to MP4 using FFmpeg

🚀 Basic Conversion

ffmpeg -i "https://example.com/playlist.m3u8" -c copy output.mp4

-c copy

  • Meaning: Copy streams without re-encoding
  • Effect: Fastest conversion method - simply copies the video and audio streams as-is from the source to the output file
  • Usage: When the source format is compatible with the output container

🛠️🎧 -bsf:a aac_adtstoasc

ffmpeg -i input.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4

M3U8 files typically contain TS segments with AAC audio. When you copy these directly to MP4, the AAC audio might not play properly because:

  1. M3U8/TS files: AAC audio is packaged differently
  2. MP4 files: Require specific AAC headers that TS files don’t have

The bitstream filter fixes this by restructuring the AAC data to be MP4-compatible.

  • Meaning: Bitstream Filter for Audio that converts AAC Data Transport Stream To Audio Specific Config
  • Effect: Fixes AAC audio in MP4 container by adding proper header information
  • When needed: Required when copying AAC audio from TS (Transport Stream) to MP4 format

Re-encoding With specific video/audio codecs

ffmpeg -i "input.m3u8" -c:v libx264 -c:a aac output.mp4
  • -c:v libx264: Use the H.264 video codec (via the libx264 encoder).
  • -c:a aac: Use AAC audio codec

Specify quality and preset

ffmpeg -i "input.m3u8" -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k output.mp4
  • -preset medium
    • Sets the encoding speed vs. compression efficiency trade-off.
    • Available presets: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow.
    • medium is the default and a good balance between speed and quality.
  • Constant Rate Factor (-crf) controls video quality and file size.
    • Lower CRF = better quality & larger file.
    • Higher CRF = lower quality & smaller file.
    • Range is 0–51 (where 0 is lossless, 18–28 is common).
    • 23 is a reasonable default.

Set output resolution

ffmpeg -i "input.m3u8" -vf "scale=1280:720" -c:a copy output.mp4

-vf "scale=1280:720": Resize video to 720p


With user agent (for protected streams)

ffmpeg -user_agent "Mozilla/5.0" -i "input.m3u8" -c copy output.mp4

With timeout and retry options

ffmpeg -timeout 3000000 -reconnect 1 -i "input.m3u8" -c copy output.mp4

⏳ -timeout 3000000

  • Sets the network timeout for reading from the input URL.
  • Unit: microseconds (3000000 µs = 3 seconds)
  • If no data is received within this time, the connection is considered stalled.
  • Useful for unreliable or slow network conditions.

⚠️ Note: -timeout is a protocol-level option and may only work with certain protocols (like http or https), not all input types.

🔁 -reconnect 1

  • Tells ffmpeg to reconnect automatically if the connection is dropped.
  • Useful when streaming from unstable sources (e.g., live HLS feeds).

⚠️ Like -timeout, this flag only works with certain input protocols (e.g. http, https, tcp). It may not work if not placed before -i, or if the protocol doesn’t support it.


Specify segment duration and format

ffmpeg -i "input.m3u8" -f mp4 -movflags faststart -c copy output.mp4

⚡ -movflags faststart

ffmpeg -i "https://example.com/stream.m3u8" -c copy -movflags faststart -stats output.mp4
  • Moves the moov atom (index of the .mp4 file) to the beginning of the file.
  • Essential for progressive download / streaming (e.g., HTML5 <video> tags).
  • Without this, the video won’t start playing until the whole file is downloaded.

Add Headers

There are several ways to add headers to FFmpeg requests. Here are the most common methods:

1. Using -headers Option

ffmpeg -headers "User-Agent: Mozilla/5.0
Referer: https://example.com
Origin: https://example.com
Cookie: session=abc123" -i "input.m3u8" output.mp4

2. Using -user_agent Option (for User-Agent only)

ffmpeg -user_agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" -i "input.m3u8" output.mp4

3. Multiple Header Options

ffmpeg -user_agent "Mozilla/5.0" -headers "Referer: https://example.com
Authorization: Bearer token123" -i "input.m3u8" output.mp4

4. Common Headers for Different Scenarios

For HLS Streams with Protection:

ffmpeg -headers "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Referer: https://streaming-site.com/
Origin: https://streaming-site.com/
X-Requested-With: XMLHttpRequest" -i "input.m3u8" output.mp4

With Authentication:

ffmpeg -headers "Authorization: Bearer your_token_here
User-Agent: Mozilla/5.0" -i "input.m3u8" output.mp4

With Cookies:

ffmpeg -headers "Cookie: session_id=abc123; user_pref=dark
User-Agent: Mozilla/5.0" -i "input.m3u8" output.mp4

5. Using Header File

Create a headers.txt file:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Referer: https://example.com
Origin: https://example.com
Accept: */*
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br

Then use

ffmpeg -headers @headers.txt -i "input.m3u8" output.mp4