A simple shadowplay/relive/OBS replay buffer-esque setup for wlroots compositors (and maybe others?) written in POSIX shell
wl-screenrec: https://github.com/russelltg/wl-screenrec/pactl- a VAAPI-capable GPU and
/dev/dri/renderD*node - any POSIX-capable shell
notify-sendand a corresponding notification daemon such as fnott if you want a desktop notification when a clip is dumped
- Put the scripts in
$PATHsomewhere. Generally, on Linux, a good location is/usr/local/bin.
- Start the replay buffer by running
replay-capture(i have mine running from runit as a user service) - Dump the current clip by running
replay-dump [output.mkv]
If no output option is given, replay-dump writes to replay-YYYYMMDD-HHMMSS.mkv in the current directory and prints the filename.
If you would like to specify a specific output location, such as to put replay-dump on a keybind, an example could be replay-dump /mnt/hdd/clips/replay-$(date +%Y%m%d-%H%M%S).mkv.
replay-capture is to be run more or less as a daemon and is agnostic as to what starts it, so one could use a service manager like runit or just start it from their window manager.
All configuration is via environment variables. Both scripts read the same REPLAY_BUFDIR so they can find each other- the rest are read by replay-capture only.
REPLAY_BUFDIR- Directory used for the lock, pidfile, and pending clip, defaults to$XDG_RUNTIME_DIR/replay-buffer, or/tmp/replay-bufferifXDG_RUNTIME_DIRis unsetREPLAY_WINDOW_SECS- How many seconds of history to keep buffered, defaults to150(2.5 minutes)REPLAY_TAIL_SECS- How many extra seconds to keep recording after a dump is triggered before the recorder is stopped, defaults to0, meaning the clip ends at the momentreplay-dumpwas invokedREPLAY_FRAMERATE- Max framerate passed towl-screenrec, defaults to60REPLAY_OUTPUT- Wayland output to record, defaults toDP-3. Usewlr-randror your compositors output list,swaymsg -t get_outputsfor example, to find the right nameREPLAY_DEVICE- DRI render node passed towl-screenrec --dri-device, defaults to/dev/dri/renderD128
REPLAY_FFMPEG_ENCODER- ffmpeg encoder name, defaults tohevc_vaapiREPLAY_RC_MODE- Rate-control mode passed through to the VAAPI encoder, defaults toCQPREPLAY_QP- Quantization parameter forCQPmode, defaults to22, lower is higher quality and larger filesREPLAY_ENCODE_PIXFMT- Pixel format passed towl-screenrec --encode-pixfmt, defaults tovuyx
replay-capture creates a null sink, loopbacks the mic and the desktop monitor source into it, then points wl-screenrec at that sinks monitor. This is so the recorded audio contains both your voice and game/desktop audio mixed together.
REPLAY_MIC_SOURCE- PipeWire/PulseAudio source name for the microphone, this does not have a default, look for your microphone inpactl list sources shortto apply to this envvar. Example value:alsa_input.usb-0c76_USB_PnP_Audio_Device-00.mono-fallbackREPLAY_DESKTOP_SOURCE- Monitor source of the sink your desktop audio plays out of, this does not have a default, look for a source name ending in.monitorinpactl list sources shortto apply to this envvar. Example value:alsa_output.usb-GuangZhou_FiiO_Electronics_Co._Ltd_FiiO_K5_Pro-00.analog-stereo.monitorREPLAY_DESKTOP_VOLUME- Raw PulseAudio volume value applied to the desktop loopbacks sink-input, defaults to23253.65536is 100%, this exists because desktop audio is usually too loud relative to the mic in the final mix. If you don't care about audio, the simplest path is to comment out thesetup_audiocall and the--audio/--audio-deviceflags inreplay-capture
$REPLAY_BUFDIR/lock- Directory created withmkdiras a mutex against multiplereplay-captureinstances$REPLAY_BUFDIR/pid- PID of the runningreplay-capture$REPLAY_BUFDIR/pending.mkv- Output filewl-screenrecwrites into, exists for the duration of one capture cycle$REPLAY_BUFDIR/pending.done.mkv- Renamed-from-pending.mkvonce the recorder has stopped.replay-dumppolls for this file as its "flush complete" signal, then renames it to the user-requested output path