How to make screencasts with FFmpeg

FFmpeg is able to use audio and video streams from many different sources. In order to record a screencast, you can use the x11grab input module for recording an X screen and the alsa input module for audio recording.

In this article I will explain how to use the x11grab and the alsa input module to record a screencast as mp4 file with h264 video and aac audio. You need to have FFmpeg compiled with --enable-x11grab and --enable-libx264 to achieve this, so run $ ffmpeg to check the compile configuration of your FFmpeg version.

When I started to write this article I spent a lot of time to figure out a way to record everything that comes out of my laptop’s soundcard. I ended up with creating an alsa loopback device and a new .asoundrc to route all output devices to the loopback device so I could record everything I hear.

First, create the alsa loopback device:
$ sudo modprobe snd_aloop
To load the module on boot, create /etc/modules-load.d/AlsaLoopbackDevice.conf and add snd_aloop

The loopback device gets its own alsa device ID and might get an ID lower than the one of your soundcard. This caused some troubles with my web browsers. I was not able to hear anything from a web page. The solution was to restore the original ID of my soundcard. You don’t need a udev rule for this, it can be achieved by adding some options to the snd_* kernel modules.

Create /etc/modprobe.d/sound.conf and add
options snd_hda_intel index=0
options snd_aloop index=1

The name of the kernel module for your soundcard might be different depending on the vendor.

The next step is to configure the loopback device such that it records everything that is also going to the speakers or line out. This part is taken from the Arch Linux Forums.
Create ~/.asoundrc and add

pcm.!default {
  type asym
  playback.pcm "LoopAndReal"
  #capture.pcm "looprec"
  capture.pcm "hw:0,0"
}

pcm.looprec {
    type hw
    card "Loopback"
    device 1
    subdevice 0
}

pcm.LoopAndReal {
  type plug
  slave.pcm mdev
  route_policy "duplicate"
}

