Created
April 16, 2015 12:19
-
-
Save mskrajnowski/6540217bc70eab5b7c8f to your computer and use it in GitHub Desktop.
Video to GIF conversion script using ffmpeg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Video to GIF conversion script using ffmpeg | |
based on http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html | |
""" | |
import argparse | |
import datetime | |
import os | |
import subprocess | |
import tempfile | |
def parse_timedelta(string): | |
error_msg = ( | |
u'{!r} is not a valid time, ' | |
u'must be a number in seconds, or in hh:mm:ss[.xxx] form' | |
.format(string) | |
) | |
parts = tuple(reversed(string.split(u':'))) | |
if len(parts) > 3: | |
raise argparse.ArgumentTypeError(error_msg) | |
try: | |
hours = int(parts[2]) if len(parts) > 2 else 0 | |
minutes = int(parts[1]) if len(parts) > 1 else 0 | |
seconds = float(parts[0]) | |
except ValueError: | |
raise argparse.ArgumentTypeError(error_msg) | |
return datetime.timedelta( | |
hours=hours, | |
minutes=minutes, | |
seconds=seconds, | |
) | |
def format_timedelta(delta): | |
total_seconds = delta.total_seconds() | |
hours = int(total_seconds / 3600) | |
minutes = int(total_seconds % 3600 / 60) | |
seconds = total_seconds % 60 | |
return '{:02d}:{:02d}:{:02.3f}'.format(hours, minutes, seconds) | |
def parse_args(): | |
parser = argparse.ArgumentParser( | |
description='Converts video to .gif using ffmpeg', | |
) | |
parser.add_argument( | |
'files', | |
nargs='+', | |
type=argparse.FileType('r'), | |
help='video files to convert' | |
) | |
parser.add_argument( | |
'--filters', | |
help='ffmpeg filters to apply', | |
default='fps=10', | |
) | |
parser.add_argument( | |
'--start', | |
help='start time', | |
type=parse_timedelta, | |
) | |
end_time_group = parser.add_mutually_exclusive_group() | |
end_time_group.add_argument( | |
'--end', | |
help='end time', | |
type=parse_timedelta, | |
) | |
end_time_group.add_argument( | |
'--duration', | |
help='maximum duration', | |
type=parse_timedelta, | |
) | |
return parser.parse_args() | |
def convert(file, args): | |
path, ext = os.path.splitext(file.name) | |
out_path = u'{}.gif'.format(path) | |
print u'converting {} -> {}'.format(file.name, out_path) | |
with tempfile.NamedTemporaryFile(suffix='.png') as palette: | |
ff_palette_cmd = ['ffmpeg', '-v', 'warning'] | |
ff_convert_cmd = ['ffmpeg', '-v', 'warning'] | |
if args.start is not None: | |
option = ('-ss', format_timedelta(args.start)) | |
ff_palette_cmd.extend(option) | |
ff_convert_cmd.extend(option) | |
if args.end is not None: | |
option = ('-to', format_timedelta(args.end)) | |
ff_palette_cmd.extend(option) | |
ff_convert_cmd.extend(option) | |
if args.duration is not None: | |
option = ('-t', format_timedelta(args.duration)) | |
ff_palette_cmd.extend(option) | |
ff_convert_cmd.extend(option) | |
ff_palette_cmd.extend(('-i', file.name)) | |
ff_convert_cmd.extend(('-i', file.name)) | |
ff_palette_cmd.extend(( | |
'-vf', '{},palettegen'.format(args.filters or ''), | |
'-y', palette.name, | |
)) | |
ff_convert_cmd.extend(( | |
'-i', palette.name, | |
'-lavfi', '{} [x]; [x][1:v] paletteuse'.format(args.filters or ''), | |
'-y', out_path, | |
)) | |
code = subprocess.call(ff_palette_cmd) | |
if code == 0: | |
subprocess.call(ff_convert_cmd) | |
def main(): | |
args = parse_args() | |
for file in args.files: | |
convert(file, args) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment