#!/bin/sh # # mkdvd by Davide Libenzi (creates and burn DVD from a video file) # Copyright (C) 2008 Davide Libenzi # # 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 2 of the License, 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Davide Libenzi # # # Requierements: # # 1) You need pretty common Unix and GNU binaries like 'expr', 'grep', # 'sed', 'egrep' and 'mount'. # # 2) You need the 'growisofs' and 'dvdauthor' packages, however they # are named inside your distribution. # # 3) Most importantly, you need a full version of 'ffmpeg'. If you own # a distribution that does not allow non-free modules to be packaged, # you need to build your own 'ffmpeg' binary from sources. Like I did. # First, grab the latest source code from here: # # http://ffmpeg.mplayerhq.hu # # Then unpack and run configure inside the package source tree like: # # $ ./configure --enable-nonfree --enable-pthreads --enable-gpl --disable-shared # # Then run make: # # $ make # # The built binary is statically linked, so it won't try to mess up # with your distribution 'ffmpeg'. I just renamed the binary as 'ffmpeg-nf', # and this is what this script uses. You can rename the variable FFMPEG # in this script, to fit it to your needs. # FFMPEG=ffmpeg-nf DEBUG=0 IRES="720x480" ASP="16:9" IPAD=1 KASP=1 BURN=0 MKDVD=0 CLEAN=0 DRYRUN=0 TITLE="" ACP="" DVDDEV="/dev/hda" # # Max size of MPG in KB (~3.7GB). Less than full DVD capacity # because FFMPEG does not strictly respect the bps we ask, and may # lead to overflow. # MAXB=3700000 usage() { printf "Usage: %s [-DBvNuCWadXYAVxyMT] INFILE\n" $(basename $1) >&2 printf "\t-D Sets debug mode\n" >&2 printf "\t-B Burn DVD (implies -v)\n" >&2 printf "\t-C Clean temporary files when done\n" >&2 printf "\t-u Dry run. Just detect the configuration and show FFMEPG options\n" >&2 printf "\t-v Create DVD directory structure\n" >&2 printf "\t-N Do not PAD the image to bring to resolution\n" >&2 printf "\t-d DEV Sets the DVD device (%s)\n" "$DVDDEV" >&2 printf "\t-T TITLE Sets title for the DVD\n" >&2 printf "\t-M SIZE Sets maximum size for the DVD in KB (%s KB)\n" $MAXB >&2 printf "\t-a NACHAN Sets the number of audio channels\n" >&2 printf "\t-A PHASP Sets DVD physical aspect ratio (%s)\n" $ASP >&2 printf "\t-r IRES Sets the output pixel resolution (%s)\n" $IRES >&2 exit 1 } dbg() { if [ $DEBUG -ne 0 ]; then echo $1 fi } round2() { expr \( $1 / 2 \) \* 2 } toint() { echo -n $1 | cut -d '.' -f 1 } kvalue() { KV1=`echo $1 | cut -d $2 -f 1` KV2=`echo $1 | cut -d $2 -f 2` echo "scale = 6; $KV1 / $KV2" | bc -l } # # Here the start of all ... # while getopts 'DBvNuCa:d:A:M:T:r:' OPTION do case $OPTION in D) DEBUG=1 ;; B) MKDVD=1 BURN=1 ;; C) CLEAN=1 ;; N) IPAD=0 ;; u) DRYRUN=1 ;; v) MKDVD=1 ;; M) MAXB=`expr $OPTARG \* 1024` ;; A) ASP="$OPTARG" ;; r) IRES="$OPTARG" ;; T) TITLE="$OPTARG" ;; a) ACP="-ac $OPTARG" ;; d) DVDDEV="$OPTARG" ;; ?) usage $0 ;; esac done shift $(($OPTIND - 1)) if [ $# -lt 1 ]; then usage $0 fi INFILE=$1 if [ ! -f "$INFILE" ]; then echo "AVI file $INFILE not found" exit 2 fi if [ ! -b "$DVDDEV" ]; then echo "Invalid DVD device $DVDDEV" exit 3 fi DMOUNT=`mount | grep "$DVDDEV"` if [ "$DMOUNT" != "" ]; then echo "Device $DVDDEV is mounted!" exit 3 fi IMGFMT= if [ $ASP != "0" ]; then if [ $(echo -n $ASP | grep -E -q '[0-9]+:[0-9]+$'; echo -n $?) -gt 0 ]; then echo "Invalid aspect ratio format $ASP" exit 4 fi # # Calculate aspect ratio constant # KASP=$(kvalue $ASP ":") IMGFMT="-aspect $ASP" fi dbg "Physical Aspect Ratio: $KASP" OXRES=`echo $IRES | cut -d 'x' -f 1` OYRES=`echo $IRES | cut -d 'x' -f 2` OIASP=`echo "scale = 6; $OXRES / $OYRES" | bc -l` dbg "Pixel Aspect Ratio: $OIASP" KVA=`echo "scale = 6; $OIASP / $KASP" | bc -l` dbg "Aspect Ratio Ratio: $KVA" dbg "Input File: $INFILE" FNAME=`basename $INFILE` BNAME=`echo -n $FNAME | sed -r -e 's/^([^.]+)\..*$/\1/'` OUTFILE=$BNAME.mpg ODIR=__DVD__.$BNAME dbg "Output File: $OUTFILE" if [ "$TITLE" == "" ]; then TITLE=$BNAME fi MINFO=`"$FFMPEG" -i "$INFILE" 2>&1 | tr '\n' '|'` MOVLEN=`echo $MINFO | tr '|' '\n' | grep Duration | egrep -o '[0-9]{2}:[0-9]{2}'` MHOUR=`echo $MOVLEN | cut -d ':' -f 1` MMIN=`echo $MOVLEN | cut -d ':' -f 2` MSEC=`expr 60 \* $MMIN \+ 3600 \* $MHOUR` dbg "Movie Duration: $MHOUR:$MMIN:$MSEC" KBS=`expr $MAXB / $MSEC` KBPS=`expr $KBS \* 1024 \* 8` dbg "Output Rate: $KBPS bps" MRES=`echo $MINFO | tr '|' '\n' | grep Video | egrep -o '[0-9]+x[0-9]+'` XMRES=`echo $MRES | cut -d 'x' -f 1` YMRES=`echo $MRES | cut -d 'x' -f 2` dbg "Movie Input Resolution: ${XMRES}x${YMRES}" # # Get Display Aspect Ratio and Pixel Aspect Ratio # DAR=`echo $MINFO | egrep -o 'DAR +[0-9]+:[0-9]+' | sed -r -e 's/DAR *([0-9]+:[0-9]+)$/\1/g'` if [ "$DAR" != "" ]; then dbg "Movie Display Aspect Ratio: $DAR" KDAR=$(kvalue $DAR ":") fi PAR=`echo $MINFO | egrep -o 'PAR +[0-9]+:[0-9]+' | sed -r -e 's/PAR *([0-9]+:[0-9]+)$/\1/g'` if [ "$PAR" != "" ]; then dbg "Movie Pixel Aspect Ratio: $PAR" KPAR=$(kvalue $PAR ":") fi IMGASP=`echo "scale = 6; $XMRES / $YMRES" | bc -l` # # Get integer approximations # SIA=$(toint `echo "$IMGASP * 100000" | bc -l`) OIA=$(toint `echo "$OIASP * 100000" | bc -l`) if [ $SIA -ge $OIA ]; then NXMRES=$OXRES NYMRES=`echo "scale = 6; ( $NXMRES / $IMGASP ) / $KVA" | bc -l` NYMRES=$(round2 $(toint $NYMRES)) else NYMRES=$OYRES NXMRES=`echo "scale = 6; $NYMRES * $IMGASP * $KVA" | bc -l` NXMRES=$(round2 $(toint $NXMRES)) fi dbg "Movie Output Resolution: ${NXMRES}x${NYMRES}" IMGPAD= NMRES="${NXMRES}x${NYMRES}" dbg "Movie Output Aspect Ratio Resolution: ${OXRES}x${OYRES}" if [ $IPAD -ne 0 -a $ASP != "0" ]; then if [ $OYRES -ge $NYMRES ]; then YPAD=`echo "($OYRES - $NYMRES) / 2" | bc -l` YPAD=$(round2 $(toint $YPAD)) RYPAD=`expr $OYRES \- $NYMRES \- $YPAD` IMGPAD="$IMGPAD -padtop $YPAD -padbottom $RYPAD" dbg "Movie Padding (Top - Bottom): $YPAD - $RYPAD" elif [ $OXRES -ge $NXMRES ]; then XPAD=`echo "($OXRES - $NXMRES) / 2" | bc -l` XPAD=$(round2 $(toint $XPAD)) RXPAD=`expr $OXRES \- $NXMRES \- $XPAD` IMGPAD="$IMGPAD -padleft $XPAD -padright $RXPAD" dbg "Movie Padding (Left - Right): $XPAD - $RXPAD" fi fi FFMPEGOPTS="-target ntsc-dvd -vsync 1 -async 10000 -b $KBPS $IMGFMT -s $NMRES $IMGPAD $ACP" if [ $DRYRUN -ne 0 ]; then dbg "FFMPEG options: $FFMPEGOPTS" exit 1 fi if [ ! -f "$OUTFILE" ]; then dbg "Running FFMPEG with: $FFMPEGOPTS" nice -n 19 "$FFMPEG" -i "$INFILE" $FFMPEGOPTS "$OUTFILE" if [ $? -ne 0 ]; then rm "$OUTFILE" exit 4 fi fi if [ $MKDVD -ne 0 -a ! -d "$ODIR" ]; then dbg "Creating DVD Directory Structure: $ODIR" mkdir "$ODIR" nice -n 19 dvdauthor --title -f "$OUTFILE" -o "$ODIR" if [ $? -ne 0 ]; then rm -rf "$ODIR" exit 5 fi dvdauthor -T -o "$ODIR" if [ $? -ne 0 ]; then rm -rf "$ODIR" exit 6 fi fi if [ $BURN -ne 0 ]; then if [ ! -d "$ODIR" ]; then echo "DVD structure directory does not exist: $ODIR" exit 7 fi growisofs -Z "$DVDDEV" -V "$TITLE" -dvd-video "$ODIR" if [ $? -ne 0 ]; then exit 8 fi fi if [ $CLEAN -ne 0 ]; then rm -rf "$OUTFILE" "$ODIR" fi