PVR Cron

For about a year now, I’ve been playing around with my digital tuner card. It wasn’t until I turned off the cable that I have a need to use it. Using some cool linux tools, I’ve made a script to record HDTV broadcasts to my computer. It is a work in progress, but here’s what I’ve got so far.

The Tuner Card
The tuner card is DVB based on a Conextant chipset, so the first step was to get my kernel to make the card usable. A quick check will show if the driver is loaded:

dmesg | grep dvb

The Tools Required

  • dvb-atsc-tools
  • azap
  • ffmpeg

Channel Scan
Scan for channels using:

dvbscan /usr/share/dvb/atsc/us-ATSC-center-frequencies-8VSB > ~/.azap/channels.conf

Edit the file ~/.azap/channels.conf to make sure the channel names are correct. Your base frequencies file may be in a different location, but it is usually under /usr/share.

Iteration 1: Crontab Recording
At first, I used only the crontab to record. Here’s an example:

24 12 * * * /usr/bin/azap -c /home/dvr/.azap/channels.conf -r WRDW-HD
25 12 * * * /bin/cat /dev/dvb/adapter0/dvr0 > /data/dvr/young-restless.mpeg
35 13 * * * /usr/bin/pkill cat
36 13 * * * /usr/bin/pkill azap
37 13 * * * /usr/bin/ffmpeg -i /data/dvr/young-restless.mpeg -s 1024x476 -vcodec libxvid -b 1600000 -acodec copy /data/dvr/y-r-friday.avi

This is a very ugly solution with lots of cracks. For instance, if I were running cat from a console when /usr/bin/pkill cat were running, it would die. Heaven forbid another processes is using cat when that runs. Also, I had to change the name of the ffmpeg output file every day.

Iteration 2: Cronable Perl Script
This script does pretty much the same thing as the above 4 lines in the crontab does. This means you don’t have to write 4 lines in the crontab for each recording, just 1 line. Also, the file name is appended with the date in yyyy-mm-dd format.


#Does someone need a reminder?
if ( $#ARGV != 2 ) {
print “Usage:\n”;
print “record.pl \n”;

#Creates a random 16 character (a-z) string
sub randstr {
my @chars=(‘a’..’z’);
my $res = “”;
for(my $i=0;$i<16;$i++) {
$res .= $chars[rand($#chars)];
return $res;

#Grab the command line args
my ( $channel, $length, $finalFileName ) = @ARGV;

#Temporary mpeg filename
my $tempFileName = randstr();

#Add date to final filename
$finalFileName .= “-“.`date +%Y-%m-%d`;
$finalFileName =~ s/\n//;

#Start Azap in the background
print “Starting azap\n”;
system( “/usr/bin/azap -c /home/barry/.azap/channels.conf -r $channel >/dev/null 2>/dev/null &” );
sleep 5;

#Start cat in the background
print “Starting cat\n”;
system( “/bin/cat /dev/dvb/adapter0/dvr0 > /data/dvr/$tempFileName.mpeg &” );

#Sleep the required seconds for the show to record
print “Recording for “.(60*$length).” seconds…\n”;
sleep 60*$length;

#TODO: Remove pkill, as it may cause problems
print “Killing cat and azap.\n”;
`pkill cat`;
`pkill azap`;

#Resize & Encode to XVID using ffmpeg
#ffmpeg sometimes stops working b/c of bad mpeg data
#TODO: Replace with mencoder
print “Encoding…\n”;
`/usr/bin/ffmpeg -i /data/dvr/$tempFileName.mpeg -s 1024×476 -vcodec libxvid -b 1600000 -acodec copy /data/dvr/$finalFileName.avi`;

#Remove the temporary mpeg file
`rm $tempFileName.mpeg`;

print “Done!\n”;

I know it’s not the most elegant of perl scripts, but it gets the job done. Here’s a sample cron:

25 12 * * * /home/dvr/record.pl WRDW-HD 70 young-restless

As you can see from the TODO comments, I continue to tinker with the script. When I make a good development, I’ll post it. If you have any suggestions, feel free to post a comment or contact me.

Cron Jobs

Cron jobs are the Linux/UNIX world’s scheduler. Many people, myself included, have a hard time understanding crons. After all, setting one up is very simple, if you know what you are doing.

The Crontab
crontab” is the utility you use to setup a cron job. Type “crontab -e” from the command prompt to edit your list of crons. It’s just a textfile with a special format. Each non-blank line is a cron job. The format of each line is 6 space separated fields. Here is what each field defines:

  1. Minute
  2. Hour (Military Time)
  3. Day of the Month
  4. Month
  5. Day of the Week (0 is Sunday, 1 is Monday, etc.)
  6. Program to run w/any arguments (may include spaces)

The Date/Time fields may be a specific number, a “*” meaning anything, a “*/x” meaning every x hours/minutes/etc., or a comma separated list of any or all of the above.

So, if you wanted a certain program to run every 30 minutes, you could say it any of these ways:

*/30 * * * * /path/to/program arg1 arg2
0,30 * * * * /path/to/program arg1 arg2
12,42 * * * * /path/to/program arg1 arg2

Use Absolute Paths
If you are writing a script to run as a cron, you must make sure your script uses full paths or it will throw an error. Yes, the script will execute without error when run from the command line, but the cron will crap out.


0 2 1 * * /home/apache/rotate-logs.sh

Runs the rotate-logs.sh script the first of every month at 2AM.

0 17 * * * /home/oddjob/send-margarita-time-email.sh

Runs the send-margarita-time-email.sh script at 5PM every day.

0 17 * * 1,2,3,4,5 /home/oddjob/send-margarita-time-email.sh

Runs the send-margarita-time-email.sh script at 5PM Monday – Friday.

0 4 * * 1 /usr/sbin/mysql_dump > /backup/mysql/backup-`date "+%Y-%m-%d"`

Performs a weekly backup of the mysql database Mondays at 4AM.

0 0 1,15 * * /home/accounting/payroll.pl

Runs payroll.pl on the first and fifteenth of every month at 12 midnight.

Points to remember

  • Use full paths in scripts
  • The crontab is only for the current user