User Tools

Site Tools


tutorials:bash_scripting:part4

Building a script with functions that encodes and shrinks videos


The previous tutorials were on the short side. Heres one to make up for my brevity :^) If something is unclear check those other tutorials to see if it is explained there. For more insight into the use of any of the commands below check out the man page.

I often save movies and documentaries for viewing later and sometimes I want to burn them onto a DVD to share with family and friends. In order to burn the show I crop anything extra before and after plus remove any commercials in the middle. I use gopdit http://gopdit.ath.cx/ for my mpg-ps editing because its fast and simple. I have it set to save without encoding so I'm left with the basic mpg-ps (program stream) format that I saved the original show with.

I can use a a short one command script to save me the trouble of remembering (and typing) a very long mencoder command that encodes the video to a good DVD quality mpg-ps but the problem is that often I end up with a video that is too big to fit on a DVD and then I have to shrink the video before I can burn it. Below is an example of a movie before and after encoding.

bash-4.1$ ls -l
total 8796992
-rwx------ 1 rick root 5070583808 Jun 19 09:24 encoded-hidden-blade.mpg
-rwx------ 1 rick root 3937532284 Jan 12  2011 hidden-blade.mpg

In order to save the time and bother of encoding and shrinking separately I can do them both in the same script. Here's what it looks like

#!/bin/bash
####################################################################
# con2vob 
# Converts and shrinks an mpg-ps video. 
# Requires mencoder, vamps, bc and assumes system (not file system)
# block size = 1024 bytes (1kB).
####################################################################
# Variables
input_file=$1
dvd_size=4400000

# Test input.
test -n "$input_file"
	if [ $? -eq 1 ]; then
		echo -e "\nUsage: con2vob [input file]"
		exit
	fi
	
# Encode video.
mencoder -oac lavc -ovc lavc -of mpeg -mpegopts format=dvd:tsaf \
-vf scale=720:576,harddup -srate 48000 -af lavcresample=48000 \
-lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=9800:vbitrate=5000:keyint=15:vstrict=0:acodec=ac3:abitrate=192:aspect=16/9 -ofps 25 \
-o encoded-$input_file $input_file

# Shrink video (if necessary).
file_size=`ls -s encoded-$input_file  | awk -F " " '{ print $1 }'`
	if [ $file_size -gt $dvd_size ]; then 
		requant_factor=`echo "scale=2; $file_size/$dvd_size" | bc -l`
		vamps -v -E $requant_factor -a 1 < encoded-$input_file > shrunk-encoded-$input_file
	fi
 	
# End of script.

In this script the video is assigned to the variable input_file and then input_file is encoded with mencoder. If no video file was entered the user is given the correct usage of the script and the script exits.

I use mencoder (from MPlayer)for encoding my videos. The command options in the script are provided in the MPlayer manual 7.8. Using MEncoder to create VCD/SVCD/DVD-compliant files http://www.mplayerhq.hu/DOCS/HTML/en/menc-feat-vcd-dvd.html and work fine with anything I have saved on my two DVB-T cards from ABC and SBS. Note that MPlayer has been forked. There is now an MPlayer2 which does not include mencoder. The mencoder command can be substituded with an ffmpeg command.

The output of mencoder is named encoded-input_file and if its size is greater than my variable dvd_size the script will shrink the video using vamps.

The linux operating systems use a block size of 1024 bytes, i.e.1kb. I want the script to shrink the video if the video exceeds 4.2gb and 4,400,000kb equals 1.19gb, close enough for me.

I will need to compare the size of encoded-input_file to my dvd_size of 4400000 so first I need to get the size of encoded-input_file in kilobytes. I use ls -s to give me the file size in blocks which you now know is a good as asking for the size in kilobytes and use awk to print just the file size.

bash-4.1$ ls -l encoded-hidden-blade.mpg 
-rwx------ 1 rick root 5070583808 Jun 19 09:24 encoded-hidden-blade.mpg

bash-4.1$ ls -s encoded-hidden-blade.mpg 
4951744 encoded-hidden-blade.mpg

bash-4.1$ ls -s encoded-hidden-blade.mpg  | awk -F " " '{ print $1 }'
4951744

Bash will do simple math but for floating point math I have to use bc. The requant factor (read shrink factor) is found by dividing the file size by my set limit of 4,400,000kb which will result in a decimal. In this usage the equation is piped to bc using an echo command. The parameter scale=X cab be passed to bc to limit the number of decimal places printed to stdout.

bash-4.1$ bash-4.1$ file_size=`ls -s encoded-hidden-blade.mpg  | awk -F " " '{ print $1 }'` \
> echo "$file_size/4400000" | bc -l
1.12539636363636363636

bash-4.1$ file_size=`ls -s encoded-hidden-blade.mpg  | awk -F " " '{ print $1 }'` \
> echo "scale=2; $file_size/4400000" | bc -l
1.12

The vamps command syntax might seem a bit different. It uses redirection for input and output files.

vamps -e $requant_factor -a 1 < encoded-$input_file > shrunk-encoded-$input_file

