Created
November 29, 2011 19:41
-
-
Save dwf/1406134 to your computer and use it in GitHub Desktop.
A batch video-resizing script.
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 | |
# Copyright (c) 2009, David Warde-Farley | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions | |
# are met: | |
# 1. Redistributions of source code must retain the above copyright | |
# notice, this list of conditions and the following disclaimer. | |
# 2. 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. | |
# 3. The name of the author may not be used to endorse or promote products | |
# derived from this software without specific prior written permission. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. | |
"""Wrappers and a command line tool for batch-resizing with ffmpeg.""" | |
import argparse | |
import os | |
import re | |
import subprocess | |
__author__ = "David Warde-Farley" | |
__copyright__ = "Copyright 2011, David Warde-Farley / Universite de Montreal" | |
__license__ = "BSD" | |
__maintainer__ = "David Warde-Farley" | |
__email__ = "wardefar@iro" | |
_size_regex = re.compile(r'(\d+)x(\d+)') | |
def get_video_size(src_file, verbose=False): | |
""" | |
Determine the frame size of a video file using the ffmpeg | |
command line tool. | |
Parameters | |
---------- | |
src_file : str | |
The path to the source file | |
verbose : boolean, optional | |
If `True`, all ffmpeg output is seen on the command line. | |
Default is `False`. | |
Returns | |
------- | |
width : int | |
The width of the video stream. | |
height : int | |
The height of the video stream. | |
Notes | |
----- | |
Technically only looks for the first video stream in the | |
file, but most files should only have one. | |
""" | |
if not os.path.exists(src_file): | |
raise IOError("No such file '%s'" % src_file) | |
elif os.path.isdir(src_file): | |
raise IOError("'%s' is a directory" % src_file) | |
proc = subprocess.Popen(['ffmpeg', '-i', src_file], | |
stdout=subprocess.PIPE, | |
stderr=subprocess.STDOUT) | |
sizes = [] | |
for line in proc.stdout: | |
if verbose: | |
print line.rstrip() | |
tokens = line.split() | |
if "Stream" in tokens and "Video:" in tokens: | |
for token in tokens: | |
match = _size_regex.match(token) | |
if match: | |
width = int(match.group(1)) | |
height = int(match.group(2)) | |
return width, height | |
raise ValueError("No video stream found in file '%s'" % src_file) | |
def resize_video(src_file, dest_file, overwrite=True, | |
scale_factor=0.5, verbose=False): | |
""" | |
Resizes a video file with the ffmpeg command line tool. | |
Parameters | |
---------- | |
src_file : str | |
The path to the source video file. | |
dest_file : str | |
The destination path for the file. If the directory | |
portion does not exist, it will be created. | |
overwrite : boolean, optional | |
Overwrite `dest_file` if it exists. Defaults to `True`. | |
scale_factor : float, optional | |
The amount by which to scale each spatial dimension. | |
Defaults to 1/2, i.e. make the video approximately | |
a quarter of the resolution of the original. | |
verbose : boolean, optional | |
If `True`, all ffmpeg output is seen on the command line. | |
Default is `False`. | |
""" | |
dirname = os.path.dirname(dest_file) | |
if len(dirname) > 0 and not os.path.exists(os.path.dirname(dest_file)): | |
os.makedirs(os.path.dirname(dest_file)) | |
width, height = get_video_size(src_file, verbose) | |
if os.path.exists(dest_file): | |
if os.path.isdir(dest_file): | |
raise IOError("'%s' is a directory" % dest_file) | |
elif overwrite: | |
os.remove(dest_file) | |
else: | |
raise IOError("file exists: '%s'" % dest_file) | |
new_size = [int(scale_factor * width), int(scale_factor * height)] | |
# ffmpeg wants even dimensions. | |
if new_size[0] % 2 == 1: | |
new_size[0] += 1 | |
if new_size[1] % 2 == 1: | |
new_size[1] += 1 | |
cmd = ['ffmpeg', '-i', src_file, '-s', 'x'.join(map(str, new_size)), | |
dest_file] | |
if verbose: | |
stdout = None | |
else: | |
stdout = subprocess.PIPE | |
ret = subprocess.call(cmd, stdout=stdout, | |
stderr=subprocess.STDOUT) | |
if ret != 0: | |
raise OSError('Command %s failed with return code %d' % | |
(str(cmd), ret)) | |
def resize_dir(src_dir, dest_dir, extensions=('mpg', 'mpeg', 'avi'), | |
recurse=True, overwrite=True, scale_factor=0.5, verbose=False): | |
""" | |
src_dir : str | |
The path to the source directory. | |
dest_dir : str | |
The path to destination directory. If it does not | |
exist, it will be created. | |
extensions : sequence of strings, optional | |
The filename extensions to try to resize with ffmpeg. Defaults to | |
`('mpg', 'mpeg', 'avi')`. | |
recurse : boolean, optional | |
Recursively convert videos in subdirectories. Defaults to | |
`True`. | |
overwrite : boolean, optional | |
Overwrite destination files if they exist. Defaults to `True`. | |
scale_factor : float, optional | |
The amount by which to scale each spatial dimension. | |
Defaults to 1/2, i.e. make the video approximately | |
a quarter of the resolution of the original. | |
verbose : boolean, optional | |
If `True`, all ffmpeg output is seen on the command line. | |
Default is `False`. | |
""" | |
files = os.listdir(src_dir) | |
for fn in files: | |
if recurse and os.path.isdir(fn): | |
resize_dir(fn, os.path.join(dest_dir, os.path.basename(fn)), | |
extensions, overwrite, scale_factor) | |
else: | |
basename = fn | |
fn = os.path.join(src_dir, fn) | |
if basename.split('.')[-1].lower() in extensions: | |
dest_fn = os.path.join(dest_dir, basename) | |
print "Resizing %s to %s..." % (fn, dest_fn) | |
resize_video(fn, dest_fn, overwrite, scale_factor, verbose) | |
def make_parser(): | |
parser = argparse.ArgumentParser(description="batch resize videos with " | |
"ffmpeg") | |
parser.add_argument('src_dir', action='store', type=str, | |
help='Path to source directory') | |
parser.add_argument('dest_dir', action='store', type=str, | |
help='Path to destination directory (created if ' | |
'non-existent)') | |
parser.add_argument('-v', '--verbose', action='store_true', | |
help='Display all ffmpeg output') | |
parser.add_argument('-s', '--scale-factor', action='store', | |
type=float, default=0.5, | |
help='Amount by which to scale the video') | |
parser.add_argument('-N', '--no-overwrite', action='store_true', | |
help='Don\'t overwrite existing files') | |
parser.add_argument('-n', '--no-recurse', action='store_false', | |
help='Don\'t recurse on sub-directories') | |
return parser | |
def main(): | |
parser = make_parser() | |
args = parser.parse_args() | |
if not os.path.exists(args.src_dir): | |
parser.error("specified source '%s' does not exist") | |
try: | |
resize_dir(args.src_dir, args.dest_dir, | |
recurse=not args.no_recurse, | |
overwrite=not args.no_overwrite, | |
scale_factor=args.scale_factor, | |
verbose=args.verbose) | |
except IOError, e: | |
parser.error(e.message) | |
except OSError, e: | |
parser.error(e.message) | |
if __name__ == "__main__": | |
main() |
I want to batch resize videos from smaller dimensions to larger dimensions
Don't ever do this. You will make the file bigger without improving quality in the slightest. You are better off playing the original file and scaling it with your player.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I would like to know this too...
I want to batch resize videos from smaller dimensions to larger dimensions, I think ffmpeg is better for these kind of thinks, usually I used handbrake and vidcoder, but it encoded video to lower quality...