Concatenating Videos with FFmpeg

I capture screencasts with OBS and then process them with ffmpeg before uploading. Surprisingly, OBS doesn’t have a pause button, so if I have to stop for anything, I stop it and restart when I come back, and then I have to concatenate multiple videos into a single one later.

The ffmpeg docs say you can concatenate like this:

ffmpeg -i "concat:video1.mp4|video2.mp4|video3.mp4" [other arguments]

That doesn’t always work, though. I don’t know exactly why, but it has something to do with mismatched aspects of the files. Sometimes it stops after the first file and ignores the rest of them. So it’s more reliable to use the other method, where you put the filenames of your videos in a separate file like this:

file video1.mp4
file video2.mp4
file video3.mp4

And then you include that file like this:

ffmpeg -f concat -i filelist.txt [other arguments]

That always works, but it’s a hassle to create the separate file each time. So I finally wrote the shell script below to automate the process.

#!/bin/sh
#
# myff - script to take a list of files from stdin,
#   save the list to a file for ffmpeg to concat from

FFTEMP=$(mktemp)
if [ ! -f $FFTEMP ]; then
    echo Unable to create temp file $FFTEMP
    exit
fi

for i in `cat -`; do
    echo -n "file "
    readlink -f $i
done >$FFTEMP

ffmpeg -safe 0 -f concat -i $FFTEMP "$@"

rm $FFTEMP

Now I can just pass the video files to that on standard input, and it’ll make the temporary file for me, give it to ffmpeg, and delete it afterwards. I use it like this:

echo video?.mp4 | myff [other arguments]

A couple of technical notes. ffmpeg is picky about filenames and paths. It expects the video files and the list file to be in the same directory by default, and doesn’t allow full pathnames in the list unless you pass `-safe 0` first on the command line. Also, this was written on FreeBSD, but I think it should be completely portable to other *nix systems, as long as they have standard `mktemp` and `readlink`.

One flaw is that it doesn’t handle spaces in filenames. That’s okay with me, because I don’t allow them out of old habit. It would have to be more complicated to tell the difference between spaces in filenames and spaces between filenames. By treating them all the same, I can pass the filenames in from `echo`, `ls`, or whatever other tool is handy.

Hope this comes in handy for someone who ran into the same frustration with “-i concat:” that I did.