VOB to AVI using Mencoder (Next Gen)

This is a recode and enhancement of my first vob2avi shell script. Pretty much does the same.

#!/bin/bash
 
function init() {
	if [ "$INIT_DONE" ]; then echo "+ init: done already" >&2; return; fi
	key="$IN $OPTS"
	echo "::: init: initializing with key '$key'" >&2 ;
#	hash=`echo "$key" | md5sum | cut -f1 -d' '`
	hash=`echo "OPTS $OPTS" | sed 's/[^[:alnum:]]/_/g' `
	echo "+ init: hash is '$hash'" >&2 ;
	basedir=`dirname "$IN"`
	basename=`basename "$IN"`
	export TMPDIR="$basedir/$basename.$hash.vob2avi"
	echo "+ init: TMPDIR is '$TMPDIR'" >&2 ;
	mkdir -p "$TMPDIR"
	INIT_DONE="yes"
	echo "+ init: done" >&2 ;
}
 
function setupaudio() {
	if [ "$DUMMYMODE" ]; then
		whiptail --title "ERROR" --msgbox "audio setup can not be done in DUMMY MODE!" 7 75 ;
		return
	fi
	init
	demux
	echo "::: setupaudio: setting up audio track configuration for '$IN'" >&2 ;
 
	pushd "$TMPDIR" > /dev/null
	find "." | egrep "audio_0x[a-f0-9]+.vob" | while read track; do
		track=`basename $track`
		while true; do
			tagfile=`mktemp`
			if \
				whiptail --title "choose action for track $track" --menu "" 24 75 17\
				listen "Listen to this track" \
				remove "Do not include this track in final avi" \
				include "Include this track in final avi" \
				first "Make this the first audio track" \
				2>$tagfile
			then \
				tag=`cat $tagfile`
				rm -f $tagfile
				echo "+ setupaudio: user voted for menu item $tag with $track" >&2 ;
				case "$tag" in
				listen)
					kaffeine $track
					;;
				remove)
					newaids=`cat aids | grep -v $track`
					echo -e "$newaids" > aids
					break
					;;
				include) 
					echo $track >> aids
					break
					;;
				first)
					newaids=`cat aids | grep -v $track`
					echo $track > aids
					echo -e "$newaids" >> aids
					break
					;;
				esac
			else
				rm -f $tagfile
				break;
			fi
		done
	done
	popd >/dev/null
}
 
function demux() {
	if [ "$DUMMYMODE" ]; then
		whiptail --title "ERROR" --msgbox "demuxing can not be done in DUMMY MODE!" 7 75 ;
		return
	fi
	init
	if [ -f "$TMPDIR/demux_done" ]; then
		echo "+ demux: done already" >&2 ;
		return
	fi	
 
	echo "::: demux: demultiplexing '$IN'" >&2 ;
 
	info=`info`
 
	videoids=`echo -e "$info" | egrep "^ID_VIDEO_ID=" | sort | uniq | cut -f2 -d"="`
	audioids=`echo -e "$info" | egrep "^ID_AUDIO_ID=" | sort | uniq | cut -f2 -d"="`
	subtitleids=`echo -e "$info" | egrep "^ID_SUBTITLE_ID=" | sort | uniq | cut -f2 -d"="`
 
	#echo -e "$info";return
 
	pushd "$TMPDIR" > /dev/null
	flagfile="demux_lock"
	rm -f "$flagfile"
	fifos=""
	for id in $videoids; do
		id=`printf "0x%02x" $(($id + 224))`
		echo "+ demux: found video ID#$id" >&2 ;
		fifo="video_$id.fifo"
		mkfifo "$fifo"
		fifos="$fifos $fifo"
		(
			#wait for flagfile
			while [ ! -f "$flagfile" ]; do sleep 1; done ; 
			tcdemux -i "$fifo" -A $id > "video_$id.vob"
		) &
	done
	for id in $audioids; do
		id=`printf "0x%02x" $id`
		echo "+ demux: found audio ID#$id" >&2 ;
		fifo="audio_$id.fifo"
		mkfifo "$fifo"
		fifos="$fifos $fifo"
		(
			#wait for flagfile
			while [ ! -f "$flagfile" ]; do sleep 1; done ; 
			tcdemux -i "$fifo" -A $id > "audio_$id.vob"
		) &
	done
	for id in $subtitleids; do
		id=`printf "0x%02x"  $(($id + 32))`
		echo "+ demux: found subtitle ID#$id" >&2 ;
		fifo="subtitle_$id.fifo"
		mkfifo "$fifo"
		fifos="$fifos $fifo"
		(
			#wait for flagfile
			while [ ! -f "$flagfile" ]; do sleep 1; done ; 
			tcdemux -i "$fifo" -A $id > "subtitle_$id.vob"
		) &
	done
	touch "$flagfile"
	pv -N "demuxing" "$IN" | tee $fifos >/dev/null
	rm -f "$flagfile"
	rm -f $fifos
	popd > /dev/null
 
	touch "$TMPDIR/demux_done"
	echo "+ demux: done" >&2 ;
	exit 0
}
 
