⚠️ Read this before running (FFmpeg can exit normally with audio or video missing)

  • If a file partway through the list is missing (a mistyped path counts too), FFmpeg prints an error, writes only the part before it and exits normally.
  • If the files differ in codec, resolution or frame rate, audio or video can drop out partway through, or the timestamps can drift.
  • After joining, check the length of the output with ffprobe.

What Is the concat Demuxer?

The concat demuxer reads a text file that lists your files and joins them in that order, without re-encoding. (A demuxer is the part of FFmpeg that reads the audio and video data out of a file.)

When all the files use the same codec and settings, this takes very little time.

Basic Usage

Step 1: Pre-flight check (mandatory)

Before you write the list file, check that every file exists and that the codecs match:

# Check that each file exists
for f in clip1.mp4 clip2.mp4 clip3.mp4; do
  if [ -f "$f" ]; then
    echo "OK: $f"
  else
    echo "MISSING: $f"
  fi
done

# Check video and audio settings (ffprobe reads one file at a time)
for f in clip1.mp4 clip2.mp4 clip3.mp4; do
  ffprobe -v error -select_streams v:0 \
    -show_entries stream=codec_name,width,height,r_frame_rate,time_base \
    -of csv=p=0 "$f"
  ffprobe -v error -select_streams a:0 \
    -show_entries stream=codec_name,sample_rate,time_base \
    -of csv=p=0 "$f"
done

For each file, this prints the video codec_name,width,height,r_frame_rate,time_base (codec, width, height, frame rate, time base) and the audio codec_name,sample_rate,time_base (codec, sample rate, time base). The time base is the unit in which play times are counted. If these lines are the same for every file and the files have the same streams in the same order, you can join them with -c copy. A different time base alone is enough to give the joined video the wrong length.

Step 2: Create the list file (concat.txt)

printf "file 'clip1.mp4'\nfile 'clip2.mp4'\nfile 'clip3.mp4'\n" > concat.txt

The resulting concat.txt contains:

file 'clip1.mp4'
file 'clip2.mp4'
file 'clip3.mp4'

Write one file per line in the form file '...', with the path in single quotes.

Start every line with the keyword file. If you misspell it, FFmpeg stops with an unknown keyword error. Extra spaces do no harm.

Step 3: Concatenate with the concat demuxer

ffmpeg -f concat -safe 0 -i concat.txt -c copy output.mp4

-c copy copies the audio and video as they are, without re-encoding.

List File Format

Relative paths

file 'clip1.mp4'
file 'clip2.mp4'
file 'clip3.mp4'
file '/path/to/clip1.mp4'
file '/path/to/clip2.mp4'

File names with special characters

If a file name contains a single quote, write that quote as '\'':

file 'it'\''s a clip.mp4'

Option Reference

Option Description
-f concat Select the concat demuxer
-safe 0 Allow absolute paths, paths with a part starting with . (such as ../), and names with characters other than letters, digits, ., _ and - (spaces, for example)
-i concat.txt Use the list file as input
-c copy Stream copy (no re-encoding)

Requirements for Lossless Concatenation

To join files with -c copy, they must all match on these points:

  • Same video codec (e.g., all H.264)
  • Same resolution (e.g., all 1920×1080)
  • Same frame rate (e.g., all 30fps)
  • Same audio codec (e.g., all AAC)
  • Same sample rate (e.g., all 44100Hz)
  • Same time base (the time_base shown in Step 1)
  • Same streams in the same order (e.g., video first, then one audio track). ffprobe clip1.mp4 lists them in order as Stream #0:0, Stream #0:1 and so on

If any of them differ, FFmpeg either stops with an error or writes a broken file and still exits normally: audio and video drift apart, a track goes missing, or audio data ends up in the video track.

Concatenating Files with Audio

printf "file 'clip1.mp4'\nfile 'clip2.mp4'\n" > concat.txt
ffmpeg -f concat -safe 0 -i concat.txt -c copy output_with_audio.mp4

-c copy copies both the video and the audio.

Verifying the Output (Important)

After joining, compare the total length of the input files with the length of the output:

# Sum of input durations
total=0
for f in clip1.mp4 clip2.mp4 clip3.mp4; do
  dur=$(ffprobe -v error -show_entries format=duration \
    -of default=noprint_wrappers=1:nokey=1 "$f")
  total=$(echo "$total + $dur" | bc)
done
echo "Total input duration: ${total}s"

# Output duration
out_dur=$(ffprobe -v error -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 output.mp4)
echo "Output duration: ${out_dur}s"

# A difference greater than 0.5s suggests the concat is incomplete

concat Demuxer vs. concat Filter

Method Re-encoding Speed Requirements
concat demuxer None Fast Identical codec and format
concat filter Yes Slow Works across different codecs (resolution must match)

To join files in different formats, use the concat filter. It only joins the clips and adds no transition between them. For a transition, use xfade (video) and acrossfade (audio) instead. Example:

ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" -map "[v]" -map "[a]" output.mp4

Troubleshooting

Video or audio drift

If some inputs lack presentation timestamps (PTS, the time at which each frame is shown), add -fflags +genpts to generate them. This does not fix drift caused by different settings between the files:

ffmpeg -f concat -safe 0 -fflags +genpts -i concat.txt -c copy output.mp4

“Unsafe file name” error

This appears when the list contains an absolute path, or a name with spaces or other characters besides letters, digits, ., _ and -, and you left out -safe 0:

Wrong: ffmpeg -f concat -i concat.txt -c copy output.mp4
Right: ffmpeg -f concat -safe 0 -i concat.txt -c copy output.mp4

Output is shorter than expected or truncates mid-file

A path in the list file is wrong, or the codecs do not match. Run the checks from Step 1 again.

Frequently Asked Questions

What is the difference between the concat protocol and the concat demuxer?

The protocol (concat:a.ts|b.ts) glues the raw bytes of the files together, so it works only for a few formats (MPEG-TS, MPEG-PS, DV). The demuxer (-f concat with a list file) works with any container: it reads the streams out of each file and writes them into a new one (remuxing).

When does concat:a.ts|b.ts actually work?

It works for formats whose files can be joined byte for byte: MPEG-TS (Transport Stream, used for broadcast recordings), MPEG-PS (.mpg, DVD .vob) and DV. It does not work for MP4 or MOV. For those, use the concat demuxer or the concat filter.

Why does my MP4 break when I use the concat protocol?

An MP4 file has a moov atom, an index that covers the whole file. Because of it, you cannot join MP4 files by gluing their bytes together. Use -f concat -i list.txt -c copy instead.

Can I concat-protocol files with different codecs?

No. Even with MPEG-TS, the codecs and their settings must match. If the codecs differ, use the concat filter and re-encode.

Is the concat protocol safe for production?

Only for MPEG-TS files from a known source, such as broadcast capture. For anything else, the concat demuxer or the concat filter is safer.