The encoded-input_file is redirected to stdin with “<” and shrunk-encoded-input_file is redirected to stdout with “>”. The shrink factor is passed to vamps using “-e” The ausio stream is nominated using an “-a” flag.

Here are the results when con2vob was run and the movie hidden-blade has been shrunk.

bash-4.1$ ls -lSr
total 13196704
-rwx------ 1 rick root 3937532284 Jan 12  2011 hidden-blade.mpg
-rwx------ 1 rick root 4505305088 Jun 19 09:36 shrunk-encoded-hidden-blade.mpg
-rwx------ 1 rick root 5070583808 Jun 19 09:24 encoded-hidden-blade.mpg

bash-4.1$ ls -sSr -1
total 13196704
3845248 hidden-blade.mpg
4399712 shrunk-encoded-hidden-blade.mpg
4951744 encoded-hidden-blade.mpg

The video file shrunk-encoded-hidden-blade.mpg has been reduced to 4399712kb. One gigabyte is 1048576kb. Consequently shrunk-encoded-hidden-blade.mpg is 4399712kb / 1048576kb = 4.19589gb.


the same script with functions


In programming a function is a subroutine that contains a command or more usually a group of commands that is called by the script when required. It is intended to do just one thing. For me it is much easier to write a long script using functions. Each subroutine can be written and tested as a mini script and then be incorporated into the final script.

The syntax for a function in a bash script is:

function_name()

	{

	commands.........

	)

or alternatively:

function function_name

	{

	commands........

	}

Functions are called by invoking their names in a script and they must be listed in the script before they are called.

My con2vob script listed above is too short to require functions but it will serve as a good example of how to use functions.

Con2vob above comprises declaring variables and then three sections each beginning with a comment that explain what is happening. These comments will now become my function names and the sections of code will become the functions.

#!/bin/bash
####################################################################
# new-con2vob 
# Converts and shrinks an mpg-ps video. 
# Requires mencoder, vamps, bc and assumes system (not file system)
# block size = 1024 bytes (1kB).
####################################################################
# Variables
input_file=$1
dvd_size=4400000

test_input()
	{
	test -n "$input_file"
	if [ $? -eq 1 ]; then
		echo -e "\nUsage: con2vob [input file]"
		exit
	fi
	}
	
encode_video()
	{
	mencoder -oac lavc -ovc lavc -of mpeg -mpegopts format=dvd:tsaf \
	-vf scale=720:576,harddup -srate 48000 -af lavcresample=48000 \
	-lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=9800:vbitrate=5000:keyint=15:vstrict=0:acodec=ac3:abitrate=192:aspect=16/9 -ofps 25 \
	-o encoded-$input_file $input_file

	}

shrink_video()	# (if necessary).
	{
	file_size=$(ls -s encoded-$input_file  | awk -F " " '{ print $1 }')
	if [ $file_size -gt $dvd_size ]; then 
		requant_factor=$(echo "scale=2; $file_size/$dvd_size" | bc -l)
		vamps -v -E $requant_factor -a 1 < encoded-$input_file > shrunk-encoded-$input_file
	fi
	}
	
####################################################################	
# Now call the functions
	test_input
	encode_video
	shrink_video

# End of script.

In this script two variable declarations are listed first. The variable input_file must be declared before the three functions because that variable is used in all three functions. The variable dvd_size does not have to be declared until required in the function shrink_video but I like to list all all variables I may want to change at the beginning of the script where they are easy to find if I want to change them.

Note that I have changed the notation when I use stdout from a command to assign a value to a variable. The use of backticks, e.g. variable=`some command` for command substitution has been depreciated and the new way is to use “ $() ” like this variable=$(some command) Backticks can still be used but you may have mixed results and they should not be used when using nested commands.

It is possible to do math in Bash if the numbers involved are all integers. In such instances you must use double quotes.

bash-4.1$ result=`8-2` ;  echo "$result"
bash: 8-2: command not found

bash-4.1$ result=$(8-2) ;  echo "$result"
bash: 8-2: command not found

bash-4.1$ result=$((8-2)) ;  echo "$result"
6

In new-con2vob I have three distinct operations which are easily tucked away into three functions. They are called one after the other by listing their names after they have been declared. In bash, functions can only be called after they have been declared.

In Part 5 of this series I will present a script that encodes shrinks and burns to a DVD. It is a little more complicated so it is a good example to show how handy functions can be in both planning and writing a script.


There are gui programs out there which will do exactly what my con2vob script does but how many mouse clicks, how much time and how much memory does it take? I started scripted encoding and burning when I had a server for printer and file shares that also had a dvb card on board so saved movies and docos would be availble to any client on demand. Encoding takes a long time and is resource intensive but it didn't matter to a headless box with memory to spare and it could be done any time, day or night. It was just a matter of ssh'ing into the server and running a script.

I'm not running that server now but I still prefer to do this sort of thing with scripts. I only use it on saved DVB broadcasts so the settings can remain the same. I either run con2vob with nice and go about doing something else or else run it when I go to bed or walk out the door and let the machine shut itself down when it is finished.


Cheers!

tutorials/bash_scripting/part4.txt · Last modified: 2017/10/12 21:58 by 127.0.0.1