April 23rd 2017

linux, media

Make a screencast with ffmpeg

Several applications to record your desktop exist, but ffmpeg can do it all by itself, via its x11grab format specifier. But there were some challenges:

  • Sound: If you use pulseaudio or jack audio, other solutions apply. But with plain ALSA it isn't so easy to simply record what the computer sends to your speakers. In the end I just plugged the cable that goes from Line Out (usually to my speakers) back into Line In. Then (via alsamixer, press F4 to get capture channels) I had to activate the main CAPTURE channel and set the Input Source to Line. Surprisingly, this did not result in feedback. I had to fiddle with the volumes to avoid distortion and background hum. It's not HiFi, but good enough.

  • Performance: What ffmpeg does when recording your screen is to transcode the X11 video into compressed output, based on file extension. But I wanted to record a game, and this proved to be too resource-intensive in combination, esp. if I want ffmpeg to produce high quality output. So I recorded the session in raw, uncompressed format. Be aware, your hard drive will fill up really quickly (and a slow hard drive might incur another performance penalty): a 5 minute 1152x864px video has 7.5GB.

In the end I changed the screen resolution of the whole desktop, started the game in fullscreen mode and told ffmpeg to record that:

$ sleep 5 ; ffmpeg -f alsa -i hw:0 -f x11grab -r 25 -s 1152x864 -i :0.0 -c:v ffvhuff -c:a flac output1.mkv

Since MKV is a container format, it can also contain uncompressed video formats.

But I had recorded two separate scenes, which now had to be slightly trimmed and concatenated.

Trim first - example:

$ ffmpeg -i output2.mkv -t 00:02:36 -acodec copy -vcodec copy output2-a.mkv

It starts at zero by default (if not, use the -ss option) and uses the first 2min 36s.

Next one needs to create a text file (let's call it list.txt) with a list of files to be concatenated:

file 'output1-a.mkv'
file 'output2-a.mkv'

and then concatenate like this:

$ ffmpeg -f concat -safe 0 -i list.txt -c copy output-full.mkv

Lastly, I make a high quality two-pass encoding of the resulting file. Please note that, although I chose the MKV format again, this is a totally different codec with much smaller file size. I also must first concatenate the videos into their final form, and then transcode.

$ ffmpeg -i output-full.mkv -an -vcodec libx264 -pass 1 -preset veryslow -threads 0 \
-b 1000k -x264opts frameref=15:fast_pskip=0 -f rawvideo -y /dev/null

$ ffmpeg -i output-full.mkv -acodec aac -ab 64k -vcodec libx264 -pass 2 -preset veryslow \
-threads 0 -b 1000k -x264opts frameref=15:fast_pskip=0 output-final.mkv

Note that the sound is only transcoded during the second pass (to 64kb AAC).

The uncompressed 7.5GB file (~5 minutes) has shrunken to a very reasonable 38.8MB at 1000kb/s while keeping the original resolution of 1152x864px. The quality is very good, I can't spot any difference to the original: