Skip to content

Instantly share code, notes, and snippets.

@m87h
Last active August 29, 2015 14:10
Show Gist options
  • Save m87h/67f955e10d48ec1afcda to your computer and use it in GitHub Desktop.
Save m87h/67f955e10d48ec1afcda to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
# ************************************************************
# * gifmaker - A tool for making animated GIFs from video
# files. This works as a wrapper around
# FFmpeg and ImageMagick's convert.
#
# For more details see:
# http://www.leshylabs.com/blog/dev/2013-08-04-Making_Animated_GIFs_from_the_Linux_Command_Line.html
# ************************************************************
#
# Copyright (c) 2013, Leshy Labs LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * The names names of the authors may not be used to endorse or
# promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS
# CONTRIBUTERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Changes
# 8/9/13 - Added some default optimizations to convert
use strict;
use File::Temp qw/ tempdir /;
my $CONFIG = {
ffmpeg => 'ffmpeg', # ffmpeg binary
convert => 'convert', # ImageMagick convert binary
output_file => 'out.gif', # Default file to write to
default_fps => 10, # Default frames per second
ffmpeg_args => '', # Extra arguments for ffmpeg
convert_args => '-layers removeDups -layers Optimize' # Extra arguments for convert
};
sub usage {
die "USAGE: $0 [input_file] [output_file] [frames_per_second]\n" .
" input_file - The input video file to convert (Required)\n" .
" output_file - The GIF file to be created (Default=$CONFIG->{output_file})\n" .
" frames_per_second - The GIF's frame rate. (Default=$CONFIG->{default_fps})\n\n" .
"ENVIRONMENT:\n" .
" FFMPEG_ARGS - Extra arguments for ffmpeg\n" .
" CONVERT_ARGS - Extra arguments for ImageMagick's convert command\n";
}
sub verify {
# Verify all parameters and settings before proceeding
my ($input_file, $output_file, $fps) = @_;
if (! -e $input_file) {
die "ERROR: Input file '$input_file' does not exist.\n";
}
elsif ($fps !~ /^\d{1,2}$/ || $fps == 0) {
die "ERROR: FPS value '$fps' is not valid. A number from 1 to 99 must be provided\n";
}
elsif ($output_file !~ /\.gif$/i) {
die "ERROR: Output file must use the '.gif' extension\n";
}
elsif (-e $output_file) {
die "ERROR: Output file '$output_file' already exists\n";
}
}
sub run_command {
my ($command) = @_;
print "*** $command\n";
system($command);
print "\n";
if ($? >> 8 != 0) {
die "ERROR: Command did not return a 0 exit status. Aborting.\n";
}
}
sub make_gif {
my ($input_file, $output_file, $fps) = @_;
my $temp_dir = tempdir(CLEANUP => 1);
my $real_output_file = $output_file;
my $delay = sprintf("%.0f", (100 / $fps)); # Delay value for convert in hundreths of second and rounded
# Protect some special characters
$output_file =~ s/([;\$\\`])/\\\1/g;
run_command("$CONFIG->{ffmpeg} $ENV{FFMPEG_INPUT_ARGS} -i $input_file " .
"$CONFIG->{ffmpeg_args} $ENV{FFMPEG_OUTPUT_ARGS} -r $fps $temp_dir/frame.\%05d.png");
run_command("$CONFIG->{convert} $CONFIG->{convert_args} -loop 0 $ENV{CONVERT_ARGS} " .
"-delay $delay $temp_dir/frame.*.png $output_file");
print "Animated gif saved to '$real_output_file'\n";
}
sub main {
my ($input_file, $output_file, $fps) = @ARGV;
if ($#ARGV < 0 || $#ARGV > 2) {
usage();
}
# If the arguments are not provided, set the default values
$fps ||= $CONFIG->{default_fps};
$output_file ||= $CONFIG->{output_file};
verify($input_file, $output_file, $fps); # Verify all parameters
make_gif($input_file, $output_file, $fps); # Create the animated GIF
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment