#!/bin/bash
# WARNING: This is a terribly dirty and messy shellscript, written over a couple of late nights.
# There was alcohol …
# You probably need to install all the gstreamer-plugins (at least -ugly and -ffmpeg) to get this
# to work. This version encodes h264 video and aac sound(s) to an MPEG4 Transport Stream file.
# 
# So this script allows you to create screencasts on Linux, with optional sound inputs (both Mic 
# and system sounds). It stores system sounds and mic in separate audio streams.
# You also get to choose to record from the whole screen or just a specified window. It has only 
# been tested on Fedora 18.
# 
# It dumps the recording in your $HOME directory with filename screencast-YYYY-MM-DD-HH-MM-SS.ts
#
# Written by Einar Jørgen Haraldseid (http://einar.slaskete.net)
# License: http://sam.zoy.org/wtfpl/COPYING

# Get device names and pretty names for playback sinks:
PLAYSINKLIST=$(pacmd list-sinks | grep -e "name: " -e "device.description = " | cut -d " " -f "2-" | sed -e "s/name: \|= //g" -e "s/<\|>\|\x22//g")

# Display playback monitor chooser
PLAYMON=$(echo "${PLAYSINKLIST}
none
Don't capture system sounds" | zenity --list --title "Choose Output Device" --text "Choose a sound device to capture system sound from:" --column "device" --column "Name" --print-column=1 --hide-column=1 2>/dev/null)

if [ -z ${PLAYMON} ]; then
  echo "No choice made on output device, assuming cancel."
  exit 1
fi

if [ ${PLAYMON} != "none" ]; then
  # Unmute monitor of the playback sink (if set):
  PLAYMON="${PLAYMON}.monitor"

  pacmd set-source-mute ${PLAYMON} false >/dev/null
fi

# Get device names and pretty names for microphones:
MICLIST=$(pacmd list-sources | grep -e "name: " -e "device.description = " | grep -v -i "monitor" | cut -d " " -f "2-" | sed -e "s/name: \|= //g" -e "s/<\|>\|\x22//g")

# Display device chooser
MIC=$(echo "${MICLIST}
none
Don't use a microphone" | zenity --list --title "Choose Microphone" --text "Choose a microphone to capture voice from:" --column "device" --column "Name" --print-column=1 --hide-column=1 2>/dev/null)

if [ -z ${MIC} ]; then
  echo "No choice made on microphone, assuming cancel."
  exit 1
fi 

# Get target window for recording:
TARGET=$(echo "root
Whole screen
window
Specific window" | zenity --list --title "Choose recording mode" --text "Do you want to record the whole screen, or record a specific window?" --column "target" --column "Mode" --print-column=1 --hide-column=1 2>/dev/null)

if [ -z ${TARGET} ]; then
  echo "No choice for recording target, assuming cancel."
  exit 1
fi

if [ ${TARGET} = "root" ]; then
  echo "Root chosen"
  XWININFO=$(xwininfo -root)
else
  echo "custom chosen"
  XWININFO=$(xwininfo)
fi

# Get Window ID and dimensions, make sure dimensions are divisible by two, or else the encoder will fail
WID=$(echo "${XWININFO}" | grep "Window id:" | awk '{print $4}')
WIDTH=$(echo "${XWININFO}" | grep "Width: " | cut -d ":" -f 2 | awk '{print $1+$1%2}')
HEIGHT=$(echo "${XWININFO}" | grep "Height: " | cut -d ":" -f 2 | awk '{print $1+$1%2}')

# Calculate a suitable bitrate based on window dimensions
BITRATE=$(echo "${WIDTH} * ${HEIGHT} * 0.006" | bc | cut -d "." -f 1 )

# Set file name.
FILENAME="screencast-$(date +%F-%H-%M-%S).ts"

# Enable inputs as suitable
if [ ${PLAYMON} != "none" ]; then
  MONITORARG="pulsesrc device=${PLAYMON} slave-method=0 provide-clock=false ! audiorate ! audioconvert ! ffenc_aac bitrate=256000 ! queue2 ! mux."
fi
if [ ${MIC} != "none" ]; then
  MICARG="pulsesrc device=${MIC} slave-method=0 provide-clock=false ! audiorate ! audioconvert ! ffenc_aac bitrate=256000 ! queue2 ! mux."
fi

# Launch gstreamer
gst-launch -e ximagesrc xid="${WID}" do-timestamp=1 use-damage=0 ! video/x-raw-rgb,framerate=30/1 \
 ! ffmpegcolorspace ! videoscale method=0 ! video/x-raw-yuv,width=${WIDTH},height=${HEIGHT} \
 ! x264enc speed-preset=ultrafast bitrate=${BITRATE} ! queue2 \
 ! h264parse ! mpegtsmux name="mux" ! filesink location="${HOME}/${FILENAME}" $MONITORARG $MICARG

