#!/usr/bin/perl

# Movies In One - Steven Goodwin 2010
# Released under the GNU GPL, v2

# This is a quirky script which takes a movie file, and renders the whole 
# thing into a single animated gif where each frame comprises a montage of 
# 20 frames from the movie.

my $imageDirectory = "/net/media/movies/s/asd/frames";
my $filename = "/net/media/movies/s/A.Scanner.Darkly.avi";
# Number of frames in each montage
my $width = 5;
my $height = 2;
# Number of frames
my $montageFrames = 20;
my $animationSpeed = 40;
my $sourceImageFrameCount;
my $filePrefix = "";
my $firstFileIndex;

# Find duration in frames
my $cmd = `mplayer -vo null -ao null -frames 0 -identify "$filename" 2>/dev/null | sed -ne '/^ID_/ { s/[]()|&;<>\`'"'"'\\!$" []/\\&/g;p }'`;

# ID_LENGTH=6022.56 is number of frames
$cmd =~ /ID_LENGTH=(.*)\n/sg;
my $duration = $1;


# Rip frames
my $imagesPerMontageFrame = $width * $height;
my $totalFrames = $imagesPerMontageFrame * $montageFrames;
my $frameGap = $duration / $totalFrames;

my $frame = 0;
my $currentTime = 0;

if (0) {
	print "Capturing $totalFrames frames...\n";
	while($frame < $totalFrames) {
		my $targetFile = "$filePrefix$frame.png";
		if (! -e $targetFile) {
			`ffmpeg -i $filename -ss $currentTime -f image2 -vframes 1 -vcodec png grab%d.png >/dev/null 2>/dev/null`;
			`mv grab1.png "$targetFile"`;
		} 
   
		$currentTime += $frameGap;
		++$frame;

		if (($frame % 100) == 0) {
				print "Now at $frame...\n";
		}
	}
	$sourceImageFrameCount = $totalFrames;
	$firstFileIndex = 0;
} else {
	# When the images have already been ripped, we need to skip
	# over some, since there may be more frames than needed.
	# (which happens when every frame is ripped, for speed, but
	# not ultimately needed.)
	my $lastImage = `ls $imageDirectory | cut -d " " -f 9 | sort -n -r| head -n 1`;
	$lastImage =~ /^(.*)\./;
	$sourceImageFrameCount = $1;
	$firstFileIndex = 1;
}

# Determine montage size
my $offsetFrame = 0;
my $montageList = "";
my $rescaledImageIndex;

for (my $currentMontageFrame=0;$currentMontageFrame<$montageFrames;++$currentMontageFrame) {
	$montageCommand = "montage -tile ${width}x$height ";
	# $montageCommand .= " -background #336699 -geometry +2+2   -border 5 ";
 
	# Image would run, in a 2 montage frame version,
	# M1: 0  2  4  6     M2: 1  3  5  7
	#     8 10 12 14         9 11 13 15
	# i.e. += montageFrames
	my $img=$offsetFrame;
	for(my $i=0;$i<$imagesPerMontageFrame;++$i) {
		# $img is currently the 'logical' image index. If the resultant
		# animated montage has 100 frames, $img ranges between 0 and 99
		# If there's more frames, then we skip. I use a computed ratio
		# for each frame because - although slower - gives better
		# accuracy and better spread of used frames.
		$rescaledImageIndex = ($img * $sourceImageFrameCount) / $totalFrames;
		$rescaledImageIndex = int($rescaledImageIndex);
		$rescaledImageIndex += $firstFileIndex;

		$montageCommand .= " $imageDirectory/$filePrefix$rescaledImageIndex.png";
		$img += $montageFrames;
	}

	print "Building montage $currentMontageFrame.png...\n";
	my $montageFilename = " $imageDirectory/montage$currentMontageFrame.png";
	$montageCommand .= $montageFilename;
	$montageList .= $montageFilename;

	`$montageCommand`;
	++$offsetFrame;
}

# Combine all montages in a complete clip
$filename=~/.*\/(.*?)$/;
my $stubname = $1;
print "Saving $stubname...\n";
`convert -delay $animationSpeed $montageList -loop 0 $stubname.gif`;

# Simples!