function info() {
	if [ "$DUMMYMODE" ]; then
		echo "ERROR: info called in DUMMY MODE" >&2
		exit -1
	fi
	init
	if [ -f "$TMPDIR/info" ]; then
		echo "+ info: done already" >&2
		cat "$TMPDIR/info"
		return
	fi
	echo "::: info: initializing" >&2
	echo "+ info: identifying $IN with mplayer" >&2
	res=`mplayer -ao null -vo null -frames 1 -identify "$IN" 2>&1`
	echo "+ info: identifying $IN with tcprobe" >&2
	res="$res"`tcprobe -H 100 -i "$IN" 2>&1`
	echo "+ info: saving info" >&2
	echo -e "$res" > "$TMPDIR/info"
	echo "+ info: done" >&2
}
 
function testencode() {
	echo "::: test-encoding '$1'" >&2 ; 
	vob="$1"
	#-ss 10:00 
	ripaudio "$vob" "-frames 2500"
	encode "$vob" "-frames 2500"
	audiomerge "$vob"
	mplayer -vo xv -fs "$vob.avi"
}
 
function ripaudio() {
	echo "::: ripping audio from '$1'" >&2 ; 
	vob="$1"
	opts="$2"
 
	info "$vob" | grep "ID_AUDIO_ID" | cut -f2 -d"=" > "${vob}.aids"
 
	cat "${vob}.aids" | while read aid; do
		mencoder "$vob" $opts -oac copy -aid $aid -ovc frameno -o "${vob}_audio_${aid}.avi"
	done
}
 
function audiomerge() {
	echo "::: encoding '$1'" >&2 ; 
	vob="$1"
 
	aids=`cat "${vob}.aids"`
 
	cp -f "${vob}.avi" "${vob}_merged.avi"
 
	for aid in $aids; do
 
		track=0
		if [ -f "${vob}_audio_${aid}.avi" ]; then
			echo "::: merging lang ${vob}_audio_${aid}.avi as track $track" >&2 ; 
			aviindex -i "${vob}_audio_${aid}.avi" -f -o "${vob}_merged.avi.idx"
			avimerge -i "${vob}_merged.avi" -o "${vob}.avi.tmp" -p "${vob}_audio_${aid}.avi" -A $aid -x "${vob}_merged.avi.idx"
			mv -f "${vob}.avi.tmp" "${vob}_merged.avi"
			track=$(($track + 1))
			rm -f "${vob}_merged.avi.idx"
		fi
 
	done
 
}
 
function encode() {
	echo "::: encoding '$1'" >&2 ; 
 
#	unlink frameno.avi 2> /dev/null
	vob="$1"
	opts="$2"
	crop=`cropdetect "$vob"`
	scale=`scaledetect "$vob"`
	vfopts=pp=de,crop=$crop,scale=$scale
	lavcopts=vcodec=mpeg4:vhq:vqmin=2:vbitrate=2000
 
#	mencoder "$vob" $opts -oac copy -alang English -ovc frameno -o "${vob}_audio_en.avi"
#	mencoder "$vob" $opts -oac copy -alang Hungarian -ovc frameno -o "${vob}_audio_hu.avi"
	mencoder "$vob" $opts -oac copy -ovc lavc -lavcopts $lavcopts:vpass=1 -vf $vfopts -o "/dev/null"
	mencoder "$vob" $opts -oac copy -ovc lavc -lavcopts $lavcopts:vpass=2 -vf $vfopts -o "$vob.avi"
 
#	unlink frameno.avi 2> /dev/null
	unlink divx2pass.log  2> /dev/null
}
 
function scaledetect() {
	echo "::: scale-detecting '$1'" >&2 ; 
 
	vob="$1"
 
	scalecache="$vob.scale"
 
	if [ -f "$scalecache" ]; then
		echo "+ reading scale data from $scalecache" >&2
		scale=`cat "$scalecache"`
		echo "+ final scale parameters: $scale" >&2
		echo "$scale"
		return
	fi
 
	info=`info "$vob"`
 
	#echo -e "$info"
	#exit 0
 
	res=`echo -e "$info" | egrep "^VIDEO:" | egrep -o "[0-9]+x[0-9]+"`
 
	case "$res" in
		720x576) 
			echo "+ video has PAL resolution, this is good" >&2
			aspect=`echo -e "$info" | egrep "^Movie-Aspect" | egrep -o "[0-9.]+:[0-9.]+" | tr ':' '/'`
			#aspect="4/3"
			ow="720"
			oh="576"
			;;
		*) 
			echo "+ video has $res resolution, this is BAD - UNIMPLEMENTED" >&2
			exit -1
			;;
	esac
 
	echo "+ video has $aspect aspect with $res resolution" >&2
 
	crop=`cropdetect "$vob"`
	w=`echo $crop | cut -f1 -d":"`
	h=`echo $crop | cut -f2 -d":"`
	echo "+ cropped video has resolution ${w}x${h}" >&2
 
	# original width:
	nw=$ow
	nh=`echo "scale=4;(1/($aspect))*($ow/$oh)*($w/$ow)*$h" | bc | cut -f1 -d'.'`
 
	# cropped width:
	#nw=$w
	#nh=`echo "scale=4;(1/($aspect))*($ow/$oh)*$h" | bc | cut -f1 -d'.'`
 
	scale="${nw}:${nh}"
 
	echo "+ storing final parameters to: $scalecache" >&2
 
	echo $scale > "$scalecache"
 
	echo "+ final scale parameters: $scale" >&2
 
	echo $scale
 
	echo "+ scale detect done." >&2
 
}
 
 
function cropdetect() {
	echo "::: crop-detecting '$1'" >&2 ; 
 
	vob="$1"
 
	cropcache="$vob.crop"
 
	if [ -f "$cropcache" ]; then
		echo "+ reading crop data from $cropcache" >&2
		bestcrop=`cat "$cropcache"`
		echo "+ final parameters: $bestcrop" >&2
		echo $bestcrop
		return
	fi
 
#	info "$vob"
#	exit 0
 
	echo "+ crop detecting movie with mplayer..." >&2
 
	crops=`mktemp`
	for x in 2 5 10 20 30 50 100; do
		mplayer -vo null -ao null -frames 5 -ss $x:00 -vf cropdetect "$vob" 2>/dev/null | grep "\[CROP\]" | egrep -o "[0-9]+:[0-9]+:[0-9]+:[0-9]+"
	done > $crops
 
	cropss=`mktemp`
	cat $crops | while read crop; do
 
		x=`echo $crop | cut -f3 -d":"`
		y=`echo $crop | cut -f4 -d":"`
		xl=`echo $crop | cut -f1 -d":"`
		yl=`echo $crop | cut -f2 -d":"`
 
		echo $(( xl*yl))  $crop
 
	done > $cropss
 
	rm -f $crops
 
	bestcrop=`cat $cropss | sort -n | tail -n1 | cut -f2 -d' '`
 
	rm -f $cropss
 
	echo "+ best crop guessed by mplayer: $bestcrop" >&2
 
	inputconf=`mktemp`
 
	echo "W change_rectangle 0 1" >> $inputconf
	echo "w change_rectangle 0 -1" >> $inputconf
	echo "E change_rectangle 1 1" >> $inputconf
	echo "e change_rectangle 1 -1" >> $inputconf
	echo "S change_rectangle 2 1" >> $inputconf
	echo "s change_rectangle 2 -1" >> $inputconf
	echo "D change_rectangle 3 1" >> $inputconf
	echo "d change_rectangle 3 -1" >> $inputconf
 
	cropfile=`mktemp`	
 
	echo "+ fine tuning with mplayer... (use keys: wWeEsSdD)" >&2
 
	mplayer "$vob" -input conf=$inputconf -vf rectangle=$bestcrop 2>/dev/null | egrep -o "[0-9]+:[0-9]+:[0-9]+:[0-9]+" > $cropfile
 
	rm -f $inputconf
 
	bestcrop=`tail -n1 $cropfile`
 
	rm -f $cropfile
 
	echo "+ storing final parameters to: $cropcache" >&2
 
	echo $bestcrop > "$cropcache"
 
	echo "+ final crop parameters: $bestcrop" >&2
 
	echo $bestcrop
 
	echo "+ crop detect done." >&2
}
 
