Skip to content

Instantly share code, notes, and snippets.

@rfht
Created September 27, 2024 01:28
Show Gist options
  • Save rfht/153388d836bcc7235b516a4216421d79 to your computer and use it in GitHub Desktop.
Save rfht/153388d836bcc7235b516a4216421d79 to your computer and use it in GitHub Desktop.
updated fauxstream script, currently still undergoing testing, with performance improvements, VA-API use, and better avoidance of desyncs
#!/bin/sh
########
# Copyright (c) 2018-2019 Thomas Frohwein <[email protected]>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
########
############
# TODO:
############
# - ADD OPTION TO SAVE MIC TO SEPARATE FILE (solene request)
# - add mention of $DISPLAY variable for the recording?
# - AFAIK '-preset ultrafast' is specific to libx264 and needs to be disabled
# for use of other codecs
# - get default framerate from xrandr? same for resolution - default to desktop
# resolution?
# - mention need to stop recording with Ctrl-C??
# - add highpass=f=200,lowpass=f=3000
# - document windowname (-n) needs to be in "" if multiple words
# - document that window needs to be visible/on top
# - link for finding twitch servers:
# http://bashtech.net/twitch/ingest.php
# - does -s option need to be in "" ? -> should not be needed
# - document that compositor has negative effect on smoothness
# - framerate 29 seems to be better than 30 in regads to Twitch stream health
# - document '-m' turns on recording from mic
# - '-m' currently uses hardcoded snd/0
# - may not need as many 'thread_queue_size options, but definitely for the -f sndio inputs
# - Cryptark crashes/hangs on this Ryzen 7 2700
# - add '-s $OUTRES' ??
# - -m and -noaudio should be mutually exclusive
# - add man page
###########
###########
# LESSONS LEARNED
###########
# - audio offset needs to be applied to the audio in the second ffmpeg,
# right of the pipe. This clearly worked as opposed to left of the pipe
# with Rogue Legacy recording at 900p 60fps.
USAGE="Usage: `basename $0` [-m] [-d mic_device] [-a audiooffset] [-n window_name] [-c codec] [-b bitrate] [-r resolution] [-o offset] [-fullscreen] [-s <scale_x>:<scale_y>] [-f framerate] [-v volume_adjustment] [-vmic mic_volume] [-vmon monitor_volume] [-t threads ] [-noaudio] [-crf crf] <rtmp url>"
# Parse arguments based on http://www.shelldorado.com/goodcoding/cmdargs.html
audiooffset=0.0
bitrate=3500
bufsize=
container=flv
filename=
framerate=30
fullscreen=0
gop=
heightline=
heightval=
hilopass=",lowpass=3000,highpass=200,afftdn"
mic=0
mic_device=snd/0
noaudio=0
offset=
resolution=1280x720
scaleres=
threads=0 # -t 0 is the default and uses as many threads as available/cores
#videocodec=libx264
videocodec=h264_vaapi
volume=1.0
volume_mic=1.0
volume_mon=1.0
windowname=
widthline=
widthval=
wininfoargs=
xline=
xval=0
yline=
yval=0
# FIXME: set up getopt(1) use
#args=`getopt abo: $*`
#if [ $? -ne 0 ]
#then
#echo 'Usage: ...'
#exit 2
#fi
#set -- $args
#while [ $# -ne 0 ]
#do
#case "$1"
#in
#-a|-b)
#flag="$1"; shift;;
#-o)
#oarg="$2"; shift; shift;;
#--)
#shift; break;;
#esac
#done
if [[ $# -eq 0 ]];then
echo $USAGE; exit 0
fi
while [[ $# -gt 0 ]]
do
case "$1" in
-a) audiooffset="$2"; shift;;
-b) bitrate="$2"; shift;;
-c) videocodec="$2"; shift;;
-container) container="$2"; shift;;
-crf) crf="$2"; shift;;
-d) mic_device="$2"; shift;;
-f) framerate="$2"; shift;;
-fullscreen) fullscreen=1;;
-h) echo $USAGE; exit 0;;
-hl) hilopass="";;
-m) mic=1;;
-n) windowname="$2"; shift;;
-noaudio) noaudio=1;;
-o) offset="$2"; shift;;
-v) volume="$2"; shift;;
-r) resolution="$2"; shift;;
-s) scaleres="$2"; shift;;
-t) threads="$2"; shift;;
-vmic) volume_mic="$2"; shift;;
-vmon) volume_mon="$2"; shift;;
-) filename="$1"; break;;
-?*) echo $USAGE; exit 0;;
*) filename="$1"; break;;
esac
shift
done
if [[ -z "$filename" ]];then
echo $USAGE; exit 0
fi
bufsize=`expr $bitrate \* 2`
gop=`expr $framerate \* 2`
if [[ -n "$scaleres" ]];then
scaleres="scale=${scaleres},"
fi
sndio_device="$(sndioctl -n server.device | cut -f1 -d'(')"
echo -n "Recording geometry ("
# if -r was used
if [ $resolution != "1280x720" -a -z "$windowname" ];then
echo -n "custom"
# if -fullscreen was used, overwrite resolution with root window parameters (overrides -r and -o)
elif [ $fullscreen -eq 1 ]; then
xline=`xwininfo -name "$windowname" | grep "Absolute upper-left X"`
yline=`xwininfo -name "$windowname" | grep "Absolute upper-left Y"`
widthline=`xwininfo -root | grep "Width:"`
heightline=`xwininfo -root | grep "Height:"`
xval=`echo "$xline" | cut -f 7 -d " "`
yval=`echo "$yline" | cut -f 7 -d " "`
# width and height need to be multiples of 2
# in the case of fullscreen, contract by 1px (if necessary; seems unlikely to happen)
widthval=`echo "$widthline" | cut -f 4 -d " "`
if [[ `expr $widthval % 2` -eq 1 ]]; then
widthval=`expr $widthval - 1`
fi
heightval=`echo "$heightline" | cut -f 4 -d " "`
if [[ `expr $heightval % 2` -eq 1 ]]; then
heightval=`expr $heightval - 1`
fi
resolution="${widthval}x${heightval}"
offset="+${xval},${yval}"
echo -n "fullscreen"
# if -n was used, overwrite resolution with window parameters (overrides -r, -o, and -fullscreen)
elif [[ -n "$windowname" ]];then
xline=`xwininfo -name "$windowname" | grep "Absolute upper-left X"`
yline=`xwininfo -name "$windowname" | grep "Absolute upper-left Y"`
widthline=`xwininfo -name "$windowname" | grep "Width:"`
heightline=`xwininfo -name "$windowname" | grep "Height:"`
xval=`echo "$xline" | cut -f 7 -d " "`
yval=`echo "$yline" | cut -f 7 -d " "`
# width and height need to be multiples of 2
# in the case of windows, expand by 1px (if necessary)
widthval=`echo "$widthline" | cut -f 4 -d " "`
if [[ `expr $widthval % 2` -eq 1 ]]; then
widthval=`expr $widthval + 1`
fi
heightval=`echo "$heightline" | cut -f 4 -d " "`
if [[ `expr $heightval % 2` -eq 1 ]]; then
heightval=`expr $heightval + 1`
fi
resolution="${widthval}x${heightval}"
offset="+${xval},${yval}"
echo -n "$windowname"
else
echo -n "default"
fi
echo -n "): ${resolution}${offset}\n"
echo "Press Ctrl+C to stop recording\n" >& 2
BASE="\
-hide_banner \
-loglevel verbose \
-thread_queue_size 512 \
-threads $threads"
# XXX: consider trying: -vf setpts=0
VIDEO="-re \
-video_size $resolution \
-thread_queue_size 512 \
-vaapi_device /dev/dri/renderD128 \
-f x11grab \
-framerate $framerate \
-i $DISPLAY$offset \
-vf "hwupload,scale_vaapi=format=nv12" \
-c:v $videocodec \
-vb ${bitrate}k \
-minrate ${bitrate}k \
-maxrate ${bitrate}k \
-bufsize ${bufsize}k \
-preset ultrafast \
-tune zerolatency \
-r $framerate \
-g $gop \
-keyint_min $framerate \
-vsync 0"
AUDIOMERGE="volume=$volume_mic,aformat=channel_layouts=stereo$hilopass[l];[1]volume=$volume_mon,aformat=channel_layouts=stereo[m];[l][m]amix=inputs=2[a]"
# if no mic (= no -m), only record from snd/$sndio_device.mon
# and the only filter is aresample=async=1
if [ $noaudio -lt 1 -a $mic -lt 1 ]; then
#only monitoring stream
ffmpeg $BASE -f sndio -i snd/$sndio_device.mon -c:a aac -f nut pipe:1 | \
ffmpeg $BASE -f nut -itsoffset $audiooffset -i pipe:0 $VIDEO -c:a copy -f "${container}" "$filename"
elif [ $noaudio -lt 1 ]; then
#mon + mic stream
ffmpeg $BASE \
-max_interleave_delta 20000 \
-thread_queue_size 512 -f sndio -i "$mic_device" \
-thread_queue_size 512 -f sndio -i snd/$sndio_device.mon \
-filter_complex "[0]${AUDIOMERGE}" -map '[a]' -c:a aac -f nut pipe:1 | \
ffmpeg $BASE -thread_queue_size 512 -f nut -itsoffset $audiooffset -i pipe:0 $VIDEO -c:a copy \
-f "${container}" "$filename"
else
#no audio
ffmpeg $BASE $VIDEO -f "${container}" "$filename"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment