DEV Community

Ghislain LEVEQUE
Ghislain LEVEQUE

Posted on

Capturing a screencast, the command-line-nerd style

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.

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 as a window manager, with multiple screens and no floating windows. Tools like 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, the swiss army knife of video
  • xdotool, a small program allowing to manipulate windows, mouse, and keyboard in a X environment
  • 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.

#!/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

Top comments (0)