Recording and streaming with FFmpeg

2016-09-12

Once I wanted to try recording and streaming my painting, hoping that it would make me more efficient. Open broadcaster didn’t work on my old hardware, luckily there is ffmpeg https://ffmpeg.org/.
I was experimenting with the options to make it as cpu unintensive as possible (while maintaining some quality) so I would still be able to paint. Here is what I settled on:

Recording

ffmpeg  \
    -video_size 1680x1050 -framerate 10 -hwaccel auto -f x11grab -i $DISPLAY.0 -vcodec libx264 \
    -qp 0 -crf 28 -threads 1 -preset superfast  -vprofile baseline -pix_fmt yuv420p \
    -f h264 $1_`date +%Y%m%d%H%M`.mkv; 

Recording and streaming (with pulseaudio)

FPS="10" # target FPS
GOP="20" # i-frame interval, should be double of FPS, 
GOPMIN="10" # min i-frame interval, should be equal to fps, 
THREADS="1"
CBR="3000k" # constant bitrate (should be between 1000k - 3000k)
AUDIO_RATE="44100"

ffmpeg -f x11grab -r "$FPS" -i $DISPLAY.0 \
        -keyint_min $GOPMIN -minrate $CBR -maxrate $CBR -strict normal -g $GOP -b:v $CBR -bufsize $CBR\
        -vcodec libx264 -f flv -qp 0 -crf 28 -threads 1 -preset superfast -vprofile baseline -pix_fmt yuv420p - \
        | tee $1_`date +%Y%m%d%H%M`.mkv | ffmpeg  -i - -f alsa -i pulse -ar $AUDIO_RATE -vcodec copy\
        -acodec aac -f flv rtmp://live.us.picarto.tv/golive/$STREAM_KEY

Making final video

The recorded videos were, of course, huge and often one picture was in several files, since I didn’t finish it in one session. Both the preceding commands take one argument - an identifier of the picture. I could use it to make a list of recorded videos of this pictures:

ffmpeg_make_filelist () { printf "file '%s'\n" ./*$1* > ffmpeg_concat.txt; }

Then ffmpeg can take this file and concat the listed videos:

ffmpeg_concat () { ffmpeg -safe 0 -f concat -i ffmpeg_concat.txt -c copy $1 }

The result is still huge, so I removed duplicate frames (where I was just staring on the screen or went away and was not drawing)

ffmpeg_rmduplicates () { ffmpeg -i $1  -vf mpdecimate,setpts=N/FRAME_RATE/TB  -threads 2 ${1}_done.mp4; }

And encoded the video:

ffmpeg_encode () { ffmpeg -i $1 -s 1280x800 -filter:v "fps=15, setpts=0.1*PTS" -vcodec libx264  -crf 22 -threads 2 ${1}_enc.mp4; }