Capturing a screencast, the command-line-nerd style
Tags: ffmpeg, bash, screencast, productivity
Published: 2019-10-24 17:53:05

## Introduction

> If you don't care about "why and how", just skip to the end of the article where the script is or go grab it [on github](https://github.com/court-jus/workbench).

I'm a bit pathologic when it comes to the tools I use on a day to day basis and I often want as much control as I can have.

I work as a full stack developer and lately, with my team, we decided that we wanted to demonstrate all the features we are releasing to other teams and screencasts can be a great medium for that purpose.

Tools already exist to capture screencasts and some of them are really close to what I wanted but I'm using [i3](https://i3wm.org/) as a window manager, with multiple screens and no floating windows. Tools like [peek](https://github.com/phw/peek) for example don't work really well with that configuration.

So I decided to take the dirty path and take advantage of the first unix principle: "Make each program do one thing well".

In this case here is what I expect from this rule: each brick I need to build my wall probably already exists (spoiler: of course they do).

## What I need:

* a tool able to capture a video from part of my screen, given coordinates
* a tool to designate coordinates
* a tool to build interaction upon

## The tools

* [FFmpeg](https://www.ffmpeg.org/), the swiss army knife of video
* [xdotool](https://www.semicomplete.com/projects/xdotool/), a small program allowing to manipulate windows, mouse, and keyboard in a X environment
* [dmenu](https://tools.suckless.org/dmenu/), a dynamic menu for X

*I already used `xdotool` and `dmenu` in a lot of productivity scripts, I may present more about them in other articles.*

## The solution

* On the first launch, the script uses `dmenu` to guide the user in selecting the coordinates for her capture, its output is not used, it just serves for talking with the user and inserting "breakpoints" in the script
* `xdotool` is used to grab mouse coordinates (top left corner then bottom right corner)
* Using that coordinates, the width and height are calculated
* `ffmpeg` is launched with its `x11grab` input to capture the desktop. It is launched has a background job and its PID is saved in a file
* When launched a second time, the script detects that the PIDFILE is there, verifies if the process is still running, kills it gently and displays the captured video.

I've bound the script to a keyboard shortcut in my `i3` config file so the calls to `echo` in the script are just for debugging.

```bash
#!/bin/bash

PIDFILE=${HOME}/.screencast_pid

if [ -f ${PIDFILE} ]; then
    PID=$(cat ${PIDFILE})
    if [ -n "$(ps -p ${PID} -o pid=)" ]; then
        echo "Screencast in progress" | dmenu
        WD=$(readlink -f /proc/${PID}/cwd)
        OUTFILE=${WD}/output.mp4
        kill ${PID}
        rm ${PIDFILE}
        vlc ${OUTFILE}
        exit 1
    else
        echo "wrong pidfile"
        rm ${PIDFILE}
    fi
fi

get_mouse_location() {
    echo "Go to $1 then press Enter." | dmenu > /dev/null
    xdotool getmouselocation
}

tlc=$(get_mouse_location "top left corner")
brc=$(get_mouse_location "bottom right corner")

top=$(echo ${tlc} | sed -e "s/.*y:\([0-9]\+\).*/\1/")
lef=$(echo ${tlc} | sed -e "s/.*x:\([0-9]\+\).*/\1/")
bot=$(echo ${brc} | sed -e "s/.*y:\([0-9]\+\).*/\1/")
rig=$(echo ${brc} | sed -e "s/.*x:\([0-9]\+\).*/\1/")

width=$(expr ${rig} - ${lef})
height=$(expr ${bot} - ${top})
echo "Do screencast ${width}x${height} from top: ${top} left: ${lef} to bottom: ${bot} right: ${rig}"

ffmpeg -v 8 -video_size ${width}x${height} -framerate 10 -y -f x11grab -i :0.0+${lef},${top} output.mp4 &
FFMPEG_PID=$!
echo ${FFMPEG_PID} > ${PIDFILE}

```

## Ideas for enhancement

* Let the user decide what to do with the video (display it, move it in a specific folder, upload it, remove it...)
* Allow capturing whole screen by giving more than one choice in the calls to `dmenu` (and even allow cancelling)

----

And you, do you like crafting your own walls with existing bricks? What is in your toolbox? Also, let me know if you find this kind of post useful.

PS: You'll find the tools I'm talking about [here](https://github.com/court-jus/workbench)