#!/bin/sh

INFILE=""
OUTFILE=""
KEEP=0
DEBUG=0
RECURSE=0
SCREENED=0
LOG=0
SOUND=1
CONT=mkv

RC="--crf"
RA="24"

MPLAYER="/usr/local/bin/mplayer"
X264="/usr/local/bin/x264"
LAME="/usr/local/bin/faac"
MERGE="/usr/local/bin/mkvmerge"
MP4B="/usr/local/bin/MP4Box"
TIME="/usr/bin/time -l"

DPAR="-vf crop=688:560:20:8,hqdn3d"
VPAR="--weightb --me umh --merange 24 --subme 9 --bframes 8 --b-pyramid --b-adapt 2 --ref 8 --mixed-refs --8x8dct --trellis 2 --direct auto --partitions all --threads auto"
APAR="-q 128"

FIFOV="xfer.y4m"
FIFOA="xfer.wav"

LOGFILE="movie2mkv.%n.log"

while getopts ":i:o:la:m:f:c:ndr:ksh?" FLAG
do
	case ${FLAG} in
		(i)
			# Input file
			INFILE=$(echo $OPTARG)
			;;
		(o)
			# Output file
			OUTFILE=$(echo $OPTARG)
			;;
		(f)
			# Container Format
			CONT=$(echo $OPTARG)
			;;
		(c)
			# Constant Rate Factor
			RC="--crf"
			RA=$(echo $OPTARG)
			;;
		(l)
			# Log option override
			LOG=1
			;;
		(a)
			# Audio option override
			APAR=$(echo $OPTARG)
			;;
		(m)
			# Decoding option override
			DPAR=$(echo $OPTARG)
			;;
		(n)
			# No Sound
			SOUND=0
			;;
		(s)
			# Start screen non-blocking
			SCREENED=1
			;;
		(k)
			# Keep intermediate files
			KEEP=1
			;;
		(d)
			# Write debug output
			DEBUG=1
			;;
		(r)
			# Internal use only
			RECURSE=$(echo $OPTARG)
			;;
		(:)
			echo Error: -$OPTARG requires an argument
			break
			;;
		(h|?|*)
			cat <<-EOF
			$0 - Convert X to MKV
			Avaliable options:
			Long Option	Short	Description
			-i <file>	- Specifies the input file (needed)
			-o <file>	- Specifies the output file (needed)
			-c <crf>	- Specifies the crf (default: $RA)
			-a <args>	- Specifies audio (faac) arguments (default: $APAR)
			-m <args>	- Specifies video (mencoder) arguments (default: $DPAR)
			-n	- Disables sound
			-l	- Enable logging
			-s	- Start in detachable screen (default: blocking)
			-k	- Keep intermediate files
			-d	- Debug mode (default: off)
			EOF
			exit
			;;
	esac
done

[ ".$OUTFILE" = . ] && echo "Missing output file" && exit
[ ".$INFILE" = . ]  && echo "Missing input file" && exit

function sanitize
{
	# Replace all spaces with underscores
	echo $* | sed 's/ /_/g'
}

function tempname
{
	# Generate an unlikely to be used name
	dd if=/dev/random bs=16 count=1 | openssl base64 | sed -e "s/\///g" -e "s/=//g"
}

# Input file can not contain spaces
INFILESANE=$(sanitize ${INFILE})
if ([ ".$INFILE" != ".$INFILESANE" ]) then
	INFILESANE="${INFILESANE}.$(tempname)"
	# Evil: Create symlink for use
	ln -s "${INFILE}" "${INFILESANE}"
	SYMLINKUSED=1
else
	SYMLINKUSED=0
fi

# Output file can not contain spaces
OUTFILE=$(sanitize ${OUTFILE})
TMPV="${OUTFILE}.tv.264"
TMPA="${OUTFILE}.ta.m4a"

RNDNAME=$()

