All posts
ffmpeg concat videos

How to Concatenate Videos with FFmpeg

Join videos with FFmpeg: the concat demuxer for files with the same format, the concat filter for files that differ, and the same job as one API call.

To concatenate videos with FFmpeg when all files have the same format, use the concat demuxer with stream copy: ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4. When the files have different codecs, resolutions or frame rates, use the concat filter, which re-encodes them into one new video.

This guide shows both methods, how to tell which one you need, and the same job as one Xora API call. For other common commands, see the FFmpeg cheat sheet.

Step 1: Check that the files match

Terminal window
ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,sample_rate,channels -of compact part1.mp4
ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,sample_rate,channels -of compact part2.mp4

If the lines are the same for every file, use the demuxer. If any value is different, use the filter. For the same check as an API call, see the ffprobe API guide.

Method 1: Concat demuxer (no re-encode)

list.txt
file 'part1.mp4'
file 'part2.mp4'
file 'part3.mp4'
Terminal window
ffmpeg -f concat -safe 0 -i list.txt -c copy -movflags +faststart output.mp4
FlagWhat it does
-f concatRead the input as a list of files to join.
-safe 0Allow absolute paths and other paths that the demuxer marks as unsafe.
-c copyCopy the streams. No quality loss and no re-encode.

Method 2: Concat filter (files that differ)

Terminal window
ffmpeg -i intro.mp4 -i main.mp4 \
-filter_complex "[0:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v0];[1:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v1];[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -crf 20 -c:a aac output.mp4
PartWhat it does
scale=...:force_original_aspect_ratio=decrease,pad=...Fit each video into 1920×1080 without stretching, with black bars where needed.
setsar=1,fps=30Make the pixel aspect ratio and the frame rate the same for every input.
concat=n=2:v=1:a=1Join 2 inputs, each with 1 video and 1 audio stream.
-map "[v]" -map "[a]"Write the joined streams to the output.

Every input must have an audio stream for a=1. If one does not, add silence for it with anullsrc, or join video only with a=0.

The same job with the Xora API

For files that match, the concat recipe joins them with stream copy, in key order. It is the only recipe that takes input_files in place of input:

POST /v1/jobs — concat recipe
{
"mode": "recipe",
"input_files": {
"in_1": "https://example.com/intro.mp4",
"in_2": "https://example.com/main.mp4"
},
"output": { "format": "mp4" },
"recipe": { "name": "concat" }
}

There is no list file to write. For files that differ, send the filter graph in mode: "ffmpeg":

POST /v1/jobs — raw args, concat filter
{
"mode": "ffmpeg",
"input_files": {
"in_1": "https://example.com/intro.mp4",
"in_2": "https://example.com/main.mp4"
},
"output_files": { "out_1": "joined.mp4" },
"ffmpeg": {
"args": [
"-i", "{{in_1}}",
"-i", "{{in_2}}",
"-filter_complex", "[0:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v0];[1:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v1];[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[v][a]",
"-map", "[v]", "-map", "[a]",
"-c:v", "libx264", "-crf", "20", "-c:a", "aac",
"{{out_1}}"
]
}
}

The filter graph is one array element, so there is no shell quoting to get wrong. Send the body to https://api.xora.sh/v1/jobs with Authorization: Bearer YOUR_API_KEY, then poll GET /v1/jobs/{id} or pass webhookUrl. See the concat recipe.

Variants

Join audio files

The same methods work for audio: ffmpeg -f concat -safe 0 -i list.txt -c copy book.mp3. The concat recipe accepts mp3, aac and wav outputs. The files must have the same sample rate and channels.

Add an intro and an outro

Use the filter method with three inputs and concat=n=3:v=1:a=1. Scale the intro and outro to the size of the main video.

Make the inputs match first

When you join the same kinds of files often, re-encode each file to one standard format when it arrives, for example with the webReady recipe. Then later joins can use stream copy.

Common errors

ErrorCauseFix
Unsafe file nameThe list has absolute paths or URLs.Add -safe 0.
Non-monotonous DTS warnings, or a frozen video after the first partThe files do not match and were joined with -c copy.Use the concat filter.
Input link ... parameters ... do not match the corresponding output linkThe inputs to the concat filter have different sizes or aspect ratios.Add scale, pad and setsar=1 to each input.
Stream specifier ':a' ... matches no streamsOne input has no audio.Add silence with anullsrc, or use a=0.
TRANSCODE_ERROR (Xora)FFmpeg failed, for example because the concat recipe got files that do not match.Read the job error, then use the filter in mode: "ffmpeg".
VALIDATION_ERROR (Xora)The concat recipe got input, or fewer than two input_files.Use input_files with at least in_1 and in_2.

Failed and rejected Xora jobs cost nothing. See error handling.

Frequently asked questions

How do I join two videos with FFmpeg without re-encoding?

Use the concat demuxer. Write the files in a list (file 'part1.mp4' on each line) and run ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4. This works only when all files have the same codecs, resolution, frame rate and audio settings.

How do I concatenate videos with different resolutions?

Use the concat filter and re-encode. Scale each input to the same size first, for example with scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2, then join them with concat=n=2:v=1:a=1.

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

The demuxer joins files end to end without re-encoding, so the inputs must match. The filter decodes every input and encodes one new output, so the inputs can differ, but it takes more machine time and re-encodes the video.

Can I merge videos without installing FFmpeg?

Yes. Send the URLs to the Xora API in input_files with the concat recipe for matching files, or with your own concat filter arguments in mode ffmpeg for files that differ.

Ready to try Xora?

Replace your local FFmpeg binaries with a secure, high-performance cloud API.