What You’ll Learn
- How to concatenate videos losslessly with the concat demuxer
- How to build the
concat.txtlist file - Same-codec / same-format requirements and caveats
- How the concat demuxer differs from the
concatfilter (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
ffprobeafter 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'
Absolute paths (on Windows, relative paths are usually recommended)
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
| Option | Description |
|---|---|
-f concat | Select the concat demuxer |
-safe 0 | Allow absolute or relative paths (disabled by default) |
-i concat.txt | Use the list file as input |
-c copy | Stream 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
| Method | Re-encoding | Speed | Requirements |
|---|---|---|---|
| concat demuxer (this article) | None | Fast | Identical codec and format |
| concat filter | Yes | Slow | Works 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.
Related Articles
- Crossfade Transitions with the xfade Filter
- Trimming and Cutting Videos (trim/ss/to)
- Video Concatenation
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