#!/bin/sh
# Copyright © 2012-2014 Jonas Smedegaard <dr@jones.dk>
# Description: record most possible video data using least CPU
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Depends: libav-tools, alsa-utils, v4l-utils
#
# TODO: add --help option
# TODO: make inputs overrideable
# TODO: optionally limit number of inputs
# TODO: make codecs, rates etc. configurable
# TODO: auto-limit number of video-inputs per USB channel
# TODO: include SMTPE channel
# TODO: Check and ask if outfile already exist
# TODO: Add --force option to skip asking for overwriting

set -e

output="${1:-dump.nut}"

# resolve audio inputs: default PCM of all ALSA cards with input devices
ainputs="$(arecord -l | perl -anE '/^card/ and say $F[2]')"
#ainputs="CARD=Generic_1,DEV=0"

# FIXME: detect and avoid more than one device on same USB channel:
# [video4linux2 @ 0xed7e00] ioctl(VIDIOC_STREAMON): No space left on device
# /dev/video2: No space left on device
# maybe with $(v4l2-ctl --list-devices)
vinputs="$(ls -1 /dev/video*)"
#vinputs="$(ls -1 /dev/video* | grep -v video1)"
#vinputs="/dev/video0 /dev/video1 /dev/video3"
#vinputs="/dev/video0"

# TODO: probe and optimize V4L settings (highest 16/9 size at >= 20fps
inrate=20
#inrate=10
outrate=$inrate # seems this ensures fixed rate when input is flaky

vcodec=copy # uncompressed
#vcodec=huffyuv # more common
#vcodec=ffvhuff # more compact, supports YV12 colorspace
#vcodec=ffv1 # most compact, supports RGBA colorspace, too CPU-hungry

acodec=copy
#acodec=pcm_s16le

parse_v4l2_formats() {
	v4l2-ctl --list-formats-ext -d "$1" | perl -n \
		-e '/^\tIndex\h*:/ and $type=$format=$raw=$size=$speed=undef;'\
		-e '/^\tType\h*: (Video Capture)/ and $type=$1;'\
		-e '/^\tPixel Format: '\''(\w+)'\''( \(compressed\))?/ and $format=$1 and $raw=int(!($2));'\
		-e '/^\t\tSize: Discrete (\d+x\d+)/ and $size=$1;'\
		-e 'next unless ($type and $format and $size);'\
		-e '/^\t\t\tInterval: .* \((\d+)\.\d+ fps\)/ and $speed=$1;'\
		-e 'next unless ($speed >= '$inrate');'\
		-E 'say "$raw $size $format"' \
		| sort -rV
}

# expand arguments
i=0
ainargs=
amaps=
for input in $ainputs; do
	# TODO: handle varying descriptions (not just pick first entry)
	# TODO: handle more descriptions (not just assume stereo)
	achannels=
	achannelsdesc="$(amixer -c $input | grep '^  Capture channels:' | head -n 1)"
	case $achannelsdesc in
	  *': Mono')
		achannels=1
		;;
	esac
	ainargs="${ainargs:+$ainargs }-f alsa -ar 44100 ${achannels:+-ac $achannels} -i hw:$input"
	amaps="${amaps:+$amaps }-map $i:a"
	i=$(expr $i + 1)
done
vinargs=
vmaps=
for input in $vinputs; do
	read raw size format dummy <<FORMATS
$(parse_v4l2_formats $input)
FORMATS
	case $format in
	  YUYV)
		format=yuyv422
		;;
	  MJPG)
		format=mjpeg
		;;
	esac
	[ -n "$format" ] || { echo "Skipping unsuitable input $input"; continue; }
	vinargs="${vinargs:+$vinargs }-f video4linux2 -input_format $format ${inrate:+-framerate $inrate }-s $size -threads auto -i $input"
	vmaps="${vmaps:+$vmaps }-map $i:v:0"
	i=$(expr $i + 1)
done
voutargs=${vinputs:+-c:v $vcodec ${outrate:+-r $outrate}}
aoutargs=${ainputs:+-c:a $acodec}

pasuspender=
test ! -x /usr/bin/pasuspender | pasuspender=pasuspender

echo $pasuspender avconv $ainargs $vinargs $vmaps $amaps $voutargs $aoutargs -y "$output"
exec $pasuspender avconv $ainargs $vinargs $vmaps $amaps $voutargs $aoutargs -y "$output"