pcm.mdev {
  type multi
  slaves.a.pcm pcm.MixReale
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

pcm.MixReale {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:0,0"
    rate 48000
    #rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0,0"
    rate 48000
    #rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

You don’t have to reboot or log out and back in for the changes to take effect. Alsa parses the .asoundrc every time a sound device is opened.

Now we need to find out the display ID from the X-Server and the loopback device ID from Alsa. Run $ arecord -l to find the ID of the loopback device and $ xrandr to get the screen ID and your actual screen resolution. Let’s assume your screen ID is 0, the resolution is set to 1600×900 and the ID of your loopback device is hw:1,1. To record a screencast with h264 video and aac audio in good quality you can try

$ ffmpeg -y -f x11grab -draw_mouse 1 -framerate 25 -video_size 1600x900 -i :0.0 -f alsa -i hw:1,1 -c:v libx264 -pix_fmt yuv420p -b:v 2500k -c:a aac -strict -2 -b:a 192k -movflags +faststart testgrab.mp4

The option -draw_mouse tells FFmpeg whether to render the cursor into the video (1) or not (0), and -framerate is pretty much self-explanatory. -video_size sets the size of the recorded video. This should be the native resolution of the screen you are recording. It does not make any sense to set a higher resolution, because FFmpeg would only scale up the video, which results in quality loss. The input ID is of the form display.screen, where display is usually 0 (your local X-Server display) and screen can be found using $ xrandr. The Alsa device ID can be found using $ arecord -l and is of the form card:device. The option -pix_fmt is needed, because the x11grab input module records video as raw RGB, which is incompatible with most players and playback devices when using h264 codec. The pixel format has to be converted to yuv420p. If you open your recorded screencast with VLC and the video is just a green screen, then you forgot to add this option. If you want your videos to have highest compatibility with target devices (older iOS versions or all Android devices), use -profile:v baseline and -level 3.0 options and values. This disables some advanced features but provides for better compatibility. If you want to increase the video quality, you can set a higher value for -b:v, but for simple video content like text, presentation frames and so on, 2500k should be sufficient. The aac encoding in FFmpeg is still experimental, so you need the -strict -2 option and value to override the “don’t use experimental codecs” setting in FFmpeg. You can add -movflags +faststart as an output option if your videos are going to be viewed in a browser. This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded.

You can add more sophisticated x264 encoding options with the x264opts option. Multiple x264 options are combined with a “:” and follow the form key=value, i.e. -x264opts keyint=123:min-keyint=20. More x264 encoding options can be found on MeWiki

About h0nk3ym0nk3y

Yeah, whatever, never mind

Posted on January 19, 2014, in Audio / Video, Command-Line and tagged , , , , , , , , , , , . Bookmark the permalink. 5 Comments.

  1. Two words: try “simplescreenrecorder”.

    • SimpleScreenRecorder is a nice and handy tool and I know there are several others out there for making screen recordings. I have used VLC for this for some time, like funkym0nk3y did for his post about how to set up a stateful firewall with iptables. The idea behind my post was to find a working solution for FFmpeg, to note down some FFmpeg options and links for further reference and to configure an alsa device for loopback recording with the intention that it might also be helpful for others.

      • VLC is nice but it does not record the mouse and you have to use a program called “extramaus” in order to record it. Dunno why this happens only in GNU/Linux. In older vlc versions I could use a .png file and the mouse was shown but in the newer versions even the .png file with the mouse pointer does not record the mouse anymore. Here’s my favourite cli vlc recording option:

        cvlc screen:// :screen-fps=30 :sout=’#transcode{vcodec=h264,acodec=mpga,ab=128,channels=2,samplerate=44100}:file{dst=/home/frost/Desktop/video-record-1.mp4}’

  2. Hello. Used your command but it records without audio(internal). Here is some output:
    r@prime:~$ ffmpeg -y -f x11grab -draw_mouse 1 -framerate 25 -video_size 1440×900 -i :0.0 -f alsa -i hw:0,0 -c:v libx264 -pix_fmt yuv420p -b:v 2500k -c:a aac -strict -2 -b:a 192k -movflags +faststart testgrab.mp4
    ffmpeg version 3.2.10-1~deb9u1 Copyright (c) 2000-2018 the FFmpeg developers
    built with gcc 6.3.0 (Debian 6.3.0-18) 20170516
    configuration: –prefix=/usr –extra-version=’1~deb9u1′ –toolchain=hardened –libdir=/usr/lib/x86_64-linux-gnu –incdir=/usr/include/x86_64-linux-gnu –enable-gpl –disable-stripping –enable-avresample –enable-avisynth –enable-gnutls –enable-ladspa –enable-libass –enable-libbluray –enable-libbs2b –enable-libcaca –enable-libcdio –enable-libebur128 –enable-libflite –enable-libfontconfig –enable-libfreetype –enable-libfribidi –enable-libgme –enable-libgsm –enable-libmp3lame –enable-libopenjpeg –enable-libopenmpt –enable-libopus –enable-libpulse –enable-librubberband –enable-libshine –enable-libsnappy –enable-libsoxr –enable-libspeex –enable-libssh –enable-libtheora –enable-libtwolame –enable-libvorbis –enable-libvpx –enable-libwavpack –enable-libwebp –enable-libx265 –enable-libxvid –enable-libzmq –enable-libzvbi –enable-omx –enable-openal –enable-opengl –enable-sdl2 –enable-libdc1394 –enable-libiec61883 –enable-chromaprint –enable-frei0r –enable-libopencv –enable-libx264 –enable-shared
    libavutil 55. 34.101 / 55. 34.101
    libavcodec 57. 64.101 / 57. 64.101
    libavformat 57. 56.101 / 57. 56.101
    libavdevice 57. 1.100 / 57. 1.100
    libavfilter 6. 65.100 / 6. 65.100
    libavresample 3. 1. 0 / 3. 1. 0
    libswscale 4. 2.100 / 4. 2.100
    libswresample 2. 3.100 / 2. 3.100
    libpostproc 54. 1.100 / 54. 1.100
    [x11grab @ 0x5569dff3be20] Stream #0: not enough frames to estimate rate; consider increasing probesize
    Input #0, x11grab, from ‚:0.0‘:
    Duration: N/A, start: 1528115857.680928, bitrate: N/A
    Stream #0:0: Video: rawvideo (BGR[0] / 0x524742), bgr0, 1440×900, 25 fps, 1000k tbr, 1000k tbn, 1000k tbc
    Guessed Channel Layout for Input Stream #1.0 : stereo
    Input #1, alsa, from ‚hw:0,0‘:
    Duration: N/A, start: 1528115857.694358, bitrate: 1536 kb/s
    Stream #1:0: Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s
    Any ideas?

    • h0nk3ym0nk3y

      Hi Rulet,

      sounds to me that you did not set up an ALSA loopback device or that you are recording the wrong device. Have a look at the video I posted and check the output of “arecord -l” to find the ID of the loopback device.

      Best regards,
      h0nk3ym0nk3y

Leave a reply to Rulet (@rulet111) Cancel reply