diff options
Diffstat (limited to 'sway-record.sh')
-rwxr-xr-x | sway-record.sh | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/sway-record.sh b/sway-record.sh new file mode 100755 index 0000000..12ebbfe --- /dev/null +++ b/sway-record.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# Sway WM screen + audio recorder +# original author: Aaron D. Fields +# blog post: https://blog.spirotot.com/2017/08/21/a-dirty-hack-to-enable-acceptable-sway-wm-screen-recording/ +# currently error 503 :-( +# +# Updated version: ernierasta +# Repo: https://gist.github.com/ernierasta +# +# Changelog: +# - switched to wf-recorder (https://github.com/ammen99/wf-recorder), swaygrab do not work anymore +# - switched to pulseaudio sound recording, this will allow us to record specific streams to separate +# files and to capture sounds from apps and microphone +# - added option to run set output without -o parameter +# - script will keep temporary files, they can be useful for manual merge (volume adjustments, cuts, ...) +# in something like openshot +# - more documentation +# - added -n option to disable post-processing (video resizing + audio,video merging) +# +# Usage: sway-record -d [display] -a [app audio sink] -m [mic audio sink] -o [project_output_name] -n +# or alternatively: +# sway-record project_output_name +# +# When you finish recording, pres CTRL+C and it will stop all processes. +# +# -n option allows to disable post-processing entirely +# +# +# INSTALL & SETTINGS: +# +# Copy this file as /usr/local/bin/sway-recorder or to any other dir in your $PATH. +# +# Displays can be listed with: +# +# swaymsg -t get_outputs +# +# PulseAudio sinks can be listed by: parecord -d <TAB> or: +# +# pacmd list-source-outputs | grep "source" | grep -oP "<\K[^ >]+" +# +# Dependencies: ffmpeg, pulseaudio, wf-recorder +# +# WARNING: you probably need to compile wf-recorder, follow instructions on project page(https://github.com/ammen99/wf-recorder). +# +# Example: sway-record -d DP-1 -a alsa_output.pci-0000_21_00.3.analog-stereo.monitor -m alsa_input.usb-audio-technica____AT2020_USB-00.analog-stereo my_recording +# +# Note: If this file is sorely out of date, it's either no longer relevant, +# and/or I decided to push changes here: https://github.com/Spirotot/dotFiles +# Note 2: Maybe I am blind ... do not see this in repo. ;-) ErnieRasta +# Note 3: This in no more outdated! Works with sway 1.0 RC5 + +# You can define some defaults, which is very convinient... +DISP="eDP-1" +AUDIO_APP="alsa_output.pci-0000_00_1f.3.analog-stereo.monitor" +AUDIO_MIC="alsa_input.usb-audio-technica____AT2020_USB-00.analog-stereo" +OUTPUT="record" + +# change to true if you want to disable post-processing entirely +NO_PROCESSING=false + +# You probably want to leave vars below empty +SCREEN_CMD="" +AUDIO_APP_CMD="" +AUDIO_MIC_CMD="" + +# These for sure should be empty! +SCREEN_PID="" +AUDIO_APP_PID="" +AUDIO_MIC_PID="" +START="" + +# Set a trap for Ctrl+C (SIGINT) so that we can forward the +# Ctrl+C to the `swaygrab` and `arecord` subprocesses. +# Inspired by: https://stackoverflow.com/questions/8993655/can-a-bash-script-run-simultaneous-commands-then-wait-for-them-to-complete +trap killandconvert SIGINT + +# `killandconvert()` kills the `swaygrab` and `arecord` subprocesses +# when Ctrl+C is pressed, and then proceeds to fix up the length +# discrepencies, and create the final output MKV. +killandconvert() { + # Forward the SIGINT to `swagrab` and `arecord` so they can shut + # themselves down properly. + kill -2 $SCREEN_PID + kill -2 $AUDIO_APP_PID + kill -2 $AUDIO_MIC_PID + + # Wait for them to exit... + wait $AUDIO_APP_PID + wait $AUDIO_MIC_PID + wait $SCREEN_PID + + if [ "$NO_PROCESSING" = true ]; then + return + fi + + # Get the lengths: + # * https://forum.videolan.org/viewtopic.php?t=56438 + # * https://stackoverflow.com/questions/20323640/ffmpeg-deocde-without-producing-output-file + # Convert the lengths with awk: https://askubuntu.com/questions/407743/convert-time-stamp-to-seconds-in-bash + SCREEN_LENGTH=`ffmpeg -i ${OUTPUT}_orig.mkv -f null /dev/null 2>&1 | \ + grep Duration | awk '{print $2}' | tr -d "," | \ + awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}'` + + if [ "$START" = "" ]; then + AUDIO_LENGTH=`ffmpeg -i ${OUTPUT}_app_orig.wav -f null /dev/null 2>&1 | \ + grep Duration | awk '{print $2}' | tr -d "," | \ + awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}'` + else + # https://unix.stackexchange.com/questions/53841/how-to-use-a-timer-in-bash + AUDIO_LENGTH=$((SECONDS - START)) + fi + + # Calculate the multiplier used to sync the video to the audio. + # https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash + MULTIPLIER=`bc -l <<< "scale=8; $AUDIO_LENGTH/$SCREEN_LENGTH"` + + # "Sync" the video to the audio by stretching it. + # https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video + `ffmpeg -i ${OUTPUT}_orig.mkv -filter:v "setpts=${MULTIPLIER}*PTS" \ + -preset ultrafast ${OUTPUT}_tmp.mkv` + + if [ "$START" = "" ]; then + # Combine the video and audio streams into one output file. + # mixing: https://stackoverflow.com/questions/50168993/merging-2-audios-files-with-a-video-in-ffmpeg + `ffmpeg -i ${OUTPUT}_tmp.mkv -i ${OUTPUT}_app_orig.wav -i ${OUTPUT}_mic_orig.wav \ + -filter_complex "[1][2]amix=inputs=2[a]" \ + -map 0:v -map "[a]" \ + -c:v copy -c:a aac ${OUTPUT}.mkv` + else + # If there is no audio stream, then just rename the video stream + # as the final outout file. + mv ${OUTPUT}_tmp.mkv ${OUTPUT}.mkv + fi + + # Cleanup + #rm -f ${OUTPUT}_orig.mkv + #rm -f ${OUTPUT}_tmp.mkv + #rm -f ${OUTPUT}_orig.wav +} + +# mandatory trick to enable non-option parameter +# https://stackoverflow.com/questions/21753340/script-with-non-option-and-option-arguments +mandatory=() +# Parse the command line options... +# http://abhipandey.com/2016/03/getopt-vs-getopts/ +while [ $# -gt 0 ] && [ "$1" != "--" ]; do + while getopts d:a:m:o:n FLAG; do + case $FLAG in + d) + DISP=$OPTARG + ;; + a) + AUDIO_APP=$OPTARG + ;; + m) + AUDIO_MIC=$OPTARG + ;; + o) + OUTPUT=$OPTARG + ;; + n) + NO_PROCESSING=true + echo "NO PROCESSING!" + ;; + esac + done + shift $((OPTIND-1)) + + while [ $# -gt 0 ] && ! [[ "$1" =~ ^- ]]; do + mandatory=("${mandatory[@]}" "$1") + shift + done +done + +if [ "$1" == "--" ]; then + shift + mandatory=("${mandatory[@]}" "$@") +fi + +if [ "${mandatory[0]}" != "" ]; then + echo ${mandatory[0]} + OUTPUT=${mandatory[0]} +fi + +# Check the user's options to make sure they're somewhat sane. +if [ "$OUTPUT" = "" ]; then + echo "No output specified." + exit 1 +fi + +if [ "$DISP" = "" ]; then + echo "No display specified." + exit 1 +else + # Build the command used for screen recording. + SCREEN_CMD="wf-recorder -o $DISP -f ${OUTPUT}_orig.mkv" +fi + +# Build the command used for app audio recording. +AUDIO_APP_CMD="parecord -d ${AUDIO_APP} ${OUTPUT}_app_orig.wav" +AUDIO_MIC_CMD="parecord -d ${AUDIO_MIC} ${OUTPUT}_mic_orig.wav" +# Start the screen recorder... +$SCREEN_CMD & +# ... and save the PID so we can kill it gracefully later. +SCREEN_PID=$! + +if [ ! "$AUDIO_APP_CMD" = "" ]; then + # Start the audio recorder... + $AUDIO_APP_CMD & + # ... and save the PID so we can kill it gracefully later. + AUDIO_APP_PID=$! +else + # Unless we're not going to record audio, in which case we'll + # simply use a timer to figure out how much we need to stretch + # the video... + # https://unix.stackexchange.com/questions/53841/how-to-use-a-timer-in-bash + START=$SECONDS +fi + +if [ ! "$AUDIO_MIC_CMD" = "" ]; then + # Start the audio recorder... + $AUDIO_MIC_CMD & + # ... and save the PID so we can kill it gracefully later. + AUDIO_MIC_PID=$! +else + # Unless we're not going to record audio, in which case we'll + # simply use a timer to figure out how much we need to stretch + # the video... + # https://unix.stackexchange.com/questions/53841/how-to-use-a-timer-in-bash + START=$SECONDS +fi + + + +# Just hang out until the user presses Ctrl+C +wait |