Skip to content

Instantly share code, notes, and snippets.

@parejkoj
Created June 24, 2015 20:34
Show Gist options
  • Save parejkoj/26666cc96f042631d89a to your computer and use it in GitHub Desktop.
Save parejkoj/26666cc96f042631d89a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
"""
Merge many jpegs into a movie using ffmpeg.
Since ffmpeg requires all the files to numbered sequentially, with no gaps,
we sequentially named symlinks to the files, run ffmpeg on those files, then
delete the temporary symlinks.
"""
import glob
import subprocess
import sys
import os
tempname = 'templinkXXX.jpg'
def make_links(files,tempdir):
"""Create sequentially-numbered symlinks to the files in a temp dir."""
if not os.path.exists(tempdir):
os.mkdir(tempdir)
links = []
for i,f in enumerate(files):
name = os.path.join(tempdir,tempname.replace('XXX','%05d')%(i+1))
os.symlink(os.path.abspath(f),name)
links.append(name)
print 'Made %d symlinks...'%len(links)
def cleanup(tempdir,noprompt):
"""Delete temporary files."""
delpath = os.path.join(tempdir,tempname.replace('XXX','*'))
files = glob.glob(delpath)
print 'Path to be deleted: %s'%delpath
if files:
if noprompt:
confirm='y'
else:
confirm=raw_input('Going to remove %d files from %s. Ok (y/n)?'%(len(files),tempdir))
if confirm == 'y':
for f in files:
#print 'Removing: ',f
os.remove(f)
else:
print 'Not removing files.'
else:
print 'No files found to delete.'
def make_movie(opts,output):
"""Create the movie with ffmpeg, from symlinks in tempdir."""
inpath = os.path.join(opts.tempdir,tempname.replace('XXX',r'%05d'))
if opts.full:
vcodec = 'prores'
profile = ['','']
pix = ['','']
else:
vcodec = 'libx264'
profile = ['-profile:v','main']
pix = ['-pix_fmt','yuv420p']
cmd = ['ffmpeg',
'-f','image2',
'-framerate',opts.framerate,
'-i',inpath,
'-r',opts.framerate,
'-s',opts.size,
'-vcodec',vcodec,
profile[0],profile[1],
pix[0],pix[1],
'-r',opts.framerate]
if not opts.full:
cmd.extend(['-crf','18'])
cmd.append(os.path.join('output/',output))
subprocess.check_call(r' '.join(cmd),shell=True)
#...
def main(argv=None):
if argv is None: argv = sys.argv[1:]
from optparse import OptionParser, OptionGroup
usage = '%prog [OPTIONS] DIR'
usage += '\nCreate a movie out using the jpegs in DIR.'
parser = OptionParser(usage)
parser.add_option('-o','--output',dest='output',default='INPATH-RATE.OUTEXT',
help='Name of the output video (output/INPATH-RATE.OUTEXT).')
#parser.add_option('--raw',dest='raw',default='""',
# help='Raw commands to pass on to ffmpeg directly (%default).')
parser.add_option('--ext',dest='ext',default='JPG',
help='Extension of the files to combine (%default).')
parser.add_option('--tempdir',dest='tempdir',default='temp',
help='Temporary directory to write symlinks to (%default).')
ffmpegGroup = OptionGroup(parser,"Options controlling ffmpeg",
"These options control the enconding settings on the final movie.")
ffmpegGroup.add_option('--outext',dest='outext',default='mp4',
help='Extension of the output file, mov recommended with --full (%default).')
ffmpegGroup.add_option('-r','--framerate',dest='framerate',default='30',
help='Frame rate of output video (%default).')
ffmpegGroup.add_option('-s','--size',dest='size',default='1920x1080',
help='Dimensions of output video (%default).')
ffmpegGroup.add_option('--full',dest='full',action='store_true',
help='Output a lossless compressed version, using Apple ProRes (%default)')
parser.add_option_group(ffmpegGroup)
parser.add_option('-y','--yes',dest='yes',default=False,action='store_true',
help='Assume "yes" in response to all prompts (%default).')
(opts,args) = parser.parse_args(args=argv)
if len(args) == 0:
parser.error('Must specify a directory!')
indir = args[0]
files = glob.glob(os.path.join(indir,'*.'+opts.ext))
if not files:
parser.error('No files with extension %s found in directory %s'%(opts.ext,indir))
tempout = os.path.split(indir)
if tempout[-1] == '':
tempout = os.path.split(tempout[0])[-1]
else:
tempout = tempout[-1]
output = opts.output.replace('INPATH',tempout).replace('RATE',opts.framerate).replace('OUTEXT',opts.outext)
if '.' not in output:
output += '.' + opts.outext
try:
make_links(files,opts.tempdir)
make_movie(opts,output)
except subprocess.CalledProcessError as e:
print 'Error running ffmpeg: %s'%e
sys.exit(e.returncode)
finally:
cleanup(opts.tempdir,opts.yes)
#...
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment