rendered paste body#!/bin/bashfunction out() { echo -en "$1" echo -n "$2: " echo -en "\x1b[m"}function err() { ( out "\x1b[01;31m" "error" # bold red echo "$*" ) >&2 exit 1}function info() { out "\x1b[01;32m" "info"; # bold green echo "$*"}function warn() { ( out "\x1b[01;33m" "warn"; # bold yellow echo "$*" ) >&2}function ggt() { local a=$1; local b=$2; while [ $a -ne $b ]; do if [ $a -gt $b ]; then a=$((a-b)); else b=$((b-a)); fi done return $a}me="$0";in="$1";tmp="$2";cropleft=0cropright=0croptop=0cropbottom=0crf=23audb=144stage=-1track=0y4mfilter=""vidffargs=""x264args=""usage="\usage: $me <in-dvd.img> <tmp-path> [<args ...>] -t <title> DVD title to encode, default: longest -A \"<a b c d ...>\" audio track indices to extract, default: all -S \"<a b c d ...>\" subtitle track indices to extract, default: all -C <l> <r> <t> <b> cropping -c <crf> CRF-value for x264, smaller: better, default: 23 -b <kbit-rate> Ogg Vorbis bitrate, default: 144 -f <ffmpeg-args> additional FFmpeg video filter arguments, e.g. '-deinterlace' -x <x264-args> additional arguments for x264-encoding, e.g. '--tune <type>' with <type> being a combination of: film, animation, grain, stillimage, fastdecode, zerolatency -y <y4m-filter> yuv4mpeg filter, pipe into command -s <stage> 1: chapters 2: extract audio 4: extract subtitles 8: process subtitles 16: encode audio files 32: encode video"if ! which lsdvd &>/dev/null; then err "lsdvd not found; check package http://untrepid.com/lsdvd/"; fiif ! which dvdxchap &>/dev/null; then err "dvdxchap not found; check package http://www.bunkus.org/videotools/ogmtools/"; fiif ! which ffmpeg &>/dev/null; then err "ffmpeg not found; check http://ffmpeg.org/"; fiif ! which tccat &>/dev/null; then err "transcode not found; check http://tcforge.berlios.de/"; fiif ! which subtitle2vobsub &>/dev/null; then err "subtitle2vobsub not found; check http://subtitleripper.sourceforge.net/"; fiif ! which oggenc &>/dev/null; then err "oggenc not found; check http://www.vorbis.com/"; fiif ! which x264 &>/dev/null; then err "x264 not found; check package http://www.videolan.org/developers/x264.html"; fiif ! which bc &>/dev/null; then err "bc not found; check package http://www.gnu.org/software/bc/bc.html"; fiif ! which dtsdec &>/dev/null; then "dtsdec not found; check package http://www.videolan.org/developers/libdca.html"; fiif ! which mkvmerge &>/dev/null; then warn "mkvmerge not installed; check http://www.bunkus.org/videotools/mkvtoolnix"; fiif [ ! -e "$in" -o ! -d "$tmp" ]; then err "$usage"fishiftshiftatracks="all"stracks="all"while [ "x$1" != "x" ]; do case "$1" in "-t") track=$2; shift ;; "-A") atracks="$2"; shift ;; "-S") stracks="$2"; shift ;; "-C") cropleft=$2; cropright=$3; croptop=$4; cropbottom=$5; shift; shift; shift; shift ;; "-c") crf=$2; shift ;; "-b") audb=$2; shift ;; "-f") vidffargs="$2"; shift ;; "-x") x264args="$2"; shift ;; "-y") y4mfilter="$2"; shift ;; "-s") stage=$2; shift ;; *) err "$usage" ;; esac shiftdoneif [ $track -eq 0 ]; then track=$(lsdvd "$in") if [ $? -ne 0 ]; then err "lsdvd failed"; fi track=$(echo "$track" | grep Longest | awk '{ print $3+0 }')fi# 21; 3: lang-idx, 5: lang",", 7: format, 9: freq, 11: "drc", "16bit", 13: channels, 20: idaudio=( $(lsdvd -t $track -a "$in" | grep "Audio:") )if [ "$atracks" = "all" ]; then naud=$((${#audio[@]}/21)) atracks=$(seq 0 $((naud-1)))else naud=0 for idx in $atracks; do naud=$((naud+1)); donefi# 11; 3: lang-idx, 5: lang",", 10: idsubs=( $(lsdvd -t $track -s "$in" | grep "Subtitle:") )if [ "$stracks" = "all" ]; then nsub=$((${#subs[@]}/11)) stracks=$(seq 0 $((nsub-1)))else nsub=0 for idx in $stracks; do nsub=$((nsub+1)); donefipal=$(lsdvd -t $track -P "$in" | grep Palette: | cut -d: -f2 | xargs | sed 's/ /,/g')len=$(lsdvd -t $track "$in" | grep ^Title: | sed 's/^.*Length: \(..\):\(..\):\([0-9.]*\)[^0-9.].*$/(\1*60+\2)*60+\3/' | bc)# 17; 5: fps, 7: PAL/NTSC, 10: aspect ratio, 12: width, 14: height, display format: 16 (Letterbox)mov=( $(lsdvd -t $track -v "$in" | grep VTS:) )frames=$(echo $len*${mov[5]/,/} | bc | cut -d. -f1)movw=${mov[12]/,/}movh=${mov[14]/,/}# pixel aspect ratiomovas=${mov[10]/,/} # display aspectmovdw=$(echo ${movh}*${movas} | bc) # display widthif [ $((movdw & 1)) -eq 1 ]; then movdw=$((movdw+1)); figgt $movdw $movwk=$?movpx0=$((movdw/k)); # nominator of pixel aspect ratiomovpx1=$((movw/k)); # denominator of pixel aspect ratio# cropped stuffmovnh=$((movh-croptop-cropbottom))movnw=$((movw-cropleft-cropright))movdnw=$((movnw*movpx0/movpx1)) # display width of cropped viechggt $movdnw $movnhk=$?movdnas="$((movdnw/k)):$((movnh/k))" # cropped display aspect ratioinfo "track : ${track}, frames : ${frames}, len: $len sec"info "org : w: $movw, h: $movh; display: w: $movdw, h: $movh, aspect: $movas, pixel aspect: $movpx0:$movpx1"info "cropped: w: $movnw, h: $movnh; new display: w: $movdnw, h: $movnh, aspect: $movdnas"if [ $((movnw&15)) -ne 0 ]; then warn "width not divisible by 16; this leads to poorer compression"; fiif [ $((movnh&15)) -ne 0 ]; then warn "height not divisible by 16; this leads to poorer compression"; fiif [ $((stage & 1)) -ne 0 ]; then info "extracting chapter information" dvdxchap -t $track "$in" > $tmp/chapters if [ $? -ne 0 ]; then err "dvdxchap failed"; fifiif [ $((stage & 6)) -ne 0 ]; then cmd="tccat -T $track,-1 -i \"$in\" 2>/dev/null | tee"fiif [ $((stage & 2)) -ne 0 -a $naud -gt 0 ]; then info "extracting $naud audio-tracks from title $track:" echo -en "\t" for idx in $atracks; do fmt=${audio[idx*21+7]/,/} if [ $fmt = "lpcm" ]; then fmt=pcm; fi echo -en " ${audio[$idx*21+3]} ($fmt @${audio[$idx*21+20]}, ${audio[idx*21+13]/,/} chans)" cmd+=" >(tcextract -t vob -x $fmt -a $idx | tee $tmp/a$idx.$fmt | " if [ $fmt = "dts" ]; then cmd+="dtsdec -r -o wav | ffmpeg -i - -f s16le - 2>/dev/null" elif [ $fmt = "pcm" ]; then cmd+="ffmpeg -f s16le -ac ${audio[idx*21+13]/,/} -ar ${audio[idx*21+9]/,/} -i - -ac 2 -ar 48000 -f s16le - 2>/dev/null" else cmd+="tcdecode -x $fmt -y pcm" fi cmd+=" | tcscan -x pcm | grep 'volume rescale' | cut -d= -f3 > $tmp/a$idx.rescale)" done echofiif [ $((stage & 4)) -ne 0 -a $nsub -gt 0 ]; then info "extracting $nsub subtitle-tracks from title $track:" echo -en "\t" for idx in $stracks; do lang=${subs[$idx*11+3]} id=${subs[$idx*11+10]/,/} echo -en " $lang ($id)" cmd+=" >(tcextract -t vob -x ps1 -a $id > $tmp/sub.$idx)" done echofiif [ $((stage & 6)) -ne 0 -a $((naud+nsub)) -gt 0 ]; then cmd+=" >/dev/null" info "running '$cmd'" bash -c "$cmd"fifunction have_lang2() { mkvmerge --list-languages | cut -d'|' -f3 | grep -q $1}if [ $((stage & 8)) -ne 0 ]; then info "processing subtitles into vobsub" if [ ${#subs[@]} -gt 0 -a ${subs[3]} != "en" ] && have_lang2 ${subs[3]}; then warn "please check that the first track's language in $tmp/vobsub.idx is '${subs[3]}'" fi rm -f "$tmp"/vobsub.{idx,sub} info "pal: $pal" for idx in $stracks; do lang=${subs[$idx*11+3]} if have_lang2 $lang; then lang=,$lang; else lang=; fi cmd="subtitle2vobsub -p $tmp/sub.$idx -o $tmp/vobsub -s $movw,$movh -c $pal -a ${idx}${lang} 2>/dev/null" info "running '$cmd'" LC_ALL=C bash -c "$cmd" donefiif [ $((stage & 16)) -ne 0 ]; then info "encoding audio files..." for idx in $atracks; do fmt=${audio[idx*21+7]/,/} if [ $fmt = "lpcm" ]; then fmt=pcm; fi if [ ! -s $tmp/a$idx.$fmt ]; then warn "skipping audio track $idx, doesn't have any data..." continue fi freq=${audio[idx*21+9]/,/} vol=$(echo 256*$(cat $tmp/a$idx.rescale) | bc | cut -d. -f1) nch=${audio[idx*21+13]/,/} nch=$((nch>1 ? 2 : 1)) tb=$(tcprobe -R -i $tmp/a$idx.$fmt) if [ $? -ne 0 ]; then tb=$(tcprobe -M -R -i $tmp/a$idx.$fmt); if [ $? -ne 0 ]; then tb=""; fi fi tbc=$audb if [ "$tb" ]; then tb=$(echo "$tb" | grep ID_AUDIO_BITRATE | cut -d= -f2) if [ "$tb" -lt "$audb" ]; then info "adjusting audio bitrate from $audb to $tb for track $idx as input bitrate is lower" tbc=$tb fi else warn "failed to probe $tmp/a$idx.$fmt for bitrate, maybe empty or corrupt?" fi if [ $fmt = "dts" ]; then cmd="dtsdec -r -o wav $tmp/a$idx.$fmt | ffmpeg -i -" elif [ $fmt = "pcm" ]; then cmd="ffmpeg -f s16le -ac ${audio[idx*21+13]/,/} -ar $freq -i $tmp/a$idx.$fmt" else cmd="tcdecode -i $tmp/a$idx.$fmt -x $fmt -y pcm 2>/dev/null | ffmpeg -ac 2 -ar $freq -f s16le -i -" fi cmd+=" -vol $vol -ac $nch -f s16le - 2>/dev/null | oggenc -r -R $freq -b $tbc -C $nch -o $tmp/a$idx.ogg -" info "running '$cmd'" bash -c "$cmd" & done wait info ""fiif [ $((stage & 32)) -ne 0 ]; then cmd="tccat -T $track,-1 -i \"$in\" 2>/dev/null" cmd+=" | ffmpeg -i - -an -sn" if [ $cropleft -gt 0 -o $cropright -gt 0 -o $croptop -gt 0 -o $cropbottom -gt 0 ]; then cmd+=" -vf crop=$movnw:$movnh:$cropleft:$croptop" fi # cmd+=" -aspect $movdnas" cmd+=" $vidffargs -f yuv4mpegpipe - 2>/dev/null" if [ -n "$y4mfilter" ]; then cmd+=" | $y4mfilter" fi cmd+=" | x264 --crf $crf -b 8 --b-adapt 2 --b-pyramid normal -r 16 --direct auto --rc-lookahead 60 --qpstep 6 -A all" cmd+=" --me tesa --merange 32 -m 10 -t 2 --non-deterministic --no-interlaced $x264args --frames $frames" cmd+=" --sar $movpx0:$movpx1 -o $tmp/out264.mkv --demuxer y4m - |& tee -i >(sed 's/^.*\r\([^\r]*\)$/\1/' > $tmp/x264.log)" info "done so far, encoding video with $frames frames:" info "'$cmd'" bash -c "$cmd"ficmd="mkvmerge -o <out.mkv> --title <title> --chapters $tmp/chapters --chapter-language <lang> $tmp/out264.mkv"for idx in $atracks; do if [ -e $tmp/a$idx.ogg ]; then cmd+=" --language 0:${audio[$idx*21+3]/,/} $tmp/a$idx.ogg" fidoneif [ $nsub -gt 0 ]; then if [ -f "$tmp"/vobsub.idx ]; then for idx in $stracks; do cmd+=" --default-track $idx:0" done cmd+=" $tmp/vobsub.idx" else warn "no vobsub file found, although $nsub subtitles were requested" fifiif [ -f "$tmp"/x264.log ]; then cmd+=" --attachment-description \"x264 encode log\" --attachment-name x264.log --attachment-mime-type text/plain --attach-file-once \"$tmp\"/x264.log"fiinfo "use the following command to assemble it all into a mkv-file"info " '$cmd'"