Created
June 24, 2015 20:34
-
-
Save parejkoj/26666cc96f042631d89a to your computer and use it in GitHub Desktop.
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
#!/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