+#!/usr/bin/perl
+
+# Prerequisites:
+# This script requires the following linux packages to be installed:
+# 1) dvgrab : apt-get install dvgrab
+# 2) mencoder : http://www.mplayerhq.hu/homepage/design7/dload.html
+# 3) transcode : vi sources.list; deb ftp://ftp.nerim.net/debian-marillat sarge main
+# apt-get install transcode
+
+use Getopt::Std;
+
+############################################################
+# GLOBALS
+############################################################
+$uid = "ajp";
+$gid = "ajp";
+#bitrate=3000 is a good choice to allow 2 90 minutes tapes to fit on one DVD
+#bitrate=4500 is a good choice to allow 2 60 minute tapes to fit on one DVD
+$bitrate = 4500;
+$encoded_dir = "xvid_clips";
+$merge_dir = "daily_clips";
+$default_tape_name = "home_video";
+$playlist_extension = "pls";
+$dvgrab = "/usr/local/bin/dvgrab";
+$mencoder = "/usr/bin/mencoder";
+$mplayer = "/usr/bin/mplayer";
+$avimerge = "/usr/bin/avimerge";
+$avisplit = "/usr/bin/avisplit";
+$xvid_suffix = "_xvid$bitrate";
+$dvgrab_log = "../dvgrab.log";
+$mencoder_log = "../encoding_errors.log";
+$merge_log = "../avimerge.log";
+$max_dv_clip_size_in_mb = "1023"; #I had to keep it at this size to avoid errors until avisplit fixed them.
+#$max_dv_clip_size_in_mb = "22000"; #avisplit still doesn't seem to do things right for large clips
+# max_tape_grab_time=7200 Allows Max time of 2 hours to allow a tape to be dumped (good for 90 min tape)
+# max_tape_grab_time=5400 Allows Max time of 1.5 hours to allow a tape to be dumped (good for 60 min tape)
+$max_tape_grab_time = 5400;
+$dvgrab_options = "--autosplit --timestamp --format dv2 --opendml --buffers 500";
+$encode_options_pass1 = "-mc 0 -oac mp3lame -lameopts cbr:cbr=128 -ffourcc XVID -fps 29.97 -ovc lavc ";
+$encode_options_pass1.= "-lavcopts vcodec=mpeg4:vhq:vbitrate=$bitrate:keyint=15:vpass=1 -vf pp=ci";
+$encode_options_pass2 = "-mc 0 -oac mp3lame -lameopts cbr:cbr=128 -ffourcc XVID -fps 29.97 -ovc lavc ";
+$encode_options_pass2.= "-lavcopts vcodec=mpeg4:vhq:vbitrate=$bitrate:keyint=15:vpass=2 -vf pp=ci";
+$mplayer_identify_options = "-vo null -ao null -identify -frames 0";
+
+$SIG{ALRM} = \&stop_dvgrab;
+$SIG{INT} = \&exit_now;
+$SIG{SUSP} = \&exit_now;
+
+############################################################
+# COMMAND LINE OPTIONS & PROCESSING
+############################################################
+getopts('hecrasvpmb:t:n:d:');
+if($opt_h) { &usage(); }
+
+sub usage() {
+ print "\n";
+ print "Usage: $0 <options>\n";
+ print " -e Encode captured raw video from camera.\n";
+ print " -c Capture raw video from camera.\n";
+ print " -m Merge videos clips found in the directory specified by -d <dir>. \n";
+ print " -p Create video playlists for each year & month of video clips in the directory specified by -d <dir>.\n";
+ print " -n <tape> Name of the tape to prepend to the front of each videoclip filename.\n";
+ print " -d <dir> Directory we want to encode. If not specified, <dir>=<tape>\n";
+ print " -a Use abbreviated names for merged video clips: yyyy-mm-dd.avi vs. <tape>.yyyy.mm.dd.avi\n";
+ print " -s Simulate Mode. Don't encode, just tell me what you would do.\n";
+ print " -h Show this usage\n";
+ print " -v Show verbosity\n";
+ print " -t <min> Time in minutes to grab from the camera (default "; print $max_tape_grab_time/60; print ")\n";
+ print " -b <mb> Max size each clip grabbed should be in MB (default $max_dv_clip_size_in_mb)\n";
+ print " -r Reboot the system once the script completes.\n";
+ print "\n";
+ print "File Structure:\n";
+ print " <dir>/<tape>*.avi MiniDV Raw AVI files\n";
+ print " <dir>/$encoded_dir/<tape>*.avi XVID compressed versions of the Raw AVI files\n";
+ print " <dir>/$merge_dir/<tape>*.avi All XVID clips taken on each day combined together\n";
+ print "\n";
+ exit 1;
+}
+
+if(!$opt_n && !$opt_d) { &usage(); }
+
+if(!$opt_c && !$opt_e && !$opt_p && $opt_m) {
+ if(!$opt_d) { &usage(); }
+ $opt_m = $opt_d;
+ $merge_only = 1;
+}
+else { $merge_only = 0; }
+
+if(!$opt_c && !$opt_e && !$opt_m && $opt_p) {
+ if(!$opt_d) { &usage(); }
+ $opt_p = $opt_d;
+ $playlist_only = 1;
+}
+else { $playlist_only = 0; }
+
+if(!$opt_d) {
+ system("mkdir -p $opt_n");
+ chdir "$opt_n";
+ $opt_d = $opt_n;
+} else {
+ if(! -d $opt_d) { die "-E- Could not cd to $opt_d\n"; }
+ chdir "$opt_d";
+ if(!$opt_n) { $opt_n = $default_tape_name; }
+}
+if($opt_t) { $max_tape_grab_time = $opt_t * 60; }
+if($opt_b) { $max_dv_clip_size_in_mb = $opt_b; }
+$video_directory = qx[pwd]; chomp($video_directory);
+
+print "\n-> Changed working directory to: $video_directory\n";
+
+############################################################
+# MAIN
+############################################################
+print "\n";
+
+if($opt_c) { &capture_videos(); }
+if($opt_e) { &encode_videos(); }
+if($opt_m) { &merge_videos(); }
+if($opt_p) { &create_playlists(); }
+
+# Fix the permissions
+print "-> Fixing permissions: chown -R $uid.$gid\n";
+system("chown -R $uid.$gid ../$opt_d");
+
+print "\n";
+if($opt_r) { &reboot_system(); }
+
+############################################################
+# SUBROUTINES
+############################################################
+
+sub exit_now {
+ print "-E- Interupt signal recieved. Exiting..\n";
+ &stop_dvgrab();
+ exit 1;
+}
+
+# A little routine to stop dvgrab. It hangs when it hits the end of a tape.
+sub stop_dvgrab {
+ my $prog = "dvgrab";
+ my @pids;
+ my $numkilled = 0;
+ @pids = `ps -efw | grep $prog | grep -v grep | grep -v $0`;
+ foreach $pid ( @pids ) {
+ $oldpid = $pid;
+ if( $pid =~ s/^\S*\s*(\d*).*$prog.*\n$/$1/ ) {
+ if($pid eq "") { next; }
+ print "$date : -I- Stopped dvgrab Process id: \"$pid\"\n";
+ system("kill -9 $pid");
+ $numkilled++;
+ }
+ }
+ sleep 5;
+}
+
+sub capture_videos {
+ if(! -f $dvgrab ){ die "-E- Missing required component: $dvgrab\n"; }
+ alarm($max_tape_grab_time);
+ $minutes = $max_tape_grab_time / 60;
+ print "-> Max tape grab time set to $minutes minutes\n";
+ print "-> Grabbing raw video from the camera\n";
+ print " Sending the status of grabbing the raw video to $dvgrab_log\n";
+ print " Press CTRL-C once to stop the video grab and move onto the next step\n";
+ print " Press CTRL-C three times to stop the video grab and exit\n";
+ print "\n";
+ if(!$opt_s) {
+ system("rm -f $dvgrab_log; touch $dvgrab_log; tail -f $dvgrab_log &");
+ print "$dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_\n";
+ system("$dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_ 2> $dvgrab_log");
+ }
+ else { print " $dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_ 2> $dvgrab_log\n"; }
+ alarm(0);
+ sleep 5; # Give some time to catch an interrupt if needed
+ print "-> Done grabbing video from camera\n\n";
+}
+
+sub encode_videos {
+ if(! -f $mencoder) { die "-E- Missing required component: $mencoder\n"; }
+ print "-> Creating high quality xvid copies of each video clip\n";
+
+ system("rm -f $mencoder_log");
+ system("touch $mencoder_log");
+
+ my $current_file = "";
+ my $current_output_file = "";
+ my $prev_output_file_dv = "";
+ my $prev_output_file_xvid = "";
+
+ print " Processing All Videos in Directory: $video_directory\n";
+
+ opendir(VIDEODIR, $video_directory) or die "-E- Could not open: $video_directory";
+ my @all_files = readdir VIDEODIR;
+ closedir(VIDEODIR);
+ @all_files = sort @all_files;
+ system "mkdir -p $encoded_dir";
+
+ foreach $current_file (@all_files) {
+ next if -d $current_file;
+ next if (!($current_file =~ /\.avi$/));
+ $current_output_file = $current_file;
+ $current_output_file =~ s/\.avi$/$xvid_suffix\.avi/;
+ if (-f "./$encoded_dir/$current_output_file") {
+ print " skipping $current_file - ./$encoded_dir/$current_output_file already exists.\n";
+ next;
+ }
+ print " $current_file -> ./$encoded_dir/$current_output_file\n";
+ if($opt_v) {
+ print "$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file\n";
+ print "$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file\n";
+ }
+ if(!$opt_s) {
+ $tempfile = `tempfile`; chomp($tempfile);
+ system("$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file 2>$tempfile");
+ system("$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
+ if(! -e "./$encoded_dir/$current_output_file") {
+ print "-WARNING- File could not be encoded. Attempting repair through avisplit. Encoding reattempt will follow.\n";
+ if(! -f $avisplit) { die "-E- Missing required component: $avisplit\n"; }
+ $split_size = $max_dv_clip_size_in_mb * 10;
+ system("$avisplit -s $split_size -i $current_file");
+ #print "$avisplit -s $split_size -i $current_file\n";
+ system("mv $current_file-0000 $current_file");
+ system("$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
+ system("$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
+ if(! -e "./$encoded_dir/$current_output_file") {
+ print "-E- Errors encountered encoding $current_file\n";
+ system("echo \"-> Errors encountered encoding $current_file\" >> $mencoder_log");
+ system("cat $tempfile >> $mencoder_log");
+ }
+ }
+ unlink "$tempfile";
+ sleep 2; # Give some time to catch an interrupt if needed
+ }
+ }
+
+ $current_output_file = "";
+ print "-> Done creating xvid copies\n\n";
+}
+
+sub merge_videos {
+ if(! -f $avimerge) { die "-E- Missing required component: $avimerge\n"; }
+ print "-> Now creating merged videos for each day\n";
+
+ system("rm -f $merge_log");
+ system("touch $merge_log");
+ system "mkdir -p $merge_dir";
+
+ if($merge_only) {
+ print "-> Merging all videos by day from this directory: $video_directory\n";
+ opendir(VIDEODIR, "$video_directory") or die "-E- Could not open: $video_directory";
+ } else {
+ print "-> Merging all videos by day from this directory: $video_directory/$encoded_dir\n";
+ opendir(VIDEODIR, "$video_directory/$encoded_dir") or die "-E- Could not open: $video_directory/$encoded_dir";
+ }
+ print "\n";
+
+ my @all_files = readdir VIDEODIR;
+ closedir(VIDEODIR);
+ @all_files = sort @all_files;
+ foreach $current_file (@all_files) {
+ next if -d $current_file;
+ next if (!($current_file =~ /\.avi$/));
+ next if (system("$mplayer $mplayer_identify_options $current_file 2>&1 | grep \"Missing video stream\" > /dev/null") == 0);
+ $current_output_file = $current_file;
+ $current_output_file =~ s/_[\-0-9]{8}\.avi$/\.avi/;
+ $current_output_file =~ s/_[\-0-9]{8}$xvid_suffix\.avi$/\.avi/;
+ if($opt_a && $current_output_file =~ /.*?(\d\d\d\d)\.(\d\d)\.(\d\d)/) {
+ $current_output_file = "$1-$2-$3.avi";
+ if( -f "./$merge_dir/$current_output_file" ) {
+ my $num = 1;
+ while(-f "./$merge_dir/$1-$2-$3.$num.avi") { $num++; }
+ $current_output_file = "$1-$2-$3.$num.avi";
+ }
+ }
+ if (!($prev_output_file eq $current_output_file) && !($prev_output_file eq "")) {
+ print " Creating merged video: ./$merge_dir/$prev_output_file_xvid\n";
+ if($opt_v) {
+ print " $avimerge -c -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge\n";
+ }
+ if(!$opt_s) {
+ system("$avimerge -c -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge >> $merge_log");
+ sleep 2; # Give some time to catch an interrupt if needed
+ }
+ $files_to_merge = "";
+ print "\n";
+ }
+ #store all the file names into one variable
+ if($merge_only) {
+ $files_to_merge .= " ./$current_file";
+ print " adding ./$current_file to merge list\n";
+ } else {
+ $files_to_merge .= " ./$encoded_dir/$current_file";
+ print " adding ./$encoded_dir/$current_file to merge list\n";
+ }
+ $prev_output_file = $current_output_file;
+ $prev_output_file_xvid = $current_output_file;
+ }
+
+ #Now merge the last one
+ if (!($current_output_file eq "")) {
+ print " Creating merged video: ./$merge_dir/$prev_output_file_xvid\n";
+ if($opt_v) {
+ print " $avimerge -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge\n";
+ }
+ if(!$opt_s) {
+ system("$avimerge -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge");
+ }
+ $files_to_merge = "";
+ print "\n";
+ }
+
+ print "-> Done creating merged videos for each day\n\n";
+}
+
+sub create_playlists {
+ print "-> Now creating playlists of merged videos for each month & year\n";
+
+ if($playlist_only) {
+ print "-> Creating playlists for each month & year of clips from this directory: $video_directory\n";
+ opendir(VIDEODIR, "$video_directory") or die "-E- could not open: $video_directory";
+ chdir "$video_directory";
+ } else {
+ print "-> Creating playlists for each month & year of clips from this directory: $video_directory/$merge_dir\n";
+ opendir(VIDEODIR, "$video_directory/$merge_dir") or die "-E- could not open: $video_directory/$merge_dir";
+ chdir "$video_directory/$merge_dir";
+ }
+
+ my @all_files = readdir VIDEODIR;
+ closedir(VIDEODIR);
+ @all_files = sort @all_files;
+
+ print " Removing all existing playlists from the directory\n";
+ system("rm *.$playlist_extension > /dev/null 2>&1");
+
+ foreach $file (@all_files) {
+ next if -d $file;
+ next if ($file !~ /\.avi$/ && $file !~ /\.mpg$/);
+ if($opt_a) { $file =~ /(\d\d\d\d)-(\d\d)-(\d\d)/; $year = $1; $month = $2; }
+ else { $file =~ /(\d\d\d\d).(\d\d).(\d\d)/; $year = $1; $month = $2; }
+ print " Adding $file to $year.$playlist_extension & $year-$month.$playlist_extension\n";
+ system("echo \"$file\" >> $year.$playlist_extension");
+ system("echo \"$file\" >> $year-$month.$playlist_extension");
+ }
+ chdir "$video_directory";
+}
+
+# Reboot the system if requested
+sub reboot_system {
+ print "-> Rebooting the system in 10 seconds... Press CTRL-C to abort...\n";
+ sleep 10;
+ system("sudo /sbin/reboot") if(!$opt_s);
+}
+