⚠️ 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'
Absolute paths (on Windows, relative paths are usually recommended)
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_baseshown in Step 1) - Same streams in the same order (e.g., video first, then one audio track).
ffprobe clip1.mp4lists them in order asStream #0:0,Stream #0:1and 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.
Related Articles
- Crossfade Transitions with the xfade Filter
- Trimming and Cutting Videos (trim/ss/to)
- Video Concatenation