Skip to content

Join video clips

Choose --mode copy when clips have matching stream layouts. It avoids re-encoding. Choose --mode reencode when codecs or containers differ and each clip has one video stream, one audio stream, and the same video dimensions.

Stream copy

Preview the metadata check before writing:

pyffmpegcore concat --mode copy --inputs "part one.mp4" part-two.mp4 --output joined.mp4 --explain

Preflight compares stream order, types, codecs, profiles, dimensions, audio properties, and available time-base/pixel-format metadata. The FFmpeg concat demuxer requires matching streams, codecs, and time bases. Matching probe facts cannot prove packet-level compatibility, so inspect the result and keep the source clips. Remote inputs receive a warning because preflight cannot compare their stream metadata. After a successful local run, output verification compares the stream types, codecs, and languages against the first input when FFprobe can inspect the first input. Copy mode maps every stream; choose an output container that supports all input codecs.

Re-encode

pyffmpegcore concat --mode reencode --inputs h264.mp4 vp9.webm --output joined.mp4 --explain

This mode selects the first video and audio stream from each input, resets timestamps, joins synchronized audio/video segments, and encodes H.264/AAC by default. It supports codec and container differences when stream layouts and video dimensions match. Preflight rejects different video dimensions before writing; resize clips to a common size first. Every input must contain video and audio. Other tracks are omitted; preflight warns when it finds extra tracks or cannot inspect a remote input. Output verification fails if the resulting file does not contain both video and audio streams. FFmpeg's concat-filter requirements are described in its filter documentation.

After reviewing the plan, run the same command without --explain and add a receipt:

pyffmpegcore concat --mode reencode --inputs h264.mp4 vp9.webm --output joined.mp4 --receipt joined.receipt.json
pyffmpegcore probe --input joined.mp4 --json
pyffmpegcore receipt validate joined.receipt.json --json

The output path is never overwritten without --force. The stream-copy demuxer contract is documented in the FFmpeg format reference.