Skip to content

Instantly share code, notes, and snippets.

@0E9B061F
Last active October 16, 2022 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0E9B061F/54fb06977c07a8a84c29a397424b9abe to your computer and use it in GitHub Desktop.
Save 0E9B061F/54fb06977c07a8a84c29a397424b9abe to your computer and use it in GitHub Desktop.
A simple screen recorder using ffmpeg, designed to be used headlessly, e.g., from a keyboard shortcut
#!/bin/bash
# ~/bin/recorder
#
# A simple screen recorder using ffmpeg, designed to be used headlessly, e.g.,
# from a keyboard shortcut. Calling the script again while another instance is
# running will kill the running instance, allowing you to toggle recording.
#
# REQUIRES: VAAPI, ffmpeg, notify-send, yad, beep
#
# * Recordings are placed in ~/recordings, which will be created if it does not
# exist.
# * A symlink to the latest recording is created at ~/recordings/latest.mp4
# * A PID file is created at ~/.recorder.pid
# * A PNG image is expected at ~/.recording.png, which will be used as a system
# tray icon. Example: https://0e9b061f.github.io/files/recording.png
#
# A system tray icon is created during recording to alert the user when running
# headlessly. There will be no indication that recording is occuring if you
# don't have a system tray.
#
# A script like `vlc ~/recordings/latest.mp4` may be useful to view recordings
# immediately.
read -r -d '' USAGE <<'EOF'
A simple screen recorder designed for headless use
recorder [-f FPS] [-q CQP] [-t TIME]
-f FPS
Frames per second to record
Default: 15
-q CQP
A number between 0-51. Controls recording quality. Higher numbers mean
lower quality, and smaller files. Zero is lossless.
Default: 26
-t TIME
End recording after TIME seconds
EOF
LIMIT=
FR="15"
CQP="26"
while getopts 'ht:f:q:' arg; do
case "${arg}" in
h) echo "${USAGE}"; exit 0 ;;
t) LIMIT="${OPTARG}" ;;
f) FR="${OPTARG}" ;;
q) CQP="${OPTARG}" ;;
*) exit 1 ;;
esac
done
shift $((OPTIND - 1))
OUT="$(date +%s)"
PIDF="${HOME}/.recorder.pid"
ICON="${HOME}/.recording.png"
OUTD="${HOME}/recordings"
OUTF="${OUTD}/${OUT}.mp4"
LINK="${OUTD}/latest.mp4"
TIMER=
YAD=
function msg() {
local msg
msg="${1}"
echo "${msg}"
notify-send -t 4000 "RECORDER" "${msg}"
}
function err() {
local msg
msg="${1}"
echo "ERROR: ${msg}"
notify-send -u critical -t 8000 "RECORDER ERROR" "${msg}"
}
function smite() {
kill "${1}" >& /dev/null
}
function on_exit() {
[[ -n "${TIMER}" ]] && smite "${TIMER}"
[[ -n "${YAD}" ]] && smite "${YAD}"
rm -f "${PIDF}"
ln -sf "${OUTF}" "${LINK}"
beep -f 1000 -l 8 -D 0
}
mkdir -p "${OUTD}"
if [[ -f "${PIDF}" ]]; then
PID="$(cat "${PIDF}")"
if ps -p "${PID}" >& /dev/null; then
if smite "${PID}"; then msg "Ended recording."
else
err "Unable to kill recorder"
exit 1
fi
else
if rm "${PIDF}"; then "${BASH_SOURCE[0]}" ${@}
else
err "Unable to remove stale PID file (${PIDF})"
exit 1
fi
fi
else
trap on_exit EXIT
echo "Begin recording:"
if [[ -f "${ICON}" ]]; then
yad --notification --command='' --image="${ICON}" &
YAD="${!}"
fi
beep -f 2000 -l 6 -D 0
ffmpeg \
-hwaccel vaapi \
-hwaccel_output_format vaapi \
-f x11grab \
-framerate "${FR}" \
-i "${DISPLAY}" \
-c:v h264_vaapi \
-compression_level 1 \
-vaapi_device /dev/dri/renderD128 \
-vf 'format=nv12,hwupload' \
-f mp4 \
-threads 12 \
-rc_mode 1 \
-qp ${CQP} \
"${OUTF}" &
FFPID="${!}"
echo "${FFPID}" > "${PIDF}"
if [[ -n "${LIMIT}" ]]; then
(
sleep "${LIMIT}"
smite "${FFPID}" && msg "Ended recording."
) &
TIMER="${!}"
fi
wait "${FFPID}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment