▶M3U8 SPACE

ffmpeg Command to Convert m3u8 to MP4

For a VOD playlist you own, the fastest conversion is a stream copy: ffmpeg -i master.m3u8 -c copy -movflags +faststart output.mp4 . It repackages the existing tracks into an MP4 container without recompressing a single frame, so quality

For a VOD playlist you own, the fastest conversion is a stream copy: ffmpeg -i master.m3u8 -c copy -movflags +faststart output.mp4. It repackages the existing tracks into an MP4 container without recompressing a single frame, so quality is identical and the job usually finishes in seconds. You only need to transcode when the source codecs cannot live inside MP4, or when you want to change resolution or bitrate.

How it works

An .m3u8 master playlist is a text index of variant playlists, each of which is a text index of media segments. ffmpeg opens the playlist through the HLS demuxer, resolves the segment URLs, concatenates the elementary streams, and hands them to the MP4 muxer. With -c copy the encoded bitstream is passed through untouched; the container changes, the pixels do not.

This distinction matters for troubleshooting. If playback fails after a stream copy, the problem is usually container-level (timestamps, AAC in MPEG-TS, missing key URI), not video quality. If you see Unknown encoder or Codec not supported in container, the track must be re-encoded.

# Inspect what is actually inside the playlist before converting
ffprobe -v error -show_entries format=duration,format_name \
        -show_entries stream=index,codec_name,codec_type,width,height \
        -of default=noprint_wrappers=1 master.m3u8

A VOD playlist ends with #EXT-X-ENDLIST. A live playlist keeps growing and has no ENDLIST, so a plain -c copy command will follow the live edge until you stop it. Only run conversion jobs against content you are authorized to process, and prefer finished VOD assets.

Step-by-step: commands that cover real cases

1. Basic remux (local playlist or a URL you control).

ffmpeg -allowed_extensions ALL \
       -i master.m3u8 \
       -map 0:v:0 -map 0:a:0? \
       -c copy -movflags +faststart \
       output.mp4

-map 0:v:0 -map 0:a:0? picks the first video stream and the first audio stream if one exists, which prevents ffmpeg from trying to mux every subtitle or data track. -movflags +faststart relocates the MP4 index to the beginning of the file so web players can start before the whole file arrives.

2. Remote playlist with non-standard segment extensions or protocols.

ffmpeg -protocol_whitelist file,http,https,tcp,tls,crypto \
       -allowed_extensions ALL \
       -i https://cdn.example.com/vod/master.m3u8 \
       -c copy -bsf:a aac_adtstoasc \
       -movflags +faststart output.mp4

ffmpeg restricts protocols for playlists by default and rejects segments whose extension is not on its allow list. -protocol_whitelist is needed when the playlist references https segments combined with local or encrypted key files, and crypto must be present for AES-128 HLS. -bsf:a aac_adtstoasc converts AAC ADTS from MPEG-TS into the ASC format MP4 expects; modern ffmpeg builds often apply it automatically, but keeping it explicit avoids a silent audio failure on older builds.

3. Transcode when stream copy is impossible.

ffmpeg -i master.m3u8 \
       -c:v libx264 -preset veryfast -crf 22 -pix_fmt yuv420p \
       -c:a aac -b:a 128k \
       -movflags +faststart output.mp4

Use this when the source uses a codec MP4 cannot carry, when you need a smaller file, or when variants are misaligned and produce timestamp warnings. It is slower because every frame is recompressed.

4. Verify the result.

ffprobe -v error -show_entries format=duration,size -of csv=p=0 output.mp4
ffmpeg -v error -i output.mp4 -f null -   # full decode, prints errors only

If the reported duration is much shorter than the playlist duration, the download was truncated or a segment returned an error.

Common mistakes and parameters

FlagWhat it doesWhen to use it
-c copyCopies encoded tracks without recompressingDefault for any remux
-movflags +faststartMoves the MP4 moov atom to the frontAny file served over the web
-allowed_extensions ALLPermits segments with unusual extensionsLocal playlists, tokenized segment names
-protocol_whitelistAllows the demuxer to open listed protocolsRemote playlists and AES-128 keys
-bsf:a aac_adtstoascFixes AAC carried in MPEG-TSOlder ffmpeg or silent audio
-map 0:v:0 -map 0:a:0?Selects one video and one audio trackMaster playlists with many renditions
-live_start_indexChooses the segment offset for a live playlistOnly when converting a live recording
SymptomLikely levelFirst thing to check
Output is tiny or emptyNetworkSegment URLs return 200, not 403
Video plays, no audioContainerAAC bitstream filter
Duration shorter than expectedPlaylistMissing ENDLIST or truncated capture
Codec not currently supported in containerCodecSwitch to transcode
Choppy playback with no decode errorsTimestampsDiscontinuity tags and variant alignment

Troubleshooting

  • Protocol 'https' not on whitelist: add -protocol_whitelist file,http,https,tcp,tls,crypto before -i.
  • Invalid data found when processing input: the URL returned HTML (a login or error page) rather than a playlist; confirm with curl -I that the response is a playlist MIME type.
  • Error opening key file or Unable to open key: the #EXT-X-KEY URI must be reachable and return exactly 16 bytes; add crypto to the whitelist and check the key path is absolute or correctly resolved.
  • Non-monotonous DTS warnings: usually a #EXT-X-DISCONTINUITY boundary or variants encoded with different GOP settings; transcode, or convert each variant separately.
  • Output has no audio: try -bsf:a aac_adtstoasc, then confirm with ffprobe -show_streams that the audio track was mapped at all.
  • Conversion runs forever: the playlist is live. Stop with q, and use a finished VOD rendition instead.

Related reading