case ${CONT} in
	(mkv)
		# Use mkvmerge to create a matroska container
		TMPV="${OUTFILE}.tv.mkv"
		MCMD="${MERGE} -o ${OUTFILE} ${TMPV} ${TMPA}"
		;;
	(mp4)
		# Use MP4Box to create an mpeg 4 container
		MCMD="${MP4B} -add ${TMPV} -add ${TMPA} ${OUTFILE}"
		;;
	(*)
		echo "Invalid Container Format specified"
		MCMD=
		;;
esac

if ([ ${SOUND} = 1 ]) then
	DFLAGS="-vo yuv4mpeg:file=${FIFOV} -ao pcm:fast:file=${FIFOA}"
else
	DFLAGS="-vo yuv4mpeg:file=${FIFOV} -nosound -benchmark"
fi
DCMD="${MPLAYER} ${INFILESANE} ${DPAR} ${DFLAGS}"
VCMD="${TIME} ${X264} ${RC} ${RA} ${VPAR} ${FIFOV} -o ${TMPV}"
ACMD="${LAME} ${APAR} ${FIFOA} -o ${TMPA}"

function prepare
{
	# Prepare named pipes
	ex mkfifo ${FIFOV} ${FIFOA}
	#ex mkfifo ${FIFOOV} ${FIFOOA}
}

function startDirect
{
	# screen can not be detached or merge will run prematurely
	# start screen that exits when all processes terminate
	if ([ ${LOG} = 1 ]) then
		# Start screen with place holder window
		ex screen -S movie2mkv -d -m -t tmp -p 9 sleep 4
		# Set up logging
		ex screen -S movie2mkv -X logfile ${LOGFILE}
		ex screen -S movie2mkv -X deflog on
		# Start up decoder
		ex screen -S movie2mkv -X screen -t dc ${DCMD}
	else
		# Just start up screen with decoder
		ex screen -S movie2mkv -d -m -t dc ${DCMD}
	fi
	# Start encoder
	ex screen -S movie2mkv -X screen -t ev ${VCMD}
	if ([ ${SOUND} = 1 ]) then
		ex screen -S movie2mkv -X screen -t ea ${ACMD}
	fi

	# Attaches screen
	ex screen -S movie2mkv -p dc -r

	ex ${MCMD}

	cleanup
}

function startScreened
{
	# start screen that continues to live on
	echo Start screen
	ex screen -S movie2mkv -t dc $*
}

function startContained
{
	if ([ ${LOG} = 1 ]) then
		# Set up logging
		ex screen -S ${STY} -X logfile ${LOGFILE}
		ex screen -S ${STY} -X deflog on
		ex screen -S ${STY} -X log on
	fi
	ex screen -S ${STY} -X screen -t ev
	ex screen -S ${STY} -p ev -X exec ${VCMD}
	ex screen -S ${STY} -X screen -t ea
	if ([ ${SOUND} = 1 ]) then
		ex screen -S ${STY} -p ea -X exec ${ACMD}
		ex screen -S ${STY} -X select dc
	fi

	ex ${DCMD}

	# wait for consumers to complete (heuristic) (evil!)
	sleep 16
	ex ${MCMD}

	cleanup

	# start a shell to replace this script
	exec ${SHELL}
}

function cleanup
{
	# Cleans up
	TRASH="${FIFOV} ${FIFOA}"
	if ([ ${SYMLINKUSED} = 1 ]) then
		TRASH="${TRASH} ${INFILESANE}"
	fi
	if ([ ${KEEP} = 0 ]) then
		TRASH="${TRASH} ${TMPV} ${TMPA}"
		#TRASH="${TRASH}"
	fi
	ex rm ${TRASH}
}

function ex
{
	if ([ ${DEBUG} = 1 ]) then
		echo $*
	fi
	$*
}

if ([ ${RECURSE} != 0 ]) then
	# Called by a parent that created a screen
	# we are in this screen
	prepare
	startContained
elif ([ ${SCREENED} = 1 ]) then
	startScreened $0 -r m2m $*
else
	prepare
	startDirect
fi

