#!/usr/bin/perl -T
#===============================================================================
#
# FILE: video2shots.pl
#
# USAGE: ./video2shots.pl [options]
#
# DESCRIPTION: captures frames from video and creates an image
#
# REQUIREMENTS: ffmpeg imagemagick
# BUGS: probably lots!
# AUTHOR: slack_guy
# VERSION: 1.1
# CREATED: 22-05-2009
# REVISION: 1.1 23-05-2009
#===============================================================================
#
#==========================================================================
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#==========================================================================
#
use strict;
use warnings;
$ENV{'PATH'} = '/bin:/usr/bin';
use Getopt::Long;
use File::Temp qw/tempfile tempdir/;
my $OPTIONS = {
in_file => undef,
out_file => q|shots.png|,
temp_dir => tempdir( CLEANUP => 1 ),
start => 1,
frames => 30,
ratio => q|.05|,
cols => 3,
rows => q||,
border => 2,
size => q|sqcif|,
shadow => q||,
};
my $options = GetOptions(
'i|in|infile=s' => \$OPTIONS->{in_file},
'o|out|outfile=s' => \$OPTIONS->{out_file},
'start=i' => \$OPTIONS->{start},
'frames=i' => \$OPTIONS->{frames},
'ratio=s' => \$OPTIONS->{ratio},
'cols=i' => \$OPTIONS->{cols},
'rows=i' => \$OPTIONS->{rows},
'border=i' => \$OPTIONS->{border},
'size=s' => \$OPTIONS->{size},
'shadow=s' => \$OPTIONS->{shadow},
);
#####
# Required applications
chomp( my $ffmpeg = qx|which ffmpeg 2>/dev/null| );
chomp( my $montage = qx|which montage 2>/dev/null| );
#####
# Exit with usage message if:
# 1. there's no input file argument
# 2. file doesn't exist
# 3. ffmpeg is not in path
# 4. montage is not in path
# 5. temporary directory doesn't exist
if ( !$OPTIONS->{in_file}
|| !-f $OPTIONS->{in_file}
|| !-e $OPTIONS->{temp_dir}
|| $ffmpeg !~ /^\//x
|| $montage !~ /^\//x )
{
print _usage();
exit 0;
}
my $video_title = video_encode( { options => $OPTIONS } );
image_montage(
{
options => $OPTIONS,
title => $video_title,
}
);
exit 0;
sub image_montage {
my $params = shift;
my $_options = $params->{options}, my $title = $params->{title};
######
# Reads png files from temporary directory and produces an image
my $cmd_create_image = qq|$montage "$_options->{temp_dir}/*.png" |;
$cmd_create_image .= qq| -frame $_options->{border} |;
if ( $_options->{shadow} eq 'yes' ) { $cmd_create_image .= q| -shadow |; }
$cmd_create_image .= qq| -pointsize 7 -title "$title"|;
$cmd_create_image .= qq| -geometry +10+10 |;
$cmd_create_image .= qq| -tile $_options->{cols}x$_options->{rows} |;
$cmd_create_image .= qq| "$_options->{out_file}"|;
$cmd_create_image = untaint($cmd_create_image);
my $create_image = qx|$cmd_create_image 2>&1|;
return;
}
sub video_encode {
my $params = shift;
my $_options = $params->{options};
######
# Encodes video and returns a string as Title
my $cmd_encode = qq|$ffmpeg -i "$_options->{in_file}" |;
$cmd_encode .= qq| -ss $_options->{start}|;
$cmd_encode .= qq| -s $_options->{size} -vframes $_options->{frames} |;
$cmd_encode .= qq| -r $_options->{ratio} -y |;
$cmd_encode .= qq| "$_options->{temp_dir}/shot\%03d.png"|;
$cmd_encode = untaint($cmd_encode);
my $encode = qx|$cmd_encode 2>&1|;
my ( $hrs_range, $min_range, $sec_range );
if ( $encode =~ /Duration: (\d{2}):(\d{2}):(\d{2})/x ) {
( $hrs_range, $min_range, $sec_range ) = ( $1, $2, $3 );
}
return qq|$_options->{in_file}\n($hrs_range:$min_range:$sec_range)|;
}
sub _usage {
return <<"TEXT";
Usage:
\t $0 [options]
Options:
\t -[infile|in|i] video_filename
\t -[outfile|out|o] image_filename
\t -start nn (default: 1 second)
\t -frames nn (default: 30)
\t -ratio .nn (default: .05)
\t -cols n (default: 2)
\t -rows n (default: )
\t -border n (default: 2)
\t -size string (default: sqcif - more options from ffmpeg)
\t -shadow [yes|no] (default: no)
Requirements:
\t ffmpeg
\t montage (image magick)
Example:
\t $0 -start 10 -frames 20 -cols 3 -border 0 -size qcif -i video.mpg -o out.png
TEXT
}
sub untaint {
my $data = shift;
if ( !$data ) { return }
if ( $data =~ /^([-\@\w.\s"\/\%\*\(\)\:\+]+)$/mx ) {
$data = $1;
}
else {
die qq|Bad data in '$data'\n|;
}
return $data;
}