#encode dvd.DVD_VIDEO.1216721459.01.vob 720:430:0:72 720:405
 
BANNER=`basename $0`" - GPLv3 - (c) 2008 by Erno Rigo <mcree_AT_tricon_DOT_hu>"
 
# try to get to our input file
if [ -z "$1" ]; then
	searchpath=`pwd`
else
	if [ -d "$1" ]; then
		searchpath="$1"
	else
		IN="$1"
	fi
fi
 
if [ ! -z "$searchpath" ]; then
	echo "::: searching for INPUT candidates in $searchpath... " >&2
	files=`find "$searchpath" -type f | egrep ".*[.](vob|mpeg|VOB|MPEG|Vob|Mpeg|mpg|MPG)$" 2>/dev/null`
	opts=`echo -e "$files" | while read file; do echo "$file" "."; done`
 
	menutag=`mktemp`
 
	if \
		whiptail --noitem --backtitle "$BANNER" --title "select INPUT file" \
		--menu "" 24 75 17 -- \
		"[Dummy mode]" "dummy"\
		$opts \
	 	2>"$menutag";\
	then
		echo "::: selected tag: "`cat "$menutag"` >&2
		IN=`cat "$menutag"`
		rm -f "$menutag"
	else
		echo "::: exiting on user request" >&2
		rm -f "$menutag"
		exit 0;
	fi
 
fi
 
if [ ! -f "$IN" ]; then
	IN="Dummy mode!"
	DUMMYMODE="yes"
else 
	# calculate absolute path for IN
	pushd `dirname "$IN"` > /dev/null
	IN=`cd -`"/"`basename "$IN"`
	popd > /dev/null
fi
 
 
OPTS="$2"
 
if [ ! -z "$3" ]; then
	$3
	exit 0
fi
 
# MAIN MENU LOOP
while true; do
 
 
menutag=`mktemp`
 
echo "::: entering main menu" >&2
 
if \
whiptail --backtitle "IN: '$IN'" --title "main menu" \
--menu "OPTS: '$OPTS'" 24 75 16 -- \
"demux" "- Demultiplex INPUT" \
"setupaudio" "- Setup audio tracks to be included in OUTPUT" \
 2>"$menutag"; \
then
	tag=`cat "$menutag"`
	rm -f "$menutag"
	echo "::: selected tag: $tag" >&2
	$tag
else
	echo "::: exiting on user request" >&2
	rm -f "$menutag"
	exit 0;
fi
 
done
# END MAIN MENU LOOP
 
exit 0
 
opts=`cat<<EOF
in:#specify input file (typically vob or mpeg)
encode:#specify action 
cropdetect:#try to detect crop parameters
scaledetect:#try to detect scale parameters
prepare:#setup and preview encoding
ripaudio:#rip audio tracks
audiomerge:#merge audio tracks
demux:#demultiplex input
EOF
`
 
# prepare long opts for getopt
optsf=`mktemp`
echo -e "$opts" | while read opt; do
	echo -ne ","`echo "$opt" | cut -f1 -d'#'`
done | cut -b 2- > $optsf
longopts=`cat $optsf`
rm -f $optsf
 
TEMP=`getopt -o "" --long $longopts -n "$0" -- "$@"`
 
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
 
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
 
while true ; do
        case "$1" in
                --encode) echo "Option encode, argument $2" ; shift 2 ;;
                --cropdetect) cropdetect "$2" ; shift 2 ;;
                --scaledetect) scaledetect "$2" ; shift 2 ;;
                --prepare) testencode "$2" ; shift 2 ;;
                --ripaudio) ripaudio "$2" ; shift 2 ;;
                --audiomerge) audiomerge "$2" ; shift 2 ;;
                --demux) demux "$2" ; shift 2 ;;
                --) shift ; break ;;
                *) echo "Internal error!" ; exit 1 ;;
        esac
done
#echo "Remaining arguments:"
#for arg do echo '--> '"\`$arg'" ; done