Skip to content

Instantly share code, notes, and snippets.

@funasoul
Last active October 15, 2022 20:10
Show Gist options
  • Save funasoul/026fb941b46c5a22793d75073161294d to your computer and use it in GitHub Desktop.
Save funasoul/026fb941b46c5a22793d75073161294d to your computer and use it in GitHub Desktop.
ffmpeg だけでカメラONのスクリーンキャストを収録 for macOS, Linux and OpenBSD
#!/usr/bin/env zsh
_has () {
return $( whence $1 &>/dev/null )
}
getresolution () {
# returns current resolution
if [[ "$OSTYPE" == "darwin"* ]]; then
system_profiler SPDisplaysDataType | awk '/Resolution:/{print $2 "x" $4}'
elif _has xrandr; then
xrandr | awk '/\*/{print $1}'
elif _has xdpyinfo; then
xdpyinfo | awk '/dimensions/{print $2}'
fi
}
ffscast () {
# Requirement:
# - ffmpeg
# - getresolution() function
# - _has() function
# References:
# - https://gist.github.com/CAFxX/dafa30845b1b3872d29980b39cf15f58
# - https://git.cbps.xyz/swindlesmccoop/not-just-dotfiles/src/branch/master/.local/bin/screencast
# - https://stackoverflow.com/a/47115192
setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR # be silent for background jobs
local TITLE
local CAMDEV
local SCRDEV
local SNDDEV
local fb # frame format
local cb # camera format
local sb # sound format
local probe_opt=()
local maxheight="1440"
local pix_opt=()
local capture_opt=()
local aenc_opt=()
local venc_opt=()
local screen_id="0"
local height
printf "Title: "
read TITLE
if [[ "$OSTYPE" == "darwin"* ]]; then
fb="avfoundation"
cb="avfoundation"
sb="avfoundation"
probe_opt=(-probesize 50M)
pix_opt=(-pix_fmt uyvy422)
capture_opt=(-capture_cursor 1 -capture_mouse_clicks 1)
aenc_opt=(-c:a libopus)
venc_opt=(-c:v hevc_videotoolbox -b:v 5000k)
local devs=$(ffmpeg -f $fb -list_devices true -i "" |& grep -i $fb | grep -v -e "mmhmm" -e "Reincubate" -e "Zoom" -e "Microsoft" -e "Virtual Desktop" -e "BlackHole")
local v=$(echo $devs | awk '/video device/,/audio device/' | cut -d']' -f 2-9 | grep -vi $fb)
local a=$(echo $devs | awk '/audio device/,/hoge/' | cut -d']' -f 2-9 | grep -vi $fb)
CAMDEV="$(echo $v | grep -i facetime | cut -d "[" -f2 | cut -d "]" -f1)"
echo $v
printf "Screen device (ex: 0, 1, 2): "
read SCRDEV
screen_id=$(echo $v | awk -v var="$SCRDEV" '$0 ~ var {print $4}')
echo $a
printf "Sound device (ex: 0, 1, 2): "
read SNDDEV
SNDDEV=":${SNDDEV}"
elif [[ "$OSTYPE" == "openbsd"* ]]; then
CAMDEV="/dev/video0"
SCRDEV=":0.0"
SNDDEV="snd/0"
fb="x11grab"
cb="v4l2"
sb="sndio"
venc_opt=(-c:v libx264)
if [ $(sysctl -n kern.audio.record) = 0 ]; then [ ~ ]
echo "Please exec 'doas sysctl kern.audio.record=1' for audio recording"
return
fi
if [ $(sysctl -n kern.video.record) = 0 ]; then [ ~ ]
echo "Please exec 'doas sysctl kern.video.record=1' for video recording"
return
fi
if [ ! -w /dev/video0 ]; then
echo "Please exec 'doas chown $USER /dev/video0' for video recording"
return
fi
else # Ubuntu
CAMDEV="/dev/video0"
SCRDEV=":0.0"
SNDDEV="default" # pacmd list-sources will list all devices
fb="x11grab"
cb="v4l2"
sb="pulse"
venc_opt=(-c:v libx264)
fi
# get minimal height from current resolution. requires getresolution() function.
local heights=( $(getresolution | awk '{n=split($0, ary, "x"); print(ary[2])}' ) )
height=$heights[(( $screen_id + 1 ))] # zsh's array is one origin!
local minheight=$([ $height -le $maxheight ] && echo "$height" || echo "$maxheight")
TITLE="$(echo $TITLE | sed 's/ /_/g')"
local DIRNAME="$TITLE-$(date '+%s')"
local DIRPATH="$HOME/Downloads/screencasts/$DIRNAME"
mkdir -p $DIRPATH
ffmpeg -nostdin \
-thread_queue_size 150 -f $fb ${probe_opt[@]} -framerate 30 ${pix_opt[@]} ${capture_opt[@]} -i "$SCRDEV" \
-thread_queue_size 150 -f $cb -video_size vga -framerate 30 ${pix_opt[@]} -i "$CAMDEV" \
-thread_queue_size 150 -f $sb -i "$SNDDEV" -filter_complex '
[0]fps=fps=30[scr],
[scr]scale=-1:'$minheight'[scr],
[1]fps=fps=30[cam],
[cam]scale=320:-1,pad=320:'$minheight'[cam],
[scr][cam]hstack[vout],
[2]afftdn[aout]
'\
-map '[vout]' ${venc_opt[@]} \
-map '[aout]' ${aenc_opt[@]} -b:a 256k \
-preset ultrafast $DIRPATH/final.mkv >/dev/null 2>&1 &
printf "Video and Audio recording started.\n\n"
printf "Press enter to stop recording"
read local lol
cd "$DIRPATH"
kill $(pgrep ffmpeg)
echo "Converting final.mkv to final.mp4 ..."
ffmpeg -v 0 -i final.mkv -q:a 0 -q:v 0 -strict -2 final.mp4
echo "Done."
}
ffscast
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment