What You’ll Learn

  • How to concatenate videos losslessly with the concat demuxer
  • How to build the concat.txt list file
  • Same-codec / same-format requirements and caveats
  • How the concat demuxer differs from the concat filter (which re-encodes)

Tested with: FFmpeg 6.1
Platform: Windows / macOS / Linux

⚠️ Read this before running (silent audio/video loss is possible)

  • Formatting mistakes in the list file or missing file paths can cause FFmpeg to produce an incomplete output without any error
  • If codec, resolution, or frame rate don't match across files, audio or video may drop mid-stream or timestamps may drift
  • Always validate the output duration with ffprobe after concatenation
  • Test with small sample files before concatenating anything important

What Is the concat Demuxer?

FFmpeg’s concat demuxer reads a text file listing input files in order and joins them without re-encoding.

When every file shares the same codec and parameters, this is extremely fast.


Basic Usage

Step 1: Pre-flight check (mandatory)

Before building the list file, confirm every file exists and that 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 codec, resolution, and frame rate
ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,width,height,r_frame_rate \
  -of csv=p=0 clip1.mp4 clip2.mp4 clip3.mp4

If codec_name,width,height,r_frame_rate are identical across all files, it’s safe to concat with -c copy.

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

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

Contents of concat.txt:

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

Each line must follow the file '...' format with the path wrapped in single quotes.

Note: Lines must start with file exactly. Typos or extra whitespace can cause FFmpeg to silently ignore the file.

Step 3: Concatenate with the concat demuxer

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

-c copy copies the streams as-is (no 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

Escape single quotes as '\'':

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

Option Reference

OptionDescription
-f concatSelect the concat demuxer
-safe 0Allow absolute or relative paths (disabled by default)
-i concat.txtUse the list file as input
-c copyStream copy (no re-encoding)

Requirements for Lossless Concatenation

To use -c copy with the concat demuxer, every file must share identical codec and parameters:

  • 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)

If any of these don’t match, FFmpeg will either error out or produce output where audio and video drift apart.


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 video and audio streams.


Verifying the Output (Important)

Always verify that the concatenation succeeded. Compare the sum of each input’s duration to the output’s duration:

# 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

MethodRe-encodingSpeedRequirements
concat demuxer (this article)NoneFastIdentical codec and format
concat filterYesSlowWorks across different codecs

Use the concat filter when you need to join files with different formats or add transitions:

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

Timestamp inconsistency is a common cause. Try adding -fflags +genpts:

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

“Unsafe file name” error

Occurs when -safe 0 is missing:

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

The list file contains a wrong path, or codecs aren’t aligned. Re-run the checks from Step 1.


Frequently Asked Questions

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

The protocol works only on a small set of formats (TS streams) by gluing raw bytes together. The demuxer works on any container by re-muxing input lists with -f concat.

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

It works for MPEG-TS streams (Transport Stream) recorded from broadcast. It does not work for MP4/MOV — those need the concat demuxer or the concat filter.

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

MP4 has a moov atom with global indexes; bytes are not interchangeable. Use -f concat -i list.txt -c copy instead.

Can I concat-protocol files with different codecs?

No — even within MPEG-TS, the codecs and parameters must match. Switch to the concat filter and re-encode if codecs differ.

Is the concat protocol safe for production?

Only for MPEG-TS pipelines with a known toolchain (e.g. broadcast capture). For anything else the concat demuxer or filter is the safer default.



Tested with ffmpeg 6.1 / Ubuntu 24.04 (GitHub Actions runner)
Primary sources: ffmpeg.org/ffmpeg-formats.html#concat-1 / trac.ffmpeg.org/wiki/Concatenate