Linux video conversion shell script

For system help, all hardware / software topics NOTE: use Coders Corner for all coders topics.

Moderators: Krom, Grendel

Post Reply
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Linux video conversion shell script

Post by snoopy »

For your viewing pleasure!

First: a brief description:

What this script will do:

Convert your video files (anything that mencoder can read) into an two=pass mpeg4/mp3 avi file.

Accept inputs for desired video bitrate, cropping, audio file, framerate, minimum title length.

Read the data from a dvd, skipping titles under a certain length.

Accept inputs in the form of command line options, or prompt you for the info that it needs.

What it won't do:

Give you any choice about what codec to use.

Be completely dummyproof.

Be necessarily bug-free.

Give you any choice about what specific dvd titles to read.

Work in windows (though, I'm sure it can be translated... any volunteers?)

Give you any choice in terms of the video filters that it uses, beyond cropping.

How to get started:

invoke the script with --help.

read through it (it's not really all that long).

ridicule my silly engineering-ish coding.

I made it because I was tired of having to look up the command line switches to use with mencoder each time I wanted to compress something, at at the same time I didn't really want to move over to a GUI-based programs, so I made myself this script. You can invoke it without any command line options, and it will ask you for everything that it needs.... so you don't have to remember anything other than where you put the script.

Prerequisites:

Code: Select all

sudo apt-get install mencoder lsdvd
And, the actual script code: (copy-paste it into a file, and flag as executable)

Code: Select all

#!/bin/sh


audiooption=\"\"
outfile=\"\"
bitrate=\"\"
audio=\"\"
framerate=\"\"
crop=\"\"
length=\"2\"
input=\"\"


echo \"This script will compress/convert videos for you into xvid video & mp3 audio\"
echo \"Use --help for info on parameters this scripts can receive\"

# command line options to look for:

if [ \"$1\" = \"--help\" ]; then
echo \"Usage: videocompress.sh [-o outfile -b bitrate -a audiofile -f framerate -c crop -l mm:ss] [infile]\"
echo \"-o   Output file name, .avi will be appended\"
echo \"\"
echo \"-b   Video bitrate\"
echo \"      350-400 kBit is about right for 320x240 video\"
echo \"      900-1100 kBit is about right for 640x480 video - myth does 1000\"
echo \"      1400-2000 kBit is about right for HD video - myth does 1500\"
echo \"\"
echo \"-a   Optional audio to use instead of audio from video file; use \\\"0\\\" to specify none\"
echo \"\"
echo \"-f   Desired output framerate.  Expects a number, greater than 10.  \\\"ntsc\\\" can be used, too\"
echo \"\"
echo \"-c   Desired cropping.  In width:height:x:y; where values may be omitted, height and width\"
echo \"      must be multiples of 16.  Use \\\"n\\\" for no cropping, use \\\"y\\\" for default 720:400\"
echo \"\"
echo \"-l   Minimum track length (dvd only) in minutes.  Causes the scripts to skip over any tracks\"
echo \"      under the specified length.  Defaults to 2 minutes.  Will not be prompted for.\"
echo \"\"
echo \"infile   Video file to be used.  Also accepts \\\"dvd://\\\".\"
echo \"\"
echo \"Any parameter not passed, with the exception of -l and -ch, will result in a prompt for the needed value.\"
exit 0
fi

# parsing through the parameters passed

until [ \"$1\" = \"\" ]
do
	if [ \"$1\" = \"-o\" ]; then
		shift
		outfile=$1
		shift

	elif [ \"$1\" = \"-b\" ]; then
		shift
		bitrate=$1
		shift

	elif [ \"$1\" = \"-a\" ]; then
		shift
		audio=$1
		shift

	elif [ \"$1\" = \"-f\" ]; then
		shift
		framerate=$1
		shift

	elif [ \"$1\" = \"-c\" ]; then
		shift
		crop=$1
		shift

	elif [ \"$1\" = \"-l\" ]; then
		shift
		length=$1
		shift

	elif [ -f \"$1\" ] || [ \"$1\" = \"dvd://\" ]; then
		input=\"$1\"
		shift

	else
		echo \"$1 not recognized or not a valid input file\"
		shift
	fi
done

if [ \"$input\" = \"dvd://\" ]; then
	echo \"Using dvd as input\"

elif [ -n \"$input\" ]; then
	echo \"Using $input as source file\"
else
	echo \"Valid input not found\"
	read -p \"Source file: (or dvd://)\" input
	if [ -f \"$input\" ]; then
		echo \"$input found!\"
	elif [ \"$input\" = \"dvd://\" ]; then
		echo \"Using dvd\"
	else
		echo \"Valid input not found\"
		exit 0
	fi
fi

if [ -n \"$outfile\" ]; then
	echo \"Using $outfile as output file name\"
else
	read -p \"Output file name? (.avi will be appended): \" outfile
fi

if [ -n \"$bitrate\" ]; then
	echo \"Using a bitrate of $bitrate\"
else
	echo \"Select a video bitrate:\"
	echo \"350-400 kBit is about right for 320x240 video\"
	echo \"900-1100 kBit is about right for 640x480 video - myth does 1000\"
	echo \"1400-2000 kBit is about right for HD video - myth does 1500\"

	read -p \"Target video bitrate (kBit): \" bitrate
fi


if [ \"$framerate\" = \"ntsc\" ]; then
	echo \"Using NTSC framerate (29.97fps)\"
	framerate=30000/1001

elif [ -z \"$framerate\" ]; then
	read -p \"Select a framerate, greater than 10 (leave blank for NTSC): \" framerate
fi

if [ -z \"$framerate\" ]; then
	echo \"Using NTSC framerate (29.97fps)\"
	framerate=30000/1001
else	
	
	echo \"Using $framerate as framerate\"
fi

if [ \"$crop\" = \"n\" ]; then
	echo \"No crop being applied\"
	videofilter=\"-vf pullup,softskip,harddup\"

elif [ \"$crop\" = \"y\" ]; then
	echo \"Cropping to 720:400\"
	crop=\"720:400\"
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

elif [ -n \"$crop\" ]; then
	echo \"cropping to $crop\"
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

else
	read -p \"do you want to crop the video? (y)\" crop

	if [ \"$crop\" = \"y\" ]; then
	echo \"both width and height must be a multiple of 16\"
	read -p \"Enter the crop size, in width:height:x:y - 720:400 for tv-recorded video:\" crop
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

	else
	videofilter=\"-vf pullup,softskip,harddup\"

	fi
fi

if [ \"$audio\" = \"0\" ]; then
	echo \"Using video source file audio\"

elif [ -z \"$audio\" ]; then
	echo \"If you want to use audio other than that of the video file, specify it\"
	read -p \"Audio file (leave blank to use audio from source file): \" audio

elif [ -f $audio ]; then
	echo \"Using $audio for audio file\"
	audiooption=\"-audiofile \"$audio\"\"

else
	echo \"Audio file not found, using video files's audio\"

fi

if [ \"$input\" = \"dvd://\" ]; then

	titles=`lsdvd | grep Title: | wc -l`
	echo \"`expr $titles - 1` Titles\"
	count=1
	until [ $count -eq $titles ]
	do
		echo \"Title $count\"
		line=`expr $count + 1`
		time=`lsdvd | grep Title: | sed -n \"$line p\" | awk {'print $4'} | tr [=:=] \" \"`
		hr=`echo \"$time\" | awk {'print $1'}`
		min=`echo \"$time\" | awk {'print $2'}`
		hr=`expr \"$hr\" '*' 60`
		time=`expr $hr + $min`
		if [ \"$time\" -ge \"$length\" ]; then
			mencoder dvd://$count -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=1:threads=3 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -quiet -o /dev/null
			mencoder dvd://$count -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=2:threads=3 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -quiet -o \"$outfile-$count.avi\"
		else
			echo \"Title not long enough ($time minutes), skipping\"
		fi
		count=`expr $count + 1`
	done

else
	mencoder -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=1:threads=3 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -quiet -o /dev/null \"$input\"
	mencoder -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=2:threads=3 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -quiet -o \"$outfile.avi\" \"$input\"

#	alternate, codec, xvid.
#	mencoder -sws 9 $videofilter -oac mp3lame -ovc xvid -xvidencopts bitrate=$bitrate:pass=1 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -o /dev/null \"$input\"
#	mencoder -sws 9 $videofilter -oac mp3lame -ovc xvid -xvidencopts bitrate=$bitrate:pass=2 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -o \"$outfile.avi\" \"$input\"

fi
rm divx2pass.log
enjoy!

EDIT: updated the code per a bug a found... now it can handle files with spaces in the name as long as you pass it with quotation marks.

Also eliminated the search for a -ch switch that I was thinking about implementing at one point, but then didn't.
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

Overall, it looks good, and seems to work well. It looks like you already made it robust to filenames with spaces, which is the canonical newbie shell scripting mistake. In fact, by habit, I quote my variables by default, since it's required 50% of the time, safe 99% of the time, and that way I don't have to try to prove to myself that the variable can never contain spaces no matter what crazy things the user attempts to do. In fact, for the 1% of the time quoting isn't safe, I'd consider rewriting your code so that it's written in a way so that it is safe. I guess I feel that relying on the shell's word expansion inside of variables is a bit ugly. Here are some other suggestions that I'd recommend for making it more robust:

For your usage line, use $0, which contains the name of your program. For instance, I named the script something other than what you had anticipated me naming it in your usage line.

For parsing command line options, use getopts (which is built into any POSIX shell, including Bourne). It's much more robust than any ad hoc command line parsing code, and much easier once you learn it. In fact, it does almost all of the work of implementing the POSIX conventions itself. For instance, your program works with -f 30 but fails with -f30, which might surprise a Unix user. Or, for a more obscure case, if a user had an input file named '-f', he could delimit it by saying -- -f, which doesn't currently work with your ad hoc code.

Similarly, for robustness, you want to put a -- in front of where you say \"$input\" when you're calling mencoder.

The problem with having an interactive mode is that it's hard to do right. For instance, file names in Linux can have line breaks in them! (The only two characters not allowed are null bytes and (forward) slashes.) For instance, in bash, you can say, using ANSI C quoting (a bashism):
touch $'foo\\nbar'
And in properly written graphical programs like nautilus, the file name will be graphically rendered with a line break. Of course, you don't regularly run into files with line breaks, and fixing this is nontrivial, so you may want to just bite the bullet on this one. Otherwise, you would have to implement some sort of quoting system similar to the ones that shells use.

Typically you want to output errors to stderr. You can say:
echo 'Error: invalid foo' 1>&2
to do this (essentially redirecting stdout to stderr).

I'm a bit nervous about the \"rm divx2pass.log\" line. For instance, it seems like you're precluding someone using your script twice simultaneously in the same working directory, and to use a fancy technical name, it's a file system race condition. If you can, I'd use something like mktemp to create a uniquely named temporary file for you, and then pass the name of it in as an argument to mencoder to use for the pass log.

On a simpler note, it looks like you wrote this in a terminal that's wider than 80 chars. Generally, to be safe, for your output, you should assume that the terminal can be as small as 80 chars wide. In fact, most people still use 80 char width terminals, including me!

To put some motivation behind some of these changes, let's look at putting -- in front of \"$input\". Suppose that this script were used by a Web application to automatically encode uploaded videos, which isn't far-fetched at all. Without the --, a malicious user may be able to upload file with a specifically crafted name to introduce arbitrary command line options to mencoder. For instance, if they introduce an option that writes to a file and that takes a file name as an argument, the user could overwrite any file that apache has write access to on your server.

I feel like I've done nothing but say negative things, so I'll end on a positive note: nice use of grep/sed/awk and pipes in general. ;)

About Windows, you can install mingw or cygwin, which partially provides POSIX environments, including bash. Back when I still used Windows, it's what I used. It was really the only sane way to write non-trivial scripts then.
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Post by snoopy »

Thanks for the feedback.

I'm at work, so I'll have to look into some of them in more depth later, but here are a few thoughts:

1. You have a good point about the removal of the logfile. It poses a second problem that you didn't bring up: the point of the log file is that pass 1 creates it to dictate the way that the variable bitrate of the video is handled in pass 2. At best, two simultaneous instances will result in a logfile that mangles the video for one of the instances. I have a different (simpler) script that I have worked on that addresses this by creating a new folder, based on the input filename, in which the logfile resides. The assumption I made in that case was that you wouldn't invoke the script twice to act on the same filename- I should probably have it create a folder based on the input filename and some random value for robustness. It's nice to have it based on the filename because it makes it easier to identify what folder belongs to what process.

2. $0 sounds like an excellent idea.

3. I'll look at getopts.

4. I think I understand the --... that's what mencoder is looking for to denote the input.

5. I think I'll let the input filename issue with the interactive mode go. The user can pass really weird filenames to the script when he invokes it.

6. Good idea on the error outputs.

Thanks for the thoughts.

I'll probably have a new revision sometime soon.
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

For (1), is there a reason why you're hesitant to use mktemp? Here's some example code:

Code: Select all

#!/bin/sh
logname=`mktemp`
if [ $? -ne 0 ]; then
    echo \"Error: failed to create temp file.\" 1>&2
    exit 1
fi
echo \"$logname\"
rm \"$logname\"
To clarify (4), suppose you wanted to create a file named '--help', so you say:
touch --help
Well, as you might have guessed, this just displays the help for touch instead. So you must say:
touch -- --help
since the bare -- says \"everything after this is an operand, not an option, no matter what it looks like\". So for your script, if you have:
touch \"$file\"
you don't know what $file will be (it could be '--help'), so you have to use the -- again:
touch -- \"$file\"
It works for multiple operands too:
cp -- -a -b
will copy a file named '-a' to a file named '-b', despite the fact that these would normally be interpreted as options. Note that if you use getopts, then if a user tries to use a -- with your script, then it will be automatically handled for you too. So that's another reason to use getopts as well.
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

Something just occurred to me rereading my post. You'll find that getopts doesn't work with long options like '--help', since these aren't found on all Unices. But since '--help' is generally the only long option that most scripts want to support, I use a fairly trivial workaround. Here's a modified example based on the example given in the Linux man page for getopts that additionally displays help if you pass -h or --help:

Code: Select all

#!/bin/sh

print_help () {
    echo 'Help!'
    exit 0
}

print_usage () {
    printf \"Usage: %s: [-a] [-b value] args\\n\" $0 1>&2
    exit 2
}

aflag=
bflag=
while getopts ab:h-: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval=\"$OPTARG\";;
    h)    print_help;;
    -)    optcurrind=$(expr $OPTIND - 1)
          str=$(eval echo \"\\${$optcurrind}\")
          if [ \"$str\" = \"--help\" ]; then
              print_help
          elif [ \"$(expr substr \"$str\" 1 2)\" != \"--\" ]; then
              echo \"Illegal option --\" 1>&2
              print_usage
          else
              echo \"Illegal option --$OPTARG\" 1>&2
              print_usage
          fi;;
    ?)    print_usage;;
    esac
done
if [ ! -z \"$aflag\" ]; then
    printf \"Option -a specified\\n\"
fi
if [ ! -z \"$bflag\" ]; then
    printf 'Option -b \"%s\" specified\\n' \"$bval\"
fi
shift $(($OPTIND - 1))
printf \"Remaining arguments are: %s\\n\" \"$*\"
The 'h)' case should be self-explanatory if you're already familiar with getopts. The '-)' case isn't as self-evident, but it's an interesting exercise to see why this works. In the optstring that we pass getopts, we said '-:', which means that we take an '-' option that requires an argument. To see what this means, consider if we had said 'f:'. This means that someone could pass either:
-f foo
or
-ffoo
to our script.
But since our option is '-', they can pass:
-- foo
or
--foo.
But, if you recall, a bare -- already has special meaning, so the former example can be eliminated, leaving only:
--foo
You'll notice that for the '-)' case, I had to add my own logic to test if the argument is '--help' or not, as opposed to say, '--foo'. If it is help, then print help, otherwise print a usage error that emulates the kind that getopts prints.

You may wonder what the elif case is for. I'll leave this as an exercise to the reader. (Hint: What if I passed in '-a-help'? What do the POSIX conventions say about options that don't take arguments?)

Of course, if you're writing a C program, then you'll want to just use the getopt_long() function, which is what most system utilities on Linux use...
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Post by snoopy »

1. I'm unfamiliar with mktemp... I obviously just need to familiarize myself with it. Also, I need to figure out what to pass mencoder to make it use a different logfile. It's just homework.

2. I can see that you're filtering out cases where you don't have an \"--\" as the beginning of the argument. I don't know getopts, so I'm not sure how/when you'd end up with the case. Again, with a bit more research I can probably be more specific... once I get time to do so.
User avatar
Xamindar
DBB Admiral
DBB Admiral
Posts: 1498
Joined: Sun Jun 06, 2004 2:44 am
Location: California
Contact:

Re:

Post by Xamindar »

Jeff250 wrote:Something just occurred to me rereading my post. You'll find that getopts doesn't work with long options like '--help', since these aren't found on all Unices. But since '--help' is generally the only long option that most scripts want to support, I use a fairly trivial workaround. Here's a modified example based on the example given in the Linux man page for getopts that additionally displays help if you pass -h or --help:
Why the desire to make it so complicated? Just set it to display help on -h or if any invalid parameter is specified. That will include --help and any other invalid parameter. Usage is usually the same as help, is there any reason you want to separate them?
Why doesn't it work?
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

It was the original behavior, and I disagree with your assertion that, in general, usage == help. Help is a bit spammy, and I can't think of any command offhand that prints out the entire help when you just use the wrong syntax. Although I'm sure some exist, it just isn't typical, and moreover undesirable. Some I just tried out include grep, cp, mv, rm, and touch.

edit: If anything, if you don't want the extra 8 or so lines of code, I'd say just drop --help in favor of just -h. It might surprise some Linux users, but it would be more Unixy in general.
User avatar
fliptw
DBB DemiGod
DBB DemiGod
Posts: 6459
Joined: Sat Oct 24, 1998 2:01 am
Location: Calgary Alberta Canada

Post by fliptw »

the short switch for help is generally -?
User avatar
Xamindar
DBB Admiral
DBB Admiral
Posts: 1498
Joined: Sun Jun 06, 2004 2:44 am
Location: California
Contact:

Post by Xamindar »

Ok, I guess you are right about help. Most programs just tell you to type it with the option \"--help\" when you try an invalid command. But you are writing a shell script here. If anyone is really confused at what it does they can just look at it themselves. The rate you are going you might as well program it in C. The more complicated you make a bash script the more likely something will break later on down the line. And believe me, I have had plenty of old bash scripts that worked years ago fail on newer machines because of a missing semi-colon or some other syntax change.

fliptw, I think you might be talking about DOS. I don't think I have ever seen \"-?\" work with any linux commands.

By the way, some of my video files will not convert through mencoder. It gives the following error:

Code: Select all

[mpeg4 @ 0x906f7c0]timebase not supported by mpeg 4 standard
Could not open codec.
FATAL: Cannot initialize video driver.
Anyone now what causes that? Google has not been much help.
Why doesn't it work?
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

Whenever possible, I try to target a minimalist POSIX shell instead of bash to help prevent future breakage.

I agree that the workaround is a bit of a hack, and if you don't like it, you don't have to use it, but it's out there for anyone who would find it useful. Besides, everyone should be writing their programs in C anyways. ;)
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Re:

Post by snoopy »

Jeff250 wrote:Whenever possible, I try to target a minimalist POSIX shell instead of bash to help prevent future breakage.

I agree that the workaround is a bit of a hack, and if you don't like it, you don't have to use it, but it's out there for anyone who would find it useful. Besides, everyone should be writing their programs in C anyways. ;)
.....And now I have to ask myself: do I have time to teach myself C in the midst of a full time job and working on a graduate degree?
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Re:

Post by snoopy »

Xamindar wrote:By the way, some of my video files will not convert through mencoder. It gives the following error:

Code: Select all

[mpeg4 @ 0x906f7c0]timebase not supported by mpeg 4 standard
Could not open codec.
FATAL: Cannot initialize video driver.
Anyone now what causes that? Google has not been much help.
I'd need a bit more info. My first question is this, though: are you trying to crop? If so, the cropped size needs to be a multiple of 16 in either direction. I remember that the error that it throws when that isn't true is kinda cryptic, in that it doesn't directly complain about your cropping.
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Re:

Post by Jeff250 »

snoopy wrote:.....And now I have to ask myself: do I have time to teach myself C in the midst of a full time job and working on a graduate degree?
Just in case my humor was lost, although C is nice to know, a shell script is a much better tool for this job, as C would be unnecessarily verbose. ;)

You'd find though that you already know a lot of the C Unix API. A lot of the commands are just wrappers for functions, e.g. mktemp for mkstemp() or tmpfile(), or getopts for getopt().
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Post by snoopy »

I thought more about the error & my expreiences with cropping.

The multiples of 16 thing is inherent to the mpeg4 codec... thus it would choke equally on video supplied that wasn't sized to a multiple of 16 regardless of a crop filter. So, the problem might be that you're not cropping when you should apply a little mini crop (or a resize filter) to get the sizing to something that the mpeg4 codec likes.

Your other option is to pick a different codec. IIRC, all mpeg-based codecs impose the sizing constraint.
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Post by snoopy »

I'd like to learn C for real sometime.

Just probably not for another 5 years.
User avatar
snoopy
DBB Benefactor
DBB Benefactor
Posts: 4435
Joined: Thu Sep 02, 1999 2:01 am

Post by snoopy »

New and improved!

I added an option: -s, that enables scaling to 720i/p. Anything larger than that starts to take up huge amounts of hrive space, so I generally scale any 1080 stuff down to that.

Code: Select all

#!/bin/bash


audiooption=
outfile=
bitrate=
audio=
framerate=
crop=
length=\"2\"
help=
scale=


echo \"This script will compress/convert videos for you into xvid video & mp3 audio\"
echo \"Use -h for info on parameters this scripts can receive\"

while getopts a:b:c:f:o:hl:s option
do
	case $option in
	a)	audio=$OPTARG;;
	b)	bitrate=$OPTARG;;
	c)	crop=$OPTARG;;
	f)	framerate=$OPTARG;;
	o)	outfile=$OPTARG;;
	h)	help=$option;;
	l)	length=$OPTARG;;
	s)	scale=$option;;
	?)	echo \"$option is not a valid option\" 1>&2;;
	esac
done
shift `expr $OPTIND - 1`

if [ -n \"$help\" ]; then
echo \"Usage: $0 [-o outfile -b br -a audio -f fr -c crop -l m -y -s] [infile]\"
echo \"-o   Output file name, .avi will be appended\"
echo \"\"
echo \"-b   Video bitrate\"
echo \"      350-400 kBit is about right for 320x240 video\"
echo \"      900-1100 kBit is about right for 640x480 video - myth does 1000\"
echo \"      1400-2000 kBit is about right for HD video - myth does 1500\"
echo \"\"
echo \"-a   Optional audio to use instead of audio from video file; use \\\"0\\\" to\"
echo \"     specify none\"
echo \"\"
echo \"-f   Desired output framerate.  Expects a number, greater than 10.\"
echo \"     \\\"ntsc\\\" can be used, too\"
echo \"\"
echo \"-c   Desired cropping.  In width:height:x:y; where values may be omitted,\"
echo \"     height and width must be multiples of 16.  Use \\\"n\\\" for no cropping,\"
echo \"     use \\\"y\\\" for default 720:400. Takes precidence over scale filter\"
echo \"\"
echo \"-l   Minimum track length (dvd only) in minutes.  Causes the scripts to skip\"
echo \"     over any tracks under the specified length.  Defaults to 2 minutes.\"
echo \"     Will not be prompted for.\"
echo \"\"
echo \"-s   Enable scaling to 1280 x 720.  Recommended for 1080P source video.\"
echo \"     A yadif filter is applied to scaled video, as well.\"
echo \"\"
echo \"infile   Video file to be used.  Also accepts \\\"dvd://\\\".\"
echo \"\"
echo \"Any parameter not passed, with the exception of -l and -ch, will result\"
echo \"in a prompt for the needed value.\"
echo \"Note that if the infile and outfile are not passed, special characters usable\"
echo \"in the file names will be limited by the prompt.\"
exit 0
fi

input=\"$1\"

if [ \"$input\" = \"dvd://\" ]; then
	echo \"Using dvd as input\"

elif [ -f \"$input\" ]; then
	echo \"Using $input as source file\"
else
	read -p \"Source file: (or dvd://)\" input
	if [ -f \"$input\" ]; then
		echo \"$input found!\"
	elif [ \"$input\" = \"dvd://\" ]; then
		echo \"Using dvd\"
	else
		echo \"Valid input not found\" 1>&2
		exit 0
	fi
fi

if [ -n \"$outfile\" ]; then
	echo \"Using $outfile as output file name\"
else
	read -p \"Output file name? (.avi will be appended): \" outfile
fi

if [ -n \"$bitrate\" ]; then
	echo \"Using a bitrate of $bitrate\"
else
	echo \"Select a video bitrate:\"
	echo \"350-400 kBit is about right for 320x240 video\"
	echo \"900-1100 kBit is about right for 640x480 video - myth does 1000\"
	echo \"1400-2000 kBit is about right for HD video - myth does 1500\"

	read -p \"Target video bitrate (kBit): \" bitrate
fi


if [ \"$framerate\" = \"ntsc\" ]; then
	echo \"Using NTSC framerate (29.97fps)\"
	framerate=30000/1001

elif [ -z \"$framerate\" ]; then
	read -p \"Select a framerate, greater than 10 (leave blank for NTSC): \" framerate
fi

if [ -z \"$framerate\" ]; then
	echo \"Using NTSC framerate (29.97fps)\"
	framerate=30000/1001
else	
	
	echo \"Using $framerate as framerate\"
fi

if [ \"$crop\" = \"n\" ]; then
	echo \"No crop being applied\"
	videofilter=\"-vf pullup,softskip,harddup\"

elif [ \"$crop\" = \"y\" ]; then
	echo \"Cropping to 720:400\"
	crop=\"720:400\"
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

elif [ -n \"$crop\" ]; then
	echo \"cropping to $crop\"
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

else
	read -p \"do you want to crop the video? (y)\" crop

	if [ \"$crop\" = \"y\" ]; then
	echo \"both width and height must be a multiple of 16\"
	read -p \"Enter the crop size, in width:height:x:y - 720:400 for tv-recorded video:\" crop
	videofilter=\"-vf crop=$crop,pullup,softskip,harddup\"

	elif [ -n \"$scale\" ]; then
	echo \"enabling 1280 x 720 scaling.\"
	videofilter=\"-vf pullup,softskip,yadif,scale=1280:720:0:0:4,harddup\"

	else
	videofilter=\"-vf pullup,softskip,harddup\"

	fi
fi

if [ \"$audio\" = \"0\" ]; then
	echo \"Using video source file audio\"

elif [ -z \"$audio\" ]; then
	echo \"If you want to use audio other than that of the video file, specify it\"
	read -p \"Audio file (leave blank to use audio from source file): \" audio

elif [ -f $audio ]; then
	echo \"Using $audio for audio file\"
	audiooption=\"-audiofile \"$audio\"\"

else
	echo \"Audio file not found, using video files's audio\" 1>&2

fi

tempfile=`mktemp`

if [ \"$input\" = \"dvd://\" ]; then

	titles=`lsdvd | grep Title: | wc -l`
	echo \"`expr $titles - 1` Titles\"
	count=1
	until [ $count -eq $titles ]
	do
		echo \"Title $count\"
		line=`expr $count + 1`
		time=`lsdvd | grep Title: | sed -n \"$line p\" | awk {'print $4'} | tr [=:=] \" \"`
		hr=`echo \"$time\" | awk {'print $1'}`
		min=`echo \"$time\" | awk {'print $2'}`
		hr=`expr \"$hr\" '*' 60`
		time=`expr $hr + $min`
		if [ \"$time\" -ge \"$length\" ]; then
			mencoder dvd://$count -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=1:threads=3 -ofps $framerate -af pan=2:1:0:0:1:1:0:0:1:0.5:0.5:1:1,volnorm=2:1.0 -lameopts cbr:br=128 $audiooption -really-quiet -passlogfile $tempfile -o /dev/null
			mencoder dvd://$count -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=2:threads=3 -ofps $framerate -af pan=2:1:0:0:1:1:0:0:1:0.5:0.5:1:1,volnorm=2:1.0 -lameopts cbr:br=128 $audiooption -really-quiet -passlogfile $tempfile -o \"$outfile-$count.avi\"
		else
			echo \"Title not long enough ($time minutes), skipping\"
		fi
		count=`expr $count + 1`
	done

else
	mencoder -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=1:threads=3 -ofps $framerate -af pan=2:1:0:0:1:1:0:0:1:0.5:0.5:1:1,volnorm=2:1.0 -lameopts cbr:br=128 $audiooption -quiet -passlogfile $tempfile -o /dev/null -- \"$input\"
	mencoder -sws 9 $videofilter -oac mp3lame -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$bitrate:vpass=2:threads=3 -ofps $framerate -af pan=2:1:0:0:1:1:0:0:1:0.5:0.5:1:1,volnorm=2:1.0 -lameopts cbr:br=128 $audiooption -quiet -passlogfile $tempfile -o \"$outfile.avi\" -- \"$input\"

#	alternate, codec, xvid.
#	mencoder -sws 9 $videofilter -oac mp3lame -ovc xvid -xvidencopts bitrate=$bitrate:pass=1 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -passlogfile $tempfile -o /dev/null \"$input\"
#	mencoder -sws 9 $videofilter -oac mp3lame -ovc xvid -xvidencopts bitrate=$bitrate:pass=2 -ofps $framerate -af channels=2 -lameopts cbr:br=128 $audiooption -passlogfile $tempfile -o \"$outfile.avi\" \"$input\"

fi
rm $tempfile
exit 0
[EDIT] I totally posted code with a couple bugs. It's fixed now, I think.
User avatar
Jeff250
DBB Master
DBB Master
Posts: 6539
Joined: Sun Sep 05, 1999 2:01 am
Location: ❄️❄️❄️

Post by Jeff250 »

It looks much more robust now!

I say this only because I care, but it looks like there are still some quoting issues. ;) One is not quoting $tempfile. This is probably safe on any *nix box, but I wouldn't be surprised if, say, on Windows, cygwin's mktemp created temp files in one of the 'Documents and Settings' temp locations, whose path would contain spaces. It would certainly be fair game. Certainly since it's always safe to quote $tempfile, I'd do so.

A bit more subtle is something like $framerate or $videofilter, which would best be quoted too. You might think that since, any valid frame rate is one word, then the user gets what she deserves if she passes in a framerate with spaces in it. But consider again the example of a Web application automatically passing in user input to your script. Suppose that a user submitted through a form that the frame rate be '30 -sometempfile /var/www/index.php'. Now your site has no home page! $videofilter will require a bit of reworking, since you're actually relying on word expansion inside of that variable to split the '-vf' part from the rest. But, especially since it always starts with '-vf', there's really no reason to put this in the variable. So you can end up, instead of saying:
$videofilter
Say:
-vf \"$videofilter\"

Like I said before, I recommend getting in the habit of quoting variables by default and writing code such that quoting the variables is always OK (i.e. that doesn't depend on word expansion inside of the variable).
Post Reply