2008-01-13 11:44:38 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
# pic2mpg: Convert picture files to MPEG still frames
|
|
|
|
#
|
|
|
|
# Converts either a single picture file or all files in a
|
|
|
|
# given directory (recursively) to MPEG still frames.
|
|
|
|
#
|
|
|
|
# See the README file for copyright information and how to reach the author.
|
|
|
|
#
|
2012-01-08 13:29:01 +01:00
|
|
|
# $Id: pic2mpg 2.4 2012/01/08 13:27:17 kls Exp $
|
2008-01-13 11:44:38 +01:00
|
|
|
|
|
|
|
use File::Path;
|
2008-02-29 14:38:44 +01:00
|
|
|
use File::Spec;
|
2008-01-13 11:44:38 +01:00
|
|
|
use Getopt::Std;
|
2011-07-23 14:29:18 +02:00
|
|
|
use Image::ExifTool qw(:Public);
|
2008-01-13 11:44:38 +01:00
|
|
|
|
|
|
|
$Usage = qq{
|
|
|
|
Usage: $0 [options] picture-dir mpeg-dir
|
|
|
|
$0 [options] picture-file mpeg-file
|
|
|
|
|
2011-08-14 13:50:26 +02:00
|
|
|
Options: -f Force conversion
|
2008-02-02 11:34:43 +01:00
|
|
|
-h print Help
|
2012-01-08 13:29:01 +01:00
|
|
|
-o percent overscan in percent
|
2011-08-14 13:50:26 +02:00
|
|
|
-s size Screen size (WIDTHxHEIGHT, default is 1920x1080)
|
2008-01-13 11:44:38 +01:00
|
|
|
-v num Verbose (0=none, 1=list files, 2=detailed)
|
|
|
|
};
|
|
|
|
|
2012-01-08 13:29:01 +01:00
|
|
|
getopts("fho:s:v:") || die $Usage;
|
2008-01-13 11:44:38 +01:00
|
|
|
|
|
|
|
die $Usage if $opt_h;
|
|
|
|
|
|
|
|
$Force = $opt_f;
|
2012-01-08 13:29:01 +01:00
|
|
|
$Overscan = $opt_o || 0;
|
2011-08-14 13:50:26 +02:00
|
|
|
$Size = $opt_s || "1920x1080";
|
2008-01-13 11:44:38 +01:00
|
|
|
$Verbose = $opt_v;
|
|
|
|
|
|
|
|
$ListFiles = $Verbose >= 1;
|
|
|
|
$Detailed = $Verbose >= 2;
|
|
|
|
|
2011-08-14 13:50:26 +02:00
|
|
|
# Supported picture types:
|
|
|
|
|
|
|
|
%PICTYPES = (
|
|
|
|
bmp => 1,
|
|
|
|
gif => 1,
|
|
|
|
jpeg => 1,
|
|
|
|
jpg => 1,
|
|
|
|
png => 1,
|
|
|
|
pnm => 1,
|
|
|
|
tif => 1,
|
|
|
|
tiff => 1,
|
2008-01-13 11:44:38 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
# Command options:
|
|
|
|
|
|
|
|
die "$0: missing parameter\n" unless $ARGV[0] && $ARGV[1];
|
|
|
|
die "$0: file or directory not found: $ARGV[0]\n" unless -e $ARGV[0];
|
|
|
|
die "$0: source and destination must be different\n" if $ARGV[0] eq $ARGV[1];
|
|
|
|
|
2012-01-08 13:29:01 +01:00
|
|
|
$Extent = $Size;
|
|
|
|
if ($Overscan > 0) {
|
|
|
|
my ($x, $y) = $Size =~ /(.*)x(.*)/;
|
|
|
|
my $r = (100 + $Overscan) / 100;
|
|
|
|
$x = int($x * $r + 0.5);
|
|
|
|
$y = int($y * $r + 0.5);
|
|
|
|
$Extent = "${x}x$y";
|
|
|
|
}
|
|
|
|
|
2008-01-13 11:44:38 +01:00
|
|
|
# Convert a single file:
|
|
|
|
|
|
|
|
if (-f $ARGV[0]) {
|
|
|
|
die "$0: mixed file and directory ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -f $ARGV[1];
|
|
|
|
ConvertFile($ARGV[0], $ARGV[1]);
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
die "$0: mixed directory and file ('$ARGV[0]' <-> '$ARGV[1]')\n" unless !-e $ARGV[1] || -d $ARGV[1];
|
|
|
|
|
2008-02-29 14:38:44 +01:00
|
|
|
$PICDIR = File::Spec->rel2abs($ARGV[0]);
|
|
|
|
$MPGDIR = File::Spec->rel2abs($ARGV[1]);
|
2008-01-13 11:44:38 +01:00
|
|
|
|
|
|
|
# Convert pictures to mpegs:
|
|
|
|
|
|
|
|
chdir($PICDIR) || die "$PICDIR: $!\n";
|
|
|
|
|
2011-08-14 13:50:26 +02:00
|
|
|
@Pictures = `find -type f | sort`;
|
2008-01-13 11:44:38 +01:00
|
|
|
chomp(@Pictures);
|
|
|
|
|
|
|
|
for $pic (@Pictures) {
|
|
|
|
my $mpg = "$MPGDIR/$pic.mpg";
|
|
|
|
if ($Force || !-e $mpg || -M $mpg > -M $pic) {
|
|
|
|
(my $dir = $mpg) =~ s/\/[^\/]*$//;
|
|
|
|
mkpath($dir);
|
|
|
|
ConvertFile($pic, $mpg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Remove mpegs without pictures:
|
|
|
|
|
|
|
|
chdir($MPGDIR) || die "$MPGDIR: $!\n";
|
|
|
|
|
|
|
|
@Mpegs = `find -type f`;
|
|
|
|
chomp(@Mpegs);
|
|
|
|
|
|
|
|
for $mpg (@Mpegs) {
|
|
|
|
my $pic = "$PICDIR/$mpg";
|
|
|
|
$pic =~ s/\.mpg$//;
|
|
|
|
if (!-e $pic) {
|
|
|
|
print "removing $mpg\n";
|
|
|
|
unlink($mpg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Remove empty directories:
|
|
|
|
|
|
|
|
chdir($MPGDIR) || die "$MPGDIR: $!\n";
|
|
|
|
|
|
|
|
for ($i = 0; $i < 10; $i++) { # dirs might become empty when removing empty subdirs
|
|
|
|
@Dirs = `find -type d -empty`;
|
|
|
|
chomp(@Dirs);
|
|
|
|
last unless @Dirs;
|
|
|
|
|
|
|
|
for $dir (@Dirs) {
|
|
|
|
$dir = EscapeMeta($dir);
|
|
|
|
print "removing $dir\n";
|
|
|
|
!system("rm -rf $dir") || die "$dir: $!\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Actual file conversion:
|
|
|
|
|
|
|
|
sub ConvertFile
|
|
|
|
{
|
|
|
|
my ($Pict, $Mpeg) = @_;
|
2008-02-25 18:11:33 +01:00
|
|
|
(my $Type) = lc($Pict) =~ /\.([^\.]*)$/;
|
2011-08-14 13:50:26 +02:00
|
|
|
return if (!defined $PICTYPES{$Type});
|
2011-07-23 14:29:18 +02:00
|
|
|
my $Exif = ImageInfo($Pict);
|
|
|
|
my $Orientation = $$Exif{"Orientation"};
|
2011-12-04 12:50:00 +01:00
|
|
|
my ($Degrees) = $Orientation =~ /Rotate ([0-9]+)/;
|
2011-08-14 13:50:26 +02:00
|
|
|
my $Rotate = $Degrees ? "-rotate $Degrees" : "";
|
2011-07-23 14:29:18 +02:00
|
|
|
print "orientation = '$Orientation' -> rotation = $Rotate\n" if ($Detailed);
|
2008-01-13 11:44:38 +01:00
|
|
|
$Pict = EscapeMeta($Pict);
|
|
|
|
$Mpeg = EscapeMeta($Mpeg);
|
2011-07-23 14:29:18 +02:00
|
|
|
print "$Pict -> $Mpeg $Rotate\n" if $ListFiles;
|
2012-01-08 13:29:01 +01:00
|
|
|
my $Cmd = "convert $Pict -background '#000000' $Rotate -resize $Size -gravity center -extent $Extent ppm:- | "
|
2011-08-14 13:50:26 +02:00
|
|
|
. "ffmpeg -f image2pipe -vcodec ppm -i pipe:0 -an -vcodec libx264 -vpre baseline -s $Size -qscale 2 -f mpegts -y $Mpeg "
|
|
|
|
. ($Detailed ? "" : "2>/dev/null");
|
2008-01-13 11:44:38 +01:00
|
|
|
!system($Cmd) || die "$Cmd: $!\n";
|
|
|
|
$Cmd = "touch -r $Pict $Mpeg";
|
|
|
|
!system($Cmd) || die "$Cmd: $!\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub EscapeMeta
|
|
|
|
{
|
|
|
|
my $META = ' !"#$%&\'()*;<>?[\\]`{|}~';
|
|
|
|
my $s = shift;
|
|
|
|
$s =~ s/([$META])/\\$1/g;
|
|
|
|
return $s;
|
|
|
|
}
|